changeset 27439:e1090ed76286

Notice when the MUC item JID on unavailable presence references another resource. Refs #8319. Openfire supports multiple resources of the same user in a room under the same nick, but will route an unavailable presence from one to the other. We need to pick up on that (via the MUC item JID) and not treat it as our leaving the room. This won't always fix server broken-ness (e.g. anonymous rooms or non-admins). Also, Don't iterate over every <x/> child of the <presence/>. Just grab the one we want. There should [can] be only one.
author Paul Aurich <paul@darkrain42.org>
date Thu, 09 Jul 2009 04:40:06 +0000
parents 889bf9af9706
children 4fc04d98e1e8
files libpurple/protocols/jabber/presence.c
diffstat 1 files changed, 39 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/presence.c	Thu Jul 09 02:27:38 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Thu Jul 09 04:40:06 2009 +0000
@@ -762,6 +762,7 @@
 				purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
 						flags);
 		} else if (g_str_equal(type, "unavailable")) {
+			xmlnode *x;
 			gboolean nick_change = FALSE;
 			gboolean kick = FALSE;
 			gboolean is_our_resource = FALSE; /* Is the presence about us? */
@@ -784,28 +785,31 @@
 			is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle));
 
 			jabber_buddy_remove_resource(jb, jid->resource);
-			if(chat->muc) {
-				xmlnode *x;
-				for(x = xmlnode_get_child(packet, "x"); x; x = xmlnode_get_next_twin(x)) {
-					const char *xmlns, *nick, *code;
-					xmlnode *stat, *item;
-					if(!(xmlns = xmlnode_get_namespace(x)) ||
-							strcmp(xmlns, "http://jabber.org/protocol/muc#user"))
-						continue;
-					if(!(stat = xmlnode_get_child(x, "status")))
-						continue;
-					if(!(code = xmlnode_get_attrib(stat, "code")))
-						continue;
+
+			x = xmlnode_get_child_with_namespace(packet, "x",
+					"http://jabber.org/protocol/muc#user");
+			if (chat->muc && x) {
+				const char *nick;
+				const char *code = NULL;
+				const char *item_jid = NULL;
+				xmlnode *stat;
+				xmlnode *item;
 
-					item = xmlnode_get_child(x, "item");
+				item = xmlnode_get_child(x, "item");
+				if (item)
+					item_jid = xmlnode_get_attrib(item, "jid");
+
 
+				stat = xmlnode_get_child(x, "status");
+
+				if (stat)
+					code = xmlnode_get_attrib(stat, "code");
+
+				if (code) {
 					if(!strcmp(code, "301")) {
 						/* XXX: we got banned */
-					} else if(!strcmp(code, "303")) {
-						if (!item)
-							continue;
-						if(!(nick = xmlnode_get_attrib(item, "nick")))
-							continue;
+					} else if(!strcmp(code, "303") && item &&
+							(nick = xmlnode_get_attrib(item, "nick"))) {
 						nick_change = TRUE;
 						if(!strcmp(jid->resource, chat->handle)) {
 							g_free(chat->handle);
@@ -813,7 +817,8 @@
 						}
 						purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick);
 						jabber_chat_remove_handle(chat, jid->resource);
-						break;
+						/* TODO: Enable this when this is in a for-loop...
+						break; */
 					} else if(!strcmp(code, "307")) {
 						/* Someone was kicked from the room */
 						xmlnode *reason = NULL, *actor = NULL;
@@ -863,6 +868,21 @@
 						/* XXX: removed due to system shutdown */
 					}
 				}
+
+				/*
+				 * Possibly another connected resource of our JID (see XEP-0045
+				 * v1.24 section 7.1.10) being disconnected. Should be
+				 * distinguished by the item_jid.
+				 * Also possibly works around bits of an Openfire bug. See
+				 * #8319.
+				 */
+				if (is_our_resource && !purple_strequal(from, item_jid)) {
+					/* TODO: When the above is a loop, this needs to still act
+					 * sanely for all cases (this code is a little fragile). */
+					if (!kick && !nick_change)
+						/* Presumably, kicks and nick changes also affect us. */
+						is_our_resource = FALSE;
+				}
 			}
 			if(!nick_change) {
 				if (is_our_resource) {