changeset 29020:64bee7ae306f

merge of '18b85ae6ad8091af43d91826cf054e4569ba74aa' and '5a7c739baddaf14efccf2017d05e0f9bb5b7278a'
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Wed, 10 Feb 2010 16:46:08 +0000
parents 6288bbd53f44 (diff) a8614b855c39 (current diff)
children 7cab6dc6b79c
files
diffstat 15 files changed, 187 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Feb 09 16:54:32 2010 +0000
+++ b/ChangeLog	Wed Feb 10 16:46:08 2010 +0000
@@ -10,6 +10,8 @@
 	* When looking up DNS records, use the type of record returned by the
 	  server (instead of the type we asked for) to determine how to process
 	  the record.
+	* Fix an issue with parsing XML attributes that contain "&lt;br&gt;".
+	  See ChangeLog.API for more details.
 
 	General:
 	* Correctly disable all missing dependencies when using the
@@ -37,6 +39,11 @@
 	* Fix a leak and crash when retrieving buddy icons.
 
 	XMPP:
+	* Upon receipt of a presence change from a contact, send further messages
+	  to that contact's bare JID [username].  A conversation "locks" to a
+	  specific resource upon receipt of a message from that resource.
+	  Previously, it would only "unlock" when that resource went offline, or
+	  upon receipt of a message from a different resource.
 	* Added support for the SCRAM-SHA-1 SASL mechanism.  This is only
 	  available when built without Cyrus SASL support.
 	* When getting info on a domain-only (server) JID, show uptime
@@ -49,6 +56,8 @@
 	* Don't send custom smileys larger than the recommended maximum object size
 	  specified in the BoB XEP.   This prevents a client from being
 	  disconnected by servers that dislike overly-large stanzas.
+	* Fix receiving messages without markup over an Openfire BOSH connection
+	  (forcibly put the stanzas in the jabber:client namespace).
 
 	Yahoo:
 	* Don't send <span> and </span> tags.  (Fartash Faghri)
--- a/ChangeLog.API	Tue Feb 09 16:54:32 2010 +0000
+++ b/ChangeLog.API	Wed Feb 10 16:46:08 2010 +0000
@@ -7,6 +7,12 @@
 		  purple_xfer_request_denied if an error is found when selecting
 		  a file to send. Request denied is still used when a receive
 		  request is not allowed.
+		* xmlnode_from_str now properly handles paring an attribute which
+		  contain "&lt;br&gt;", which were previously transformed into a
+		  newline character (libxml2 unescapes all entities except
+		  representations of '&', and libpurple's purple_unescape_html
+		  converts "<br>" to a newline).
+
 	Perl:
 		Changed:
 		* Corrected the package names for the PurpleProxyType and
--- a/configure.ac	Tue Feb 09 16:54:32 2010 +0000
+++ b/configure.ac	Wed Feb 10 16:46:08 2010 +0000
@@ -2393,30 +2393,6 @@
         AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm])
 fi
 
-AC_CACHE_CHECK([whether va_lists can be copied by value], ac_cv_va_val_copy,[
-	AC_TRY_RUN([#include <stdarg.h>
-#include <stdlib.h>
-	void f (int i, ...) {
-	va_list args1, args2;
-	va_start (args1, i);
-	args2 = args1;
-	if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42)
-	  exit (1);
-	va_end (args1); va_end (args2);
-	}
-	int main() {
-	  f (0, 42);
-	  return 0;
-	}],
-	[ac_cv_va_val_copy=yes],
-	[ac_cv_va_val_copy=no],
-	[ac_cv_va_val_copy=yes])
-])
-
-if test "x$ac_cv_va_val_copy" = "xno"; then
-	AC_DEFINE(VA_COPY_AS_ARRAY, 1, ['va_lists' cannot be copied as values])
-fi
-
 dnl #######################################################################
 dnl # Check for check
 dnl #######################################################################
--- a/libpurple/plugins/perl/perl-common.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/plugins/perl/perl-common.c	Wed Feb 10 16:46:08 2010 +0000
@@ -472,74 +472,74 @@
 }
 
 SV *
-purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg)
+purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg)
 {
 	if (purple_value_is_outgoing(value)) {
 		switch (purple_value_get_type(value)) {
 			case PURPLE_TYPE_SUBTYPE:
-				if ((*copy_arg = va_arg(*args, void **)) == NULL)
+				if ((*copy_arg = va_arg(args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return purple_perl_sv_from_subtype(value, *(void **)*copy_arg);
 
 			case PURPLE_TYPE_BOOLEAN:
-				if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, gboolean *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(gboolean *)*copy_arg);
 
 			case PURPLE_TYPE_INT:
-				if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, int *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(int *)*copy_arg);
 
 			case PURPLE_TYPE_UINT:
-				if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, unsigned int *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(unsigned int *)*copy_arg);
 
 			case PURPLE_TYPE_LONG:
-				if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, long *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(long *)*copy_arg);
 
 			case PURPLE_TYPE_ULONG:
-				if ((*copy_arg = (void *)va_arg(*args,
+				if ((*copy_arg = (void *)va_arg(args,
 												unsigned long *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(unsigned long *)*copy_arg);
 
 			case PURPLE_TYPE_INT64:
-				if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, gint64 *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(gint64 *)*copy_arg);
 
 			case PURPLE_TYPE_UINT64:
-				if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, guint64 *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(guint64 *)*copy_arg);
 
 			case PURPLE_TYPE_STRING:
-				if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, char **)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVGChar(*(char **)*copy_arg);
 
 			case PURPLE_TYPE_POINTER:
-				if ((*copy_arg = va_arg(*args, void **)) == NULL)
+				if ((*copy_arg = va_arg(args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv((IV)*(void **)*copy_arg);
 
 			case PURPLE_TYPE_BOXED:
 				/* Uh.. I dunno. Try this? */
-				if ((*copy_arg = va_arg(*args, void **)) == NULL)
+				if ((*copy_arg = va_arg(args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return sv_2mortal(purple_perl_bless_object(
@@ -553,40 +553,40 @@
 	} else {
 		switch (purple_value_get_type(value)) {
 			case PURPLE_TYPE_SUBTYPE:
-				if ((*copy_arg = va_arg(*args, void *)) == NULL)
+				if ((*copy_arg = va_arg(args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return purple_perl_sv_from_subtype(value, *copy_arg);
 
 			case PURPLE_TYPE_BOOLEAN:
-				*copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) );
+				*copy_arg = GINT_TO_POINTER( va_arg(args, gboolean) );
 
 				return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg));
 
 			case PURPLE_TYPE_INT:
-				*copy_arg = GINT_TO_POINTER( va_arg(*args, int) );
+				*copy_arg = GINT_TO_POINTER( va_arg(args, int) );
 
 				return newSViv(GPOINTER_TO_INT(*copy_arg));
 
 			case PURPLE_TYPE_UINT:
-				*copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int));
+				*copy_arg = GUINT_TO_POINTER(va_arg(args, unsigned int));
 
 				return newSVuv(GPOINTER_TO_UINT(*copy_arg));
 
 			case PURPLE_TYPE_LONG:
-				*copy_arg = (void *)va_arg(*args, long);
+				*copy_arg = (void *)va_arg(args, long);
 
 				return newSViv((long)*copy_arg);
 
 			case PURPLE_TYPE_ULONG:
-				*copy_arg = (void *)va_arg(*args, unsigned long);
+				*copy_arg = (void *)va_arg(args, unsigned long);
 
 				return newSVuv((unsigned long)*copy_arg);
 
 			case PURPLE_TYPE_INT64:
 #if 0
 				/* XXX This yells and complains. */
-				*copy_arg = va_arg(*args, gint64);
+				*copy_arg = va_arg(args, gint64);
 
 				return newSViv(*copy_arg);
 #endif
@@ -595,27 +595,27 @@
 			case PURPLE_TYPE_UINT64:
 				/* XXX This also yells and complains. */
 #if 0
-				*copy_arg = (void *)va_arg(*args, guint64);
+				*copy_arg = (void *)va_arg(args, guint64);
 
 				return newSVuv(*copy_arg);
 #endif
 				break;
 
 			case PURPLE_TYPE_STRING:
-				if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, char *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVGChar((char *)*copy_arg);
 
 			case PURPLE_TYPE_POINTER:
-				if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv((IV)*copy_arg);
 
 			case PURPLE_TYPE_BOXED:
 				/* Uh.. I dunno. Try this? */
-				if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return sv_2mortal(purple_perl_bless_object(*copy_arg,
--- a/libpurple/plugins/perl/perl-common.h	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/plugins/perl/perl-common.h	Wed Feb 10 16:46:08 2010 +0000
@@ -65,7 +65,7 @@
 #endif
 
 void *purple_perl_data_from_sv(PurpleValue *value, SV *sv);
-SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args,
+SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list args,
                             void ***copy_arg);
 SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback);
 #endif /* _PURPLE_PERL_COMMON_H_ */
--- a/libpurple/plugins/perl/perl-handlers.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Wed Feb 10 16:46:08 2010 +0000
@@ -298,11 +298,7 @@
 
 	for (i = 0; i < value_count; i++) {
 		sv_args[i] = purple_perl_sv_from_vargs(values[i],
-#ifdef VA_COPY_AS_ARRAY
 		                                       args,
-#else
-		                                       (va_list*)&args,
-#endif
 		                                       &copy_args[i]);
 
 		XPUSHs(sv_args[i]);
--- a/libpurple/protocols/bonjour/parser.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Wed Feb 10 16:46:08 2010 +0000
@@ -49,6 +49,31 @@
 	return FALSE;
 }
 
+static char *purple_unescape_text(const char *in)
+{
+    GString *ret;
+    const char *c = in;
+
+    if (in == NULL)
+        return NULL;
+
+    ret = g_string_new("");
+    while (*c) {
+        int len;
+        const char *ent;
+
+        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
+            g_string_append(ret, ent);
+            c += len;
+        } else {
+            g_string_append_c(ret, *c);
+            c++;
+        }
+    }
+
+    return g_string_free(ret, FALSE);
+}
+
 static void
 bonjour_parser_element_start_libxml(void *user_data,
 				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
@@ -102,7 +127,7 @@
 			attrib[attrib_len] = '\0';
 
 			txt = attrib;
-			attrib = purple_unescape_html(txt);
+			attrib = purple_unescape_text(txt);
 			g_free(txt);
 			xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
 			g_free(attrib);
--- a/libpurple/protocols/jabber/bosh.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/jabber/bosh.c	Wed Feb 10 16:46:08 2010 +0000
@@ -476,6 +476,18 @@
 		/* jabber_process_packet might free child */
 		xmlnode *next = child->next;
 		if (child->type == XMLNODE_TYPE_TAG) {
+			const char *xmlns = xmlnode_get_namespace(child);
+			/*
+			 * Workaround for non-compliant servers that don't stamp
+			 * the right xmlns on these packets.  See #11315.
+			 */
+			if ((xmlns == NULL /* shouldn't happen, but is equally wrong */ ||
+					g_str_equal(xmlns, NS_BOSH)) &&
+				(g_str_equal(child->name, "iq") ||
+				 g_str_equal(child->name, "message") ||
+				 g_str_equal(child->name, "presence"))) {
+				xmlnode_set_namespace(child, NS_XMPP_CLIENT);
+			}
 			jabber_process_packet(js, &child);
 		}
 
--- a/libpurple/protocols/jabber/jutil.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Wed Feb 10 16:46:08 2010 +0000
@@ -651,25 +651,6 @@
 	return equal;
 }
 
-PurpleConversation *
-jabber_find_unnormalized_conv(const char *name, PurpleAccount *account)
-{
-	PurpleConversation *c = NULL;
-	GList *cnv;
-
-	g_return_val_if_fail(name != NULL, NULL);
-
-	for(cnv = purple_get_conversations(); cnv; cnv = cnv->next) {
-		c = (PurpleConversation*)cnv->data;
-		if(purple_conversation_get_type(c) == PURPLE_CONV_TYPE_IM &&
-				!purple_utf8_strcasecmp(name, purple_conversation_get_name(c)) &&
-				account == purple_conversation_get_account(c))
-			return c;
-	}
-
-	return NULL;
-}
-
 /* The same as purple_util_get_image_checksum, but guaranteed to remain SHA1 */
 char *
 jabber_calculate_data_sha1sum(gconstpointer data, size_t len)
--- a/libpurple/protocols/jabber/jutil.h	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Wed Feb 10 16:46:08 2010 +0000
@@ -63,7 +63,5 @@
  */
 char *jabber_saslprep(const char *);
 
-PurpleConversation *jabber_find_unnormalized_conv(const char *name, PurpleAccount *account);
-
 char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len);
 #endif /* PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/message.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/jabber/message.c	Wed Feb 10 16:46:08 2010 +0000
@@ -59,33 +59,37 @@
 static void handle_chat(JabberMessage *jm)
 {
 	JabberID *jid = jabber_id_new(jm->from);
-	char *from;
 
+	PurpleConnection *gc;
+	PurpleAccount *account;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
 
 	if(!jid)
 		return;
 
+	gc = jm->js->gc;
+	account = purple_connection_get_account(gc);
+
 	jb = jabber_buddy_find(jm->js, jm->from, TRUE);
 	jbr = jabber_buddy_find_resource(jb, jid->resource);
 
-	if(jabber_find_unnormalized_conv(jm->from, jm->js->gc->account)) {
-		from = g_strdup(jm->from);
-	} else  if(jid->node) {
-		if(jid->resource) {
-			PurpleConversation *conv;
+	if (jid->resource) {
+		/*
+		 * We received a message from a specific resource, so we probably want a
+		 * reply to go to this specific resource (i.e. bind/lock the
+		 * conversation to this resource).
+		 *
+		 * This works because purple_conv_im_send gets the name from
+		 * purple_conversation_get_name()
+		 */
+		PurpleConversation *conv;
 
-			from = g_strdup_printf("%s@%s", jid->node, jid->domain);
-			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, jm->js->gc->account);
-			if(conv) {
-				purple_conversation_set_name(conv, jm->from);
-				}
-			g_free(from);
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account);
+		if (conv && !g_str_equal(jm->from, purple_conversation_get_name(conv))) {
+			purple_debug_info("jabber", "Binding conversation to %s\n", jm->from);
+			purple_conversation_set_name(conv, jm->from);
 		}
-		from = g_strdup(jm->from);
-	} else {
-		from = g_strdup(jid->domain);
 	}
 
 	if(!jm->xhtml && !jm->body) {
@@ -97,19 +101,19 @@
 		}
 
 		if(JM_STATE_COMPOSING == jm->chat_state) {
-			serv_got_typing(jm->js->gc, from, 0, PURPLE_TYPING);
+			serv_got_typing(gc, jm->from, 0, PURPLE_TYPING);
 		} else if(JM_STATE_PAUSED == jm->chat_state) {
-			serv_got_typing(jm->js->gc, from, 0, PURPLE_TYPED);
+			serv_got_typing(gc, jm->from, 0, PURPLE_TYPED);
 		} else if(JM_STATE_GONE == jm->chat_state) {
 			PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-					from, jm->js->gc->account);
+					jm->from, account);
 			if (conv && jid->node && jid->domain) {
 				char buf[256];
 				PurpleBuddy *buddy;
 
 				g_snprintf(buf, sizeof(buf), "%s@%s", jid->node, jid->domain);
 
-				if ((buddy = purple_find_buddy(jm->js->gc->account, buf))) {
+				if ((buddy = purple_find_buddy(account, buf))) {
 					const char *who;
 					char *escaped;
 
@@ -127,10 +131,10 @@
 					                        PURPLE_MESSAGE_SYSTEM, time(NULL));
 				}
 			}
-			serv_got_typing_stopped(jm->js->gc, from);
+			serv_got_typing_stopped(gc, jm->from);
 
 		} else {
-			serv_got_typing_stopped(jm->js->gc, from);
+			serv_got_typing_stopped(gc, jm->from);
 		}
 	} else {
 		if(jbr) {
@@ -149,12 +153,9 @@
 			jm->body = jabber_google_format_to_html(jm->body);
 			g_free(tmp);
 		}
-		serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0,
-				jm->sent);
+		serv_got_im(gc, jm->from, jm->xhtml ? jm->xhtml : jm->body, 0, jm->sent);
 	}
 
-
-	g_free(from);
 	jabber_id_free(jid);
 }
 
--- a/libpurple/protocols/jabber/parser.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/jabber/parser.c	Wed Feb 10 16:46:08 2010 +0000
@@ -31,6 +31,31 @@
 #include "util.h"
 #include "xmlnode.h"
 
+static char *purple_unescape_text(const char *in)
+{
+    GString *ret;
+    const char *c = in;
+
+    if (in == NULL)
+        return NULL;
+
+    ret = g_string_new("");
+    while (*c) {
+        int len;
+        const char *ent;
+
+        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
+            g_string_append(ret, ent);
+            c += len;
+        } else {
+            g_string_append_c(ret, *c);
+            c++;
+        }
+    }
+
+    return g_string_free(ret, FALSE);
+}
+
 static void
 jabber_parser_element_start_libxml(void *user_data,
 				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
@@ -89,7 +114,7 @@
 			char *attrib = g_strndup((gchar *)attributes[i+3], attrib_len);
 
 			txt = attrib;
-			attrib = purple_unescape_html(txt);
+			attrib = purple_unescape_text(txt);
 			g_free(txt);
 			xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
 			g_free(attrib);
--- a/libpurple/protocols/jabber/presence.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/protocols/jabber/presence.c	Wed Feb 10 16:46:08 2010 +0000
@@ -502,6 +502,7 @@
 	PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
 	gboolean delayed = FALSE;
 	const gchar *stamp = NULL; /* from <delayed/> element */
+	PurpleAccount *account;
 	PurpleBuddy *b = NULL;
 	char *buddy_name;
 	JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN;
@@ -523,6 +524,8 @@
 	if (signal_return)
 		return;
 
+	account = purple_connection_get_account(js->gc);
+
 	jid = jabber_id_new(from);
 	if (jid == NULL) {
 		purple_debug_error("jabber", "Ignoring presence with malformed 'from' "
@@ -559,12 +562,10 @@
 	} else if (g_str_equal(type, "subscribe")) {
 		struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1);
 		gboolean onlist = FALSE;
-		PurpleAccount *account;
 		PurpleBuddy *buddy;
 		JabberBuddy *jb = NULL;
 		xmlnode *nick;
 
-		account = purple_connection_get_account(js->gc);
 		buddy = purple_find_buddy(account, from);
 		nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
 		if (nick)
@@ -675,6 +676,7 @@
 		idle += offset;
 	}
 
+	/* DEALING WITH CHATS */
 	if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) {
 		static int i = 1;
 
@@ -748,7 +750,7 @@
 											" you like to configure it, or"
 											" accept the default settings?"),
 										/* Default Action */ 1,
-										purple_connection_get_account(js->gc), NULL, chat->conv,
+										account, NULL, chat->conv,
 										chat, 2,
 										_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
 										_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
@@ -954,13 +956,31 @@
 			g_free(nickname);
 			g_return_if_reached();
 		}
+		/* End of DEALING WITH CHATS...about 5000 lines ago */
 	} else {
+		/* DEALING WITH CONTACT (i.e. not a chat) */
+		PurpleConversation *conv;
+
 		buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
 									 jid->node ? "@" : "", jid->domain);
-		if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) {
+
+		/*
+		 * Unbind/unlock from sending messages to a specific resource on
+		 * presence changes.  This is locked to a specific resource when
+		 * receiving a message (in message.c).
+		 */
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+				buddy_name, account);
+		if (conv) {
+			purple_debug_info("jabber", "Changed conversation binding from %s to %s\n",
+					purple_conversation_get_name(conv), buddy_name);
+			purple_conversation_set_name(conv, buddy_name);
+		}
+
+		if((b = purple_find_buddy(account, buddy_name)) == NULL) {
 			if (jb != js->user_jb) {
 				purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n",
-									 buddy_name, purple_account_get_username(js->gc->account), js->gc->account);
+									 buddy_name, purple_account_get_username(account), account);
 				jabber_id_free(jid);
 				g_free(avatar_hash);
 				g_free(buddy_name);
@@ -1001,12 +1021,7 @@
 		if(state == JABBER_BUDDY_STATE_ERROR ||
 				(type && (g_str_equal(type, "unavailable") ||
 				          g_str_equal(type, "unsubscribed")))) {
-			PurpleConversation *conv;
-
 			jabber_buddy_remove_resource(jb, jid->resource);
-			if((conv = jabber_find_unnormalized_conv(from, js->gc->account)))
-				purple_conversation_set_name(conv, buddy_name);
-
 		} else {
 			jbr = jabber_buddy_track_resource(jb, jid->resource, priority,
 					state, status);
@@ -1019,12 +1034,12 @@
 
 		if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {
 			jabber_google_presence_incoming(js, buddy_name, found_jbr);
-			purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL);
-			purple_prpl_got_user_idle(js->gc->account, buddy_name, found_jbr->idle, found_jbr->idle);
+			purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL);
+			purple_prpl_got_user_idle(account, buddy_name, found_jbr->idle, found_jbr->idle);
 			if (nickname)
 				serv_got_alias(js->gc, buddy_name, nickname);
 		} else {
-			purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
+			purple_prpl_got_user_status(account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
 		}
 		g_free(buddy_name);
 	}
--- a/libpurple/util.h	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/util.h	Wed Feb 10 16:46:08 2010 +0000
@@ -518,8 +518,10 @@
  * Unescapes HTML entities to their literal characters. Also translates
  * "<br>" to "\n".
  * For example "&amp;" is replaced by '&' and so on.
- * Actually only "&amp;", "&quot;", "&lt;" and "&gt;" are currently
- * supported.
+ *
+ * The following named entities are supported (in addition to numerical
+ * entities):
+ *    "&amp;", "&lt;", "&gt;", "&copy;", "&quot;", "&reg;", "&apos;"
  *
  * @param html The string in which to unescape any HTML entities
  *
--- a/libpurple/xmlnode.c	Tue Feb 09 16:54:32 2010 +0000
+++ b/libpurple/xmlnode.c	Wed Feb 10 16:46:08 2010 +0000
@@ -545,6 +545,31 @@
 	return xml_with_declaration;
 }
 
+static char *purple_unescape_text(const char *in)
+{
+    GString *ret;
+    const char *c = in;
+
+    if (in == NULL)
+        return NULL;
+
+    ret = g_string_new("");
+    while (*c) {
+        int len;
+        const char *ent;
+
+        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
+            g_string_append(ret, ent);
+            c += len;
+        } else {
+            g_string_append_c(ret, *c);
+            c++;
+        }
+    }
+
+    return g_string_free(ret, FALSE);
+}
+
 struct _xmlnode_parser_data {
 	xmlnode *current;
 	gboolean error;
@@ -590,7 +615,7 @@
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_strndup((const char *)attributes[i+3], attrib_len);
 			txt = attrib;
-			attrib = purple_unescape_html(txt);
+			attrib = purple_unescape_text(txt);
 			g_free(txt);
 			xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
 			g_free(attrib);