changeset 25500:24d035ad12cd

propagate from branch 'im.pidgin.pidgin' (head cf74b964fddd2185233987f6e7743623bf21efb8) to branch 'im.pidgin.pidgin.yaz' (head e611312336fc5f025cfd6ef561ee9318827cae80)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Thu, 07 Jun 2007 07:32:06 +0000
parents a8d6f071eb6d (diff) 6df81f2760a2 (current diff)
children be1c61b5f30d
files configure.ac libpurple/conversation.c libpurple/protocols/bonjour/dns_sd.c libpurple/protocols/bonjour/dns_sd.h libpurple/protocols/jabber/jabber.c libpurple/util.c
diffstat 51 files changed, 1533 insertions(+), 1053 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Jun 05 16:04:04 2007 +0000
+++ b/COPYRIGHT	Thu Jun 07 07:32:06 2007 +0000
@@ -89,6 +89,7 @@
 Jeramey Crawford
 Michael Culbertson
 Steven Danna
+Chris Davies
 Martijn Dekker
 Vinicius Depizzol
 Philip Derrin
--- a/ChangeLog.win32	Tue Jun 05 16:04:04 2007 +0000
+++ b/ChangeLog.win32	Thu Jun 07 07:32:06 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/config.h.mingw	Tue Jun 05 16:04:04 2007 +0000
+++ b/config.h.mingw	Thu Jun 07 07:32:06 2007 +0000
@@ -344,9 +344,6 @@
 /* Define to the version of this package. */
 /* #define PACKAGE_VERSION "2.0.0dev" */
 
-/* Define to make assertions fatal (useful for debugging). */
-/* #define PURPLE_FATAL_ASSERTS 1 */
-
 /* Define if plugins are enabled. */
 #define PURPLE_PLUGINS 1
 
--- a/configure.ac	Tue Jun 05 16:04:04 2007 +0000
+++ b/configure.ac	Thu Jun 07 07:32:06 2007 +0000
@@ -569,14 +569,15 @@
 AC_ARG_ENABLE(gstreamer,
 	[AC_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])],
 	enable_gst="$enableval", enable_gst="yes")
-PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], , [
-	AC_MSG_RESULT(no)
-	enable_gst="no"
-])
 if test "x$enable_gst" != "xno"; then
-	AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
-	AC_SUBST(GSTREAMER_CFLAGS)
-	AC_SUBST(GSTREAMER_LIBS)
+	PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
+		AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
+		AC_SUBST(GSTREAMER_CFLAGS)
+		AC_SUBST(GSTREAMER_LIBS)
+	], [
+		AC_MSG_RESULT(no)
+		enable_gst="no"
+	])
 fi
 
 dnl #######################################################################
--- a/doc/funniest_home_convos.txt	Tue Jun 05 16:04:04 2007 +0000
+++ b/doc/funniest_home_convos.txt	Thu Jun 07 07:32:06 2007 +0000
@@ -471,4 +471,6 @@
 14:08 <elb> "... yes"
 14:08 <elb> I mean, what do you say
 14:08 <Robot101> elb: was their nick "idi"?
-          
+
+19:23 <-- elb has quit (K-lined)
+
--- a/libpurple/conversation.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/conversation.c	Thu Jun 07 07:32:06 2007 +0000
@@ -1820,7 +1820,7 @@
 	if (ops != NULL && ops->chat_remove_users != NULL) {
 		for (l = users; l; l = l->next) {
 			PurpleConvChatBuddy *cb = l->data;
-			names = g_list_append(names, cb->name);
+			names = g_list_prepend(names, cb->name);
 		}
 		ops->chat_remove_users(conv, names);
 		g_list_free(names);
--- a/libpurple/dbus-analyze-functions.py	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/dbus-analyze-functions.py	Thu Jun 07 07:32:06 2007 +0000
@@ -165,9 +165,9 @@
         self.returncode = []
 
     def flush(self):
-	paramslist = ", ".join(self.paramshdr)
-	if (paramslist == "") :
-	    paramslist = "void"
+        paramslist = ", ".join(self.paramshdr)
+        if (paramslist == "") :
+            paramslist = "void"
         print "%s %s(%s)" % (self.functiontype, self.function.name,
                              paramslist),
 
@@ -279,7 +279,7 @@
         for decl in self.cdecls:
             print decl
 
-        print "\t%s(message_DBUS, error_DBUS, " % self.argfunc,
+        print "\t%s(message_DBUS, error_DBUS," % self.argfunc,
         for param in self.cparams:
             print "DBUS_TYPE_%s, &%s," % param,
         print "DBUS_TYPE_INVALID);"
@@ -289,14 +289,14 @@
         for code in self.ccode:
             print code
 
-        print "\treply_DBUS =  dbus_message_new_method_return (message_DBUS);"
+        print "\treply_DBUS = dbus_message_new_method_return (message_DBUS);"
 
-        print "\tdbus_message_append_args(reply_DBUS, ",
+        print "\tdbus_message_append_args(reply_DBUS,",
         for param in self.cparamsout:
             if type(param) is str:
-                print "%s, " % param
+                print "%s," % param
             else:
-                print "DBUS_TYPE_%s, &%s, " % param,
+                print "DBUS_TYPE_%s, &%s," % param,
         print "DBUS_TYPE_INVALID);"
 
         for code in self.ccodeout:
--- a/libpurple/network.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/network.c	Thu Jun 07 07:32:06 2007 +0000
@@ -598,8 +598,6 @@
 void
 nm_callback_func(libnm_glib_ctx* ctx, gpointer user_data)
 {
-	GList *l;
-	PurpleAccount *account;
 	static libnm_glib_state prev = LIBNM_NO_DBUS;
 	libnm_glib_state current;
 	PurpleConnectionUiOps *ui_ops = purple_connections_get_ui_ops();
--- a/libpurple/plugins/log_reader.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Thu Jun 07 07:32:06 2007 +0000
@@ -1425,7 +1425,7 @@
 	char *escaped;
 	GString *formatted;
 	char *c;
-	char *line;
+	const char *line;
 
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
@@ -1460,240 +1460,234 @@
 	read = escaped;
 
 	/* Apply formatting... */
-	formatted = g_string_new("");
+	formatted = g_string_sized_new(strlen(read));
 	c = read;
 	line = read;
-	while (*c)
+	while (c)
 	{
-		if (*c == '\n')
-		{
-			char *link_temp_line;
-			char *link;
-			char *timestamp;
-			char *footer = NULL;
-			*c = '\0';
+		const char *link;
+		const char *footer = NULL;
+		GString *temp = NULL;
 
-			/* Convert links.
-			 *
-			 * The format is (Link: URL)URL
-			 * So, I want to find each occurance of "(Link: " and replace that chunk with:
-			 * <a href="
-			 * Then, replace the next ")" with:
-			 * ">
-			 * Then, replace the next " " (or add this if the end-of-line is reached) with:
-			 * </a>
-			 * 
-			 * As implemented, this isn't perfect, but it should cover common cases.
-			 */
-			link_temp_line = NULL;
-			while ((link = strstr(line, "(Link: ")))
-			{
-				char *tmp = link;
-
-				link += 7;
-				if (*link)
-				{
-					char *end_paren;
-					char *space;
-					GString *temp;
-
-					if (!(end_paren = strstr(link, ")")))
-					{
-						/* Something is not as we expect.  Bail out. */
-						break;
-					}
-
-					*tmp = '\0';
-					temp = g_string_new(line);
-
-					/* Start an <a> tag. */
-					g_string_append(temp, "<a href=\"");
-
-					/* Append up to the ) */
-					g_string_append_len(temp, link, end_paren - link);
+		if ((c = strstr(c, "\n")))
+		{
+			*c = '\0';
+			c++;
+		}
 
-					/* Finish the <a> tag. */
-					g_string_append(temp, "\">");
-
-					/* The \r is a bit of a hack to keep there from being a \r in
-					 * the link text, which may not matter. */
-					if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r")))
-					{
-						g_string_append_len(temp, end_paren + 1, space - end_paren - 1);
-
-						/* Close the <a> tag. */
-						g_string_append(temp, "</a>");
+		/* Convert links.
+		 *
+		 * The format is (Link: URL)URL
+		 * So, I want to find each occurance of "(Link: " and replace that chunk with:
+		 * <a href="
+		 * Then, replace the next ")" with:
+		 * ">
+		 * Then, replace the next " " (or add this if the end-of-line is reached) with:
+		 * </a>
+		 * 
+		 * As implemented, this isn't perfect, but it should cover common cases.
+		 */
+		while (line && (link = strstr(line, "(Link: ")))
+		{
+			const char *tmp = link;
 
-						space++;
-						if (*space)
-						{
-							g_string_append_c(temp, ' ');
-							/* Keep the rest of the line. */
-							g_string_append(temp, space);
-						}
-					}
-					else
-					{
-						/* There is no space before the end of the line. */
-						g_string_append(temp, end_paren + 1);
-						/* Close the <a> tag. */
-						g_string_append(temp, "</a>");
-					}
-
-					g_free(link_temp_line);
-					line = g_string_free(temp, FALSE);
+			link += 7;
+			if (*link)
+			{
+				char *end_paren;
+				char *space;
 
-					/* Save this memory location so we can free it later. */
-					link_temp_line = line;
-				}
-			}
-
-			timestamp = "";
-			if (*line == '[') {
-				timestamp = line;
-				while (*timestamp && *timestamp != ']')
-					timestamp++;
-				if (*timestamp == ']') {
-					*timestamp = '\0';
-					line++;
-					/* TODO: Parse the timestamp and convert it to Purple's format. */
-					g_string_append_printf(formatted,
-						"<font size=\"2\">(%s)</font> ", line);
-					line = timestamp;
-					if (line[1] && line[2])
-						line += 2;
+				if (!(end_paren = strstr(link, ")")))
+				{
+					/* Something is not as we expect.  Bail out. */
+					break;
 				}
 
-				if (purple_str_has_prefix(line, "*** ")) {
-					line += (sizeof("*** ") - 1);
-					g_string_append(formatted, "<b>");
-					footer = "</b>";
-					if (purple_str_has_prefix(line, "NOTE: This user is offline.")) {
-						line = _("User is offline.");
-					} else if (purple_str_has_prefix(line,
-							"NOTE: Your status is currently set to ")) {
+				if (!temp)
+					temp = g_string_sized_new(c ? (c - 1 - line) : strlen(line));
+
+				g_string_append_len(temp, line, (tmp - line));
+
+				/* Start an <a> tag. */
+				g_string_append(temp, "<a href=\"");
+
+				/* Append up to the ) */
+				g_string_append_len(temp, link, end_paren - link);
+
+				/* Finish the <a> tag. */
+				g_string_append(temp, "\">");
+
+				/* The \r is a bit of a hack to keep there from being a \r in
+				 * the link text, which may not matter. */
+				if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r")))
+				{
+					g_string_append_len(temp, end_paren + 1, space - end_paren - 1);
+
+					/* Close the <a> tag. */
+					g_string_append(temp, "</a>");
+
+					space++;
+				}
+				else
+				{
+					/* There is no space before the end of the line. */
+					g_string_append(temp, end_paren + 1);
+					/* Close the <a> tag. */
+					g_string_append(temp, "</a>");
+				}
+				line = space;
+			}
+			else
+			{
+				/* Something is not as we expect.  Bail out. */
+				break;
+			}
+		}
+
+		if (temp)
+		{
+			if (line)
+				g_string_append(temp, line);
+			line = temp->str;
+		}
+
+		if (*line == '[') {
+			const char *timestamp;
+
+			if ((timestamp = strstr(line, "]"))) {
+				line++;
+				/* TODO: Parse the timestamp and convert it to Purple's format. */
+				g_string_append(formatted, "<font size=\"2\">(");
+				g_string_append_len(formatted, line, (timestamp - line));
+				g_string_append(formatted,")</font> ");
+				line = timestamp + 1;
+				if (line[0] && line[1])
+					line++;
+			}
 
-						line += (sizeof("NOTE: ") - 1);
-					} else if (purple_str_has_prefix(line, "Auto-response sent to ")) {
-						g_string_append(formatted, _("Auto-response sent:"));
-						while (*line && *line != ':')
-							line++;
-						if (*line)
-							line++;
-						g_string_append(formatted, "</b>");
-						footer = NULL;
-					} else if (strstr(line, " signed off ")) {
-						if (buddy != NULL && buddy->alias)
-							g_string_append_printf(formatted,
-								_("%s has signed off."), buddy->alias);
-						else
-							g_string_append_printf(formatted,
-								_("%s has signed off."), log->name);
-						line = "";
-					} else if (strstr(line, " signed on ")) {
-						if (buddy != NULL && buddy->alias)
-							g_string_append(formatted, buddy->alias);
-						else
-							g_string_append(formatted, log->name);
-						line = " logged in.";
-					} else if (purple_str_has_prefix(line,
-						"One or more messages may have been undeliverable.")) {
+			if (purple_str_has_prefix(line, "*** ")) {
+				line += (sizeof("*** ") - 1);
+				g_string_append(formatted, "<b>");
+				footer = "</b>";
+				if (purple_str_has_prefix(line, "NOTE: This user is offline.")) {
+					line = _("User is offline.");
+				} else if (purple_str_has_prefix(line,
+						"NOTE: Your status is currently set to ")) {
+
+					line += (sizeof("NOTE: ") - 1);
+				} else if (purple_str_has_prefix(line, "Auto-response sent to ")) {
+					g_string_append(formatted, _("Auto-response sent:"));
+					while (*line && *line != ':')
+						line++;
+					if (*line)
+						line++;
+					g_string_append(formatted, "</b>");
+					footer = NULL;
+				} else if (strstr(line, " signed off ")) {
+					if (buddy != NULL && buddy->alias)
+						g_string_append_printf(formatted,
+							_("%s has signed off."), buddy->alias);
+					else
+						g_string_append_printf(formatted,
+							_("%s has signed off."), log->name);
+					line = "";
+				} else if (strstr(line, " signed on ")) {
+					if (buddy != NULL && buddy->alias)
+						g_string_append(formatted, buddy->alias);
+					else
+						g_string_append(formatted, log->name);
+					line = " logged in.";
+				} else if (purple_str_has_prefix(line,
+					"One or more messages may have been undeliverable.")) {
 
-						g_string_append(formatted,
-							"<span style=\"color: #ff0000;\">");
-						g_string_append(formatted,
-							_("One or more messages may have been "
-							  "undeliverable."));
-						line = "";
-						footer = "</span></b>";
-					} else if (purple_str_has_prefix(line,
-							"You have been disconnected.")) {
+					g_string_append(formatted,
+						"<span style=\"color: #ff0000;\">");
+					g_string_append(formatted,
+						_("One or more messages may have been "
+						  "undeliverable."));
+					line = "";
+					footer = "</span></b>";
+				} else if (purple_str_has_prefix(line,
+						"You have been disconnected.")) {
 
-						g_string_append(formatted,
-							"<span style=\"color: #ff0000;\">");
-						g_string_append(formatted,
-							_("You were disconnected from the server."));
-						line = "";
-						footer = "</span></b>";
-					} else if (purple_str_has_prefix(line,
-							"You are currently disconnected.")) {
+					g_string_append(formatted,
+						"<span style=\"color: #ff0000;\">");
+					g_string_append(formatted,
+						_("You were disconnected from the server."));
+					line = "";
+					footer = "</span></b>";
+				} else if (purple_str_has_prefix(line,
+						"You are currently disconnected.")) {
+
+					g_string_append(formatted,
+						"<span style=\"color: #ff0000;\">");
+					line = _("You are currently disconnected. Messages "
+					         "will not be received unless you are "
+					         "logged in.");
+					footer = "</span></b>";
+				} else if (purple_str_has_prefix(line,
+						"Your previous message has not been sent.")) {
+
+					g_string_append(formatted,
+						"<span style=\"color: #ff0000;\">");
+
+					if (purple_str_has_prefix(line,
+						"Your previous message has not been sent.  "
+						"Reason: Maximum length exceeded.")) {
 
 						g_string_append(formatted,
-							"<span style=\"color: #ff0000;\">");
-						line = _("You are currently disconnected. Messages "
-						         "will not be received unless you are "
-						         "logged in.");
-						footer = "</span></b>";
-					} else if (purple_str_has_prefix(line,
-							"Your previous message has not been sent.")) {
-
+							_("Message could not be sent because "
+							  "the maximum length was exceeded."));
+						line = "";
+					} else {
 						g_string_append(formatted,
-							"<span style=\"color: #ff0000;\">");
-
-						if (purple_str_has_prefix(line,
-							"Your previous message has not been sent.  "
-							"Reason: Maximum length exceeded.")) {
+							_("Message could not be sent."));
+						line += (sizeof(
+							"Your previous message "
+							"has not been sent. ") - 1);
+					}
 
-							g_string_append(formatted,
-								_("Message could not be sent because "
-								  "the maximum length was exceeded."));
-							line = "";
-						} else {
-							g_string_append(formatted,
-								_("Message could not be sent."));
-							line += (sizeof(
-								"Your previous message "
-								"has not been sent. ") - 1);
-						}
+					footer = "</span></b>";
+				}
+			} else if (purple_str_has_prefix(line, data->their_nickname)) {
+				if (buddy != NULL && buddy->alias) {
+					line += strlen(data->their_nickname) + 2;
+					g_string_append_printf(formatted,
+						"<span style=\"color: #A82F2F;\">"
+						"<b>%s</b></span>: ", buddy->alias);
+				}
+			} else {
+				const char *line2 = strstr(line, ":");
+				if (line2) {
+					const char *acct_name;
+					line2++;
+					line = line2;
+					acct_name = purple_account_get_alias(log->account);
+					if (!acct_name)
+						acct_name = purple_account_get_username(log->account);
 
-						footer = "</span></b>";
-					}
-				} else if (purple_str_has_prefix(line, data->their_nickname)) {
-					if (buddy != NULL && buddy->alias) {
-						line += strlen(data->their_nickname) + 2;
-						g_string_append_printf(formatted,
-							"<span style=\"color: #A82F2F;\">"
-							"<b>%s</b></span>: ", buddy->alias);
-					}
-				} else {
-					char *line2 = line;
-					while (*line2 && *line2 != ':')
-						line2++;
-					if (*line2 == ':') {
-						const char *acct_name;
-						line2++;
-						line = line2;
-						acct_name = purple_account_get_alias(log->account);
-						if (!acct_name)
-							acct_name = purple_account_get_username(log->account);
-
-						g_string_append_printf(formatted,
-							"<span style=\"color: #16569E;\">"
-							"<b>%s</b></span>:", acct_name);
-					}
+					g_string_append_printf(formatted,
+						"<span style=\"color: #16569E;\">"
+						"<b>%s</b></span>:", acct_name);
 				}
 			}
-
-			g_string_append(formatted, line);
+		}
 
-			if (footer)
-				g_string_append(formatted, footer);
+		g_string_append(formatted, line);
 
-			g_string_append_c(formatted, '\n');
-
-			g_free(link_temp_line);
+		line = c;
+		if (temp)
+			g_string_free(temp, TRUE);
 
-			c++;
-			line = c;
-		} else
-			c++;
+		if (footer)
+			g_string_append(formatted, footer);
+
+		g_string_append_c(formatted, '\n');
 	}
 
 	g_free(read);
-	read = formatted->str;
-	g_string_free(formatted, FALSE);
-
-	return read;
+	/* XXX: TODO: Avoid this g_strchomp() */
+	return g_strchomp(g_string_free(formatted, FALSE));
 }
 
 static int trillian_logger_size (PurpleLog *log)
--- a/libpurple/plugins/ssl/ssl-nss.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-nss.c	Thu Jun 07 07:32:06 2007 +0000
@@ -311,8 +311,13 @@
 	if(!nss_data)
 		return;
 
-	if (nss_data->in) PR_Close(nss_data->in);
-	/* if (nss_data->fd) PR_Close(nss_data->fd); */
+	if (nss_data->in) {
+		PR_Close(nss_data->in);
+		gsc->fd = -1;
+	} else if (nss_data->fd) {
+		PR_Close(nss_data->fd);
+		gsc->fd = -1;
+	}
 
 	if (nss_data->handshake_handler)
 		purple_input_remove(nss_data->handshake_handler);
--- a/libpurple/plugins/tcl/tcl.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/plugins/tcl/tcl.c	Thu Jun 07 07:32:06 2007 +0000
@@ -457,11 +457,23 @@
 
 	if ((version = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, regkey, "CurrentVersion"))
 			|| (version = wpurple_read_reg_string(HKEY_CURRENT_USER, regkey, "CurrentVersion"))) {
-		char *path;
+		char *path = NULL;
 		char *regkey2;
+		char **tokens;
+		int major = 0, minor = 0, micro = 0;
+
+		tokens = g_strsplit(version, ".", 0);
+		if (tokens[0] && tokens[1] && tokens[2]) {
+			major = atoi(tokens[0]);
+			minor = atoi(tokens[1]);
+			micro = atoi(tokens[2]);
+		}
+		g_strfreev(tokens);
 
 		regkey2 = g_strdup_printf("%s%s\\", regkey, version);
-		if ((path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, regkey2, NULL)) || (path = wpurple_read_reg_string(HKEY_CURRENT_USER, regkey2, NULL))) {
+		if (!(major == 8 && minor == 4 && micro >= 5))
+			purple_debug(PURPLE_DEBUG_INFO, "tcl", "Unsupported ActiveTCL version %s found.\n", version);
+		else if ((path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, regkey2, NULL)) || (path = wpurple_read_reg_string(HKEY_CURRENT_USER, regkey2, NULL))) {
 			char *tclpath;
 			char *tkpath;
 
--- a/libpurple/protocols/Makefile.mingw	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/Makefile.mingw	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.h	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Thu Jun 07 07:32:06 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 16:04:04 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 16:04:04 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	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/issues.txt	Thu Jun 07 07:32:06 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 16:04:04 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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	Thu Jun 07 07:32:06 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/jabber/.todo	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/jabber/.todo	Thu Jun 07 07:32:06 2007 +0000
@@ -38,7 +38,7 @@
             formatted. enhancement-request so that the birthday field in the setinfo form would split up into relevant fields allowing for a strict syntax (like year--month--day or so, perhaps even dropdown menus)
         </note>
         <note priority="low" time="1037890968">
-            have set info pre-fill values from the server when no local vcard exists. this will help people migrating to gaim
+            have set info pre-fill values from the server when no local vcard exists. this will help people migrating to libpurple-based clients
         </note>
     </note>
     <note priority="verylow" time="1036044192">
--- a/libpurple/protocols/jabber/jabber.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu Jun 07 07:32:06 2007 +0000
@@ -464,7 +464,11 @@
 	JabberStream *js = gc->proto_data;
 
 	if (source < 0) {
-		purple_connection_error(gc, _("Couldn't connect to host"));
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+				error);
+		purple_connection_error(gc, tmp);
+		g_free(tmp);
 		return;
 	}
 
--- a/libpurple/protocols/jabber/si.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/jabber/si.c	Thu Jun 07 07:32:06 2007 +0000
@@ -105,6 +105,9 @@
 	jsx->connect_data = NULL;
 
 	if(source < 0) {
+		purple_debug_warning("jabber",
+				"si connection failed, jid was %s, host was %s, error was %s\n",
+				streamhost->jid, streamhost->host, error_message);
 		jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
 		g_free(streamhost->jid);
 		g_free(streamhost->host);
--- a/libpurple/protocols/msn/servconn.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Thu Jun 07 07:32:06 2007 +0000
@@ -195,6 +195,7 @@
 	}
 	else
 	{
+		purple_debug_error("msn", "Connection error: %s\n", error_message);
 		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_CONNECT);
 	}
 }
--- a/libpurple/protocols/oscar/.todo	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/oscar/.todo	Thu Jun 07 07:32:06 2007 +0000
@@ -15,16 +15,10 @@
             <note priority="veryhigh" time="1036040919">
                 some way to close direct connect w/out closing convo.
             </note>
-            <note priority="low" time="1036040970">
-                canceled direct im should still allow new attempt
-            </note>
             <note priority="low" time="1036041084">
                 failed direct im attempt should allow new attempt some way to cancel an attempt that isn't happening
             </note>
         </note>
-        <note priority="low" time="1036041105">
-            Colors in Chat room are wrong (using Gold too much)
-        </note>
         <note priority="verylow" time="1036041121">
             Voice Chat
         </note>
@@ -44,7 +38,7 @@
             color support
         </note>
         <note priority="high" time="1036041251">
-            set status message and of course when gaim can set them, it needs to be able to get the ones it sets. (yes this is redundant. its a reflection of my current mood)
+            set status message and of course when libpurple can set them, it needs to be able to get the ones it sets. (yes this is redundant. its a reflection of my current mood)
         </note>
         <note priority="medium" time="1036041165">
             Chat (this is different from aim chat)
@@ -66,6 +60,6 @@
         </note>
     </note>
     <note priority="medium" time="1036040870">
-        The order of groups and buddies in the server list is not updated when groups and buddies are re-arranged locally in Gaim.
+        The order of groups and buddies in the server list is not updated when groups and buddies are re-arranged locally in libpurple.
     </note>
 </todo>
--- a/libpurple/protocols/oscar/AUTHORS	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/oscar/AUTHORS	Thu Jun 07 07:32:06 2007 +0000
@@ -35,7 +35,7 @@
 N: Eric Warmenhoven
 T: 1998-2001
 E: warmenhoven a.t linux d.o.t com
-D: Some OFT info, author of the faim interface for gaim
+D: Some OFT info, initial author of the libpurple-side of the oscar protocol plugin
 
 N: Brock Wilcox
 T: 1998-2001
--- a/libpurple/protocols/qq/AUTHORS	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/qq/AUTHORS	Thu Jun 07 07:32:06 2007 +0000
@@ -1,7 +1,7 @@
 Code Contributors
 =====
 puzzlebird  : original author
-gfhuang     : patches for gaim 2.0.0beta2, maintainer
+gfhuang     : patches for libpurple 2.0.0beta2, maintainer
 henryouly   : file transfer, udp sock5 proxy and qq_show, maintainer
 hzhr        : maintainer
 joymarquis  : maintainer
@@ -10,7 +10,7 @@
 yyw         : improved performance on PPC linux
 lvxiang     : provided ip to location original code
 csyfek      : faces
-markhuetsch : OpenQ merge into gaim, maintainer 2006-2007
+markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007
 
 Acknowledgement
 =====
--- a/libpurple/protocols/toc/PROTOCOL	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/protocols/toc/PROTOCOL	Thu Jun 07 07:32:06 2007 +0000
@@ -14,10 +14,10 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-# Note from Jim Duchek, gaim maintainer -- this may not be the latest
-# version of this document, I provide it as a service.  Download a copy
-# of TiK (http://www.aim.aol.com/tik/) for the latest version of this
-# doc.
+# Note from Jim Duchek, former libpurple maintainer -- this may not be
+# the latest version of this document, I provide it as a service.
+# Download a copy of TiK (http://www.aim.aol.com/tik/) for the latest
+# version of this doc.
 
 # Note from Eric Warmenhoven, random guy -- this appears to be the last
 # published version of the protocol, and AOL has stopped hosting the TiK
--- a/libpurple/util.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/libpurple/util.c	Thu Jun 07 07:32:06 2007 +0000
@@ -2656,14 +2656,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 16:04:04 2007 +0000
+++ b/libpurple/win32/global.mak	Thu Jun 07 07:32:06 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.spec.in	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin.spec.in	Thu Jun 07 07:32:06 2007 +0000
@@ -83,6 +83,7 @@
 Summary:    Development headers, documentation, and libraries for Pidgin
 Group:      Applications/Internet
 Requires:   pidgin = %{version}, libpurple-devel = %{version}
+Requires:   gtk2-devel
 Requires:   pkgconfig
 Obsoletes:  gaim-devel
 Provides:   gaim-devel
@@ -102,6 +103,12 @@
 Group:      Applications/Internet
 Requires:   libpurple = %{version}
 Requires:   pkgconfig
+%if "%{_vendor}" == "suse"
+# For SuSE:
+%{?_with_dbus:Requires: dbus-1-devel >= 0.35}
+%else
+%{?_with_dbus:Requires: dbus-devel >= 0.35}
+%endif
 
 %if 0%{?_with_howl:1} || 0%{?_with_avahi:1}
 %package -n libpurple-bonjour
@@ -134,6 +141,7 @@
 Summary:    Headers etc. for finch stuffs
 Group:      Applications/Internet
 Requires:   finch = %{version}, libpurple-devel = %{version}
+Requires:   ncurses-devel
 Requires:   pkgconfig
 %endif
 
@@ -450,6 +458,10 @@
 %endif
 
 %changelog
+* Tue Jun 5 2007 Stu Tomlinson <stu@nosnilmot.com>
+- Add missing Requires for gtk2-devel, dbus-devel & ncurses-devel to
+  appropriate -devel subpackages
+
 * Sun May 27 2007 Stu Tomlinson <stu@nosnilmot.com>
 - add cyrus-sasl-plain & cyrus-sasl-md5 to Requires
 
--- a/pidgin/gtkblist.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/gtkblist.c	Thu Jun 07 07:32:06 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/gtkdocklet-x11.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/gtkdocklet-x11.c	Thu Jun 07 07:32:06 2007 +0000
@@ -281,7 +281,7 @@
 	 * The x11 docklet tracks whether it successfully embedded in a pref and
 	 * allows for a longer timeout period if it successfully embedded the last
 	 * time it was run. This should hopefully solve problems with the buddy
-	 * list not properly starting hidden when gaim is started on login.
+	 * list not properly starting hidden when Pidgin is started on login.
 	 */
 	if(!recreate) {
 		pidgin_docklet_embedded();
--- a/pidgin/gtkimhtmltoolbar.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Thu Jun 07 07:32:06 2007 +0000
@@ -852,64 +852,6 @@
 	update_buttons(toolbar);
 }
 
-
-/* This comes from gtkmenutoolbutton.c from gtk+
- * Copyright (C) 2003 Ricardo Fernandez Pascual
- * Copyright (C) 2004 Paolo Borelli
- */
-static void
-menu_position_func (GtkMenu           *menu,
-                    int               *x,
-                    int               *y,
-                    gboolean          *push_in,
-                    gpointer data)
-{
-  GtkRequisition menu_req;
-  GtkTextDirection direction;
-  GdkRectangle monitor;
-  gint monitor_num;
-  GdkScreen *screen;
-  GtkWidget *widget = data;
-
-  gtk_widget_size_request (GTK_WIDGET (widget), &menu_req);
-
-  direction = gtk_widget_get_direction (widget);
-
-  screen = gtk_widget_get_screen (GTK_WIDGET (menu));
-  monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
-  if (monitor_num < 0)
-    monitor_num = 0;
-  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
-  gdk_window_get_origin (widget->window, x, y);
-  *x += widget->allocation.x;
-    *y += widget->allocation.y;
-
-      if (direction == GTK_TEXT_DIR_LTR)
-	*x += MAX (widget->allocation.width - menu_req.width, 0);
-      else if (menu_req.width > widget->allocation.width)
-        *x -= menu_req.width - widget->allocation.width;
-
-      if ((*y + widget->allocation.height + menu_req.height) <= monitor.y + monitor.height)
-	*y += widget->allocation.height;
-      else if ((*y - menu_req.height) >= monitor.y)
-	*y -= menu_req.height;
-      else if (monitor.y + monitor.height - (*y + widget->allocation.height) > *y)
-	*y += widget->allocation.height;
-      else
-	*y -= menu_req.height;
-  *push_in = FALSE;
-}
-
-static void pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu) {
-	gtk_widget_show_all(GTK_WIDGET(menu));
-	gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time());
-}
-
-static void pidgin_menu_deactivate(GtkWidget *menu, GtkToggleButton *button) {
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
-}
-
 enum {
 	LAST_SIGNAL
 };
@@ -957,91 +899,12 @@
 	gobject_class->finalize = gtk_imhtmltoolbar_finalize;
 }
 
-static void gtk_imhtmltoolbar_create_old_buttons(GtkIMHtmlToolbar *toolbar)
-{
-	GtkWidget *button;
-	/* Bold */
-	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_BOLD);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_bold), toolbar);
-	toolbar->bold = button;
-
-
-	/* Italic */
-	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_ITALIC);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_italic), toolbar);
-	toolbar->italic = button;
-
-	/* Underline */
-	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_UNDERLINE);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_underline), toolbar);
-	toolbar->underline = button;
-
-	/* Increase font size */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_LARGER);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_big), toolbar);
-	toolbar->larger_size = button;
-
-	/* Decrease font size */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(do_small), toolbar);
-	toolbar->smaller_size = button;
-
-	/* Font Face */
-
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FONT_FACE);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(toggle_font), toolbar);
-	toolbar->font = button;
-
-	/* Foreground Color */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FGCOLOR);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(toggle_fg_color), toolbar);
-	toolbar->fgcolor = button;
-
-	/* Background Color */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_BGCOLOR);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(toggle_bg_color), toolbar);
-	toolbar->bgcolor = button;
-
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_LINK);
-	g_signal_connect(G_OBJECT(button), "clicked",
-				 G_CALLBACK(insert_link_cb), toolbar);
-	toolbar->link = button;
-
-	/* Insert IM Image */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(insert_image_cb), toolbar);
-	toolbar->image = button;
-
-	/* Insert Smiley */
-	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(insert_smiley_cb), toolbar);
-	toolbar->smiley = button;
-
-
-}
-
 static void gtk_imhtmltoolbar_init (GtkIMHtmlToolbar *toolbar)
 {
 	GtkWidget *hbox = GTK_WIDGET(toolbar);
-	GtkWidget *bbox;
-	GtkWidget *image;
-	GtkWidget *label;
-	GtkWidget *insert_button;
-	GtkWidget *font_button;
-	GtkWidget *font_menu;
-	GtkWidget *insert_menu;
 	GtkWidget *button;
 	GtkWidget *sep;
+	GtkSizeGroup *sg;
 
 	toolbar->imhtml = NULL;
 	toolbar->font_dialog = NULL;
@@ -1054,116 +917,164 @@
 	toolbar->tooltips = gtk_tooltips_new();
 
 	gtk_box_set_spacing(GTK_BOX(toolbar), 3);
-
-	gtk_imhtmltoolbar_create_old_buttons(toolbar);
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
 
 	/* Bold */
-	font_button = gtk_toggle_button_new();
-	gtk_button_set_relief(GTK_BUTTON(font_button), GTK_RELIEF_NONE);
-	bbox = gtk_hbox_new(FALSE, 3);
-	gtk_container_add(GTK_CONTAINER(font_button), bbox);
-	image = gtk_image_new_from_stock(GTK_STOCK_BOLD, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
-	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
-	label = gtk_label_new_with_mnemonic(_("_Font"));
-	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(hbox), font_button, FALSE, FALSE, 0);
-	gtk_widget_show_all(font_button);
+	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_BOLD);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Bold"), NULL);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_bold), toolbar);
 
-	font_menu = gtk_menu_new();
-
-	button = gtk_check_menu_item_new_with_mnemonic(_("_Bold"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->bold);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
+	toolbar->bold = button;
 
-	button = gtk_check_menu_item_new_with_mnemonic(_("_Italic"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->italic);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
-
-	button = gtk_check_menu_item_new_with_mnemonic(_("_Underline"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->underline);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
+	/* Italic */
+	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_ITALIC);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Italic"), NULL);
 
-	button = gtk_menu_item_new_with_mnemonic(_("_Larger"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->larger_size);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_italic), toolbar);
 
-	button = gtk_menu_item_new_with_mnemonic(_("_Normal"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->normal_size);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
-
-	button = gtk_menu_item_new_with_mnemonic(_("_Smaller"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->smaller_size);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
+	toolbar->italic = button;
 
-	button = gtk_menu_item_new_with_mnemonic(_("_Font face"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->font);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
+	/* Underline */
+	button = pidgin_pixbuf_toolbar_button_from_stock(GTK_STOCK_UNDERLINE);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Underline"), NULL);
 
-	button = gtk_menu_item_new_with_mnemonic(_("_Foreground color"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->fgcolor);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_underline), toolbar);
 
-	button = gtk_menu_item_new_with_mnemonic(_("_Background color"));
-	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->bgcolor);
-	gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), button);
-
-	g_signal_connect(G_OBJECT(font_button), "clicked", G_CALLBACK(pidgin_menu_clicked), font_menu);
-	g_signal_connect(G_OBJECT(font_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), font_button);	
+	toolbar->underline = button;
 
 	/* Sep */
 	sep = gtk_vseparator_new();
 	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
-	gtk_widget_show_all(sep);
+
+	/* Increase font size */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_LARGER);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button,
+			     _("Larger font size"), NULL);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_big), toolbar);
+
+	toolbar->larger_size = button;
+
+	/* Decrease font size */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button,
+			     _("Smaller font size"), NULL);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(do_small), toolbar);
+
+	toolbar->smaller_size = button;
+
+	/* Sep */
+	sep = gtk_vseparator_new();
+	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
+
+	/* Font Face */
+
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FONT_FACE);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button,
+			_("Font face"), NULL);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(toggle_font), toolbar);
+
+	toolbar->font = button;
+
+	/* Foreground Color */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_FGCOLOR);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button,
+			     _("Foreground font color"), NULL);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(toggle_fg_color), toolbar);
+
+	toolbar->fgcolor = button;
+
+	/* Background Color */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_BGCOLOR);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button,
+			     _("Background color"), NULL);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(toggle_bg_color), toolbar);
+
+	toolbar->bgcolor = button;
+
+	/* Sep */
+	sep = gtk_vseparator_new();
+	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
 
 	/* Reset Formatting */
-	button = gtk_toggle_button_new();
-	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-	bbox = gtk_hbox_new(FALSE, 3);
-	gtk_container_add(GTK_CONTAINER(button), bbox);
-	image = gtk_image_new_from_stock(PIDGIN_STOCK_CLEAR, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
-	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
-	label = gtk_label_new_with_mnemonic(_("_Reset font"));
-	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_CLEAR);
+	gtk_size_group_add_widget(sg, button);
 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
-	gtk_widget_show_all(button);
+	gtk_tooltips_set_tip(toolbar->tooltips, button,
+			     _("Reset formatting"), NULL);
+
 	g_signal_connect(G_OBJECT(button), "clicked",
 			 G_CALLBACK(clear_formatting_cb), toolbar);
+
 	toolbar->clear = button;
 
 	/* Sep */
 	sep = gtk_vseparator_new();
 	gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
-	gtk_widget_show_all(sep);
 
 	/* Insert Link */
-	insert_button = gtk_toggle_button_new();
-	gtk_button_set_relief(GTK_BUTTON(insert_button), GTK_RELIEF_NONE);
-	bbox = gtk_hbox_new(FALSE, 3);
-	gtk_container_add(GTK_CONTAINER(insert_button), bbox);
-	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
-	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
-	label = gtk_label_new_with_mnemonic(_("_Insert"));
-	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(hbox), insert_button, FALSE, FALSE, 0);
-	gtk_widget_show_all(insert_button);
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_LINK);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert link"), NULL);
+	g_signal_connect(G_OBJECT(button), "clicked",
+				 G_CALLBACK(insert_link_cb), toolbar);
 
-	insert_menu = gtk_menu_new();
+	toolbar->link = button;
+
+	/* Insert IM Image */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert image"), NULL);
 
-	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);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(insert_image_cb), toolbar);
 
-	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);
+	toolbar->image = button;
 
-	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);
+	/* Insert Smiley */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY);
+	gtk_size_group_add_widget(sg, button);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert smiley"), NULL);
 
-	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);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(insert_smiley_cb), toolbar);
+
+	toolbar->smiley = button;
+
 	toolbar->sml = NULL;
+	gtk_widget_show_all(hbox);
 }
 
 GtkWidget *gtk_imhtmltoolbar_new()
--- a/pidgin/gtkutils.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/gtkutils.c	Thu Jun 07 07:32:06 2007 +0000
@@ -3034,20 +3034,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/pidginstock.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/pidginstock.c	Thu Jun 07 07:32:06 2007 +0000
@@ -151,7 +151,6 @@
 	{ PIDGIN_STOCK_TOOLBAR_FONT_FACE, "toolbar", "font-face.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, "toolbar", "font-size-down.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
--- a/pidgin/pidginstock.h	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/pidginstock.h	Thu Jun 07 07:32:06 2007 +0000
@@ -116,7 +116,6 @@
 #define PIDGIN_STOCK_TOOLBAR_FONT_FACE	  "pidgin-font-face"
 #define PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER "pidgin-text-smaller"
 #define PIDGIN_STOCK_TOOLBAR_TEXT_LARGER  "pidgin-text-larger"
-#define PIDGIN_STOCK_TOOLBAR_INSERT       "pidgin-insert"
 #define PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE "pidgin-insert-image"
 #define PIDGIN_STOCK_TOOLBAR_INSERT_LINK  "pidgin-insert-link"
 #define PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW  "pidgin-message-new"
--- a/pidgin/pixmaps/toolbar/16/Makefile.am	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/pixmaps/toolbar/16/Makefile.am	Thu Jun 07 07:32:06 2007 +0000
@@ -6,7 +6,6 @@
 		font-face.png \
 		font-size-down.png \
 		font-size-up.png \
-		insert.png \
 		insert-image.png \
 		insert-link.png \
 		message-new.png \
--- a/pidgin/plugins/gevolution/gevolution.c	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/plugins/gevolution/gevolution.c	Thu Jun 07 07:32:06 2007 +0000
@@ -38,7 +38,6 @@
 
 #include <libedata-book/Evolution-DataServer-Addressbook.h>
 
-#include <libebook/e-book-listener.h>
 #include <libedata-book/e-data-book-factory.h>
 #include <bonobo/bonobo-main.h>
 
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Tue Jun 05 16:04:04 2007 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Thu Jun 07 07:32:06 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"
@@ -1067,6 +1068,7 @@
 
   have_gtk:
     ; GTK+ is already installed; check version.
+	; Change this to not even run the GTK installer if this version is already installed.
     ${VersionCompare} ${GTK_INSTALL_VERSION} $0 $3
     IntCmp $3 1 +1 good_version good_version
     ${VersionCompare} ${GTK_MIN_VERSION} $0 $3
--- a/valgrind-suppressions	Tue Jun 05 16:04:04 2007 +0000
+++ b/valgrind-suppressions	Thu Jun 07 07:32:06 2007 +0000
@@ -147,8 +147,8 @@
    fun:PR_Init
    fun:rsa_nss_init
    fun:GE_plugin_load
-   fun:gaim_plugin_load
-   fun:gaim_plugins_load_saved
+   fun:purple_plugin_load
+   fun:purple_plugins_load_saved
    fun:main
 }