changeset 26847:12c45d148c32

merge of 'df25bed2c1c0d097b7f52fbfae76223d8552f0c3' and 'f16d13c3e5a15443b2bfb4f84c34e9b9a8e28850'
author Paul Aurich <paul@darkrain42.org>
date Fri, 08 May 2009 02:42:13 +0000
parents 2760311c492a (current diff) d0a049ede31e (diff)
children 7d924c723d7e
files
diffstat 3 files changed, 89 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/caps.c	Thu May 07 19:34:27 2009 +0000
+++ b/libpurple/protocols/jabber/caps.c	Fri May 08 02:42:13 2009 +0000
@@ -35,13 +35,7 @@
 	GList *values;
 } JabberDataFormField;
 
-typedef struct _JabberCapsKey {
-	char *node;
-	char *ver;
-	char *hash;
-} JabberCapsKey;
-
-static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsClientInfo */
+static GHashTable *capstable = NULL; /* JabberCapsTuple -> JabberCapsClientInfo */
 static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */
 static guint       save_timer = 0;
 
@@ -86,7 +80,7 @@
 }
 
 static guint jabber_caps_hash(gconstpointer data) {
-	const JabberCapsKey *key = data;
+	const JabberCapsTuple *key = data;
 	guint nodehash = g_str_hash(key->node);
 	guint verhash  = g_str_hash(key->ver);
 	/*
@@ -99,22 +93,14 @@
 }
 
 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) {
-	const JabberCapsKey *name1 = v1;
-	const JabberCapsKey *name2 = v2;
+	const JabberCapsTuple *name1 = v1;
+	const JabberCapsTuple *name2 = v2;
 
 	return g_str_equal(name1->node, name2->node) &&
 	       g_str_equal(name1->ver, name2->ver) &&
 	       purple_strequal(name1->hash, name2->hash);
 }
 
-void jabber_caps_destroy_key(gpointer data) {
-	JabberCapsKey *key = data;
-	g_free(key->node);
-	g_free(key->ver);
-	g_free(key->hash);
-	g_free(key);
-}
-
 static void
 jabber_caps_client_info_destroy(JabberCapsClientInfo *info)
 {
@@ -140,6 +126,10 @@
 
 	jabber_caps_node_exts_unref(info->exts);
 
+	g_free((char *)info->tuple.node);
+	g_free((char *)info->tuple.ver);
+	g_free((char *)info->tuple.hash);
+
 	g_free(info);
 }
 
@@ -176,16 +166,16 @@
 }
 
 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) {
-	JabberCapsKey *clientinfo = key;
-	JabberCapsClientInfo *props = value;
+	const JabberCapsTuple *tuple = key;
+	const JabberCapsClientInfo *props = value;
 	xmlnode *root = user_data;
 	xmlnode *client = xmlnode_new_child(root, "client");
 	GList *iter;
 
-	xmlnode_set_attrib(client, "node", clientinfo->node);
-	xmlnode_set_attrib(client, "ver", clientinfo->ver);
-	if (clientinfo->hash)
-		xmlnode_set_attrib(client, "hash", clientinfo->hash);
+	xmlnode_set_attrib(client, "node", tuple->node);
+	xmlnode_set_attrib(client, "ver", tuple->ver);
+	if (tuple->hash)
+		xmlnode_set_attrib(client, "hash", tuple->hash);
 	for(iter = props->identities; iter; iter = g_list_next(iter)) {
 		JabberIdentity *id = iter->data;
 		xmlnode *identity = xmlnode_new_child(client, "identity");
@@ -255,8 +245,8 @@
 		if(client->type != XMLNODE_TYPE_TAG)
 			continue;
 		if(!strcmp(client->name, "client")) {
-			JabberCapsKey *key = g_new0(JabberCapsKey, 1);
 			JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1);
+			JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple;
 			xmlnode *child;
 			JabberCapsNodeExts *exts = NULL;
 			key->node = g_strdup(xmlnode_get_attrib(client,"node"));
@@ -340,7 +330,7 @@
 void jabber_caps_init(void)
 {
 	nodetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_caps_node_exts_unref); 
-	capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, (GDestroyNotify)jabber_caps_client_info_destroy);
+	capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, NULL, (GDestroyNotify)jabber_caps_client_info_destroy);
 	jabber_caps_load();
 }
 
@@ -356,6 +346,27 @@
 	capstable = nodetable = NULL;
 }
 
+gboolean jabber_caps_exts_known(const JabberCapsClientInfo *info,
+                                char **exts)
+{
+	int i;
+	g_return_val_if_fail(info != NULL, FALSE);
+
+	if (!exts)
+		return TRUE;
+
+	for (i = 0; exts[i]; ++i) {
+		/* Hack since we advertise the ext but don't have any */
+		if (g_str_equal(exts[i], "voice-v1") && !info->exts)
+			continue;
+		if (!info->exts ||
+				!g_hash_table_lookup(info->exts->exts, exts[i]))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
 typedef struct _jabber_caps_cbplususerdata {
 	guint ref;
 
@@ -429,7 +440,7 @@
 		"http://jabber.org/protocol/disco#info");
 	jabber_caps_cbplususerdata *userdata = data;
 	JabberCapsClientInfo *info = NULL, *value;
-	JabberCapsKey key;
+	JabberCapsTuple key;
 
 	if (!query || type == JABBER_IQ_ERROR) {
 		/* Any outstanding exts will be dealt with via ref-counting */
@@ -481,7 +492,7 @@
 		jabber_caps_client_info_destroy(info);
 		info = value;
 	} else {
-		JabberCapsKey *n_key = g_new(JabberCapsKey, 1);
+		JabberCapsTuple *n_key = (JabberCapsTuple *)&info->tuple;
 		n_key->node = userdata->node;
 		n_key->ver  = userdata->ver;
 		n_key->hash = userdata->hash;
@@ -549,16 +560,19 @@
 }
 
 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
-        const char *ver, const char *hash, const char *ext,
+        const char *ver, const char *hash, char **exts,
         jabber_caps_get_info_cb cb, gpointer user_data)
 {
 	JabberCapsClientInfo *info;
-	JabberCapsKey key;
+	JabberCapsTuple key;
 	jabber_caps_cbplususerdata *userdata;
 
-	if (ext && *ext && hash)
+	if (exts && hash) {
 		purple_debug_info("jabber", "Ignoring exts in new-style caps from %s\n",
 		                     who);
+		g_strfreev(exts);
+		exts = NULL;
+	}
 
 	/* Using this in a read-only fashion, so the cast is OK */
 	key.node = (char *)node;
@@ -607,9 +621,8 @@
 	}
 
 	/* Are there any exts that we don't recognize? */
-	if (ext && *ext && !hash) {
+	if (exts) {
 		JabberCapsNodeExts *node_exts;
-		gchar **splat = g_strsplit(ext, " ", 0);
 		int i;
 
 		if (info) {
@@ -621,23 +634,23 @@
 			/* We'll put it in later once we have the client info */
 			node_exts = userdata->node_exts = jabber_caps_find_exts_by_node(node);
 
-		for (i = 0; splat[i]; ++i) {
-			userdata->exts = g_list_prepend(userdata->exts, splat[i]);
+		for (i = 0; exts[i]; ++i) {
+			userdata->exts = g_list_prepend(userdata->exts, exts[i]);
 			/* Look it up if we don't already know what it means */
-			if (!g_hash_table_lookup(node_exts->exts, splat[i])) {
+			if (!g_hash_table_lookup(node_exts->exts, exts[i])) {
 				JabberIq *iq;
 				xmlnode *query;
 				char *nodeext;
 				ext_iq_data *cbdata = g_new(ext_iq_data, 1);
 
-				cbdata->name = splat[i];
+				cbdata->name = exts[i];
 				cbdata->data = cbplususerdata_ref(userdata);
 
 				iq = jabber_iq_new_query(js, JABBER_IQ_GET,
 				            "http://jabber.org/protocol/disco#info");
 				query = xmlnode_get_child_with_namespace(iq->node, "query",
 				            "http://jabber.org/protocol/disco#info");
-				nodeext = g_strdup_printf("%s#%s", node, splat[i]);
+				nodeext = g_strdup_printf("%s#%s", node, exts[i]);
 				xmlnode_set_attrib(query, "node", nodeext);
 				g_free(nodeext);
 				xmlnode_set_attrib(iq->node, "to", who);
@@ -647,11 +660,11 @@
 
 				++userdata->extOutstanding;	
 			}
-			splat[i] = NULL;
+			exts[i] = NULL;
 		}
 		/* All the strings are now part of the GList, so don't need
 		 * g_strfreev. */
-		g_free(splat);
+		g_free(exts);
 	}
 
 	if (userdata->info && userdata->extOutstanding == 0) {
--- a/libpurple/protocols/jabber/caps.h	Thu May 07 19:34:27 2009 +0000
+++ b/libpurple/protocols/jabber/caps.h	Fri May 08 02:42:13 2009 +0000
@@ -30,11 +30,19 @@
 
 typedef struct _JabberCapsNodeExts JabberCapsNodeExts;
 
+typedef struct _JabberCapsTuple {
+	const char *node;
+	const char *ver;
+	const char *hash;
+} JabberCapsTuple;
+
 struct _JabberCapsClientInfo {
 	GList *identities; /* JabberIdentity */
 	GList *features; /* char * */
 	GList *forms; /* xmlnode * */
 	JabberCapsNodeExts *exts;
+
+	const JabberCapsTuple tuple;
 };
 
 /*
@@ -60,7 +68,10 @@
 void jabber_caps_init(void);
 void jabber_caps_uninit(void);
 
-void jabber_caps_destroy_key(gpointer value);
+/**
+ * Check whether all of the exts in a char* array are known to the given info.
+ */
+gboolean jabber_caps_exts_known(const JabberCapsClientInfo *info, char **exts);
 
 /**
  * Main entity capabilites function to get the capabilities of a contact.
@@ -68,10 +79,13 @@
  * The callback will be called synchronously if we already have the
  * capabilities for the specified (node,ver,hash) (and, if exts are specified,
  * if we know what each means)
+ *
+ * @param exts A g_strsplit'd (NULL-terminated) array of strings. This
+ *             function is responsible for freeing it.
  */
 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
                           const char *ver, const char *hash,
-                          const char *ext, jabber_caps_get_info_cb cb,
+                          char **exts, jabber_caps_get_info_cb cb,
                           gpointer user_data);
 
 /**
--- a/libpurple/protocols/jabber/presence.c	Thu May 07 19:34:27 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Fri May 08 02:42:13 2009 +0000
@@ -854,13 +854,26 @@
 		/* v1.3 uses: node, ver, and optionally ext.
 		 * v1.5 uses: node, ver, and hash. */
 		if (node && *node && ver && *ver) {
-			JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
-			userdata->js = js;
-			userdata->jb = jb;
-			userdata->from = g_strdup(from);
-			jabber_caps_get_info(js, from, node, ver, hash, ext,
-			    (jabber_caps_get_info_cb)jabber_presence_set_capabilities,
-			    userdata);
+			gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL;
+			jbr = jabber_buddy_find_resource(jb, jid->resource);
+
+			/* Look it up if we don't already have all this information */
+			if (!jbr || !jbr->caps.info ||
+					!g_str_equal(node, jbr->caps.info->tuple.node) ||
+					!g_str_equal(ver, jbr->caps.info->tuple.ver) ||
+					!purple_strequal(hash, jbr->caps.info->tuple.hash) ||
+					!jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) {
+				JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
+				userdata->js = js;
+				userdata->jb = jb;
+				userdata->from = g_strdup(from);
+				jabber_caps_get_info(js, from, node, ver, hash, exts,
+				    (jabber_caps_get_info_cb)jabber_presence_set_capabilities,
+				    userdata);
+			} else {
+				if (exts)
+					g_strfreev(exts);
+			}
 		}
 	}