diff libpurple/protocols/jabber/presence.c @ 19277:f821d4bffb0a

propagate from branch 'im.pidgin.pidgin' (head 37793415aab363acafa9ca7ce5a76c974dbc6c4c) to branch 'im.pidgin.soc.2007.xmpp' (head c9129cb5ffdd67387b55d418b79fd8c2b6665f55)
author Evan Schoenberg <evan.s@dreskin.net>
date Tue, 14 Aug 2007 22:00:24 +0000
parents 1ca6c4b234ab 177552010f1d
children b0733d5d7621
line wrap: on
line diff
--- a/libpurple/protocols/jabber/presence.c	Tue Aug 14 21:33:53 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Tue Aug 14 22:00:24 2007 +0000
@@ -36,6 +36,9 @@
 #include "presence.h"
 #include "iq.h"
 #include "jutil.h"
+#include "adhoccommands.h"
+
+#include "usertune.h"
 
 
 static void chats_send_presence_foreach(gpointer key, gpointer val,
@@ -101,6 +104,9 @@
 	char *stripped = NULL;
 	JabberBuddyState state;
 	int priority;
+	const char *artist, *title, *source, *uri, *track;
+	int length;
+	gboolean allowBuzz;
 
 	if(NULL == status) {
 		PurplePresence *gpresence = purple_account_get_presence(account);
@@ -127,28 +133,95 @@
 	}
 
 	purple_status_to_jabber(status, &state, &stripped, &priority);
-
+	
+	/* check for buzz support */
+	allowBuzz = purple_status_get_attr_boolean(status,"buzz");
+	/* changing the buzz state has to trigger a re-broadcasting of the presence for caps */
+	
+#define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \
+					  (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b)))
+	/* check if there are any differences to the <presence> and send them in that case */
+	if (allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) ||
+		js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) {
+		js->allowBuzz = allowBuzz;
+		presence = jabber_presence_create_js(js, state, stripped, priority);
 
-	presence = jabber_presence_create(state, stripped, priority);
-	g_free(stripped);
+		if(js->avatar_hash) {
+			x = xmlnode_new_child(presence, "x");
+			xmlnode_set_namespace(x, "vcard-temp:x:update");
+			photo = xmlnode_new_child(x, "photo");
+			xmlnode_insert_data(photo, js->avatar_hash, -1);
+		}
+
+		jabber_send(js, presence);
 
-	if(js->avatar_hash) {
-		x = xmlnode_new_child(presence, "x");
-		xmlnode_set_namespace(x, "vcard-temp:x:update");
-		photo = xmlnode_new_child(x, "photo");
-		xmlnode_insert_data(photo, js->avatar_hash, -1);
+		g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
+		xmlnode_free(presence);
+		
+		/* update old values */
+		
+		if(js->old_msg)
+			g_free(js->old_msg);
+		if(js->old_avatarhash)
+			g_free(js->old_avatarhash);
+		js->old_msg = g_strdup(stripped);
+		js->old_avatarhash = g_strdup(js->avatar_hash);
+		js->old_state = state;
+		js->old_priority = priority;
+		g_free(stripped);
 	}
-
-	jabber_send(js, presence);
-
-	g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
-	xmlnode_free(presence);
-
+					  	
+	/* next, check if there are any changes to the tune values */
+	artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+	title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
+	source = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+	uri = purple_status_get_attr_string(status, PURPLE_TUNE_URL);
+	track = purple_status_get_attr_string(status, PURPLE_TUNE_TRACK);
+	length = (!purple_status_get_attr_value(status, PURPLE_TUNE_TIME))?-1:purple_status_get_attr_int(status, PURPLE_TUNE_TIME);
+	
+	if(CHANGED(artist, js->old_artist) || CHANGED(title, js->old_title) || CHANGED(source, js->old_source) ||
+	   CHANGED(uri, js->old_uri) || CHANGED(track, js->old_track) || (length != js->old_length)) {
+		PurpleJabberTuneInfo tuneinfo = {
+			(char*)artist,
+			(char*)title,
+			(char*)source,
+			(char*)track,
+			length,
+			(char*)uri
+		};
+		jabber_tune_set(js->gc, &tuneinfo);
+		
+		/* update old values */
+		if(js->old_artist)
+			g_free(js->old_artist);
+		if(js->old_title)
+			g_free(js->old_title);
+		if(js->old_source)
+			g_free(js->old_source);
+		if(js->old_uri)
+			g_free(js->old_uri);
+		if(js->old_track)
+			g_free(js->old_track);
+		js->old_artist = g_strdup(artist);
+		js->old_title = g_strdup(title);
+		js->old_source = g_strdup(source);
+		js->old_uri = g_strdup(uri);
+		js->old_length = length;
+		js->old_track = g_strdup(track);
+	}
+	
+#undef CHANGED(a,b)
+		
 	jabber_presence_fake_to_self(js, status);
 }
 
 xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority)
 {
+    return jabber_presence_create_js(NULL, state, msg, priority);
+}
+
+xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority)
+{
 	xmlnode *show, *status, *presence, *pri, *c;
 	const char *show_string = NULL;
 
@@ -183,6 +256,38 @@
 	xmlnode_set_namespace(c, "http://jabber.org/protocol/caps");
 	xmlnode_set_attrib(c, "node", CAPS0115_NODE);
 	xmlnode_set_attrib(c, "ver", VERSION);
+    
+    if(js != NULL) {
+        /* add the extensions */
+        char extlist[1024];
+        unsigned remaining = 1023; /* one less for the \0 */
+        GList *feature;
+        
+        extlist[0] = '\0';
+        for(feature = jabber_features; feature && remaining > 0; feature = feature->next) {
+            JabberFeature *feat = (JabberFeature*)feature->data;
+            unsigned featlen;
+
+			if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE)
+				continue; /* skip this feature */
+			
+			featlen = strlen(feat->shortname);
+
+            /* cut off when we don't have any more space left in our buffer (too bad) */
+            if(featlen > remaining)
+                break;
+            
+            strncat(extlist,feat->shortname,remaining);
+            remaining -= featlen;
+            if(feature->next) { /* no space at the end */
+                strncat(extlist," ",remaining);
+                --remaining;
+            }
+        }
+        /* did we add anything? */
+        if(remaining < 1023)
+            xmlnode_set_attrib(c, "ext", extlist);
+    }
 
 	return presence;
 }
@@ -252,6 +357,35 @@
 	}
 }
 
+typedef struct _JabberPresenceCapabilities {
+	JabberStream *js;
+	JabberBuddyResource *jbr;
+	char *from;
+} JabberPresenceCapabilities;
+
+static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) {
+	JabberPresenceCapabilities *userdata = user_data;
+	GList *iter;
+	
+	if(userdata->jbr->caps)
+		jabber_caps_free_clientinfo(userdata->jbr->caps);
+	userdata->jbr->caps = info;
+	
+	for(iter = info->features; iter; iter = g_list_next(iter)) {
+		if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) {
+			JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
+			xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
+			xmlnode_set_attrib(iq->node, "to", userdata->from);
+			xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
+			
+			jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
+			jabber_iq_send(iq);
+			break;
+		}
+	}
+	g_free(user_data);
+}
+
 void jabber_presence_parse(JabberStream *js, xmlnode *packet)
 {
 	const char *from = xmlnode_get_attrib(packet, "from");
@@ -273,6 +407,7 @@
 	xmlnode *y;
 	gboolean muc = FALSE;
 	char *avatar_hash = NULL;
+	xmlnode *caps = NULL;
 
 	if(!(jb = jabber_buddy_find(js, from, TRUE)))
 		return;
@@ -335,8 +470,10 @@
 
 
 	for(y = packet->child; y; y = y->next) {
+		const char *xmlns;
 		if(y->type != XMLNODE_TYPE_TAG)
 			continue;
+		xmlns = xmlnode_get_namespace(y);
 
 		if(!strcmp(y->name, "status")) {
 			g_free(status);
@@ -347,6 +484,11 @@
 				priority = atoi(p);
 				g_free(p);
 			}
+		} else if(!strcmp(y->name, "delay") && !strcmp(xmlns, "urn:xmpp:delay")) {
+			/* XXX: compare the time.  jabber:x:delay can happen on presence packets that aren't really and truly delayed */
+			delayed = TRUE;
+		} else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) {
+			caps = y; /* store for later, when creating buddy resource */
 		} else if(!strcmp(y->name, "x")) {
 			const char *xmlns = xmlnode_get_namespace(y);
 			if(xmlns && !strcmp(xmlns, "jabber:x:delay")) {
@@ -524,18 +666,22 @@
 		g_free(room_jid);
 	} else {
 		buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
-				jid->node ? "@" : "", jid->domain);
+									 jid->node ? "@" : "", jid->domain);
 		if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) {
-			purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%x)\n",
-				buddy_name, purple_account_get_username(js->gc->account), js->gc->account);
-			jabber_id_free(jid);
-			g_free(avatar_hash);
-			g_free(buddy_name);
-			g_free(status);
-			return;
+			if(!jid->node || strcmp(jid->node,js->user->node) || strcmp(jid->domain,js->user->domain)) {
+				purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%x)\n",
+									 buddy_name, purple_account_get_username(js->gc->account), js->gc->account);
+				jabber_id_free(jid);
+				g_free(avatar_hash);
+				g_free(buddy_name);
+				g_free(status);
+				return;
+			} else {
+				/* this is a different resource of our own account. Resume even when this account isn't on our blist */
+			}
 		}
 
-		if(avatar_hash) {
+		if(b && avatar_hash) {
 			const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b);
 			if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) {
 				JabberIq *iq;
@@ -573,6 +719,19 @@
 		} else {
 			jbr = jabber_buddy_track_resource(jb, jid->resource, priority,
 					state, status);
+			if(caps) {
+				const char *node = xmlnode_get_attrib(caps,"node");
+				const char *ver = xmlnode_get_attrib(caps,"ver");
+				const char *ext = xmlnode_get_attrib(caps,"ext");
+				
+				if(node && ver) {
+					JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
+					userdata->js = js;
+					userdata->jbr = jbr;
+					userdata->from = g_strdup(from);
+					jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata);
+				}
+			}
 		}
 
 		if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {