libpurple/protocols/jabber/caps.c

changeset 41784
f864fee87775
parent 41453
380febc4aae7
child 41840
fe350460fb1c
--- a/libpurple/protocols/jabber/caps.c	Wed Oct 05 02:13:21 2022 -0500
+++ b/libpurple/protocols/jabber/caps.c	Wed Oct 05 02:15:13 2022 -0500
@@ -38,50 +38,13 @@
 } JabberDataFormField;
 
 static GHashTable *capstable = NULL; /* JabberCapsTuple -> JabberCapsClientInfo */
-static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */
 static guint       save_timer = 0;
 
-/* Free a GList of allocated char* */
-static void
-free_string_glist(GList *list)
-{
-	g_list_free_full(list, g_free);
-}
-
-static JabberCapsNodeExts*
-jabber_caps_node_exts_ref(JabberCapsNodeExts *exts)
-{
-	g_return_val_if_fail(exts != NULL, NULL);
-
-	++exts->ref;
-	return exts;
-}
-
-static void
-jabber_caps_node_exts_unref(JabberCapsNodeExts *exts)
-{
-	if (exts == NULL)
-		return;
-
-	g_return_if_fail(exts->ref != 0);
-
-	if (--exts->ref != 0)
-		return;
-
-	g_hash_table_destroy(exts->exts);
-	g_free(exts);
-}
-
 static guint jabber_caps_hash(gconstpointer data) {
 	const JabberCapsTuple *key = data;
 	guint nodehash = g_str_hash(key->node);
 	guint verhash  = g_str_hash(key->ver);
-	/*
-	 * 'hash' was optional in XEP-0115 v1.4 and g_str_hash crashes on NULL >:O.
-	 * Okay, maybe I've played too much Zelda, but that looks like
-	 * a Deku Shrub...
-	 */
-	guint hashhash = (key->hash ? g_str_hash(key->hash) : 0);
+	guint hashhash = g_str_hash(key->hash);
 	return nodehash ^ verhash ^ hashhash;
 }
 
@@ -102,12 +65,10 @@
 
 	g_list_free_full(info->identities, (GDestroyNotify)jabber_identity_free);
 
-	free_string_glist(info->features);
+	g_list_free_full(info->features, g_free);
 
 	g_list_free_full(info->forms, (GDestroyNotify)purple_xmlnode_free);
 
-	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);
@@ -115,38 +76,6 @@
 	g_free(info);
 }
 
-/* NOTE: Takes a reference to the exts, unref it if you don't really want to
- * keep it around. */
-static JabberCapsNodeExts*
-jabber_caps_find_exts_by_node(const char *node)
-{
-	JabberCapsNodeExts *exts;
-	if (NULL == (exts = g_hash_table_lookup(nodetable, node))) {
-		exts = g_new0(JabberCapsNodeExts, 1);
-		exts->exts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
-		                                   (GDestroyNotify)free_string_glist);
-		g_hash_table_insert(nodetable, g_strdup(node), jabber_caps_node_exts_ref(exts));
-	}
-
-	return jabber_caps_node_exts_ref(exts);
-}
-
-static void
-exts_to_xmlnode(gconstpointer key, gconstpointer value, gpointer user_data)
-{
-	const char *identifier = key;
-	const GList *features = value, *node;
-	PurpleXmlNode *client = user_data, *ext, *feature;
-
-	ext = purple_xmlnode_new_child(client, "ext");
-	purple_xmlnode_set_attrib(ext, "identifier", identifier);
-
-	for (node = features; node; node = node->next) {
-		feature = purple_xmlnode_new_child(ext, "feature");
-		purple_xmlnode_set_attrib(feature, "var", (const gchar *)node->data);
-	}
-}
-
 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) {
 	const JabberCapsTuple *tuple = key;
 	const JabberCapsClientInfo *props = value;
@@ -156,8 +85,7 @@
 
 	purple_xmlnode_set_attrib(client, "node", tuple->node);
 	purple_xmlnode_set_attrib(client, "ver", tuple->ver);
-	if (tuple->hash)
-		purple_xmlnode_set_attrib(client, "hash", tuple->hash);
+	purple_xmlnode_set_attrib(client, "hash", tuple->hash);
 	for(iter = props->identities; iter; iter = g_list_next(iter)) {
 		JabberIdentity *id = iter->data;
 		PurpleXmlNode *identity = purple_xmlnode_new_child(client, "identity");
@@ -180,10 +108,6 @@
 		PurpleXmlNode *xdata = iter->data;
 		purple_xmlnode_insert_child(client, purple_xmlnode_copy(xdata));
 	}
-
-	/* TODO: Ideally, only save this once-per-node... */
-	if (props->exts)
-		g_hash_table_foreach(props->exts->exts, (GHFunc)exts_to_xmlnode, client);
 }
 
 static gboolean
@@ -231,15 +155,10 @@
 			JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1);
 			JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple;
 			PurpleXmlNode *child;
-			JabberCapsNodeExts *exts = NULL;
 			key->node = g_strdup(purple_xmlnode_get_attrib(client,"node"));
 			key->ver  = g_strdup(purple_xmlnode_get_attrib(client,"ver"));
 			key->hash = g_strdup(purple_xmlnode_get_attrib(client,"hash"));
 
-			/* v1.3 capabilities */
-			if (key->hash == NULL)
-				exts = jabber_caps_find_exts_by_node(key->node);
-
 			for (child = client->child; child; child = child->next) {
 				if (child->type != PURPLE_XMLNODE_TYPE_TAG)
 					continue;
@@ -266,42 +185,10 @@
 					 * work properly, that bug needs to be fixed in
 					 * purple_xmlnode_from_str, not the output version... */
 					value->forms = g_list_append(value->forms, purple_xmlnode_copy(child));
-				} else if (purple_strequal(child->name, "ext")) {
-					if (key->hash != NULL)
-						purple_debug_warning("jabber", "Ignoring exts when reading new-style caps\n");
-					else {
-						/* TODO: Do we care about reading in the identities listed here? */
-						const char *identifier = purple_xmlnode_get_attrib(child, "identifier");
-						PurpleXmlNode *node;
-						GList *features = NULL;
-
-						if (!identifier)
-							continue;
-
-						for (node = child->child; node; node = node->next) {
-							if (node->type != PURPLE_XMLNODE_TYPE_TAG)
-								continue;
-							if (purple_strequal(node->name, "feature")) {
-								const char *var = purple_xmlnode_get_attrib(node, "var");
-								if (!var)
-									continue;
-								features = g_list_prepend(features, g_strdup(var));
-							}
-						}
-
-						if (features) {
-							g_hash_table_insert(exts->exts, g_strdup(identifier),
-							                    features);
-						} else
-							purple_debug_warning("jabber", "Caps ext %s had no features.\n",
-							                     identifier);
-					}
 				}
 			}
 
-			value->exts = exts;
 			g_hash_table_replace(capstable, key, value);
-
 		}
 	}
 	purple_xmlnode_free(capsdata);
@@ -309,7 +196,6 @@
 
 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, NULL, (GDestroyNotify)jabber_caps_client_info_destroy);
 	jabber_caps_load();
 }
@@ -322,31 +208,10 @@
 		do_jabber_caps_store(NULL);
 	}
 	g_hash_table_destroy(capstable);
-	g_hash_table_destroy(nodetable);
-	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) {
-		if (!info->exts ||
-				!g_hash_table_lookup(info->exts->exts, exts[i]))
-			return FALSE;
-	}
-
-	return TRUE;
+	capstable = NULL;
 }
 
 typedef struct {
-	guint ref;
-
 	jabber_caps_get_info_cb cb;
 	gpointer cb_data;
 
@@ -356,42 +221,21 @@
 	char *hash;
 
 	JabberCapsClientInfo *info;
-
-	GList *exts;
-	guint extOutstanding;
-	JabberCapsNodeExts *node_exts;
 } jabber_caps_cbplususerdata;
 
-static jabber_caps_cbplususerdata*
-cbplususerdata_ref(jabber_caps_cbplususerdata *data)
-{
-	g_return_val_if_fail(data != NULL, NULL);
-
-	++data->ref;
-	return data;
-}
-
 static void
-cbplususerdata_unref(jabber_caps_cbplususerdata *data)
+cbplususerdata_destroy(jabber_caps_cbplususerdata *data)
 {
 	if (data == NULL)
 		return;
 
-	g_return_if_fail(data->ref != 0);
-
-	if (--data->ref > 0)
-		return;
-
 	g_free(data->who);
 	g_free(data->node);
 	g_free(data->ver);
 	g_free(data->hash);
 
 	/* If we have info here, it's already in the capstable, so don't free it */
-	if (data->exts)
-		free_string_glist(data->exts);
-	if (data->node_exts)
-		jabber_caps_node_exts_unref(data->node_exts);
+
 	g_free(data);
 }
 
@@ -399,77 +243,62 @@
 jabber_caps_get_info_complete(jabber_caps_cbplususerdata *userdata)
 {
 	if (userdata->cb) {
-		userdata->cb(userdata->info, userdata->exts, userdata->cb_data);
+		userdata->cb(userdata->info, userdata->cb_data);
 		userdata->info = NULL;
-		userdata->exts = NULL;
 	}
-
-	if (userdata->ref != 1)
-		purple_debug_warning("jabber", "Lost a reference to caps cbdata: %d\n",
-		                     userdata->ref);
 }
 
 static void
 jabber_caps_client_iqcb(JabberStream *js, const char *from, JabberIqType type,
                         const char *id, PurpleXmlNode *packet, gpointer data)
 {
-	PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
-		NS_DISCO_INFO);
 	jabber_caps_cbplususerdata *userdata = data;
+	PurpleXmlNode *query = NULL;
 	JabberCapsClientInfo *info = NULL, *value;
 	JabberCapsTuple key;
+	gchar *hash = NULL;
+	GChecksumType hash_type;
+	gboolean supported_hash = TRUE;
 
-	if (!query || type == JABBER_IQ_ERROR) {
-		/* Any outstanding exts will be dealt with via ref-counting */
-		userdata->cb(NULL, NULL, userdata->cb_data);
-		cbplususerdata_unref(userdata);
+	query = purple_xmlnode_get_child_with_namespace(packet, "query",
+	                                                NS_DISCO_INFO);
+	if(query == NULL || type == JABBER_IQ_ERROR) {
+		userdata->cb(NULL, userdata->cb_data);
+		cbplususerdata_destroy(userdata);
 		return;
 	}
 
 	/* check hash */
 	info = jabber_caps_parse_client_info(query);
 
-	/* Only validate if these are v1.5 capabilities */
-	if (userdata->hash) {
-		gchar *hash = NULL;
-		GChecksumType hash_type;
-		gboolean supported_hash = TRUE;
-
-		if (purple_strequal(userdata->hash, "sha-1")) {
-			hash_type = G_CHECKSUM_SHA1;
-		} else if (purple_strequal(userdata->hash, "md5")) {
-			hash_type = G_CHECKSUM_MD5;
-		} else {
-			supported_hash = FALSE;
-		}
+	if(purple_strequal(userdata->hash, "sha-1")) {
+		hash_type = G_CHECKSUM_SHA1;
+	} else if(purple_strequal(userdata->hash, "md5")) {
+		hash_type = G_CHECKSUM_MD5;
+	} else {
+		supported_hash = FALSE;
+	}
 
-		if (supported_hash) {
-			hash = jabber_caps_calculate_hash(info, hash_type);
-		}
-
-		if (!hash || !purple_strequal(hash, userdata->ver)) {
-			purple_debug_warning("jabber", "Could not validate caps info from "
-			                     "%s. Expected %s, got %s\n",
-			                     purple_xmlnode_get_attrib(packet, "from"),
-			                     userdata->ver, hash ? hash : "(null)");
-
-			userdata->cb(NULL, NULL, userdata->cb_data);
-			jabber_caps_client_info_destroy(info);
-			cbplususerdata_unref(userdata);
-			g_free(hash);
-			return;
-		}
-
-		g_free(hash);
+	if (supported_hash) {
+		hash = jabber_caps_calculate_hash(info, hash_type);
 	}
 
-	if (!userdata->hash && userdata->node_exts) {
-		/* If the ClientInfo doesn't have information about the exts, give them
-		 * ours (along with our ref) */
-		info->exts = userdata->node_exts;
-		userdata->node_exts = NULL;
+	if (hash == NULL || !purple_strequal(hash, userdata->ver)) {
+		purple_debug_warning("jabber",
+		                     "Could not validate caps info from %s. "
+		                     "Expected %s, got %s",
+		                     purple_xmlnode_get_attrib(packet, "from"),
+		                     userdata->ver, hash ? hash : "(null)");
+
+		userdata->cb(NULL, userdata->cb_data);
+		jabber_caps_client_info_destroy(info);
+		cbplususerdata_destroy(userdata);
+		g_free(hash);
+		return;
 	}
 
+	g_free(hash);
+
 	key.node = userdata->node;
 	key.ver  = userdata->ver;
 	key.hash = userdata->hash;
@@ -500,91 +329,22 @@
 
 	userdata->info = info;
 
-	if (userdata->extOutstanding == 0)
-		jabber_caps_get_info_complete(userdata);
+	jabber_caps_get_info_complete(userdata);
 
-	cbplususerdata_unref(userdata);
+	cbplususerdata_destroy(userdata);
 }
 
-static void
-jabber_caps_ext_iqcb(JabberStream *js, const char *from, JabberIqType type,
-                     const char *id, PurpleXmlNode *packet, gpointer data)
+void
+jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
+                     const char *ver, const char *hash,
+                     jabber_caps_get_info_cb cb, gpointer user_data)
 {
-	PurpleXmlNode *query = purple_xmlnode_get_child_with_namespace(packet, "query",
-		NS_DISCO_INFO);
-	PurpleXmlNode *child;
-	PurpleKeyValuePair *cbdata = data;
-	jabber_caps_cbplususerdata *userdata = cbdata->value;
-	GList *features = NULL;
-	JabberCapsNodeExts *node_exts;
-
-	if (!query || type == JABBER_IQ_ERROR) {
-		purple_key_value_pair_free(cbdata);
-		return;
-	}
-
-	node_exts = (userdata->info ? userdata->info->exts : userdata->node_exts);
-
-	/* TODO: I don't see how this can actually happen, but it crashed khc. */
-	if (!node_exts) {
-		purple_debug_error("jabber", "Couldn't find JabberCapsNodeExts. If you "
-		                   "see this, please tell darkrain42 and save your debug log.\n"
-		                   "JabberCapsClientInfo = %p", userdata->info);
-
-
-		/* Try once more to find the exts and then fail */
-		node_exts = jabber_caps_find_exts_by_node(userdata->node);
-		if (node_exts) {
-			purple_debug_info("jabber", "Found the exts on the second try.\n");
-			if (userdata->info) {
-				userdata->info->exts = node_exts;
-			} else {
-				userdata->node_exts = node_exts;
-			}
-		} else {
-			purple_key_value_pair_free(cbdata);
-			g_return_if_reached();
-		}
-	}
-
-	/* So, we decrement this after checking for an error, which means that
-	 * if there *is* an error, we'll never call the callback passed to
-	 * jabber_caps_get_info. We will still free all of our data, though.
-	 */
-	--userdata->extOutstanding;
-
-	for (child = purple_xmlnode_get_child(query, "feature"); child;
-	        child = purple_xmlnode_get_next_twin(child)) {
-		const char *var = purple_xmlnode_get_attrib(child, "var");
-		if (var)
-			features = g_list_prepend(features, g_strdup(var));
-	}
-
-	g_hash_table_insert(node_exts->exts, g_strdup(cbdata->key), features);
-	schedule_caps_save();
-
-	/* Are we done? */
-	if (userdata->info && userdata->extOutstanding == 0) {
-		jabber_caps_get_info_complete(userdata);
-	}
-
-	purple_key_value_pair_free(cbdata);
-}
-
-void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
-        const char *ver, const char *hash, char **exts,
-        jabber_caps_get_info_cb cb, gpointer user_data)
-{
-	JabberCapsClientInfo *info;
+	JabberCapsClientInfo *info = NULL;
 	JabberCapsTuple key;
-	jabber_caps_cbplususerdata *userdata;
-
-	if (exts && hash) {
-		purple_debug_misc("jabber", "Ignoring exts in new-style caps from %s\n",
-		                  who);
-		g_strfreev(exts);
-		exts = NULL;
-	}
+	jabber_caps_cbplususerdata *userdata = NULL;
+	JabberIq *iq = NULL;
+	PurpleXmlNode *query = NULL;
+	char *nodever = NULL;
 
 	/* Using this in a read-only fashion, so the cast is OK */
 	key.node = (char *)node;
@@ -592,10 +352,11 @@
 	key.hash = (char *)hash;
 
 	info = g_hash_table_lookup(capstable, &key);
-	if (info && hash) {
-		/* v1.5 - We already have all the information we care about */
-		if (cb)
-			cb(info, NULL, user_data);
+	if (info != NULL) {
+		/* We already have all the information we care about */
+		if (cb) {
+			cb(info, user_data);
+		}
 		return;
 	}
 
@@ -608,82 +369,18 @@
 	userdata->ver = g_strdup(ver);
 	userdata->hash = g_strdup(hash);
 
-	if (info) {
-		userdata->info = info;
-	} else {
-		/* If we don't have the basic information about the client, we need
-		 * to fetch it. */
-		JabberIq *iq;
-		PurpleXmlNode *query;
-		char *nodever;
-
-		iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
-		query = purple_xmlnode_get_child_with_namespace(iq->node, "query",
-					NS_DISCO_INFO);
-		nodever = g_strdup_printf("%s#%s", node, ver);
-		purple_xmlnode_set_attrib(query, "node", nodever);
-		g_free(nodever);
-		purple_xmlnode_set_attrib(iq->node, "to", who);
-
-		cbplususerdata_ref(userdata);
-
-		jabber_iq_set_callback(iq, jabber_caps_client_iqcb, userdata);
-		jabber_iq_send(iq);
-	}
-
-	/* Are there any exts that we don't recognize? */
-	if (exts) {
-		JabberCapsNodeExts *node_exts;
-		int i;
-
-		if (info) {
-			if (info->exts)
-				node_exts = info->exts;
-			else
-				node_exts = info->exts = jabber_caps_find_exts_by_node(node);
-		} else
-			/* We'll put it in later once we have the client info */
-			node_exts = userdata->node_exts = jabber_caps_find_exts_by_node(node);
+	/* If we don't have the basic information about the client, we need to
+	 * fetch it. */
+	iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
+	query = purple_xmlnode_get_child_with_namespace(iq->node, "query",
+	                                                NS_DISCO_INFO);
+	nodever = g_strdup_printf("%s#%s", node, ver);
+	purple_xmlnode_set_attrib(query, "node", nodever);
+	g_free(nodever);
+	purple_xmlnode_set_attrib(iq->node, "to", who);
 
-		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, exts[i])) {
-				JabberIq *iq;
-				PurpleXmlNode *query;
-				char *nodeext;
-				PurpleKeyValuePair *cbdata;
-
-				iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_DISCO_INFO);
-				query = purple_xmlnode_get_child_with_namespace(iq->node, "query",
-				            NS_DISCO_INFO);
-				nodeext = g_strdup_printf("%s#%s", node, exts[i]);
-				purple_xmlnode_set_attrib(query, "node", nodeext);
-				g_free(nodeext);
-				purple_xmlnode_set_attrib(iq->node, "to", who);
-
-				cbdata = purple_key_value_pair_new_full(exts[i], cbplususerdata_ref(userdata),
-				                                        (GDestroyNotify)cbplususerdata_unref);
-				jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, cbdata);
-				jabber_iq_send(iq);
-
-				++userdata->extOutstanding;
-			}
-			exts[i] = NULL;
-		}
-		/* All the strings are now part of the GList, so don't need
-		 * g_strfreev. */
-		g_free(exts);
-	}
-
-	if (userdata->info && userdata->extOutstanding == 0) {
-		/* Start with 1 ref so the below functions are happy */
-		userdata->ref = 1;
-
-		/* We have everything we need right now */
-		jabber_caps_get_info_complete(userdata);
-		cbplususerdata_unref(userdata);
-	}
+	jabber_iq_set_callback(iq, jabber_caps_client_iqcb, userdata);
+	jabber_iq_send(iq);
 }
 
 static gint

mercurial