comparison libpurple/protocols/jabber/caps.c @ 27946:e0bcdc2bad7d

propagate from branch 'im.pidgin.pidgin' (head 70ee1de1cc79688256a3dd6ac1a519e24c00a12c) to branch 'im.pidgin.pidgin.yaz' (head 48181b0dbca52e9e9bd367a1dcc01fe5f9a678a1)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 11 May 2009 02:18:47 +0000
parents 7d924c723d7e
children a8537bbcfb79
comparison
equal deleted inserted replaced
27945:cc45ac2782cc 27946:e0bcdc2bad7d
33 typedef struct _JabberDataFormField { 33 typedef struct _JabberDataFormField {
34 gchar *var; 34 gchar *var;
35 GList *values; 35 GList *values;
36 } JabberDataFormField; 36 } JabberDataFormField;
37 37
38 typedef struct _JabberCapsKey { 38 static GHashTable *capstable = NULL; /* JabberCapsTuple -> JabberCapsClientInfo */
39 char *node;
40 char *ver;
41 char *hash;
42 } JabberCapsKey;
43
44 static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsClientInfo */
45 static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */ 39 static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */
46 static guint save_timer = 0; 40 static guint save_timer = 0;
47 41
48 /** 42 /**
49 * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info. 43 * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info.
84 g_hash_table_destroy(exts->exts); 78 g_hash_table_destroy(exts->exts);
85 g_free(exts); 79 g_free(exts);
86 } 80 }
87 81
88 static guint jabber_caps_hash(gconstpointer data) { 82 static guint jabber_caps_hash(gconstpointer data) {
89 const JabberCapsKey *key = data; 83 const JabberCapsTuple *key = data;
90 guint nodehash = g_str_hash(key->node); 84 guint nodehash = g_str_hash(key->node);
91 guint verhash = g_str_hash(key->ver); 85 guint verhash = g_str_hash(key->ver);
92 /* 86 /*
93 * 'hash' was optional in XEP-0115 v1.4 and g_str_hash crashes on NULL >:O. 87 * 'hash' was optional in XEP-0115 v1.4 and g_str_hash crashes on NULL >:O.
94 * Okay, maybe I've played too much Zelda, but that looks like 88 * Okay, maybe I've played too much Zelda, but that looks like
97 guint hashhash = (key->hash ? g_str_hash(key->hash) : 0); 91 guint hashhash = (key->hash ? g_str_hash(key->hash) : 0);
98 return nodehash ^ verhash ^ hashhash; 92 return nodehash ^ verhash ^ hashhash;
99 } 93 }
100 94
101 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { 95 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) {
102 const JabberCapsKey *name1 = v1; 96 const JabberCapsTuple *name1 = v1;
103 const JabberCapsKey *name2 = v2; 97 const JabberCapsTuple *name2 = v2;
104 98
105 return g_str_equal(name1->node, name2->node) && 99 return g_str_equal(name1->node, name2->node) &&
106 g_str_equal(name1->ver, name2->ver) && 100 g_str_equal(name1->ver, name2->ver) &&
107 purple_strequal(name1->hash, name2->hash); 101 purple_strequal(name1->hash, name2->hash);
108 }
109
110 void jabber_caps_destroy_key(gpointer data) {
111 JabberCapsKey *key = data;
112 g_free(key->node);
113 g_free(key->ver);
114 g_free(key->hash);
115 g_free(key);
116 } 102 }
117 103
118 static void 104 static void
119 jabber_caps_client_info_destroy(JabberCapsClientInfo *info) 105 jabber_caps_client_info_destroy(JabberCapsClientInfo *info)
120 { 106 {
138 info->forms = g_list_delete_link(info->forms, info->forms); 124 info->forms = g_list_delete_link(info->forms, info->forms);
139 } 125 }
140 126
141 jabber_caps_node_exts_unref(info->exts); 127 jabber_caps_node_exts_unref(info->exts);
142 128
129 g_free((char *)info->tuple.node);
130 g_free((char *)info->tuple.ver);
131 g_free((char *)info->tuple.hash);
132
143 g_free(info); 133 g_free(info);
144 } 134 }
145 135
146 /* NOTE: Takes a reference to the exts, unref it if you don't really want to 136 /* NOTE: Takes a reference to the exts, unref it if you don't really want to
147 * keep it around. */ 137 * keep it around. */
174 xmlnode_set_attrib(feature, "var", (const gchar *)node->data); 164 xmlnode_set_attrib(feature, "var", (const gchar *)node->data);
175 } 165 }
176 } 166 }
177 167
178 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { 168 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) {
179 JabberCapsKey *clientinfo = key; 169 const JabberCapsTuple *tuple = key;
180 JabberCapsClientInfo *props = value; 170 const JabberCapsClientInfo *props = value;
181 xmlnode *root = user_data; 171 xmlnode *root = user_data;
182 xmlnode *client = xmlnode_new_child(root, "client"); 172 xmlnode *client = xmlnode_new_child(root, "client");
183 GList *iter; 173 GList *iter;
184 174
185 xmlnode_set_attrib(client, "node", clientinfo->node); 175 xmlnode_set_attrib(client, "node", tuple->node);
186 xmlnode_set_attrib(client, "ver", clientinfo->ver); 176 xmlnode_set_attrib(client, "ver", tuple->ver);
187 if (clientinfo->hash) 177 if (tuple->hash)
188 xmlnode_set_attrib(client, "hash", clientinfo->hash); 178 xmlnode_set_attrib(client, "hash", tuple->hash);
189 for(iter = props->identities; iter; iter = g_list_next(iter)) { 179 for(iter = props->identities; iter; iter = g_list_next(iter)) {
190 JabberIdentity *id = iter->data; 180 JabberIdentity *id = iter->data;
191 xmlnode *identity = xmlnode_new_child(client, "identity"); 181 xmlnode *identity = xmlnode_new_child(client, "identity");
192 xmlnode_set_attrib(identity, "category", id->category); 182 xmlnode_set_attrib(identity, "category", id->category);
193 xmlnode_set_attrib(identity, "type", id->type); 183 xmlnode_set_attrib(identity, "type", id->type);
253 243
254 for(client = capsdata->child; client; client = client->next) { 244 for(client = capsdata->child; client; client = client->next) {
255 if(client->type != XMLNODE_TYPE_TAG) 245 if(client->type != XMLNODE_TYPE_TAG)
256 continue; 246 continue;
257 if(!strcmp(client->name, "client")) { 247 if(!strcmp(client->name, "client")) {
258 JabberCapsKey *key = g_new0(JabberCapsKey, 1);
259 JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1); 248 JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1);
249 JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple;
260 xmlnode *child; 250 xmlnode *child;
261 JabberCapsNodeExts *exts = NULL; 251 JabberCapsNodeExts *exts = NULL;
262 key->node = g_strdup(xmlnode_get_attrib(client,"node")); 252 key->node = g_strdup(xmlnode_get_attrib(client,"node"));
263 key->ver = g_strdup(xmlnode_get_attrib(client,"ver")); 253 key->ver = g_strdup(xmlnode_get_attrib(client,"ver"));
264 key->hash = g_strdup(xmlnode_get_attrib(client,"hash")); 254 key->hash = g_strdup(xmlnode_get_attrib(client,"hash"));
338 } 328 }
339 329
340 void jabber_caps_init(void) 330 void jabber_caps_init(void)
341 { 331 {
342 nodetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_caps_node_exts_unref); 332 nodetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_caps_node_exts_unref);
343 capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, (GDestroyNotify)jabber_caps_client_info_destroy); 333 capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, NULL, (GDestroyNotify)jabber_caps_client_info_destroy);
344 jabber_caps_load(); 334 jabber_caps_load();
345 } 335 }
346 336
347 void jabber_caps_uninit(void) 337 void jabber_caps_uninit(void)
348 { 338 {
352 do_jabber_caps_store(NULL); 342 do_jabber_caps_store(NULL);
353 } 343 }
354 g_hash_table_destroy(capstable); 344 g_hash_table_destroy(capstable);
355 g_hash_table_destroy(nodetable); 345 g_hash_table_destroy(nodetable);
356 capstable = nodetable = NULL; 346 capstable = nodetable = NULL;
347 }
348
349 gboolean jabber_caps_exts_known(const JabberCapsClientInfo *info,
350 char **exts)
351 {
352 int i;
353 g_return_val_if_fail(info != NULL, FALSE);
354
355 if (!exts)
356 return TRUE;
357
358 for (i = 0; exts[i]; ++i) {
359 /* Hack since we advertise the ext along with v1.5 caps but don't
360 * store any exts */
361 if (g_str_equal(exts[i], "voice-v1") && !info->exts)
362 continue;
363 if (!info->exts ||
364 !g_hash_table_lookup(info->exts->exts, exts[i]))
365 return FALSE;
366 }
367
368 return TRUE;
357 } 369 }
358 370
359 typedef struct _jabber_caps_cbplususerdata { 371 typedef struct _jabber_caps_cbplususerdata {
360 guint ref; 372 guint ref;
361 373
427 { 439 {
428 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", 440 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
429 "http://jabber.org/protocol/disco#info"); 441 "http://jabber.org/protocol/disco#info");
430 jabber_caps_cbplususerdata *userdata = data; 442 jabber_caps_cbplususerdata *userdata = data;
431 JabberCapsClientInfo *info = NULL, *value; 443 JabberCapsClientInfo *info = NULL, *value;
432 JabberCapsKey key; 444 JabberCapsTuple key;
433 445
434 if (!query || type == JABBER_IQ_ERROR) { 446 if (!query || type == JABBER_IQ_ERROR) {
435 /* Any outstanding exts will be dealt with via ref-counting */ 447 /* Any outstanding exts will be dealt with via ref-counting */
436 userdata->cb(NULL, NULL, userdata->cb_data); 448 userdata->cb(NULL, NULL, userdata->cb_data);
437 cbplususerdata_unref(userdata); 449 cbplususerdata_unref(userdata);
479 * a new one if we need to */ 491 * a new one if we need to */
480 if ((value = g_hash_table_lookup(capstable, &key))) { 492 if ((value = g_hash_table_lookup(capstable, &key))) {
481 jabber_caps_client_info_destroy(info); 493 jabber_caps_client_info_destroy(info);
482 info = value; 494 info = value;
483 } else { 495 } else {
484 JabberCapsKey *n_key = g_new(JabberCapsKey, 1); 496 JabberCapsTuple *n_key = (JabberCapsTuple *)&info->tuple;
485 n_key->node = userdata->node; 497 n_key->node = userdata->node;
486 n_key->ver = userdata->ver; 498 n_key->ver = userdata->ver;
487 n_key->hash = userdata->hash; 499 n_key->hash = userdata->hash;
488 userdata->node = userdata->ver = userdata->hash = NULL; 500 userdata->node = userdata->ver = userdata->hash = NULL;
489 501
547 cbplususerdata_unref(userdata->data); 559 cbplususerdata_unref(userdata->data);
548 g_free(userdata); 560 g_free(userdata);
549 } 561 }
550 562
551 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, 563 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
552 const char *ver, const char *hash, const char *ext, 564 const char *ver, const char *hash, char **exts,
553 jabber_caps_get_info_cb cb, gpointer user_data) 565 jabber_caps_get_info_cb cb, gpointer user_data)
554 { 566 {
555 JabberCapsClientInfo *info; 567 JabberCapsClientInfo *info;
556 JabberCapsKey key; 568 JabberCapsTuple key;
557 jabber_caps_cbplususerdata *userdata; 569 jabber_caps_cbplususerdata *userdata;
558 570
559 if (ext && *ext && hash) 571 if (exts && hash) {
560 purple_debug_info("jabber", "Ignoring exts in new-style caps from %s\n", 572 purple_debug_info("jabber", "Ignoring exts in new-style caps from %s\n",
561 who); 573 who);
574 g_strfreev(exts);
575 exts = NULL;
576 }
562 577
563 /* Using this in a read-only fashion, so the cast is OK */ 578 /* Using this in a read-only fashion, so the cast is OK */
564 key.node = (char *)node; 579 key.node = (char *)node;
565 key.ver = (char *)ver; 580 key.ver = (char *)ver;
566 key.hash = (char *)hash; 581 key.hash = (char *)hash;
605 jabber_iq_set_callback(iq, jabber_caps_client_iqcb, userdata); 620 jabber_iq_set_callback(iq, jabber_caps_client_iqcb, userdata);
606 jabber_iq_send(iq); 621 jabber_iq_send(iq);
607 } 622 }
608 623
609 /* Are there any exts that we don't recognize? */ 624 /* Are there any exts that we don't recognize? */
610 if (ext && *ext && !hash) { 625 if (exts) {
611 JabberCapsNodeExts *node_exts; 626 JabberCapsNodeExts *node_exts;
612 gchar **splat = g_strsplit(ext, " ", 0);
613 int i; 627 int i;
614 628
615 if (info) { 629 if (info) {
616 if (info->exts) 630 if (info->exts)
617 node_exts = info->exts; 631 node_exts = info->exts;
619 node_exts = info->exts = jabber_caps_find_exts_by_node(node); 633 node_exts = info->exts = jabber_caps_find_exts_by_node(node);
620 } else 634 } else
621 /* We'll put it in later once we have the client info */ 635 /* We'll put it in later once we have the client info */
622 node_exts = userdata->node_exts = jabber_caps_find_exts_by_node(node); 636 node_exts = userdata->node_exts = jabber_caps_find_exts_by_node(node);
623 637
624 for (i = 0; splat[i]; ++i) { 638 for (i = 0; exts[i]; ++i) {
625 userdata->exts = g_list_prepend(userdata->exts, splat[i]); 639 userdata->exts = g_list_prepend(userdata->exts, exts[i]);
626 /* Look it up if we don't already know what it means */ 640 /* Look it up if we don't already know what it means */
627 if (!g_hash_table_lookup(node_exts->exts, splat[i])) { 641 if (!g_hash_table_lookup(node_exts->exts, exts[i])) {
628 JabberIq *iq; 642 JabberIq *iq;
629 xmlnode *query; 643 xmlnode *query;
630 char *nodeext; 644 char *nodeext;
631 ext_iq_data *cbdata = g_new(ext_iq_data, 1); 645 ext_iq_data *cbdata = g_new(ext_iq_data, 1);
632 646
633 cbdata->name = splat[i]; 647 cbdata->name = exts[i];
634 cbdata->data = cbplususerdata_ref(userdata); 648 cbdata->data = cbplususerdata_ref(userdata);
635 649
636 iq = jabber_iq_new_query(js, JABBER_IQ_GET, 650 iq = jabber_iq_new_query(js, JABBER_IQ_GET,
637 "http://jabber.org/protocol/disco#info"); 651 "http://jabber.org/protocol/disco#info");
638 query = xmlnode_get_child_with_namespace(iq->node, "query", 652 query = xmlnode_get_child_with_namespace(iq->node, "query",
639 "http://jabber.org/protocol/disco#info"); 653 "http://jabber.org/protocol/disco#info");
640 nodeext = g_strdup_printf("%s#%s", node, splat[i]); 654 nodeext = g_strdup_printf("%s#%s", node, exts[i]);
641 xmlnode_set_attrib(query, "node", nodeext); 655 xmlnode_set_attrib(query, "node", nodeext);
642 g_free(nodeext); 656 g_free(nodeext);
643 xmlnode_set_attrib(iq->node, "to", who); 657 xmlnode_set_attrib(iq->node, "to", who);
644 658
645 jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, cbdata); 659 jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, cbdata);
646 jabber_iq_send(iq); 660 jabber_iq_send(iq);
647 661
648 ++userdata->extOutstanding; 662 ++userdata->extOutstanding;
649 } 663 }
650 splat[i] = NULL; 664 exts[i] = NULL;
651 } 665 }
652 /* All the strings are now part of the GList, so don't need 666 /* All the strings are now part of the GList, so don't need
653 * g_strfreev. */ 667 * g_strfreev. */
654 g_free(splat); 668 g_free(exts);
655 } 669 }
656 670
657 if (userdata->info && userdata->extOutstanding == 0) { 671 if (userdata->info && userdata->extOutstanding == 0) {
658 jabber_caps_get_info_complete(userdata); 672 jabber_caps_get_info_complete(userdata);
659 cbplususerdata_unref(userdata); 673 cbplususerdata_unref(userdata);