changeset 27747:2be2eec7d273

propagate from branch 'im.pidgin.pidgin' (head e1e3e43c119da08c2f86638d37867881f3742d4c) to branch 'im.pidgin.pidgin.yaz' (head b4b1a0c7e3730e0fff77d9d340c1d2ad577d2a8f)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sat, 15 Dec 2007 05:17:48 +0000
parents e13759a83714 (current diff) cc0809ec0c85 (diff)
children d0df170ca20a
files pidgin/gtkconv.c
diffstat 12 files changed, 482 insertions(+), 254 deletions(-) [+]
line wrap: on
line diff
--- a/doc/pidgin.1.in	Sat Dec 15 05:15:31 2007 +0000
+++ b/doc/pidgin.1.in	Sat Dec 15 05:17:48 2007 +0000
@@ -88,8 +88,8 @@
 .TP
 .B Add Buddy Pounce
 A Buddy Pounce is a configurable automated action to be performed when the
-buddy's state changes.  This will open the \fBBuddy Pounce\fR dialog to be
-discussed later.
+buddy's state changes.  This will open the \fBBuddy Pounce\fR dialog, which
+will be discussed later.
 .TP
 .B View Log
 Pidgin is capable of automatically logging messages.  These logs are
@@ -118,7 +118,7 @@
 
 .SH ACCOUNT EDITOR
 The account editor consists of a list of accounts and information about
-them.  It can be accessed by selecting \fBManage\fR from the Tools menu.
+them.  It can be accessed by selecting \fBManage\fR from the Accounts menu.
 Clicking \fIDelete\fR will delete the currently selected account.
 Clicking \fIAdd\fR or \fIModify\fR will invoke a \fBModify Account\fR
 window.  Here, the user  can add or alter account information.  When creating
--- a/libpurple/protocols/bonjour/bonjour.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Sat Dec 15 05:17:48 2007 +0000
@@ -109,7 +109,7 @@
 	gc->proto_data = bd = g_new0(BonjourData, 1);
 
 	/* Start waiting for jabber connections (iChat style) */
-	bd->jabber_data = g_new(BonjourJabber, 1);
+	bd->jabber_data = g_new0(BonjourJabber, 1);
 	bd->jabber_data->port = BONJOUR_DEFAULT_PORT_INT;
 	bd->jabber_data->account = account;
 
--- a/libpurple/protocols/bonjour/bonjour.h	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.h	Sat Dec 15 05:17:48 2007 +0000
@@ -44,7 +44,7 @@
 {
 	BonjourDnsSd *dns_sd_data;
 	BonjourJabber *jabber_data;
-	GList *xfer_lists;
+	GSList *xfer_lists;
 } BonjourData;
 
 #endif /* _BONJOUR_H_ */
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Sat Dec 15 05:17:48 2007 +0000
@@ -149,7 +149,7 @@
 static PurpleXfer*
 bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
 {
-	GList *xfers = NULL;
+	GSList *xfers = NULL;
 	PurpleXfer *xfer = NULL;
 	XepXfer *xf = NULL;
 
@@ -309,7 +309,7 @@
 	if(xf != NULL) {
 		bd = (BonjourData*)xf->data;
 		if(bd != NULL) {
-			bd->xfer_lists = g_list_remove(bd->xfer_lists, xfer);
+			bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer);
 			purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
 		}
 		if (xf->proxy_connection != NULL)
@@ -359,7 +359,7 @@
 	purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send);
 	purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
 
-	bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+	bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
 
 	return xfer;
 }
@@ -605,7 +605,7 @@
 	purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv);
 	purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
 
-	bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+	bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
 
 	purple_xfer_request(xfer);
 }
--- a/libpurple/protocols/bonjour/jabber.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Sat Dec 15 05:17:48 2007 +0000
@@ -78,7 +78,7 @@
 xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
 
 static BonjourJabberConversation *
-bonjour_jabber_conv_new(PurpleBuddy *pb) {
+bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
 
 	BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
 	bconv->socket = -1;
@@ -86,13 +86,14 @@
 	bconv->tx_handler = 0;
 	bconv->rx_handler = 0;
 	bconv->pb = pb;
+	bconv->account = account;
+	bconv->ip = g_strdup(ip);
 
 	bonjour_parser_setup(bconv);
 
 	return bconv;
 }
 
-
 static const char *
 _font_size_ichat_to_purple(int size)
 {
@@ -204,39 +205,36 @@
 	g_free(body);
 }
 
-struct _check_buddy_by_address_t {
+struct _match_buddies_by_address_t {
 	const char *address;
-	PurpleBuddy **pb;
-	BonjourJabber *bj;
+	GSList *matched_buddies;
+	BonjourJabber *jdata;
 };
 
 static void
-_check_buddy_by_address(gpointer key, gpointer value, gpointer data)
+_match_buddies_by_address(gpointer key, gpointer value, gpointer data)
 {
 	PurpleBuddy *pb = value;
-	BonjourBuddy *bb;
-	struct _check_buddy_by_address_t *cbba = data;
+	struct _match_buddies_by_address_t *mbba = data;
 
 	/*
 	 * If the current PurpleBuddy's data is not null and the PurpleBuddy's account
 	 * is the same as the account requesting the check then continue to determine
 	 * whether one of the buddies IPs matches the target IP.
 	 */
-	if (cbba->bj->account == pb->account)
+	if (mbba->jdata->account == pb->account && pb->proto_data != NULL)
 	{
-		bb = pb->proto_data;
-		if (bb != NULL) {
-			const char *ip;
-			GSList *tmp = bb->ips;
+		const char *ip;
+		BonjourBuddy *bb = pb->proto_data;
+		GSList *tmp = bb->ips;
 
-			while(tmp) {
-				ip = tmp->data;
-				if (ip != NULL && g_ascii_strcasecmp(ip, cbba->address) == 0) {
-					*(cbba->pb) = pb;
-					break;
-				}
-				tmp = tmp->next;
+		while(tmp) {
+			ip = tmp->data;
+			if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
+				mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
+				break;
 			}
+			tmp = tmp->next;
 		}
 	}
 }
@@ -249,8 +247,6 @@
 	BonjourJabberConversation *bconv = bb->conversation;
 	int ret, writelen;
 
-	/* TODO: Make sure that the stream has been established before sending */
-
 	writelen = purple_circ_buffer_get_max_read(bconv->tx_buf);
 
 	if (writelen == 0) {
@@ -338,6 +334,7 @@
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
 
 	g_return_if_fail(packet != NULL);
+	g_return_if_fail(pb != NULL);
 
 	if (!strcmp(packet->name, "message"))
 		_jabber_parse_and_write_message_to_ui(packet, pb);
@@ -351,31 +348,31 @@
 static void
 _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition)
 {
-	PurpleBuddy *pb = data;
+	BonjourJabberConversation *bconv = data;
 	gint len, message_length;
 	static char message[4096];
 
-	/*TODO: use a static buffer */
-
 	/* Read the data from the socket */
 	if ((len = recv(socket, message, sizeof(message) - 1, 0)) == -1) {
 		/* There have been an error reading from the socket */
 		if (errno != EAGAIN) {
-			BonjourBuddy *bb = pb->proto_data;
 			const char *err = g_strerror(errno);
 
 			purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)");
 
-			bonjour_jabber_close_conversation(bb->conversation);
-			bb->conversation = NULL;
+			bonjour_jabber_close_conversation(bconv);
+			if (bconv->pb != NULL) {
+				BonjourBuddy *bb = bconv->pb->proto_data;
+				bb->conversation = NULL;
+			}
 
 			/* I guess we really don't need to notify the user.
 			 * If they try to send another message it'll reconnect */
 		}
 		return;
 	} else if (len == 0) { /* The other end has closed the socket */
-		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", pb->name ? pb->name : "(null)");
-		bonjour_jabber_stream_ended(pb);
+		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (bconv->pb && bconv->pb->name) ? bconv->pb->name : "(unknown)");
+		bonjour_jabber_stream_ended(bconv);
 		return;
 	} else {
 		message_length = len;
@@ -389,30 +386,34 @@
 
 	purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", message, len);
 
-	bonjour_parser_process(pb, message, message_length);
+	bonjour_parser_process(bconv, message, message_length);
 }
 
-void bonjour_jabber_stream_ended(PurpleBuddy *pb) {
-	BonjourBuddy *bb = pb->proto_data;
+void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
 
-	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", pb->name);
-
-	g_return_if_fail(bb != NULL);
+	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)");
 
 	/* Inform the user that the conversation has been closed */
-	if (bb->conversation != NULL) {
+	if (bconv != NULL) {
+		BonjourBuddy *bb = NULL;
+
+		if(bconv->pb != NULL)
+			bb = bconv->pb->proto_data;
 #if 0
-		PurpleConversation *conv;
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, pb->account);
-		if (conv != NULL) {
-			char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name);
-			purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
-			g_free(tmp);
+		if(bconv->pb != NULL) {
+			PurpleConversation *conv;
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account);
+			if (conv != NULL) {
+				char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name);
+				purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+				g_free(tmp);
+			}
 		}
 #endif
 		/* Close the socket, clear the watcher and free memory */
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
+		bonjour_jabber_close_conversation(bconv);
+		if(bb)
+			bb->conversation = NULL;
 	}
 }
 
@@ -425,9 +426,7 @@
 static void
 _start_stream(gpointer data, gint source, PurpleInputCondition condition)
 {
-	PurpleBuddy *pb = data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = data;
 	struct _stream_start_data *ss = bconv->stream_data;
 	int len, ret;
 
@@ -441,23 +440,26 @@
 	else if (ret <= 0) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
-		const char *ip = NULL;
+		const char *bname = bconv->buddy_name;
+		BonjourBuddy *bb = NULL;
 
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
+		if(bconv->pb) {
+			bb = bconv->pb->proto_data;
+			bname = purple_buddy_get_name(bconv->pb);
+		}
 
-		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
+				   bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
 				  _("Unable to send the message, the conversation couldn't be started."),
 				  PURPLE_MESSAGE_SYSTEM, time(NULL));
 
 		bonjour_jabber_close_conversation(bconv);
-		bb->conversation = NULL;
+		if(bb != NULL)
+			bb->conversation = NULL;
 
 		return;
 	}
@@ -479,20 +481,27 @@
 	bconv->tx_handler = 0;
 	bconv->sent_stream_start = FULLY_SENT;
 
-	bonjour_jabber_stream_started(pb);
+	bonjour_jabber_stream_started(bconv);
 }
 
-static gboolean bonjour_jabber_send_stream_init(PurpleBuddy *pb, int client_socket)
+static gboolean bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv, int client_socket)
 {
 	int ret, len;
 	char *stream_start;
-	BonjourBuddy *bb = pb->proto_data;
+	const char *bname = bconv->buddy_name;
+
+	if (bconv->pb != NULL)
+		bname = purple_buddy_get_name(bconv->pb);
 
-	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account),
-								   purple_buddy_get_name(pb));
+	/* If we have no idea who "to" is, use an empty string.
+	 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
+	if (bname == NULL)
+		bname = "";
+
+	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(bconv->account), bname);
 	len = strlen(stream_start);
 
-	bb->conversation->sent_stream_start = PARTIALLY_SENT;
+	bconv->sent_stream_start = PARTIALLY_SENT;
 
 	/* Start the stream */
 	ret = send(client_socket, stream_start, len, 0);
@@ -501,14 +510,18 @@
 		ret = 0;
 	else if (ret <= 0) {
 		const char *err = g_strerror(errno);
-		const char *ip = NULL;
+
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
+				   (*bname) ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
 
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
-
-		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+		if (bconv->pb) {
+			PurpleConversation *conv;
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+			if (conv != NULL)
+				purple_conversation_write(conv, NULL,
+					  _("Unable to send the message, the conversation couldn't be started."),
+					  PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
 
 		close(client_socket);
 		g_free(stream_start);
@@ -520,63 +533,61 @@
 	if (ret < len) {
 		struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
 		ss->msg = g_strdup(stream_start + ret);
-		bb->conversation->stream_data = ss;
+		bconv->stream_data = ss;
 		/* Finish sending the stream start */
-		bb->conversation->tx_handler = purple_input_add(client_socket,
-			PURPLE_INPUT_WRITE, _start_stream, pb);
+		bconv->tx_handler = purple_input_add(client_socket,
+			PURPLE_INPUT_WRITE, _start_stream, bconv);
 	} else
-		bb->conversation->sent_stream_start = FULLY_SENT;
+		bconv->sent_stream_start = FULLY_SENT;
 
 	g_free(stream_start);
 
 	return TRUE;
 }
 
-static gboolean
-_async_bonjour_jabber_close_conversation(gpointer data) {
-	BonjourJabberConversation *bconv = data;
-	bonjour_jabber_close_conversation(bconv);
-	return FALSE;
-}
+/* This gets called when we've successfully sent our <stream:stream />
+ * AND when we've recieved a <stream:stream /> */
+void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
 
-void bonjour_jabber_stream_started(PurpleBuddy *pb) {
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
-
-	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(pb, bconv->socket)) {
+	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(bconv, bconv->socket)) {
 		const char *err = g_strerror(errno);
-		PurpleConversation *conv;
-		const char *ip = NULL;
+		const char *bname = bconv->buddy_name;
 
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
+		if (bconv->pb)
+			bname = purple_buddy_get_name(bconv->pb);
+
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
+				   bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
 
-		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+		if (bconv->pb) {
+			PurpleConversation *conv;
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
+			if (conv != NULL)
+				purple_conversation_write(conv, NULL,
+					  _("Unable to send the message, the conversation couldn't be started."),
+					  PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
-		if (conv != NULL)
-			purple_conversation_write(conv, NULL,
-				  _("Unable to send the message, the conversation couldn't be started."),
-				  PURPLE_MESSAGE_SYSTEM, time(NULL));
+		/* We don't want to recieve anything else */
+		close(bconv->socket);
+		bconv->socket = -1;
 
-		close(bconv->socket);
 		/* This must be asynchronous because it destroys the parser and we
 		 * may be in the middle of parsing.
 		 */
-		purple_timeout_add(0, _async_bonjour_jabber_close_conversation, bb->conversation);
-		bb->conversation = NULL;
+		async_bonjour_jabber_close_conversation(bconv);
 		return;
 	}
 
-	/* If the stream has been completely started, we can start doing stuff */
-	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+	/* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
+	/* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
+	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
+			&& bconv->pb && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
 		/* Watch for when we can write the buffered messages */
 		bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
-			_send_data_write_cb, pb);
+			_send_data_write_cb, bconv->pb);
 		/* We can probably write the data right now. */
-		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
+		_send_data_write_cb(bconv->pb, bconv->socket, PURPLE_INPUT_WRITE);
 	}
 
 }
@@ -584,15 +595,14 @@
 static void
 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
 {
-	PurpleBuddy *pb = NULL;
+	BonjourJabber *jdata = data;
 	struct sockaddr_in their_addr; /* connector's address information */
 	socklen_t sin_size = sizeof(struct sockaddr);
 	int client_socket;
 	int flags;
-	BonjourBuddy *bb;
 	char *address_text = NULL;
-	PurpleBuddyList *bl = purple_get_blist();
-	struct _check_buddy_by_address_t *cbba;
+	struct _match_buddies_by_address_t *mbba;
+	BonjourJabberConversation *bconv;
 
 	/* Check that it is a read condition */
 	if (condition != PURPLE_INPUT_READ)
@@ -607,39 +617,35 @@
 	/* Look for the buddy that has opened the conversation and fill information */
 	address_text = inet_ntoa(their_addr.sin_addr);
 	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
-	cbba = g_new0(struct _check_buddy_by_address_t, 1);
-	cbba->address = address_text;
-	cbba->pb = &pb;
-	cbba->bj = data;
-	g_hash_table_foreach(bl->buddies, _check_buddy_by_address, cbba);
-	g_free(cbba);
-	if (pb == NULL)
-	{
+	mbba = g_new0(struct _match_buddies_by_address_t, 1);
+	mbba->address = address_text;
+	mbba->jdata = jdata;
+	g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
+
+	if (mbba->matched_buddies == NULL) {
 		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
+		g_slist_free(mbba->matched_buddies);
+		g_free(mbba);
 		close(client_socket);
 		return;
 	}
-	bb = pb->proto_data;
 
-	/* Check if the conversation has been previously started */
-	/* This really shouldn't ever happen unless something weird is going on */
-	if (bb->conversation == NULL)
-	{
-		bb->conversation = bonjour_jabber_conv_new(pb);
+	g_slist_free(mbba->matched_buddies);
+	g_free(mbba);
 
-		/* We wait for the stream start before doing anything else */
-		bb->conversation->socket = client_socket;
-		bb->conversation->rx_handler = purple_input_add(client_socket,
-			PURPLE_INPUT_READ, _client_socket_handler, pb);
+	/* We've established that this *could* be from one of our buddies.
+	 * Wait for the stream open to see if that matches too before assigning it.
+	 */
+	bconv = bonjour_jabber_conv_new(NULL, jdata->account, address_text);
 
-	} else {
-		purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n");
-		close(client_socket);
-	}
+	/* We wait for the stream start before doing anything else */
+	bconv->socket = client_socket;
+	bconv->rx_handler = purple_input_add(client_socket, PURPLE_INPUT_READ, _client_socket_handler, bconv);
+
 }
 
 gint
-bonjour_jabber_start(BonjourJabber *data)
+bonjour_jabber_start(BonjourJabber *jdata)
 {
 	struct sockaddr_in my_addr;
 	int yes = 1;
@@ -647,20 +653,20 @@
 	gboolean bind_successful;
 
 	/* Open a listening socket for incoming conversations */
-	if ((data->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+	if ((jdata->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
 	{
 		purple_debug_error("bonjour", "Cannot open socket: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Cannot open socket"));
 		return -1;
 	}
 
 	/* Make the socket reusable */
-	if (setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
+	if (setsockopt(jdata->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
 	{
 		purple_debug_error("bonjour", "Error setting socket options: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Error setting socket options"));
 		return -1;
@@ -673,30 +679,30 @@
 	bind_successful = FALSE;
 	for (i = 0; i < 10; i++)
 	{
-		my_addr.sin_port = htons(data->port);
-		if (bind(data->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == 0)
+		my_addr.sin_port = htons(jdata->port);
+		if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == 0)
 		{
 			bind_successful = TRUE;
 			break;
 		}
-		data->port++;
+		jdata->port++;
 	}
 
 	/* On no!  We tried 10 ports and could not bind to ANY of them */
 	if (!bind_successful)
 	{
 		purple_debug_error("bonjour", "Cannot bind socket: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Could not bind socket to port"));
 		return -1;
 	}
 
 	/* Attempt to listen on the bound socket */
-	if (listen(data->socket, 10) != 0)
+	if (listen(jdata->socket, 10) != 0)
 	{
 		purple_debug_error("bonjour", "Cannot listen on socket: %s\n", g_strerror(errno));
-		purple_connection_error_reason (data->account->gc,
+		purple_connection_error_reason (jdata->account->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Could not listen on socket"));
 		return -1;
@@ -704,18 +710,18 @@
 
 #if 0
 	/* TODO: Why isn't this being used? */
-	data->socket = purple_network_listen(data->port, SOCK_STREAM);
+	data->socket = purple_network_listen(jdata->port, SOCK_STREAM);
 
-	if (data->socket == -1)
+	if (jdata->socket == -1)
 	{
 		purple_debug_error("bonjour", "No se ha podido crear el socket\n");
 	}
 #endif
 
 	/* Open a watcher in the socket we have just opened */
-	data->watcher_id = purple_input_add(data->socket, PURPLE_INPUT_READ, _server_socket_handler, data);
+	jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
 
-	return data->port;
+	return jdata->port;
 }
 
 static void
@@ -728,14 +734,9 @@
 
 	if (source < 0) {
 		PurpleConversation *conv;
-		const char *ip = NULL;
-
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, error ? error : "(null)");
+				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error ? error : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -748,17 +749,12 @@
 		return;
 	}
 
-	if (!bonjour_jabber_send_stream_init(pb, source)) {
+	if (!bonjour_jabber_send_stream_init(bb->conversation, source)) {
 		const char *err = g_strerror(errno);
 		PurpleConversation *conv;
-		const char *ip = NULL;
-
-		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, err ? err : "(null)");
 
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
 		if (conv != NULL)
@@ -775,19 +771,116 @@
 	/* Start listening for the stream acknowledgement */
 	bb->conversation->socket = source;
 	bb->conversation->rx_handler = purple_input_add(source,
-		PURPLE_INPUT_READ, _client_socket_handler, pb);
+		PURPLE_INPUT_READ, _client_socket_handler, bb->conversation);
+}
+
+void
+bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
+	PurpleBuddy *pb;
+
+	g_return_if_fail(bconv->ip != NULL);
+	g_return_if_fail(bconv->pb == NULL);
+
+	pb = purple_find_buddy(bconv->account, bconv->buddy_name);
+	if (pb && pb->proto_data) {
+		BonjourBuddy *bb = pb->proto_data;
+		const char *ip;
+		GSList *tmp = bb->ips;
+
+		purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
+			purple_buddy_get_name(pb));
+
+		/* Check that one of the buddy's IPs matches */
+		while(tmp) {
+			ip = tmp->data;
+			if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
+				BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+
+				purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
+					purple_buddy_get_name(pb), bconv->ip);
+
+				/* Attach conv. to buddy and remove from pending list */
+				jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+				/* Check if the buddy already has a conversation and, if so, replace it */
+				if(bb->conversation != NULL && bb->conversation != bconv)
+					bonjour_jabber_close_conversation(bb->conversation);
+
+				bconv->pb = pb;
+				bb->conversation = bconv;
+
+				break;
+			}
+			tmp = tmp->next;
+		}
+	}
+
+	/* We've failed to match a buddy - give up */
+	if (bconv->pb == NULL) {
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_jabber_close_conversation(bconv);
+	}
+}
+
+
+void
+bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
+	BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+	struct _match_buddies_by_address_t *mbba;
+
+	mbba = g_new0(struct _match_buddies_by_address_t, 1);
+	mbba->address = bconv->ip;
+	mbba->jdata = jdata;
+	g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
+
+	/* If there is exactly one match, use it */
+	if(mbba->matched_buddies != NULL) {
+		if(mbba->matched_buddies->next != NULL)
+			purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
+		else {
+			PurpleBuddy *pb = mbba->matched_buddies->data;
+			BonjourBuddy *bb = pb->proto_data;
+
+			purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
+				purple_buddy_get_name(pb), bconv->ip);
+
+			/* Attach conv. to buddy and remove from pending list */
+			jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+			/* Check if the buddy already has a conversation and, if so, replace it */
+			if (bb->conversation != NULL && bb->conversation != bconv)
+				bonjour_jabber_close_conversation(bb->conversation);
+
+			bconv->pb = pb;
+			bb->conversation = bconv;
+		}
+	} else
+		purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
+
+	/* We've failed to match a buddy - give up */
+	if (bconv->pb == NULL) {
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_jabber_close_conversation(bconv);
+	}
+
+	g_slist_free(mbba->matched_buddies);
+	g_free(mbba);
 }
 
 static PurpleBuddy *
-_find_or_start_conversation(BonjourJabber *data, const gchar *to)
+_find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
 {
 	PurpleBuddy *pb = NULL;
 	BonjourBuddy *bb = NULL;
 
-	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(jdata != NULL, NULL);
 	g_return_val_if_fail(to != NULL, NULL);
 
-	pb = purple_find_buddy(data->account, to);
+	pb = purple_find_buddy(jdata->account, to);
 	if (pb == NULL || pb->proto_data == NULL)
 		/* You can not send a message to an offline buddy */
 		return NULL;
@@ -799,24 +892,21 @@
 	{
 		PurpleProxyConnectData *connect_data;
 		PurpleProxyInfo *proxy_info;
-		const char *ip = NULL;
-
 		/* For better or worse, use the first IP*/
-		if (bb->ips)
-			ip = bb->ips->data;
+		const char *ip = bb->ips->data;
 
 		purple_debug_info("bonjour", "Starting conversation with %s\n", to);
 
 		/* Make sure that the account always has a proxy of "none".
 		 * This is kind of dirty, but proxy_connect_none() isn't exposed. */
-		proxy_info = purple_account_get_proxy_info(data->account);
+		proxy_info = purple_account_get_proxy_info(jdata->account);
 		if (proxy_info == NULL) {
 			proxy_info = purple_proxy_info_new();
-			purple_account_set_proxy_info(data->account, proxy_info);
+			purple_account_set_proxy_info(jdata->account, proxy_info);
 		}
 		purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
 
-		connect_data = purple_proxy_connect(NULL, data->account,
+		connect_data = purple_proxy_connect(NULL, jdata->account,
 						    ip, bb->port_p2pj, _connected_to_buddy, pb);
 
 		if (connect_data == NULL) {
@@ -824,7 +914,7 @@
 			return NULL;
 		}
 
-		bb->conversation = bonjour_jabber_conv_new(pb);
+		bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
 		bb->conversation->connect_data = connect_data;
 		/* We don't want _send_data() to register the tx_handler;
 		 * that neeeds to wait until we're actually connected. */
@@ -834,7 +924,7 @@
 }
 
 int
-bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
+bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
 {
 	xmlnode *message_node, *node, *node2;
 	gchar *message;
@@ -842,7 +932,7 @@
 	BonjourBuddy *bb;
 	int ret;
 
-	pb = _find_or_start_conversation(data, to);
+	pb = _find_or_start_conversation(jdata, to);
 	if (pb == NULL) {
 		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
 		/* You can not send a message to an offline buddy */
@@ -853,7 +943,7 @@
 
 	message_node = xmlnode_new("message");
 	xmlnode_set_attrib(message_node, "to", bb->name);
-	xmlnode_set_attrib(message_node, "from", purple_account_get_username(data->account));
+	xmlnode_set_attrib(message_node, "from", purple_account_get_username(jdata->account));
 	xmlnode_set_attrib(message_node, "type", "chat");
 
 	/* Enclose the message from the UI within a "font" node */
@@ -885,26 +975,57 @@
 	return ret;
 }
 
+static gboolean
+_async_bonjour_jabber_close_conversation_cb(gpointer data) {
+	BonjourJabberConversation *bconv = data;
+	bonjour_jabber_close_conversation(bconv);
+	return FALSE;
+}
+
+void
+async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
+	BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+
+	jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+	/* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
+	if(bconv->pb != NULL) {
+		BonjourBuddy *bb = bconv->pb->proto_data;
+		if (bb->conversation == bconv)
+			bb->conversation = NULL;
+	}
+
+	purple_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv);
+}
+
 void
 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
 {
 	if (bconv != NULL) {
-		GList *xfers, *tmp_next;
-		BonjourData *bd = bconv->pb->account->gc->proto_data;
+		BonjourData *bd = NULL;
+
+		if(PURPLE_CONNECTION_IS_VALID(bconv->account->gc)) {
+			bd = bconv->account->gc->proto_data;
+			bd->jabber_data->pending_conversations = g_slist_remove(bd->jabber_data->pending_conversations, bconv);
+		}
 
 		/* Cancel any file transfers that are waiting to begin */
-		xfers = bd->xfer_lists;
-		while(xfers != NULL) {
-			PurpleXfer *xfer = xfers->data;
-			tmp_next = xfers->next;
-			/* We only need to cancel this if it hasn't actually started transferring. */
-			/* This will change if we ever support IBB transfers. */
-			if (strcmp(xfer->who, bconv->pb->name) == 0
-					&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
-					    || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
-				purple_xfer_cancel_remote(xfer);
+		/* There wont be any transfers if it hasn't been attached to a buddy */
+		if (bconv->pb != NULL && bd != NULL) {
+			GSList *xfers, *tmp_next;
+			xfers = bd->xfer_lists;
+			while(xfers != NULL) {
+				PurpleXfer *xfer = xfers->data;
+				tmp_next = xfers->next;
+				/* We only need to cancel this if it hasn't actually started transferring. */
+				/* This will change if we ever support IBB transfers. */
+				if (strcmp(xfer->who, bconv->pb->name) == 0
+						&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
+							|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
+					purple_xfer_cancel_remote(xfer);
+				}
+				xfers = tmp_next;
 			}
-			xfers = tmp_next;
 		}
 
 		/* Close the socket and remove the watcher */
@@ -933,25 +1054,26 @@
 		if (bconv->context != NULL)
 			bonjour_parser_setup(bconv);
 
+		g_free(bconv->buddy_name);
+		g_free(bconv->ip);
 		g_free(bconv);
 	}
 }
 
 void
-bonjour_jabber_stop(BonjourJabber *data)
+bonjour_jabber_stop(BonjourJabber *jdata)
 {
 	/* Close the server socket and remove the watcher */
-	if (data->socket >= 0)
-		close(data->socket);
-	if (data->watcher_id > 0)
-		purple_input_remove(data->watcher_id);
+	if (jdata->socket >= 0)
+		close(jdata->socket);
+	if (jdata->watcher_id > 0)
+		purple_input_remove(jdata->watcher_id);
 
 	/* Close all the conversation sockets and remove all the watchers after sending end streams */
-	if (data->account->gc != NULL)
-	{
+	if (jdata->account->gc != NULL) {
 		GSList *buddies, *l;
 
-		buddies = purple_find_buddies(data->account, purple_account_get_username(data->account));
+		buddies = purple_find_buddies(jdata->account, NULL);
 		for (l = buddies; l; l = l->next) {
 			BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data;
 			bonjour_jabber_close_conversation(bb->conversation);
@@ -960,6 +1082,11 @@
 
 		g_slist_free(buddies);
 	}
+
+	while (jdata->pending_conversations != NULL) {
+		bonjour_jabber_close_conversation(jdata->pending_conversations->data);
+		jdata->pending_conversations = g_slist_delete_link(jdata->pending_conversations, jdata->pending_conversations);
+	}
 }
 
 XepIq *
@@ -1056,7 +1183,7 @@
 	PurpleBuddy *pb = NULL;
 
 	/* start the talk, reuse the message socket  */
-	pb = _find_or_start_conversation ((BonjourJabber*)iq->data, iq->to);
+	pb = _find_or_start_conversation((BonjourJabber*) iq->data, iq->to);
 	/* Send the message */
 	if (pb != NULL) {
 		/* Convert xml node into stream */
--- a/libpurple/protocols/bonjour/jabber.h	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Sat Dec 15 05:17:48 2007 +0000
@@ -38,7 +38,8 @@
 	gint port;
 	gint socket;
 	gint watcher_id;
-	PurpleAccount* account;
+	PurpleAccount *account;
+	GSList *pending_conversations;
 } BonjourJabber;
 
 typedef struct _BonjourJabberConversation
@@ -54,6 +55,11 @@
 	xmlParserCtxt *context;
 	xmlnode *current;
 	PurpleBuddy *pb;
+	PurpleAccount *account;
+
+	/* The following are only needed before attaching to a PurpleBuddy */
+	gchar *buddy_name;
+	gchar *ip;
 } BonjourJabberConversation;
 
 /**
@@ -68,14 +74,20 @@
 
 void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
 
-void bonjour_jabber_stream_started(PurpleBuddy *pb);
+void async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
 
-void bonjour_jabber_stream_ended(PurpleBuddy *pb);
+void bonjour_jabber_stream_started(BonjourJabberConversation *bconv);
+
+void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv);
 
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet);
 
 void bonjour_jabber_stop(BonjourJabber *data);
 
+void bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv);
+
+void bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv);
+
 typedef enum {
 	XEP_IQ_SET,
 	XEP_IQ_GET,
--- a/libpurple/protocols/bonjour/parser.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Sat Dec 15 05:17:48 2007 +0000
@@ -31,26 +31,59 @@
 #include "util.h"
 #include "xmlnode.h"
 
+static gboolean
+parse_from_attrib_and_find_buddy(BonjourJabberConversation *bconv, int nb_attributes, const xmlChar **attributes) {
+	int i;
+
+	/* If the "from" attribute is specified, attach it to the conversation. */
+	for(i=0; i < nb_attributes * 5; i+=5) {
+		if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) {
+			int len = attributes[i+4] - attributes[i+3];
+			bconv->buddy_name = g_strndup(attributes[i+3], len);
+			bonjour_jabber_conv_match_by_name(bconv);
+
+			return (bconv->pb != NULL);
+		}
+	}
+
+	return FALSE;
+}
+
 static void
 bonjour_parser_element_start_libxml(void *user_data,
 				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
 				   int nb_namespaces, const xmlChar **namespaces,
 				   int nb_attributes, int nb_defaulted, const xmlChar **attributes)
 {
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = user_data;
 
 	xmlnode *node;
 	int i;
 
-	if(!element_name) {
-		return;
-	} else if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
-		bconv->recv_stream_start = TRUE;
-		bonjour_jabber_stream_started(pb);
+	g_return_if_fail(element_name != NULL);
+
+	if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
+		if(!bconv->recv_stream_start) {
+			bconv->recv_stream_start = TRUE;
+
+			if (bconv->pb == NULL)
+				parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes);
+
+			bonjour_jabber_stream_started(bconv);
+		}
 	} else {
 
+		/* If we haven't yet attached a buddy and this isn't "<stream:features />",
+		 * try to get a "from" attribute as a last resort to match our buddy. */
+		if(bconv->pb == NULL
+				&& !(prefix && !xmlStrcmp(prefix, (xmlChar*) "stream")
+					&& !xmlStrcmp(element_name, (xmlChar*) "features"))
+				&& !parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes))
+			/* We've run out of options for finding who the conversation is from
+			   using explicitly specified stuff; see if we can make a good match
+			   by using the IP */
+			bonjour_jabber_conv_match_by_ip(bconv);
+
 		if(bconv->current)
 			node = xmlnode_new_child(bconv->current, (const char*) element_name);
 		else
@@ -82,27 +115,19 @@
 	}
 }
 
-static gboolean _async_bonjour_jabber_stream_ended_cb(gpointer data) {
-	bonjour_jabber_stream_ended((PurpleBuddy *) data);
-	return FALSE;
-}
-
 static void
 bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
 				 const xmlChar *prefix, const xmlChar *namespace)
 {
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = user_data;
 
 	if(!bconv->current) {
 		/* We don't keep a reference to the start stream xmlnode,
 		 * so we have to check for it here to close the conversation */
-		if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
+		if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
 			/* Asynchronously close the conversation to prevent bonjour_parser_setup()
 			 * being called from within this context */
-			purple_timeout_add(0, _async_bonjour_jabber_stream_ended_cb, pb);
-		}
+			async_bonjour_jabber_close_conversation(bconv);
 		return;
 	}
 
@@ -112,7 +137,7 @@
 	} else {
 		xmlnode *packet = bconv->current;
 		bconv->current = NULL;
-		bonjour_jabber_process_packet(pb, packet);
+		bonjour_jabber_process_packet(bconv->pb, packet);
 		xmlnode_free(packet);
 	}
 }
@@ -120,9 +145,7 @@
 static void
 bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
 {
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = pb->proto_data;
-	BonjourJabberConversation *bconv = bb->conversation;
+	BonjourJabberConversation *bconv = user_data;
 
 	if(!bconv->current)
 		return;
@@ -184,21 +207,17 @@
 }
 
 
-void bonjour_parser_process(PurpleBuddy *pb, const char *buf, int len)
+void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len)
 {
-	BonjourBuddy *bb = pb->proto_data;
 
-	g_return_if_fail(bb != NULL);
-	g_return_if_fail(bb->conversation != NULL);
-
-	if (bb->conversation->context ==  NULL) {
+	if (bconv->context == NULL) {
 		/* libxml inconsistently starts parsing on creating the
 		 * parser, so do a ParseChunk right afterwards to force it. */
-		bb->conversation->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, pb, buf, len, NULL);
-		xmlParseChunk(bb->conversation->context, "", 0, 0);
-	} else if (xmlParseChunk(bb->conversation->context, buf, len, 0) < 0) {
+		bconv->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, bconv, buf, len, NULL);
+		xmlParseChunk(bconv->context, "", 0, 0);
+	} else if (xmlParseChunk(bconv->context, buf, len, 0) < 0)
 		/* TODO: What should we do here - I assume we should display an error or something (maybe just print something to the conv?) */
 		purple_debug_error("bonjour", "Error parsing xml.\n");
-	}
+
 }
 
--- a/libpurple/protocols/bonjour/parser.h	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/bonjour/parser.h	Sat Dec 15 05:17:48 2007 +0000
@@ -28,6 +28,6 @@
 #include "jabber.h"
 
 void bonjour_parser_setup(BonjourJabberConversation *bconv);
-void bonjour_parser_process(PurpleBuddy *pb, const char *buf, int len);
+void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len);
 
 #endif /* _PURPLE_BONJOUR_PARSER_H_ */
--- a/libpurple/protocols/simple/sipmsg.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/libpurple/protocols/simple/sipmsg.c	Sat Dec 15 05:17:48 2007 +0000
@@ -45,7 +45,10 @@
 	line = g_strndup(msg, tmp - msg);
 
 	smsg = sipmsg_parse_header(line);
-	smsg->body = g_strdup(tmp + 4);
+	if(smsg != NULL)
+		smsg->body = g_strdup(tmp + 4);
+	else
+		purple_debug_error("SIMPLE", "No header parsed from line: %s\n", line);
 
 	g_free(line);
 	return smsg;
--- a/pidgin/gtkconn.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/pidgin/gtkconn.c	Sat Dec 15 05:17:48 2007 +0000
@@ -184,36 +184,39 @@
 
 static void pidgin_connection_network_connected ()
 {
-	GList *list = purple_accounts_get_all_active();
+	GList *list, *l;
 	PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist();
 
 	if(gtkblist)
 		pidgin_status_box_set_network_available(PIDGIN_STATUS_BOX(gtkblist->statusbox), TRUE);
 
-	while (list) {
-		PurpleAccount *account = (PurpleAccount*)list->data;
+	l = list = purple_accounts_get_all_active();
+	while (l) {
+		PurpleAccount *account = (PurpleAccount*)l->data;
 		g_hash_table_remove(auto_reconns, account);
 		if (purple_account_is_disconnected(account))
 			do_signon(account);
-		list = list->next;
+		l = l->next;
 	}
+	g_list_free(list);
 }
 
 static void pidgin_connection_network_disconnected ()
 {
-	GList *l = purple_accounts_get_all_active();
+	GList *list, *l;
 	PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist();
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = NULL;
-	
+
 	if(gtkblist)
 		pidgin_status_box_set_network_available(PIDGIN_STATUS_BOX(gtkblist->statusbox), FALSE);
 
+	l = list = purple_accounts_get_all_active();
 	while (l) {
 		PurpleAccount *a = (PurpleAccount*)l->data;
 		if (!purple_account_is_disconnected(a)) {
 			gc = purple_account_get_connection(a);
-			if (gc && gc->prpl) 
+			if (gc && gc->prpl)
 				prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 			if (prpl_info) {
 				if (prpl_info->keepalive)
@@ -224,6 +227,7 @@
 		}
 		l = l->next;
 	}
+	g_list_free(list);
 }
 
 static void pidgin_connection_notice(PurpleConnection *gc, const char *text)
--- a/pidgin/gtkconv.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/pidgin/gtkconv.c	Sat Dec 15 05:17:48 2007 +0000
@@ -4420,16 +4420,17 @@
 
 	lines = gtk_text_buffer_get_line_count(buffer);
 
-	/* Show a maximum of 4 lines */
-	lines = MIN(lines, 4);
-	wrapped_lines = MIN(wrapped_lines, 4);
+	/* Show a maximum of 4 lines, minimum of 2 */
+	lines = MIN(MAX(lines, 2), 4);
+	wrapped_lines = MIN(MAX(wrapped_lines, 2), 4);
 
 	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
 	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(gtkconv->entry));
 	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(gtkconv->entry));
 
-	height = (oneline.height + pad_top + pad_bottom) * MAX(lines, 2);
-	height += (oneline.height + pad_inside) * (wrapped_lines - lines);
+	height = (oneline.height + pad_top + pad_bottom) * lines;
+	if (wrapped_lines > lines)
+		height += (oneline.height + pad_inside) * (wrapped_lines - lines);
 
 	gtkconv->auto_resize = TRUE;
 	g_idle_add(reset_auto_resize_cb, gtkconv);
--- a/pidgin/win32/gtkwin32dep.c	Sat Dec 15 05:15:31 2007 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Sat Dec 15 05:17:48 2007 +0000
@@ -40,6 +40,7 @@
 
 #include "debug.h"
 #include "notify.h"
+#include "network.h"
 
 #include "resource.h"
 #include "idletrack.h"
@@ -51,6 +52,7 @@
 #include "gtkwin32dep.h"
 #include "win32dep.h"
 #include "gtkconv.h"
+#include "gtkconn.h"
 #include "util.h"
 #include "wspell.h"
 
@@ -64,6 +66,7 @@
 
 typedef BOOL (CALLBACK* LPFNFLASHWINDOWEX)(PFLASHWINFO);
 static LPFNFLASHWINDOWEX MyFlashWindowEx = NULL;
+static gboolean pwm_handles_connections = TRUE;
 
 
 /*
@@ -202,6 +205,43 @@
 #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
 #define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14)
 
+static void*
+winpidgin_netconfig_changed_cb(void *data)
+{
+	pwm_handles_connections = FALSE;
+
+	return NULL;
+}
+
+static void*
+winpidgin_get_handle(void)
+{
+	static int handle;
+
+	return &handle;
+}
+
+static gboolean
+winpidgin_pwm_reconnect()
+{
+	purple_signal_disconnect(purple_network_get_handle(), "network-configuration-changed",
+		winpidgin_get_handle(), PURPLE_CALLBACK(winpidgin_netconfig_changed_cb));
+
+	if (pwm_handles_connections == TRUE) {
+		PurpleConnectionUiOps *ui_ops = pidgin_connections_get_ui_ops();
+
+		purple_debug_info("winpidgin", "Resumed from standby, reconnecting accounts.\n");
+
+		if (ui_ops != NULL && ui_ops->network_connected != NULL)
+			ui_ops->network_connected();
+	} else {
+		purple_debug_info("winpidgin", "Resumed from standby, gtkconn will handle reconnecting.\n");
+		pwm_handles_connections = TRUE;
+	}
+
+	return FALSE;
+}
+
 static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
 	if (msg == PIDGIN_WM_FOCUS_REQUEST) {
@@ -213,6 +253,28 @@
 		purple_debug_info("winpidgin", "Got protocol handler request: %s\n", proto_msg ? proto_msg : "");
 		purple_got_protocol_handler_uri(proto_msg);
 		return TRUE;
+	} else if (msg == WM_POWERBROADCAST) {
+		if (wparam == PBT_APMQUERYSUSPEND) {
+			purple_debug_info("winpidgin", "Windows requesting permission to suspend.\n");
+			return TRUE;
+		} else if (wparam == PBT_APMSUSPEND) {
+			PurpleConnectionUiOps *ui_ops = pidgin_connections_get_ui_ops();
+
+			purple_debug_info("winpidgin", "Entering system standby, disconnecting accounts.\n");
+
+			if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
+				ui_ops->network_disconnected();
+
+			purple_signal_connect(purple_network_get_handle(), "network-configuration-changed", winpidgin_get_handle(),
+				PURPLE_CALLBACK(winpidgin_netconfig_changed_cb), NULL);
+
+			return TRUE;
+		} else if (wparam == PBT_APMRESUMESUSPEND) {
+			purple_debug_info("winpidgin", "Resuming from system standby.\n");
+			/* TODO: It seems like it'd be wise to use the NLA message, if possible, instead of this. */
+			purple_timeout_add_seconds(1, winpidgin_pwm_reconnect, NULL);
+			return TRUE;
+		}
 	}
 
 	return DefWindowProc(hwnd, msg, wparam, lparam);
@@ -242,7 +304,7 @@
 
 	/* Create the window */
 	if(!(win_hwnd = CreateWindow(wname, TEXT("WinpidginMsgWin"), 0, 0, 0, 0, 0,
-			HWND_MESSAGE, NULL, winpidgin_exe_hinstance(), 0))) {
+			NULL, NULL, winpidgin_exe_hinstance(), 0))) {
 		purple_debug_error("winpidgin",
 			"Unable to create message window.\n");
 		return NULL;