Mercurial > pidgin.yaz
comparison libpurple/protocols/jabber/caps.c @ 25765:a259d2711416
merge of '0da3644551b2442b2db4cf9d828865d167df8072'
and 'efc71f836438eb97ec8d2c8e0874326b6fc64774'
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sun, 23 Nov 2008 03:52:40 +0000 |
parents | 6adbaf3d25e3 18fdbe507fdd |
children | 5d2e4a3b0c63 |
comparison
equal
deleted
inserted
replaced
25744:6adbaf3d25e3 | 25765:a259d2711416 |
---|---|
19 * | 19 * |
20 */ | 20 */ |
21 | 21 |
22 #include "internal.h" | 22 #include "internal.h" |
23 | 23 |
24 #include <glib.h> | 24 #include "debug.h" |
25 #include "caps.h" | 25 #include "caps.h" |
26 #include "cipher.h" | 26 #include "cipher.h" |
27 #include <string.h> | |
28 #include "iq.h" | 27 #include "iq.h" |
29 #include "presence.h" | 28 #include "presence.h" |
30 #include "util.h" | 29 #include "util.h" |
31 | 30 |
32 #define JABBER_CAPS_FILENAME "xmpp-caps.xml" | 31 #define JABBER_CAPS_FILENAME "xmpp-caps.xml" |
33 | 32 |
34 GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ | 33 typedef struct _JabberDataFormField { |
34 gchar *var; | |
35 GList *values; | |
36 } JabberDataFormField; | |
37 | |
38 typedef struct _JabberCapsKey { | |
39 char *node; | |
40 char *ver; | |
41 char *hash; | |
42 } JabberCapsKey; | |
43 | |
44 static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsClientInfo */ | |
45 | |
46 /** | |
47 * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info. | |
48 * | |
49 * @param query A query object. | |
50 * @return A JabberCapsClientInfo object. | |
51 */ | |
52 static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); | |
35 | 53 |
36 #if 0 | 54 #if 0 |
37 typedef struct _JabberCapsValue { | 55 typedef struct _JabberCapsValue { |
38 GList *identities; /* JabberCapsIdentity */ | 56 GList *identities; /* JabberCapsIdentity */ |
39 GList *features; /* char * */ | 57 GList *features; /* char * */ |
40 GHashTable *ext; /* char * -> JabberCapsValueExt */ | 58 GHashTable *ext; /* char * -> JabberCapsValueExt */ |
41 } JabberCapsValue; | 59 } JabberCapsValue; |
42 #endif | 60 #endif |
43 typedef struct _JabberCapsClientInfo JabberCapsValue; | |
44 | 61 |
45 static guint jabber_caps_hash(gconstpointer key) { | 62 static guint jabber_caps_hash(gconstpointer key) { |
46 const JabberCapsKey *name = key; | 63 const JabberCapsKey *name = key; |
47 guint nodehash = g_str_hash(name->node); | 64 guint nodehash = g_str_hash(name->node); |
48 guint verhash = g_str_hash(name->ver); | 65 guint verhash = g_str_hash(name->ver); |
52 | 69 |
53 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { | 70 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { |
54 const JabberCapsKey *name1 = v1; | 71 const JabberCapsKey *name1 = v1; |
55 const JabberCapsKey *name2 = v2; | 72 const JabberCapsKey *name2 = v2; |
56 | 73 |
57 return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0 && strcmp(name1->hash,name2->hash) == 0; | 74 return strcmp(name1->node, name2->node) == 0 && |
75 strcmp(name1->ver, name2->ver) == 0 && | |
76 strcmp(name1->hash, name2->hash) == 0; | |
58 } | 77 } |
59 | 78 |
60 void jabber_caps_destroy_key(gpointer key) { | 79 void jabber_caps_destroy_key(gpointer key) { |
61 JabberCapsKey *keystruct = key; | 80 JabberCapsKey *keystruct = key; |
62 g_free(keystruct->node); | 81 g_free(keystruct->node); |
63 g_free(keystruct->ver); | 82 g_free(keystruct->ver); |
64 g_free(keystruct->hash); | 83 g_free(keystruct->hash); |
65 g_free(keystruct); | 84 g_free(keystruct); |
66 } | 85 } |
67 | 86 |
68 static void jabber_caps_destroy_value(gpointer value) { | 87 static void |
69 JabberCapsValue *valuestruct = value; | 88 jabber_caps_client_info_destroy(gpointer data) { |
70 while(valuestruct->identities) { | 89 JabberCapsClientInfo *info = data; |
71 JabberCapsIdentity *id = valuestruct->identities->data; | 90 while(info->identities) { |
91 JabberIdentity *id = info->identities->data; | |
72 g_free(id->category); | 92 g_free(id->category); |
73 g_free(id->type); | 93 g_free(id->type); |
74 g_free(id->name); | 94 g_free(id->name); |
95 g_free(id->lang); | |
75 g_free(id); | 96 g_free(id); |
76 | 97 info->identities = g_list_delete_link(info->identities, info->identities); |
77 valuestruct->identities = g_list_delete_link(valuestruct->identities,valuestruct->identities); | 98 } |
78 } | 99 |
79 while(valuestruct->features) { | 100 while(info->features) { |
80 g_free(valuestruct->features->data); | 101 g_free(info->features->data); |
81 valuestruct->features = g_list_delete_link(valuestruct->features,valuestruct->features); | 102 info->features = g_list_delete_link(info->features, info->features); |
82 } | 103 } |
83 | 104 |
84 while(valuestruct->forms) { | 105 while(info->forms) { |
85 g_free(valuestruct->forms->data); | 106 g_free(info->forms->data); |
86 valuestruct->forms = g_list_delete_link(valuestruct->forms,valuestruct->forms); | 107 info->forms = g_list_delete_link(info->forms, info->forms); |
87 } | 108 } |
88 //g_hash_table_destroy(valuestruct->ext); | 109 |
89 g_free(valuestruct); | 110 #if 0 |
111 g_hash_table_destroy(valuestruct->ext); | |
112 #endif | |
113 | |
114 g_free(info); | |
90 } | 115 } |
91 | 116 |
92 #if 0 | 117 #if 0 |
93 static void jabber_caps_ext_destroy_value(gpointer value) { | 118 static void jabber_caps_ext_destroy_value(gpointer value) { |
94 JabberCapsValueExt *valuestruct = value; | 119 JabberCapsValueExt *valuestruct = value; |
109 } | 134 } |
110 #endif | 135 #endif |
111 | 136 |
112 static void jabber_caps_load(void); | 137 static void jabber_caps_load(void); |
113 | 138 |
114 void jabber_caps_init(void) { | 139 void jabber_caps_init(void) |
115 capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, jabber_caps_destroy_value); | 140 { |
141 capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, jabber_caps_client_info_destroy); | |
116 jabber_caps_load(); | 142 jabber_caps_load(); |
143 } | |
144 | |
145 void jabber_caps_uninit(void) | |
146 { | |
147 g_hash_table_destroy(capstable); | |
148 capstable = NULL; | |
117 } | 149 } |
118 | 150 |
119 static void jabber_caps_load(void) { | 151 static void jabber_caps_load(void) { |
120 xmlnode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); | 152 xmlnode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); |
121 xmlnode *client; | 153 xmlnode *client; |
131 for(client = capsdata->child; client; client = client->next) { | 163 for(client = capsdata->child; client; client = client->next) { |
132 if(client->type != XMLNODE_TYPE_TAG) | 164 if(client->type != XMLNODE_TYPE_TAG) |
133 continue; | 165 continue; |
134 if(!strcmp(client->name, "client")) { | 166 if(!strcmp(client->name, "client")) { |
135 JabberCapsKey *key = g_new0(JabberCapsKey, 1); | 167 JabberCapsKey *key = g_new0(JabberCapsKey, 1); |
136 JabberCapsValue *value = g_new0(JabberCapsValue, 1); | 168 JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1); |
137 xmlnode *child; | 169 xmlnode *child; |
138 key->node = g_strdup(xmlnode_get_attrib(client,"node")); | 170 key->node = g_strdup(xmlnode_get_attrib(client,"node")); |
139 key->ver = g_strdup(xmlnode_get_attrib(client,"ver")); | 171 key->ver = g_strdup(xmlnode_get_attrib(client,"ver")); |
140 key->hash = g_strdup(xmlnode_get_attrib(client,"hash")); | 172 key->hash = g_strdup(xmlnode_get_attrib(client,"hash")); |
141 for(child = client->child; child; child = child->next) { | 173 for(child = client->child; child; child = child->next) { |
148 value->features = g_list_append(value->features,g_strdup(var)); | 180 value->features = g_list_append(value->features,g_strdup(var)); |
149 } else if(!strcmp(child->name,"identity")) { | 181 } else if(!strcmp(child->name,"identity")) { |
150 const char *category = xmlnode_get_attrib(child, "category"); | 182 const char *category = xmlnode_get_attrib(child, "category"); |
151 const char *type = xmlnode_get_attrib(child, "type"); | 183 const char *type = xmlnode_get_attrib(child, "type"); |
152 const char *name = xmlnode_get_attrib(child, "name"); | 184 const char *name = xmlnode_get_attrib(child, "name"); |
153 | 185 const char *lang = xmlnode_get_attrib(child, "lang"); |
154 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); | 186 JabberIdentity *id; |
187 | |
188 if (!category || !type) | |
189 continue; | |
190 | |
191 id = g_new0(JabberIdentity, 1); | |
155 id->category = g_strdup(category); | 192 id->category = g_strdup(category); |
156 id->type = g_strdup(type); | 193 id->type = g_strdup(type); |
157 id->name = g_strdup(name); | 194 id->name = g_strdup(name); |
195 id->lang = g_strdup(lang); | |
158 | 196 |
159 value->identities = g_list_append(value->identities,id); | 197 value->identities = g_list_append(value->identities,id); |
160 } else if(!strcmp(child->name,"x")) { | 198 } else if(!strcmp(child->name,"x")) { |
161 value->forms = g_list_append(value->forms, xmlnode_copy(child)); | 199 value->forms = g_list_append(value->forms, xmlnode_copy(child)); |
162 } | 200 } |
194 } | 232 } |
195 #endif | 233 #endif |
196 | 234 |
197 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { | 235 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { |
198 JabberCapsKey *clientinfo = key; | 236 JabberCapsKey *clientinfo = key; |
199 JabberCapsValue *props = value; | 237 JabberCapsClientInfo *props = value; |
200 xmlnode *root = user_data; | 238 xmlnode *root = user_data; |
201 xmlnode *client = xmlnode_new_child(root, "client"); | 239 xmlnode *client = xmlnode_new_child(root, "client"); |
202 GList *iter; | 240 GList *iter; |
203 | 241 |
204 xmlnode_set_attrib(client, "node", clientinfo->node); | 242 xmlnode_set_attrib(client, "node", clientinfo->node); |
205 xmlnode_set_attrib(client, "ver", clientinfo->ver); | 243 xmlnode_set_attrib(client, "ver", clientinfo->ver); |
206 xmlnode_set_attrib(client, "hash", clientinfo->hash); | 244 xmlnode_set_attrib(client, "hash", clientinfo->hash); |
207 for(iter = props->identities; iter; iter = g_list_next(iter)) { | 245 for(iter = props->identities; iter; iter = g_list_next(iter)) { |
208 JabberCapsIdentity *id = iter->data; | 246 JabberIdentity *id = iter->data; |
209 xmlnode *identity = xmlnode_new_child(client, "identity"); | 247 xmlnode *identity = xmlnode_new_child(client, "identity"); |
210 xmlnode_set_attrib(identity, "category", id->category); | 248 xmlnode_set_attrib(identity, "category", id->category); |
211 xmlnode_set_attrib(identity, "type", id->type); | 249 xmlnode_set_attrib(identity, "type", id->type); |
212 if (id->name) | 250 if (id->name) |
213 xmlnode_set_attrib(identity, "name", id->name); | 251 xmlnode_set_attrib(identity, "name", id->name); |
252 if (id->lang) | |
253 xmlnode_set_attrib(identity, "lang", id->lang); | |
214 } | 254 } |
215 | 255 |
216 for(iter = props->features; iter; iter = g_list_next(iter)) { | 256 for(iter = props->features; iter; iter = g_list_next(iter)) { |
217 const char *feat = iter->data; | 257 const char *feat = iter->data; |
218 xmlnode *feature = xmlnode_new_child(client, "feature"); | 258 xmlnode *feature = xmlnode_new_child(client, "feature"); |
299 #endif | 339 #endif |
300 return result; | 340 return result; |
301 } | 341 } |
302 #endif | 342 #endif |
303 | 343 |
304 void jabber_caps_free_clientinfo(JabberCapsClientInfo *clientinfo) { | |
305 if(!clientinfo) | |
306 return; | |
307 while(clientinfo->identities) { | |
308 JabberCapsIdentity *id = clientinfo->identities->data; | |
309 g_free(id->category); | |
310 g_free(id->type); | |
311 g_free(id->name); | |
312 g_free(id); | |
313 | |
314 clientinfo->identities = g_list_delete_link(clientinfo->identities,clientinfo->identities); | |
315 } | |
316 while(clientinfo->features) { | |
317 char *feat = clientinfo->features->data; | |
318 g_free(feat); | |
319 | |
320 clientinfo->features = g_list_delete_link(clientinfo->features,clientinfo->features); | |
321 } | |
322 | |
323 g_free(clientinfo); | |
324 } | |
325 | |
326 typedef struct _jabber_caps_cbplususerdata { | 344 typedef struct _jabber_caps_cbplususerdata { |
327 jabber_caps_get_info_cb cb; | 345 jabber_caps_get_info_cb cb; |
328 gpointer user_data; | 346 gpointer user_data; |
329 | 347 |
330 char *who; | 348 char *who; |
331 char *node; | 349 char *node; |
332 char *ver; | 350 char *ver; |
333 char *hash; | 351 char *hash; |
352 #if 0 | |
334 unsigned extOutstanding; | 353 unsigned extOutstanding; |
354 #endif | |
335 } jabber_caps_cbplususerdata; | 355 } jabber_caps_cbplususerdata; |
336 | 356 |
357 #if 0 | |
337 typedef struct jabber_ext_userdata { | 358 typedef struct jabber_ext_userdata { |
338 jabber_caps_cbplususerdata *userdata; | 359 jabber_caps_cbplususerdata *userdata; |
339 char *node; | 360 char *node; |
340 } jabber_ext_userdata; | 361 } jabber_ext_userdata; |
362 #endif | |
341 | 363 |
342 #if 0 | 364 #if 0 |
343 static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *userdata) { | 365 static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *userdata) { |
344 if(userdata->extOutstanding == 0) { | 366 if(userdata->extOutstanding == 0) { |
345 userdata->cb(jabber_caps_collect_info(userdata->node, userdata->ver, userdata->ext), userdata->user_data); | 367 userdata->cb(jabber_caps_collect_info(userdata->node, userdata->ver, userdata->ext), userdata->user_data); |
416 g_free(extuserdata); | 438 g_free(extuserdata); |
417 jabber_caps_get_info_check_completion(userdata); | 439 jabber_caps_get_info_check_completion(userdata); |
418 } | 440 } |
419 #endif | 441 #endif |
420 | 442 |
421 static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { | 443 static void |
422 /* collect data and fetch all exts */ | 444 jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) |
445 { | |
423 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", | 446 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", |
424 "http://jabber.org/protocol/disco#info"); | 447 "http://jabber.org/protocol/disco#info"); |
425 jabber_caps_cbplususerdata *userdata = data; | 448 jabber_caps_cbplususerdata *userdata = data; |
426 | 449 JabberCapsClientInfo *info, *value; |
427 /* TODO: Better error checking! */ | 450 gchar *hash; |
428 if (!strcmp(xmlnode_get_attrib(packet, "type"), "error"))return; | 451 const char *type = xmlnode_get_attrib(packet, "type"); |
429 if (query) { | 452 JabberCapsKey key; |
430 // check hash | 453 |
431 JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); | 454 if (!query || !strcmp(type, "error")) { |
432 gchar *hash = 0; | 455 userdata->cb(NULL, userdata->user_data); |
433 JabberCapsValue *value; | 456 |
434 JabberCapsKey *key; | 457 g_free(userdata->who); |
435 xmlnode *child; | 458 g_free(userdata->node); |
436 | 459 g_free(userdata->ver); |
437 if (!strcmp(userdata->hash, "sha-1")) { | 460 g_free(userdata->hash); |
438 hash = jabber_caps_calculate_hash(info, "sha1"); | 461 g_free(userdata); |
439 } else if (!strcmp(userdata->hash, "md5")) { | 462 return; |
440 hash = jabber_caps_calculate_hash(info, "md5"); | 463 } |
441 } else { | 464 |
442 // clean up | 465 /* check hash */ |
443 return; | 466 info = jabber_caps_parse_client_info(query); |
444 } | 467 |
445 | 468 if (!strcmp(userdata->hash, "sha-1")) { |
446 printf("\n\tfrom: %s", xmlnode_get_attrib(packet, "from")); | 469 hash = jabber_caps_calculate_hash(info, "sha1"); |
447 printf("\n\tnode: %s", xmlnode_get_attrib(query, "node")); | 470 } else if (!strcmp(userdata->hash, "md5")) { |
448 printf("\n\tcalculated key: %s", hash); | 471 hash = jabber_caps_calculate_hash(info, "md5"); |
449 printf("\n\thash: %s", userdata->hash); | 472 } else { |
450 printf("\n"); | 473 purple_debug_warning("jabber", "unknown caps hash algorithm: %s\n", userdata->hash); |
451 | 474 |
452 if (strcmp(hash, userdata->ver)) { | 475 userdata->cb(NULL, userdata->user_data); |
453 g_free(info); | 476 |
454 g_free(hash); | 477 jabber_caps_client_info_destroy(info); |
455 printf("\n! ! ! invalid hash ! ! !"); | 478 g_free(userdata->who); |
456 return; | 479 g_free(userdata->node); |
457 } | 480 g_free(userdata->ver); |
458 | 481 g_free(userdata->hash); |
482 g_free(userdata); | |
483 return; | |
484 } | |
485 | |
486 if (!hash || strcmp(hash, userdata->ver)) { | |
487 purple_debug_warning("jabber", "caps hash from %s did not match\n", xmlnode_get_attrib(packet, "from")); | |
488 userdata->cb(NULL, userdata->user_data); | |
489 | |
490 jabber_caps_client_info_destroy(info); | |
491 g_free(userdata->who); | |
492 g_free(userdata->node); | |
493 g_free(userdata->ver); | |
494 g_free(userdata->hash); | |
495 g_free(userdata); | |
459 g_free(hash); | 496 g_free(hash); |
460 | 497 return; |
461 value = g_new0(JabberCapsValue, 1); | 498 } |
462 key = g_new0(JabberCapsKey, 1); | 499 |
463 | 500 key.node = userdata->node; |
464 #if 0 | 501 key.ver = userdata->ver; |
465 value->ext = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_ext_destroy_value); | 502 key.hash = userdata->hash; |
466 #endif | 503 |
467 key->node = g_strdup(userdata->node); | 504 /* check whether it's not in the table */ |
468 key->ver = g_strdup(userdata->ver); | 505 if ((value = g_hash_table_lookup(capstable, &key))) { |
469 key->hash = g_strdup(userdata->hash); | 506 JabberCapsClientInfo *tmp = info; |
470 | 507 info = value; |
471 /* check whether it's stil not in the table */ | 508 jabber_caps_client_info_destroy(tmp); |
472 if (g_hash_table_lookup(capstable, key)) { | 509 } else { |
473 jabber_caps_destroy_key(key); | 510 JabberCapsKey *n_key = g_new(JabberCapsKey, 1); |
474 g_free(value); | 511 n_key->node = userdata->node; |
475 return; | 512 n_key->ver = userdata->ver; |
476 } | 513 n_key->hash = userdata->hash; |
477 | 514 userdata->node = userdata->ver = userdata->hash = NULL; |
478 | 515 |
479 for(child = query->child; child; child = child->next) { | 516 g_hash_table_insert(capstable, n_key, info); |
480 if(child->type != XMLNODE_TYPE_TAG) | |
481 continue; | |
482 if(!strcmp(child->name,"feature")) { | |
483 const char *var = xmlnode_get_attrib(child, "var"); | |
484 if(!var) | |
485 continue; | |
486 value->features = g_list_append(value->features, g_strdup(var)); | |
487 } else if(!strcmp(child->name,"identity")) { | |
488 const char *category = xmlnode_get_attrib(child, "category"); | |
489 const char *type = xmlnode_get_attrib(child, "type"); | |
490 const char *name = xmlnode_get_attrib(child, "name"); | |
491 | |
492 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); | |
493 id->category = g_strdup(category); | |
494 id->type = g_strdup(type); | |
495 id->name = g_strdup(name); | |
496 | |
497 value->identities = g_list_append(value->identities,id); | |
498 } else if(!strcmp(child->name, "x")) { | |
499 value->forms = g_list_append(value->forms, xmlnode_copy(child)); | |
500 } | |
501 } | |
502 | |
503 g_hash_table_replace(capstable, key, value); | |
504 jabber_caps_store(); | 517 jabber_caps_store(); |
505 | 518 } |
506 | 519 |
507 #if 0 | 520 userdata->cb(info, userdata->user_data); |
508 /* fetch all exts */ | 521 |
509 for(iter = userdata->ext; iter; iter = g_list_next(iter)) { | 522 /* capstable will free info */ |
510 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, | 523 g_free(userdata->who); |
511 "http://jabber.org/protocol/disco#info"); | 524 g_free(userdata->node); |
512 xmlnode *query = xmlnode_get_child_with_namespace(iq->node, | 525 g_free(userdata->ver); |
513 "query", "http://jabber.org/protocol/disco#info"); | 526 g_free(userdata->hash); |
514 char *node = g_strdup_printf("%s#%s", userdata->node, (const char*)iter->data); | 527 g_free(userdata); |
515 jabber_ext_userdata *ext_data = g_new0(jabber_ext_userdata, 1); | 528 g_free(hash); |
516 ext_data->node = node; | 529 } |
517 ext_data->userdata = userdata; | 530 |
518 | 531 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, |
519 xmlnode_set_attrib(query, "node", node); | 532 const char *ver, const char *hash, jabber_caps_get_info_cb cb, |
520 xmlnode_set_attrib(iq->node, "to", userdata->who); | 533 gpointer user_data) |
521 | 534 { |
522 jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, ext_data); | 535 JabberCapsClientInfo *client; |
523 jabber_iq_send(iq); | |
524 } | |
525 | |
526 } else | |
527 /* Don't wait for the ext discoveries; they aren't going to happen */ | |
528 userdata->extOutstanding = 0; | |
529 | |
530 jabber_caps_get_info_check_completion(userdata); | |
531 #endif | |
532 } | |
533 } | |
534 | |
535 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) { | |
536 JabberCapsValue *client; | |
537 JabberCapsKey *key = g_new0(JabberCapsKey, 1); | 536 JabberCapsKey *key = g_new0(JabberCapsKey, 1); |
538 jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1); | 537 jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1); |
539 userdata->cb = cb; | 538 userdata->cb = cb; |
540 userdata->user_data = user_data; | 539 userdata->user_data = user_data; |
541 userdata->who = g_strdup(who); | 540 userdata->who = g_strdup(who); |
546 key->node = g_strdup(node); | 545 key->node = g_strdup(node); |
547 key->ver = g_strdup(ver); | 546 key->ver = g_strdup(ver); |
548 key->hash = g_strdup(hash); | 547 key->hash = g_strdup(hash); |
549 | 548 |
550 client = g_hash_table_lookup(capstable, key); | 549 client = g_hash_table_lookup(capstable, key); |
551 | |
552 g_hash_table_replace(jabber_contact_info, g_strdup(who), key); | |
553 | 550 |
554 if(!client) { | 551 if(!client) { |
555 JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#info"); | 552 JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#info"); |
556 xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#info"); | 553 xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#info"); |
557 char *nodever = g_strdup_printf("%s#%s", node, ver); | 554 char *nodever = g_strdup_printf("%s#%s", node, ver); |
605 gint cat_cmp; | 602 gint cat_cmp; |
606 gint typ_cmp; | 603 gint typ_cmp; |
607 | 604 |
608 ac = a; | 605 ac = a; |
609 bc = b; | 606 bc = b; |
610 | 607 |
611 if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { | 608 if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { |
612 if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) { | 609 if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) { |
613 return strcmp(ac->lang, bc->lang); | 610 if (!ac->lang && !bc->lang) { |
611 return 0; | |
612 } else if (ac->lang && !bc->lang) { | |
613 return 1; | |
614 } else if (!ac->lang && bc->lang) { | |
615 return -1; | |
616 } else { | |
617 return strcmp(ac->lang, bc->lang); | |
618 } | |
614 } else { | 619 } else { |
615 return typ_cmp; | 620 return typ_cmp; |
616 } | 621 } |
617 } else { | 622 } else { |
618 return cat_cmp; | 623 return cat_cmp; |
628 bc = b; | 633 bc = b; |
629 | 634 |
630 return strcmp(ac->namespace, bc->namespace); | 635 return strcmp(ac->namespace, bc->namespace); |
631 } | 636 } |
632 #endif | 637 #endif |
633 | |
634 static gint jabber_caps_string_compare(gconstpointer a, gconstpointer b) { | |
635 const gchar *ac; | |
636 const gchar *bc; | |
637 | |
638 ac = a; | |
639 bc = b; | |
640 | |
641 return strcmp(ac, bc); | |
642 } | |
643 | 638 |
644 static gchar *jabber_caps_get_formtype(const xmlnode *x) { | 639 static gchar *jabber_caps_get_formtype(const xmlnode *x) { |
645 xmlnode *formtypefield; | 640 xmlnode *formtypefield; |
646 formtypefield = xmlnode_get_child(x, "field"); | 641 formtypefield = xmlnode_get_child(x, "field"); |
647 while (formtypefield && strcmp(xmlnode_get_attrib(formtypefield, "var"), "FORM_TYPE")) formtypefield = xmlnode_get_next_twin(formtypefield); | 642 while (formtypefield && strcmp(xmlnode_get_attrib(formtypefield, "var"), "FORM_TYPE")) formtypefield = xmlnode_get_next_twin(formtypefield); |
663 g_free(aformtype); | 658 g_free(aformtype); |
664 g_free(bformtype); | 659 g_free(bformtype); |
665 return result; | 660 return result; |
666 } | 661 } |
667 | 662 |
668 JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { | 663 static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) |
664 { | |
669 xmlnode *child; | 665 xmlnode *child; |
670 JabberCapsClientInfo *info; | 666 JabberCapsClientInfo *info; |
671 | 667 |
672 if (!query) return 0; | 668 if (!query || strcmp(query->xmlns, "http://jabber.org/protocol/disco#info")) |
673 if (strcmp(query->xmlns,"http://jabber.org/protocol/disco#info")) return 0; | 669 return 0; |
674 | 670 |
675 info = g_new0(JabberCapsClientInfo, 1); | 671 info = g_new0(JabberCapsClientInfo, 1); |
676 | 672 |
677 for(child = query->child; child; child = child->next) { | 673 for(child = query->child; child; child = child->next) { |
678 if (!strcmp(child->name,"identity")) { | 674 if (!strcmp(child->name,"identity")) { |
679 /* parse identity */ | 675 /* parse identity */ |
680 const char *category = xmlnode_get_attrib(child, "category"); | 676 const char *category = xmlnode_get_attrib(child, "category"); |
681 const char *type = xmlnode_get_attrib(child, "type"); | 677 const char *type = xmlnode_get_attrib(child, "type"); |
682 const char *name = xmlnode_get_attrib(child, "name"); | 678 const char *name = xmlnode_get_attrib(child, "name"); |
683 | 679 const char *lang = xmlnode_get_attrib(child, "lang"); |
684 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); | 680 JabberIdentity *id; |
681 | |
682 if (!category || !type) | |
683 continue; | |
684 | |
685 id = g_new0(JabberIdentity, 1); | |
685 id->category = g_strdup(category); | 686 id->category = g_strdup(category); |
686 id->type = g_strdup(type); | 687 id->type = g_strdup(type); |
687 id->name = g_strdup(name); | 688 id->name = g_strdup(name); |
688 | 689 id->lang = g_strdup(lang); |
690 | |
689 info->identities = g_list_append(info->identities, id); | 691 info->identities = g_list_append(info->identities, id); |
690 } else if (!strcmp(child->name, "feature")) { | 692 } else if (!strcmp(child->name, "feature")) { |
691 /* parse feature */ | 693 /* parse feature */ |
692 const char *var = xmlnode_get_attrib(child, "var"); | 694 const char *var = xmlnode_get_attrib(child, "var"); |
693 if(!var) | 695 if(!var) |
694 continue; | 696 continue; |
695 info->features = g_list_append(info->features, g_strdup(var)); | 697 info->features = g_list_append(info->features, g_strdup(var)); |
696 } else if (!strcmp(child->name, "x")) { | 698 } else if (!strcmp(child->name, "x")) { |
697 if (!strcmp(child->xmlns, "jabber:x:data")) { | 699 if (child->xmlns && !strcmp(child->xmlns, "jabber:x:data")) { |
698 /* x-data form */ | 700 /* x-data form */ |
699 xmlnode *dataform = xmlnode_copy(child); | 701 xmlnode *dataform = xmlnode_copy(child); |
700 info->forms = g_list_append(info->forms, dataform); | 702 info->forms = g_list_append(info->forms, dataform); |
701 } | 703 } |
702 } | 704 } |
703 } | 705 } |
704 return info; | 706 return info; |
705 } | 707 } |
706 | 708 |
707 static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) { | 709 static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) |
708 const JabberDataFormField *ac; | 710 { |
709 const JabberDataFormField *bc; | 711 const JabberDataFormField *ac = a; |
710 | 712 const JabberDataFormField *bc = b; |
711 ac = a; | 713 |
712 bc = b; | |
713 | |
714 return strcmp(ac->var, bc->var); | 714 return strcmp(ac->var, bc->var); |
715 } | 715 } |
716 | 716 |
717 static GList *jabber_caps_xdata_get_fields(const xmlnode *x) { | 717 static GList* jabber_caps_xdata_get_fields(const xmlnode *x) |
718 GList *fields = 0; | 718 { |
719 GList *fields = NULL; | |
719 xmlnode *field; | 720 xmlnode *field; |
720 xmlnode *value; | 721 |
721 JabberDataFormField *xdatafield; | 722 if (!x) |
722 | 723 return NULL; |
723 if(!x) return 0; | 724 |
724 | 725 for (field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { |
725 for(field = xmlnode_get_child(x, "field"); field != 0; field = xmlnode_get_next_twin(field)) { | 726 xmlnode *value; |
726 xdatafield = g_new0(JabberDataFormField, 1); | 727 JabberDataFormField *xdatafield = g_new0(JabberDataFormField, 1); |
727 xdatafield->var = g_strdup(xmlnode_get_attrib(field, "var")); | 728 xdatafield->var = g_strdup(xmlnode_get_attrib(field, "var")); |
728 for(value = xmlnode_get_child(field, "value"); value != 0; value = xmlnode_get_next_twin(value)) { | 729 |
730 for (value = xmlnode_get_child(field, "value"); value; value = xmlnode_get_next_twin(value)) { | |
729 gchar *val = xmlnode_get_data(value); | 731 gchar *val = xmlnode_get_data(value); |
730 xdatafield->values = g_list_append(xdatafield->values, val); | 732 xdatafield->values = g_list_append(xdatafield->values, val); |
731 } | 733 } |
732 xdatafield->values = g_list_sort(xdatafield->values, jabber_caps_string_compare); | 734 |
735 xdatafield->values = g_list_sort(xdatafield->values, (GCompareFunc)strcmp); | |
733 fields = g_list_append(fields, xdatafield); | 736 fields = g_list_append(fields, xdatafield); |
734 } | 737 } |
738 | |
735 fields = g_list_sort(fields, jabber_caps_xdata_field_compare); | 739 fields = g_list_sort(fields, jabber_caps_xdata_field_compare); |
736 return fields; | 740 return fields; |
737 } | 741 } |
738 | 742 |
739 static gchar *jabber_caps_verification_append(gchar *verification_string, gchar *string) { | 743 static GString* |
740 gchar *verification; | 744 jabber_caps_verification_append(GString *verification, const gchar *string) |
741 verification = g_strconcat(verification_string, string, "<", NULL); | 745 { |
742 g_free(verification_string); | 746 verification = g_string_append(verification, string); |
743 return verification; | 747 return g_string_append_c(verification, '<'); |
744 } | 748 } |
745 | 749 |
746 gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash) { | 750 gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash) |
747 GList *identities; | 751 { |
748 GList *features; | 752 GList *node; |
749 GList *xdata; | 753 GString *verification; |
750 gchar *verification = 0; | |
751 gchar *feature_string; | |
752 gchar *free_verification; | |
753 gchar *identity_string; | |
754 PurpleCipherContext *context; | 754 PurpleCipherContext *context; |
755 guint8 checksum[20]; | 755 guint8 checksum[20]; |
756 gsize checksum_size = 20; | 756 gsize checksum_size = 20; |
757 | 757 |
758 if (!info) return 0; | 758 if (!info || !(context = purple_cipher_context_new_by_name(hash, NULL))) |
759 | 759 return NULL; |
760 | |
760 /* sort identities, features and x-data forms */ | 761 /* sort identities, features and x-data forms */ |
761 info->identities = g_list_sort(info->identities, jabber_caps_jabber_identity_compare); | 762 info->identities = g_list_sort(info->identities, jabber_caps_jabber_identity_compare); |
762 info->features = g_list_sort(info->features, jabber_caps_string_compare); | 763 info->features = g_list_sort(info->features, (GCompareFunc)strcmp); |
763 info->forms = g_list_sort(info->forms, jabber_caps_jabber_xdata_compare); | 764 info->forms = g_list_sort(info->forms, jabber_caps_jabber_xdata_compare); |
764 | 765 |
766 verification = g_string_new(""); | |
767 | |
765 /* concat identities to the verification string */ | 768 /* concat identities to the verification string */ |
766 for(identities = info->identities; identities; identities = identities->next) { | 769 for (node = info->identities; node; node = node->next) { |
767 JabberIdentity *ident = (JabberIdentity*)identities->data; | 770 JabberIdentity *id = (JabberIdentity*)node->data; |
768 identity_string = g_strdup_printf("%s/%s//%s<", ident->category, ident->type, ident->name); | 771 |
769 free_verification = verification; | 772 g_string_append_printf(verification, "%s/%s/%s/%s<", id->category, |
770 if(verification == 0) verification = g_strdup(identity_string); | 773 id->type, id->lang ? id->lang : "", id->name); |
771 else verification = g_strconcat(verification, identity_string, NULL); | 774 } |
772 g_free(identity_string); | 775 |
773 g_free(free_verification); | |
774 } | |
775 | |
776 /* concat features to the verification string */ | 776 /* concat features to the verification string */ |
777 for(features = info->features; features; features = features->next) { | 777 for (node = info->features; node; node = node->next) { |
778 feature_string = g_strdup_printf("%s", (gchar*)features->data); | 778 verification = jabber_caps_verification_append(verification, node->data); |
779 verification = jabber_caps_verification_append(verification, feature_string); | 779 } |
780 g_free(feature_string); | 780 |
781 } | |
782 | |
783 /* concat x-data forms to the verification string */ | 781 /* concat x-data forms to the verification string */ |
784 for(xdata = info->forms; xdata; xdata = xdata->next) { | 782 for(node = info->forms; node; node = node->next) { |
785 gchar *formtype = 0; | 783 xmlnode *data = (xmlnode *)node->data; |
786 GList *fields; | 784 gchar *formtype = jabber_caps_get_formtype(data); |
785 GList *fields = jabber_caps_xdata_get_fields(data); | |
786 | |
787 /* append FORM_TYPE's field value to the verification string */ | 787 /* append FORM_TYPE's field value to the verification string */ |
788 formtype = jabber_caps_get_formtype((xmlnode*)xdata->data); | |
789 verification = jabber_caps_verification_append(verification, formtype); | 788 verification = jabber_caps_verification_append(verification, formtype); |
790 g_free(formtype); | 789 g_free(formtype); |
791 | 790 |
792 for(fields = jabber_caps_xdata_get_fields((xmlnode*)(xdata->data)); fields != 0; fields = fields->next) { | 791 while (fields) { |
793 GList *value; | 792 GList *value; |
794 JabberDataFormField *field = (JabberDataFormField*)fields->data; | 793 JabberDataFormField *field = (JabberDataFormField*)fields->data; |
795 if(strcmp(field->var, "FORM_TYPE")) { | 794 |
796 /* Append the value of the "var" attribute, followed by the '<' character. */ | 795 if (strcmp(field->var, "FORM_TYPE")) { |
796 /* Append the "var" attribute */ | |
797 verification = jabber_caps_verification_append(verification, field->var); | 797 verification = jabber_caps_verification_append(verification, field->var); |
798 /* For each <value/> element, append the XML character data, followed by the '<' character. */ | 798 /* Append <value/> element's cdata */ |
799 for(value = field->values; value != 0; value = value->next) { | 799 for(value = field->values; value; value = value->next) { |
800 verification = jabber_caps_verification_append(verification, value->data); | 800 verification = jabber_caps_verification_append(verification, value->data); |
801 g_free(value->data); | |
801 } | 802 } |
802 } | 803 } |
803 for(value = field->values; value != 0; value = value->next) { | 804 |
804 g_free(value->data); | |
805 } | |
806 g_free(field->var); | 805 g_free(field->var); |
807 g_list_free(field->values); | 806 g_list_free(field->values); |
808 } | 807 |
809 g_list_free(fields); | 808 fields = g_list_delete_link(fields, fields); |
810 } | 809 } |
811 | 810 } |
811 | |
812 /* generate hash */ | 812 /* generate hash */ |
813 context = purple_cipher_context_new_by_name(hash, NULL); | 813 purple_cipher_context_append(context, (guchar*)verification->str, verification->len); |
814 if (context == NULL) { | 814 |
815 //purple_debug_error("jabber", "Could not find cipher\n"); | 815 if (!purple_cipher_context_digest(context, verification->len, checksum, &checksum_size)) { |
816 return 0; | 816 /* purple_debug_error("util", "Failed to get digest.\n"); */ |
817 } | 817 g_string_free(verification, TRUE); |
818 purple_cipher_context_append(context, (guchar*)verification, strlen(verification)); | 818 purple_cipher_context_destroy(context); |
819 | 819 return NULL; |
820 if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) { | 820 } |
821 //purple_debug_error("util", "Failed to get digest.\n"); | 821 |
822 } | 822 g_string_free(verification, TRUE); |
823 purple_cipher_context_destroy(context); | 823 purple_cipher_context_destroy(context); |
824 | 824 |
825 /* apply Base64 on hash */ | 825 return purple_base64_encode(checksum, checksum_size); |
826 | |
827 g_free(verification); | |
828 verification = purple_base64_encode(checksum, checksum_size); | |
829 | |
830 return verification; | |
831 } | 826 } |
832 | 827 |
833 void jabber_caps_calculate_own_hash(JabberStream *js) { | 828 void jabber_caps_calculate_own_hash(JabberStream *js) { |
834 JabberCapsClientInfo *info; | 829 JabberCapsClientInfo info; |
835 GList *iter = 0; | 830 GList *iter = 0; |
836 GList *features = 0; | 831 GList *features = 0; |
837 | 832 |
838 if (jabber_identities == 0 && jabber_features == 0) return; | 833 if (!jabber_identities && !jabber_features) { |
839 | 834 /* This really shouldn't ever happen */ |
840 /* sort features */ | 835 purple_debug_warning("jabber", "No features or identities, cannot calculate own caps hash.\n"); |
836 g_free(js->caps_hash); | |
837 js->caps_hash = NULL; | |
838 return; | |
839 } | |
840 | |
841 /* build the currently-supported list of features */ | |
841 if (jabber_features) { | 842 if (jabber_features) { |
842 for (iter = jabber_features; iter; iter = iter->next) { | 843 for (iter = jabber_features; iter; iter = iter->next) { |
843 JabberFeature *feat = iter->data; | 844 JabberFeature *feat = iter->data; |
844 if(feat->is_enabled == NULL || feat->is_enabled(js, feat->namespace) == TRUE) { | 845 if(!feat->is_enabled || feat->is_enabled(js, feat->namespace)) { |
845 features = g_list_append(features, feat->namespace); | 846 features = g_list_append(features, feat->namespace); |
846 } | 847 } |
847 } | 848 } |
848 } | 849 } |
849 | 850 |
850 info = g_new0(JabberCapsClientInfo, 1); | 851 info.features = features; |
851 info->features = features; | 852 info.identities = jabber_identities; |
852 info->identities = jabber_identities; | 853 info.forms = NULL; |
853 info->forms = 0; | 854 |
854 | 855 g_free(js->caps_hash); |
855 if (js->caps_hash) | 856 js->caps_hash = jabber_caps_calculate_hash(&info, "sha1"); |
856 g_free(js->caps_hash); | |
857 js->caps_hash = jabber_caps_calculate_hash(info, "sha1"); | |
858 g_free(info); | |
859 g_list_free(features); | 857 g_list_free(features); |
860 } | 858 } |
861 | 859 |
862 const gchar* jabber_caps_get_own_hash(JabberStream *js) | 860 const gchar* jabber_caps_get_own_hash(JabberStream *js) |
863 { | 861 { |