Mercurial > pidgin.yaz
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); |