# HG changeset patch # User Paul Aurich # Date 1241750533 0 # Node ID 12c45d148c3233837ef2ccfff1b84c74bbd7d0fc # Parent 2760311c492acc25d3131a99d1e8a65d37c5baaa# Parent d0a049ede31edec77d0466b9acb898bd2e0d0d58 merge of 'df25bed2c1c0d097b7f52fbfae76223d8552f0c3' and 'f16d13c3e5a15443b2bfb4f84c34e9b9a8e28850' diff -r 2760311c492a -r 12c45d148c32 libpurple/protocols/jabber/caps.c --- 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) { diff -r 2760311c492a -r 12c45d148c32 libpurple/protocols/jabber/caps.h --- 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); /** diff -r 2760311c492a -r 12c45d148c32 libpurple/protocols/jabber/presence.c --- 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); + } } }