changeset 18803:abb9aac69507

merge of '7ee71f1a32c4a5d7001c0049f2c7ec1e6cb54fa3' and 'c896d38533c1a23a6748a75257104e1f501999e5'
author Ka-Hing Cheung <khc@hxbc.us>
date Sun, 05 Aug 2007 19:42:29 +0000
parents 42161f9233bf (current diff) 355f24d20501 (diff)
children b839f427cbb2 1244b5f43661 b9a0b1bd321b
files libpurple/protocols/bonjour/mdns_howl.h libpurple/protocols/bonjour/mdns_win32.h pidgin/gtkconv.c
diffstat 14 files changed, 449 insertions(+), 375 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/plugins/log_reader.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Sun Aug 05 19:42:29 2007 +0000
@@ -60,14 +60,14 @@
 	char *path;
 	GDir *dir;
 
-	g_return_val_if_fail(sn != NULL, list);
-	g_return_val_if_fail(account != NULL, list);
+	g_return_val_if_fail(sn != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL);
 
 	logdir = purple_prefs_get_string("/plugins/core/log_reader/adium/log_directory");
 
 	/* By clearing the log directory path, this logger can be (effectively) disabled. */
-	if (!*logdir)
-		return list;
+	if (!logdir || !*logdir)
+		return NULL;
 
 	plugin = purple_find_prpl(purple_account_get_protocol_id(account));
 	if (!plugin)
@@ -236,8 +236,8 @@
 
 	/* XXX: TODO: We probably want to set PURPLE_LOG_READ_NO_NEWLINE
 	 * XXX: TODO: for HTML logs. */
-	if (flags != NULL)
-		*flags = 0;
+	if (flags != NULL)
+		*flags = 0;
 
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
@@ -626,17 +626,17 @@
 	const char *old_session_id = "";
 	struct msn_logger_data *data = NULL;
 
-	g_return_val_if_fail(sn != NULL, list);
-	g_return_val_if_fail(account != NULL, list);
+	g_return_val_if_fail(sn != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL);
 
 	if (strcmp(account->protocol_id, "prpl-msn"))
-		return list;
+		return NULL;
 
 	logdir = purple_prefs_get_string("/plugins/core/log_reader/msn/log_directory");
 
 	/* By clearing the log directory path, this logger can be (effectively) disabled. */
-	if (!*logdir)
-		return list;
+	if (!logdir || !*logdir)
+		return NULL;
 
 	buddy = purple_find_buddy(account, sn);
 
@@ -1121,7 +1121,7 @@
 		if (name_guessed != NAME_GUESS_UNKNOWN)
 			text = g_string_append(text, "</span>");
 
-		style     = xmlnode_get_attrib(text_node, "Style");
+		style = xmlnode_get_attrib(text_node, "Style");
 
 		tmp = xmlnode_get_data(text_node);
 		if (style && *style) {
@@ -1211,14 +1211,14 @@
 	gchar *line;
 	gchar *c;
 
-	g_return_val_if_fail(sn != NULL, list);
-	g_return_val_if_fail(account != NULL, list);
+	g_return_val_if_fail(sn != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL);
 
 	logdir = purple_prefs_get_string("/plugins/core/log_reader/trillian/log_directory");
 
 	/* By clearing the log directory path, this logger can be (effectively) disabled. */
-	if (!*logdir)
-		return list;
+	if (!logdir || !*logdir)
+		return NULL;
 
 	plugin = purple_find_prpl(purple_account_get_protocol_id(account));
 	if (!plugin)
@@ -1429,8 +1429,8 @@
 	char *c;
 	const char *line;
 
-	if (flags != NULL)
-		*flags = PURPLE_LOG_READ_NO_NEWLINE;
+	if (flags != NULL)
+		*flags = PURPLE_LOG_READ_NO_NEWLINE;
 
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
@@ -1776,18 +1776,18 @@
 	int offset = 0;
 	GError *error;
 
-	g_return_val_if_fail(sn != NULL, list);
-	g_return_val_if_fail(account != NULL, list);
+	g_return_val_if_fail(sn != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL);
 
 	/* QIP only supports ICQ. */
 	if (strcmp(account->protocol_id, "prpl-icq"))
-		return list;
+		return NULL;
 
 	logdir = purple_prefs_get_string("/plugins/core/log_reader/qip/log_directory");
 
 	/* By clearing the log directory path, this logger can be (effectively) disabled. */
-	if (!*logdir)
-		return list;
+	if (!logdir || !*logdir)
+		return NULL;
 
 	plugin = purple_find_prpl(purple_account_get_protocol_id(account));
 	if (!plugin)
@@ -1927,8 +1927,8 @@
 	char *utf8_string;
 	FILE *file;
 
-	if (flags != NULL)
-		*flags = PURPLE_LOG_READ_NO_NEWLINE;
+	if (flags != NULL)
+		*flags = PURPLE_LOG_READ_NO_NEWLINE;
 
 	g_return_val_if_fail(log != NULL, g_strdup(""));
 
--- a/libpurple/protocols/bonjour/Makefile.am	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Sun Aug 05 19:42:29 2007 +0000
@@ -1,6 +1,5 @@
 EXTRA_DIST = \
 		mdns_win32.c \
-		mdns_win32.h \
 		Makefile.mingw
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
@@ -16,7 +15,7 @@
 	mdns_common.c \
 	mdns_common.h \
 	mdns_howl.c \
-	mdns_howl.h \
+	mdns_interface.h \
 	mdns_types.h \
 	parser.c \
 	parser.h
--- a/libpurple/protocols/bonjour/buddy.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Sun Aug 05 19:42:29 2007 +0000
@@ -22,6 +22,7 @@
 #include "account.h"
 #include "blist.h"
 #include "bonjour.h"
+#include "mdns_interface.h"
 #include "debug.h"
 
 /**
@@ -35,6 +36,8 @@
 	buddy->account = account;
 	buddy->name = g_strdup(name);
 
+	_mdns_init_buddy(buddy);
+
 	return buddy;
 }
 
@@ -102,8 +105,9 @@
 {
 	PurpleBuddy *buddy;
 	PurpleGroup *group;
-	const char *status_id, *first, *last;
-	gchar *alias;
+	PurpleAccount *account = bonjour_buddy->account;
+	const char *status_id, *first, *last, *old_hash, *new_hash;
+	gchar *alias = NULL;
 
 	/* Translate between the Bonjour status and the Purple status */
 	if (g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0)
@@ -116,44 +120,55 @@
 	 * field from the DNS SD.
 	 */
 
-	/* Create the alias for the buddy using the first and the last name */
-	first = bonjour_buddy->first;
-	last = bonjour_buddy->last;
-	alias = g_strdup_printf("%s%s%s",
-							(first && *first ? first : ""),
-							(first && *first && last && *last ? " " : ""),
-							(last && *last ? last : ""));
-
 	/* Make sure the Bonjour group exists in our buddy list */
 	group = purple_find_group(BONJOUR_GROUP_NAME); /* Use the buddy's domain, instead? */
-	if (group == NULL)
-	{
+	if (group == NULL) {
 		group = purple_group_new(BONJOUR_GROUP_NAME);
 		purple_blist_add_group(group, NULL);
 	}
 
 	/* Make sure the buddy exists in our buddy list */
-	buddy = purple_find_buddy(bonjour_buddy->account, bonjour_buddy->name);
+	buddy = purple_find_buddy(account, bonjour_buddy->name);
 
-	if (buddy == NULL)
-	{
-		buddy = purple_buddy_new(bonjour_buddy->account, bonjour_buddy->name, alias);
+	if (buddy == NULL) {
+		buddy = purple_buddy_new(account, bonjour_buddy->name, NULL);
 		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);
 	}
 
+	/* Create the alias for the buddy using the first and the last name */
+	first = bonjour_buddy->first;
+	last = bonjour_buddy->last;
+	if ((first && *first) || (last && *last))
+		alias = g_strdup_printf("%s%s%s",
+					(first && *first ? first : ""),
+					(first && *first && last && *last ? " " : ""),
+					(last && *last ? last : ""));
+	serv_got_alias(purple_account_get_connection(account), buddy->name, alias);
+	g_free(alias);
+
 	/* Set the user's status */
 	if (bonjour_buddy->msg != NULL)
-		purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id,
-								  "message", bonjour_buddy->msg,
-								  NULL);
+		purple_prpl_got_user_status(account, buddy->name, status_id,
+					    "message", bonjour_buddy->msg, NULL);
 	else
-		purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id,
-								  NULL);
-	purple_prpl_got_user_idle(bonjour_buddy->account, buddy->name, FALSE, 0);
+		purple_prpl_got_user_status(account, buddy->name, status_id, NULL);
+
+	purple_prpl_got_user_idle(account, buddy->name, FALSE, 0);
+
+	/* TODO: Because we don't save Bonjour buddies in blist.xml,
+	 * we will always have to look up the buddy icon at login time.
+	 * I think we should figure out a way to do something about this. */
 
-	g_free(alias);
+	/* Deal with the buddy icon */
+	old_hash = purple_buddy_icons_get_checksum_for_user(buddy);
+	new_hash = (bonjour_buddy->phsh && *(bonjour_buddy->phsh)) ? bonjour_buddy->phsh : NULL;
+	if (new_hash && (!old_hash || strcmp(old_hash, new_hash) != 0)) {
+		/* Look up the new icon data */
+		bonjour_dns_sd_retrieve_buddy_icon(bonjour_buddy);
+	} else
+		purple_buddy_icons_set_for_user(account, buddy->name, NULL, 0, NULL);
 }
 
 /**
@@ -164,6 +179,7 @@
 {
 	g_free(buddy->name);
 	g_free(buddy->ip);
+	g_free(buddy->full_service_name);
 
 	g_free(buddy->first);
 	g_free(buddy->phsh);
@@ -182,13 +198,8 @@
 	bonjour_jabber_close_conversation(buddy->conversation);
 	buddy->conversation = NULL;
 
-#ifdef USE_BONJOUR_APPLE
-	if (buddy->txt_query != NULL)
-	{
-		purple_input_remove(buddy->txt_query_fd);
-		DNSServiceRefDeallocate(buddy->txt_query);
-	}
-#endif
+	/* Clean up any mdns implementation data */
+	_mdns_delete_buddy(buddy);
 
 	g_free(buddy);
 }
--- a/libpurple/protocols/bonjour/buddy.h	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Sun Aug 05 19:42:29 2007 +0000
@@ -19,22 +19,17 @@
 
 #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;
+	/* TODO: Remove and just use the hostname */
 	gchar *ip;
+	gchar *full_service_name;
 	gint port_p2pj;
 
 	gchar *first;
@@ -53,11 +48,7 @@
 
 	BonjourJabberConversation *conversation;
 
-#ifdef USE_BONJOUR_APPLE
-	DNSServiceRef txt_query;
-	int txt_query_fd;
-#endif
-
+	gpointer mdns_impl_data;
 } BonjourBuddy;
 
 static const char *const buddy_TXT_records[] = {
--- a/libpurple/protocols/bonjour/mdns_common.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_common.c	Sun Aug 05 19:42:29 2007 +0000
@@ -17,8 +17,8 @@
 #include <string.h>
 
 #include "internal.h"
-#include "config.h"
 #include "mdns_common.h"
+#include "mdns_interface.h"
 #include "bonjour.h"
 #include "buddy.h"
 #include "debug.h"
@@ -73,65 +73,28 @@
 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);
+	gc = purple_account_get_connection(data->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;
-
+	if (!_mdns_init_session(data))
 		return FALSE;
-	}
-#endif
 
 	/* Publish our bonjour IM client at the mDNS daemon */
-
-	if (0 != _mdns_publish(data, PUBLISH_START))
-	{
+	if (!_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
-	{
+	if (!_mdns_browse(data)) {
 		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);
+	gc->inpa = _mdns_register_to_mainloop(data);
 
 	return TRUE;
 }
@@ -143,34 +106,10 @@
 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;
+	_mdns_stop(data);
 
-#ifdef USE_BONJOUR_HOWL
-	sw_discovery_cancel(data->session, data->session_id);
-#endif
-
-	account = data->account;
-	gc = purple_account_get_connection(account);
+	gc = purple_account_get_connection(data->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_handler);
-
-	DNSServiceRefDeallocate(data->advertisement);
-	DNSServiceRefDeallocate(data->browser);
-	data->advertisement = NULL;
-	data->browser = NULL;
-#else /* USE_BONJOUR_HOWL */
-	g_free(data->session);
-	data->session = NULL;
-#endif
 }
--- a/libpurple/protocols/bonjour/mdns_common.h	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_common.h	Sun Aug 05 19:42:29 2007 +0000
@@ -19,11 +19,7 @@
 
 #include "mdns_types.h"
 
-#ifdef USE_BONJOUR_APPLE
-#include "mdns_win32.h"
-#elif defined USE_BONJOUR_HOWL
-#include "mdns_howl.h"
-#endif
+#include "buddy.h"
 
 /**
  * Allocate space for the dns-sd data.
@@ -41,6 +37,11 @@
 void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message);
 
 /**
+ * Retrieve the buddy icon blob
+ */
+void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy);
+
+/**
  * Advertise our presence within the dns-sd daemon and start
  * browsing for other bonjour peers.
  */
--- a/libpurple/protocols/bonjour/mdns_howl.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_howl.c	Sun Aug 05 19:42:29 2007 +0000
@@ -15,12 +15,20 @@
  */
 
 #include "internal.h"
-#include "mdns_howl.h"
 
+#include "mdns_interface.h"
 #include "debug.h"
 #include "buddy.h"
 
-sw_result HOWL_API
+#include <howl.h>
+
+/* data used by howl bonjour implementation */
+typedef struct _howl_impl_data {
+	sw_discovery session;
+	sw_discovery_oid session_id;
+} HowlSessionImplData;
+
+static sw_result HOWL_API
 _publish_reply(sw_discovery discovery, sw_discovery_oid oid,
 			   sw_discovery_publish_status status, sw_opaque extra)
 {
@@ -46,7 +54,7 @@
 	return SW_OKAY;
 }
 
-sw_result HOWL_API
+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,
@@ -95,7 +103,7 @@
 	return SW_OKAY;
 }
 
-sw_result HOWL_API
+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,
@@ -154,19 +162,49 @@
 	return SW_OKAY;
 }
 
-int
-_mdns_publish(BonjourDnsSd *data, PublishType type)
+static void
+_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition)
 {
+	sw_discovery_read_socket((sw_discovery)data);
+}
+
+/****************************
+ * mdns_interface functions *
+ ****************************/
+
+gboolean _mdns_init_session(BonjourDnsSd *data) {
+	HowlSessionImplData *idata = g_new0(HowlSessionImplData, 1);
+
+	if (sw_discovery_init(&idata->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 */
+		idata->session = NULL;
+
+		g_free(idata);
+
+		return FALSE;
+	}
+
+	data->mdns_impl_data = idata;
+
+	return TRUE;
+}
+
+
+gboolean _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;
+	HowlSessionImplData *idata = data->mdns_impl_data;
+
+	g_return_val_if_fail(idata != NULL, FALSE);
 
 	/* Fill the data for the service */
-	if (sw_text_record_init(&dns_data) != SW_OKAY)
-	{
+	if (sw_text_record_init(&dns_data) != SW_OKAY) {
 		purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n");
-		return -1;
+		return FALSE;
 	}
 
 	/* Convert the port to a string */
@@ -208,32 +246,70 @@
 	/* TODO: ext, nick, node */
 
 	/* Publish the service */
-	switch (type)
-	{
+	switch (type) {
 		case PUBLISH_START:
-			publish_result = sw_discovery_publish(data->session, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL,
+			publish_result = sw_discovery_publish(idata->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);
+								_publish_reply, NULL, &idata->session_id);
 			break;
 		case PUBLISH_UPDATE:
-			publish_result = sw_discovery_publish_update(data->session, data->session_id,
+			publish_result = sw_discovery_publish_update(idata->session, idata->session_id,
 								sw_text_record_bytes(dns_data), sw_text_record_len(dns_data));
 			break;
 	}
-	if (publish_result != SW_OKAY)
-	{
+
+	if (publish_result != SW_OKAY) {
 		purple_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.\n");
-		return -1;
+		return FALSE;
 	}
 
 	/* Free the memory used by temp data */
 	sw_text_record_fina(dns_data);
 
-	return 0;
+	return TRUE;
+}
+
+gboolean _mdns_browse(BonjourDnsSd *data) {
+	HowlSessionImplData *idata = data->mdns_impl_data;
+	/* TODO: don't we need to hang onto this to cancel later? */
+	sw_discovery_oid session_id;
+
+	g_return_val_if_fail(idata != NULL, FALSE);
+
+	return (sw_discovery_browse(idata->session, 0, ICHAT_SERVICE, NULL, _browser_reply,
+				    data->account, &session_id) == SW_OKAY);
+}
+
+guint _mdns_register_to_mainloop(BonjourDnsSd *data) {
+	HowlSessionImplData *idata = data->mdns_impl_data;
+
+	g_return_val_if_fail(idata != NULL, FALSE);
+
+	return purple_input_add(sw_discovery_socket(idata->session),
+				PURPLE_INPUT_READ, _mdns_handle_event, idata->session);
 }
 
-void
-_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition)
-{
-	sw_discovery_read_socket((sw_discovery)data);
+void _mdns_stop(BonjourDnsSd *data) {
+	HowlSessionImplData *idata = data->mdns_impl_data;
+
+	if (idata == NULL || idata->session == NULL)
+		return;
+
+	sw_discovery_cancel(idata->session, idata->session_id);
+
+	/* TODO: should this really be g_free()'d ??? */
+	g_free(idata->session);
+
+	g_free(idata);
+
+	data->mdns_impl_data = NULL;
 }
+
+void _mdns_init_buddy(BonjourBuddy *buddy) {
+}
+
+void _mdns_delete_buddy(BonjourBuddy *buddy) {
+}
+
+void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) {
+}
--- a/libpurple/protocols/bonjour/mdns_howl.h	Sun Aug 05 19:41:08 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +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_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_interface.h	Sun Aug 05 19:42:29 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_INTERFACE
+#define _BONJOUR_MDNS_INTERFACE
+
+#include "mdns_types.h"
+#include "buddy.h"
+
+gboolean _mdns_init_session(BonjourDnsSd *data);
+
+gboolean _mdns_publish(BonjourDnsSd *data, PublishType type);
+
+gboolean _mdns_browse(BonjourDnsSd *data);
+
+guint _mdns_register_to_mainloop(BonjourDnsSd *data);
+
+void _mdns_stop(BonjourDnsSd *data);
+
+void _mdns_init_buddy(BonjourBuddy *buddy);
+
+void _mdns_delete_buddy(BonjourBuddy *buddy);
+
+/* This doesn't quite belong here, but there really isn't any shared functionality */
+void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy);
+
+#endif
--- a/libpurple/protocols/bonjour/mdns_types.h	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_types.h	Sun Aug 05 19:42:29 2007 +0000
@@ -19,31 +19,14 @@
 
 #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_handler; /* hack... windows bonjour is broken, so we have to have this */
-#else /* USE_BONJOUR_HOWL */
-	sw_discovery session;
-	sw_discovery_oid session_id;
-#endif
-
+typedef struct _BonjourDnsSd {
+	gpointer mdns_impl_data;
 	PurpleAccount *account;
 	gchar *first;
 	gchar *last;
@@ -59,5 +42,4 @@
 	PUBLISH_UPDATE
 } PublishType;
 
-
 #endif
--- a/libpurple/protocols/bonjour/mdns_win32.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Sun Aug 05 19:42:29 2007 +0000
@@ -15,22 +15,44 @@
  */
 
 #include "internal.h"
-#include "mdns_win32.h"
+#include "debug.h"
 
-#include "debug.h"
+#include "buddy.h"
+#include "mdns_interface.h"
+#include "dns_sd_proxy.h"
+#include "dnsquery.h"
+
 
 /* data structure for the resolve callback */
-typedef struct _ResolveCallbackArgs
-{
+typedef struct _ResolveCallbackArgs {
 	DNSServiceRef resolver;
-	int resolver_fd;
+	guint resolver_handler;
 
 	PurpleDnsQueryData *query;
-	gchar *fqn;
 
 	BonjourBuddy* buddy;
 } ResolveCallbackArgs;
 
+/* data used by win32 bonjour implementation */
+typedef struct _win32_session_impl_data {
+	DNSServiceRef advertisement;
+	DNSServiceRef browser;
+
+	guint advertisement_handler; /* hack... windows bonjour is broken, so we have to have this */
+} Win32SessionImplData;
+
+typedef struct _win32_buddy_impl_data {
+	DNSServiceRef txt_query;
+	guint txt_query_handler;
+	DNSServiceRef null_query;
+	guint null_query_handler;
+} Win32BuddyImplData;
+
+static void
+_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
+	DNSServiceProcessResult((DNSServiceRef) data);
+}
+
 static void
 _mdns_parse_text_record(BonjourBuddy* buddy, const char* record, uint16_t record_len)
 {
@@ -51,13 +73,32 @@
 	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");
+		purple_debug_error("bonjour", "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);
+		if (rrtype == kDNSServiceType_TXT) {
+			/* New Buddy */
+			BonjourBuddy *buddy = (BonjourBuddy*) context;
+			_mdns_parse_text_record(buddy, rdata, rdlen);
+			bonjour_buddy_add_to_purple(buddy);
+		} else if (rrtype == kDNSServiceType_NULL) {
+			/* Buddy Icon response */
+			BonjourBuddy *buddy = (BonjourBuddy*) context;
+			Win32BuddyImplData *idata = buddy->mdns_impl_data;
+
+			g_return_if_fail(idata != NULL);
+
+			purple_buddy_icons_set_for_user(buddy->account, buddy->name,
+							g_memdup(rdata, rdlen), rdlen, buddy->phsh);
+
+			/* We've got what we need; stop listening */
+			purple_input_remove(idata->null_query_handler);
+			idata->null_query_handler = -1;
+			DNSServiceRefDeallocate(idata->null_query);
+			idata->null_query = NULL;
+		}
 	}
 }
 
@@ -68,26 +109,26 @@
 
 	if (!hosts || !hosts->data)
 		purple_debug_error("bonjour", "host resolution - callback error.\n");
-	else
-	{
+	else {
 		struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1);
 		BonjourBuddy* buddy = args->buddy;
+		Win32BuddyImplData *idata = buddy->mdns_impl_data;
+
+		g_return_if_fail(idata != NULL);
 
 		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);
+		if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->txt_query, 0, 0, buddy->full_service_name,
+				kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) {
+			int fd = DNSServiceRefSockFD(idata->txt_query);
+			idata->txt_query_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->txt_query);
 
 			bonjour_buddy_add_to_purple(buddy);
 
 			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", buddy->name, buddy->ip, buddy->port_p2pj);
-		}
-		else
+		} else
 			bonjour_buddy_delete(buddy);
 
 	}
@@ -97,7 +138,6 @@
 
 	/* free the remaining args memory */
 	purple_dnsquery_destroy(args->query);
-	g_free(args->fqn);
 	g_free(args);
 }
 
@@ -108,7 +148,7 @@
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*)context;
 
 	/* remove the input fd and destroy the service ref */
-	purple_input_remove(args->resolver_fd);
+	purple_input_remove(args->resolver_handler);
 	DNSServiceRefDeallocate(args->resolver);
 
 	if (kDNSServiceErr_NoError != errorCode)
@@ -125,14 +165,13 @@
 		_mdns_parse_text_record(args->buddy, txtRecord, txtLen);
 
 		/* set more arguments, and start the host resolver */
-		args->fqn = g_strdup(fullname);
+		args->buddy->full_service_name = 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);
 		}
 	}
@@ -150,7 +189,7 @@
 		purple_debug_info("bonjour", "service advertisement - callback.\n");
 }
 
-void DNSSD_API
+static 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)
 {
@@ -159,50 +198,52 @@
 
 	if (kDNSServiceErr_NoError != errorCode)
 		purple_debug_error("bonjour", "service browser - callback error");
-	else if (flags & kDNSServiceFlagsAdd)
-	{
+	else if (flags & kDNSServiceFlagsAdd) {
 		/* A presence service instance has been discovered... check it isn't us! */
-		if (g_ascii_strcasecmp(serviceName, account->username) != 0)
-		{
+		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))
-			{
+			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
-			{
+			} 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);
+				gint fd = DNSServiceRefSockFD(args->resolver);
+				args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
 			}
 		}
-	}
-	else
-	{
+	} 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)
-		{
+		if (gb != NULL) {
 			bonjour_buddy_delete(gb->proto_data);
 			purple_blist_remove_buddy(gb);
 		}
 	}
 }
 
-int
-_mdns_publish(BonjourDnsSd *data, PublishType type)
-{
+/****************************
+ * mdns_interface functions *
+ ****************************/
+
+gboolean _mdns_init_session(BonjourDnsSd *data) {
+	data->mdns_impl_data = g_new0(Win32SessionImplData, 1);
+	return TRUE;
+}
+
+gboolean _mdns_publish(BonjourDnsSd *data, PublishType type) {
 	TXTRecordRef dns_data;
 	char portstring[6];
-	int ret = 0;
+	gboolean ret = TRUE;
 	const char *jid, *aim, *email;
 	DNSServiceErrorType set_ret;
+	Win32SessionImplData *idata = data->mdns_impl_data;
+
+	g_return_val_if_fail(idata != NULL, FALSE);
 
 	TXTRecordCreate(&dns_data, 256, NULL);
 
@@ -250,41 +291,34 @@
 
 	/* TODO: ext, nick, node */
 
-	if (set_ret != kDNSServiceErr_NoError)
-	{
+	if (set_ret != kDNSServiceErr_NoError) {
 		purple_debug_error("bonjour", "Unable to allocate memory for text record.\n");
-		ret = -1;
-	}
-	else
-	{
+		ret = FALSE;
+	} else {
 		DNSServiceErrorType err = kDNSServiceErr_NoError;
 
 		/* OK, we're done constructing the text record, (re)publish the service */
 
-		switch (type)
-		{
+		switch (type) {
 			case PUBLISH_START:
 				purple_debug_info("bonjour", "Registering service on port %d\n", data->port_p2pj);
-				err = DNSServiceRegister(&data->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
+				err = DNSServiceRegister(&idata->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);
+				err = DNSServiceUpdateRecord(idata->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
 				break;
 		}
 
-		if (kDNSServiceErr_NoError != err)
-		{
+		if (err != kDNSServiceErr_NoError) {
 			purple_debug_error("bonjour", "Failed to publish presence service.\n");
-			ret = -1;
-		}
-		else if (PUBLISH_START == type)
-		{
+			ret = FALSE;
+		} else if (type == PUBLISH_START) {
 			/* 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_handler = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement);
+			gint fd = DNSServiceRefSockFD(idata->advertisement);
+			idata->advertisement_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->advertisement);
 		}
 	}
 
@@ -293,8 +327,84 @@
 	return ret;
 }
 
-void
-_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition)
-{
-	DNSServiceProcessResult((DNSServiceRef)data);
+gboolean _mdns_browse(BonjourDnsSd *data) {
+	Win32SessionImplData *idata = data->mdns_impl_data;
+
+	g_return_val_if_fail(idata != NULL, FALSE);
+
+	return (DNSServiceBrowse(&idata->browser, 0, 0, ICHAT_SERVICE, NULL,
+				 _mdns_service_browse_callback, data->account)
+			== kDNSServiceErr_NoError);
+}
+
+guint _mdns_register_to_mainloop(BonjourDnsSd *data) {
+	Win32SessionImplData *idata = data->mdns_impl_data;
+
+	g_return_val_if_fail(idata != NULL, FALSE);
+
+	return purple_input_add(DNSServiceRefSockFD(idata->browser),
+				PURPLE_INPUT_READ, _mdns_handle_event, idata->browser);
+}
+
+void _mdns_stop(BonjourDnsSd *data) {
+	Win32SessionImplData *idata = data->mdns_impl_data;
+
+	if (idata == NULL || idata->advertisement == NULL || idata->browser == NULL)
+		return;
+
+	/* hack: for win32, we need to stop listening to the advertisement pipe too */
+	purple_input_remove(idata->advertisement_handler);
+
+	DNSServiceRefDeallocate(idata->advertisement);
+	DNSServiceRefDeallocate(idata->browser);
+
+	g_free(idata);
+
+	data->mdns_impl_data = NULL;
+}
+
+void _mdns_init_buddy(BonjourBuddy *buddy) {
+	buddy->mdns_impl_data = g_new0(Win32BuddyImplData, 1);
 }
+
+void _mdns_delete_buddy(BonjourBuddy *buddy) {
+	Win32BuddyImplData *idata = buddy->mdns_impl_data;
+
+	g_return_if_fail(idata != NULL);
+
+	if (idata->txt_query != NULL) {
+		purple_input_remove(idata->txt_query_handler);
+		DNSServiceRefDeallocate(idata->txt_query);
+	}
+
+	if (idata->null_query != NULL) {
+		purple_input_remove(idata->null_query_handler);
+		DNSServiceRefDeallocate(idata->null_query);
+	}
+
+	g_free(idata);
+
+	buddy->mdns_impl_data = NULL;
+}
+
+void bonjour_dns_sd_retrieve_buddy_icon(BonjourBuddy* buddy) {
+	Win32BuddyImplData *idata = buddy->mdns_impl_data;
+
+	g_return_if_fail(idata != NULL);
+
+	/* Cancel any existing query */
+	if (idata->null_query != NULL) {
+		purple_input_remove(idata->null_query_handler);
+		idata->null_query_handler = 0;
+		DNSServiceRefDeallocate(idata->null_query);
+		idata->null_query = NULL;
+	}
+
+	if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, 0, buddy->full_service_name,
+			kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) {
+		int fd = DNSServiceRefSockFD(idata->null_query);
+		idata->null_query_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
+	}
+
+}
+
--- a/libpurple/protocols/bonjour/mdns_win32.h	Sun Aug 05 19:41:08 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +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_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/pidgin/gtkconv.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/pidgin/gtkconv.c	Sun Aug 05 19:42:29 2007 +0000
@@ -6256,13 +6256,15 @@
 			(fields & PIDGIN_CONV_SET_TITLE) ||
     			(fields & PIDGIN_CONV_TOPIC))
 	{
-		char *title;
+		char *title, *truncate = NULL, truncchar;
 		PurpleConvIm *im = NULL;
 		PurpleAccount *account = purple_conversation_get_account(conv);
+	 	PurpleBuddy *buddy = NULL;
+		PurplePresence *p = NULL;
 		char *markup = NULL;
 		AtkObject *accessibility_obj;
 		/* I think this is a little longer than it needs to be but I'm lazy. */
-		char style[51];
+		char *style, *status_style;
 
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 			im = PURPLE_CONV_IM(conv);
@@ -6275,12 +6277,20 @@
 		else
 			title = g_strdup(purple_conversation_get_title(conv));
 
+		if ((truncate = strchr(title, ' ')) || 
+		    (truncate = strchr(title, '@'))) {
+			truncchar = *truncate;
+			*truncate = '\0';
+		}
+
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
-		 	PurpleBuddy *buddy = purple_find_buddy(account, conv->name);
-			if (buddy)
+			buddy = purple_find_buddy(account, conv->name);
+			if (buddy) {
+				p = purple_buddy_get_presence(buddy);
 				markup = pidgin_blist_get_name_markup(buddy, FALSE, FALSE);
-			else
+			} else {
 				markup = title;
+			}
 		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 			PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
 			const char *topic = purple_conv_chat_get_topic(chat);
@@ -6296,54 +6306,56 @@
 		if (title != markup)
 			g_free(markup);
 
-		*style = '\0';
-
 		if (!GTK_WIDGET_REALIZED(gtkconv->tab_label))
 			gtk_widget_realize(gtkconv->tab_label);
 
 		accessibility_obj = gtk_widget_get_accessible(gtkconv->tab_cont);
 		if (im != NULL &&
-		    purple_conv_im_get_typing_state(im) == PURPLE_TYPING)
-		{
+		    purple_conv_im_get_typing_state(im) == PURPLE_TYPING) {
 			atk_object_set_description(accessibility_obj, _("Typing"));
-			strncpy(style, "color=\"#4e9a06\"", sizeof(style));
-		}
-		else if (im != NULL &&
-		         purple_conv_im_get_typing_state(im) == PURPLE_TYPED)
-		{
+			style = "color=\"#4e9a06\"";
+		} else if (im != NULL &&
+		         purple_conv_im_get_typing_state(im) == PURPLE_TYPED) {
 			atk_object_set_description(accessibility_obj, _("Stopped Typing"));
-			strncpy(style, "color=\"#c4a000\"", sizeof(style));
-		}
-		else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK)
-		{
+			style = "color=\"#c4a000\"";
+		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK)	{
 			atk_object_set_description(accessibility_obj, _("Nick Said"));
-			strncpy(style, "color=\"#204a87\" style=\"italic\" weight=\"bold\"", sizeof(style));
-		}
-		else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT)
-		{
+			style = "color=\"#204a87\" style=\"italic\" weight=\"bold\"";
+		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT)	{
 			atk_object_set_description(accessibility_obj, _("Unread Messages"));
-			strncpy(style, "color=\"#cc0000\" weight=\"bold\"", sizeof(style));
+			style = "color=\"#cc0000\" weight=\"bold\"";
+		} else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) {
+			atk_object_set_description(accessibility_obj, _("New Event"));
+			style = "color=\"#888a85\" style=\"italic\"";
+		} else {
+			style = "";
 		}
-		else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT)
-		{
-			atk_object_set_description(accessibility_obj, _("New Event"));
-			strncpy(style, "color=\"#888a85\" style=\"italic\"", sizeof(style));
+		
+		if (p && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) {
+			status_style = "strikethrough='true'";
+		} else if (p && !purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE) &&
+			 !purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE)) {
+			status_style = "style='italic'";
+		} else {
+			status_style = "";
 		}
 
-		if (*style != '\0')
+		if (*style != '\0' || *status_style != '\0')
 		{
 			char *html_title,*label;
 
 			html_title = g_markup_escape_text(title, -1);
-
-			label = g_strdup_printf("<span %s>%s</span>",
-			                        style, html_title);
+			label = g_strdup_printf("<span %s %s>%s</span>",
+			                        style, status_style, html_title);
 			g_free(html_title);
 			gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label);
 			g_free(label);
 		}
 		else
 			gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
+		
+		if (truncate)
+			*truncate = truncchar;
 
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
@@ -8606,6 +8618,7 @@
 	gtk_misc_set_alignment(GTK_MISC(gtkconv->menu_label), 0, 0);
 
 	gtk_widget_show(gtkconv->menu_tabby);
+	gtk_widget_set_size_request(gtkconv->menu_tabby, 0, -1);
 
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 		pidgin_conv_update_buddy_icon(conv);
@@ -8722,7 +8735,7 @@
 					   TRUE, GTK_PACK_START);
 
 	/* show the widgets */
-	gtk_widget_show(gtkconv->icon);
+/*	gtk_widget_show(gtkconv->icon); */
 	gtk_widget_show(gtkconv->tab_label);
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs"))
 		gtk_widget_show(gtkconv->close);
--- a/pidgin/gtkimhtmltoolbar.c	Sun Aug 05 19:41:08 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sun Aug 05 19:42:29 2007 +0000
@@ -1109,7 +1109,6 @@
 	GtkWidget *font_menu;
 	GtkWidget *insert_menu;
 	GtkWidget *menuitem;
-	GtkWidget *button;
 	GtkWidget *sep;
 	int i;
 	struct {
@@ -1126,10 +1125,10 @@
 #endif
 		{_("_Smaller"), &toolbar->smaller_size, TRUE},
 		{_("_Font face"), &toolbar->font, TRUE},
-		{_("_Foreground color"), &toolbar->fgcolor, TRUE},
-		{_("_Background color"), &toolbar->bgcolor, TRUE},
+		{_("Foreground _color"), &toolbar->fgcolor, TRUE},
+		{_("Bac_kground color"), &toolbar->bgcolor, TRUE},
 		{_("_Reset formatting"), &toolbar->clear, FALSE},
-		{NULL, NULL}
+		{NULL, NULL, FALSE}
 	};