comparison libpurple/protocols/jabber/caps.c @ 25124:ba362a67278c

propagate from branch 'im.pidgin.pidgin' (head 2747d5e0324ca6b81e83bbb8b75e1efebcbbad6e) to branch 'im.pidgin.soc.2008.xmpp' (head 72d6870ed2c62423a05ed89822db25d9916ecf2b)
author Tobias Markmann <tfar@soc.pidgin.im>
date Sun, 03 Aug 2008 23:16:24 +0000
parents affaa4c4836e
children 9ab681f23007
comparison
equal deleted inserted replaced
23640:58bb7fc244e4 25124:ba362a67278c
19 * 19 *
20 */ 20 */
21 21
22 #include "internal.h" 22 #include "internal.h"
23 23
24 #include <glib.h>
24 #include "caps.h" 25 #include "caps.h"
26 #include "cipher.h"
25 #include <string.h> 27 #include <string.h>
26 #include "internal.h" 28 #include "internal.h"
27 #include "util.h" 29 #include "util.h"
28 #include "iq.h" 30 #include "iq.h"
29 31
30 #define JABBER_CAPS_FILENAME "xmpp-caps.xml" 32 #define JABBER_CAPS_FILENAME "xmpp-caps.xml"
31 33
32 static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ 34 GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */
33 35 static gchar *caps_hash = NULL;
34 typedef struct _JabberCapsKey { 36
35 char *node; 37 #if 0
36 char *ver;
37 } JabberCapsKey;
38
39 typedef struct _JabberCapsValueExt {
40 GList *identities; /* JabberCapsIdentity */
41 GList *features; /* char * */
42 } JabberCapsValueExt;
43
44 typedef struct _JabberCapsValue { 38 typedef struct _JabberCapsValue {
45 GList *identities; /* JabberCapsIdentity */ 39 GList *identities; /* JabberCapsIdentity */
46 GList *features; /* char * */ 40 GList *features; /* char * */
47 GHashTable *ext; /* char * -> JabberCapsValueExt */ 41 GHashTable *ext; /* char * -> JabberCapsValueExt */
48 } JabberCapsValue; 42 } JabberCapsValue;
43 #endif
44 typedef struct _JabberCapsClientInfo JabberCapsValue;
49 45
50 static guint jabber_caps_hash(gconstpointer key) { 46 static guint jabber_caps_hash(gconstpointer key) {
51 const JabberCapsKey *name = key; 47 const JabberCapsKey *name = key;
52 guint nodehash = g_str_hash(name->node); 48 guint nodehash = g_str_hash(name->node);
53 guint verhash = g_str_hash(name->ver); 49 guint verhash = g_str_hash(name->ver);
54 50 guint hashhash = g_str_hash(name->hash);
55 return nodehash ^ verhash; 51 return nodehash ^ verhash ^ hashhash;
56 } 52 }
57 53
58 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { 54 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) {
59 const JabberCapsKey *name1 = v1; 55 const JabberCapsKey *name1 = v1;
60 const JabberCapsKey *name2 = v2; 56 const JabberCapsKey *name2 = v2;
61 57
62 return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0; 58 return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0 && strcmp(name1->hash,name2->hash) == 0;
63 } 59 }
64 60
65 static void jabber_caps_destroy_key(gpointer key) { 61 void jabber_caps_destroy_key(gpointer key) {
66 JabberCapsKey *keystruct = key; 62 JabberCapsKey *keystruct = key;
67 g_free(keystruct->node); 63 g_free(keystruct->node);
68 g_free(keystruct->ver); 64 g_free(keystruct->ver);
65 g_free(keystruct->hash);
69 g_free(keystruct); 66 g_free(keystruct);
70 } 67 }
71 68
72 static void jabber_caps_destroy_value(gpointer value) { 69 static void jabber_caps_destroy_value(gpointer value) {
73 JabberCapsValue *valuestruct = value; 70 JabberCapsValue *valuestruct = value;
82 } 79 }
83 while(valuestruct->features) { 80 while(valuestruct->features) {
84 g_free(valuestruct->features->data); 81 g_free(valuestruct->features->data);
85 valuestruct->features = g_list_delete_link(valuestruct->features,valuestruct->features); 82 valuestruct->features = g_list_delete_link(valuestruct->features,valuestruct->features);
86 } 83 }
87 g_hash_table_destroy(valuestruct->ext); 84
85 while(valuestruct->forms) {
86 g_free(valuestruct->forms->data);
87 valuestruct->forms = g_list_delete_link(valuestruct->forms,valuestruct->forms);
88 }
89 //g_hash_table_destroy(valuestruct->ext);
88 g_free(valuestruct); 90 g_free(valuestruct);
89 } 91 }
90 92
91 static void jabber_caps_ext_destroy_value(gpointer value) { 93 static void jabber_caps_ext_destroy_value(gpointer value) {
92 JabberCapsValueExt *valuestruct = value; 94 JabberCapsValueExt *valuestruct = value;
109 static void jabber_caps_load(void); 111 static void jabber_caps_load(void);
110 112
111 void jabber_caps_init(void) { 113 void jabber_caps_init(void) {
112 capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, jabber_caps_destroy_value); 114 capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, jabber_caps_destroy_value);
113 jabber_caps_load(); 115 jabber_caps_load();
116 jabber_caps_calculate_own_hash();
114 } 117 }
115 118
116 static void jabber_caps_load(void) { 119 static void jabber_caps_load(void) {
117 xmlnode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); 120 xmlnode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache");
118 xmlnode *client; 121 xmlnode *client;
132 JabberCapsKey *key = g_new0(JabberCapsKey, 1); 135 JabberCapsKey *key = g_new0(JabberCapsKey, 1);
133 JabberCapsValue *value = g_new0(JabberCapsValue, 1); 136 JabberCapsValue *value = g_new0(JabberCapsValue, 1);
134 xmlnode *child; 137 xmlnode *child;
135 key->node = g_strdup(xmlnode_get_attrib(client,"node")); 138 key->node = g_strdup(xmlnode_get_attrib(client,"node"));
136 key->ver = g_strdup(xmlnode_get_attrib(client,"ver")); 139 key->ver = g_strdup(xmlnode_get_attrib(client,"ver"));
137 value->ext = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_ext_destroy_value); 140 key->hash = g_strdup(xmlnode_get_attrib(client,"hash"));
138 for(child = client->child; child; child = child->next) { 141 for(child = client->child; child; child = child->next) {
139 if(child->type != XMLNODE_TYPE_TAG) 142 if(child->type != XMLNODE_TYPE_TAG)
140 continue; 143 continue;
141 if(!strcmp(child->name,"feature")) { 144 if(!strcmp(child->name,"feature")) {
142 const char *var = xmlnode_get_attrib(child, "var"); 145 const char *var = xmlnode_get_attrib(child, "var");
152 id->category = g_strdup(category); 155 id->category = g_strdup(category);
153 id->type = g_strdup(type); 156 id->type = g_strdup(type);
154 id->name = g_strdup(name); 157 id->name = g_strdup(name);
155 158
156 value->identities = g_list_append(value->identities,id); 159 value->identities = g_list_append(value->identities,id);
157 } else if(!strcmp(child->name,"ext")) { 160 } else if(!strcmp(child->name,"x")) {
158 const char *identifier = xmlnode_get_attrib(child, "identifier"); 161 value->forms = g_list_append(value->forms, xmlnode_copy(child));
159 if(identifier) {
160 xmlnode *extchild;
161
162 JabberCapsValueExt *extvalue = g_new0(JabberCapsValueExt, 1);
163
164 for(extchild = child->child; extchild; extchild = extchild->next) {
165 if(extchild->type != XMLNODE_TYPE_TAG)
166 continue;
167 if(!strcmp(extchild->name,"feature")) {
168 const char *var = xmlnode_get_attrib(extchild, "var");
169 if(!var)
170 continue;
171 extvalue->features = g_list_append(extvalue->features,g_strdup(var));
172 } else if(!strcmp(extchild->name,"identity")) {
173 const char *category = xmlnode_get_attrib(extchild, "category");
174 const char *type = xmlnode_get_attrib(extchild, "type");
175 const char *name = xmlnode_get_attrib(extchild, "name");
176
177 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1);
178 id->category = g_strdup(category);
179 id->type = g_strdup(type);
180 id->name = g_strdup(name);
181
182 extvalue->identities = g_list_append(extvalue->identities,id);
183 }
184 }
185 g_hash_table_replace(value->ext, g_strdup(identifier), extvalue);
186 }
187 } 162 }
188 } 163 }
189 g_hash_table_replace(capstable, key, value); 164 g_hash_table_replace(capstable, key, value);
190 } 165 }
191 } 166 }
192 xmlnode_free(capsdata); 167 xmlnode_free(capsdata);
193 } 168 }
194 169
170 #if 0
195 static void jabber_caps_store_ext(gpointer key, gpointer value, gpointer user_data) { 171 static void jabber_caps_store_ext(gpointer key, gpointer value, gpointer user_data) {
196 const char *extname = key; 172 const char *extname = key;
197 JabberCapsValueExt *props = value; 173 JabberCapsValueExt *props = value;
198 xmlnode *root = user_data; 174 xmlnode *root = user_data;
199 xmlnode *ext = xmlnode_new_child(root,"ext"); 175 xmlnode *ext = xmlnode_new_child(root,"ext");
214 const char *feat = iter->data; 190 const char *feat = iter->data;
215 xmlnode *feature = xmlnode_new_child(ext, "feature"); 191 xmlnode *feature = xmlnode_new_child(ext, "feature");
216 xmlnode_set_attrib(feature, "var", feat); 192 xmlnode_set_attrib(feature, "var", feat);
217 } 193 }
218 } 194 }
195 #endif
219 196
220 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { 197 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) {
221 JabberCapsKey *clientinfo = key; 198 JabberCapsKey *clientinfo = key;
222 JabberCapsValue *props = value; 199 JabberCapsValue *props = value;
223 xmlnode *root = user_data; 200 xmlnode *root = user_data;
224 xmlnode *client = xmlnode_new_child(root,"client"); 201 xmlnode *client = xmlnode_new_child(root, "client");
225 GList *iter; 202 GList *iter;
226 203
227 xmlnode_set_attrib(client,"node",clientinfo->node); 204 xmlnode_set_attrib(client, "node", clientinfo->node);
228 xmlnode_set_attrib(client,"ver",clientinfo->ver); 205 xmlnode_set_attrib(client, "ver", clientinfo->ver);
229 206 xmlnode_set_attrib(client, "hash", clientinfo->hash);
230 for(iter = props->identities; iter; iter = g_list_next(iter)) { 207 for(iter = props->identities; iter; iter = g_list_next(iter)) {
231 JabberCapsIdentity *id = iter->data; 208 JabberCapsIdentity *id = iter->data;
232 xmlnode *identity = xmlnode_new_child(client, "identity"); 209 xmlnode *identity = xmlnode_new_child(client, "identity");
233 xmlnode_set_attrib(identity, "category", id->category); 210 xmlnode_set_attrib(identity, "category", id->category);
234 xmlnode_set_attrib(identity, "type", id->type); 211 xmlnode_set_attrib(identity, "type", id->type);
240 const char *feat = iter->data; 217 const char *feat = iter->data;
241 xmlnode *feature = xmlnode_new_child(client, "feature"); 218 xmlnode *feature = xmlnode_new_child(client, "feature");
242 xmlnode_set_attrib(feature, "var", feat); 219 xmlnode_set_attrib(feature, "var", feat);
243 } 220 }
244 221
245 g_hash_table_foreach(props->ext,jabber_caps_store_ext,client); 222 for(iter = props->forms; iter; iter = g_list_next(iter)) {
223 xmlnode *xdata = iter->data;
224 xmlnode_insert_child(client, xmlnode_copy(xdata));
225 }
246 } 226 }
247 227
248 static void jabber_caps_store(void) { 228 static void jabber_caps_store(void) {
249 char *str; 229 char *str;
230 int length = 0;
250 xmlnode *root = xmlnode_new("capabilities"); 231 xmlnode *root = xmlnode_new("capabilities");
251 g_hash_table_foreach(capstable, jabber_caps_store_client, root); 232 g_hash_table_foreach(capstable, jabber_caps_store_client, root);
252 str = xmlnode_to_formatted_str(root, NULL); 233 str = xmlnode_to_formatted_str(root, &length);
253 xmlnode_free(root); 234 xmlnode_free(root);
254 purple_util_write_data_to_file(JABBER_CAPS_FILENAME, str, -1); 235 purple_util_write_data_to_file(JABBER_CAPS_FILENAME, str, length);
255 g_free(str); 236 g_free(str);
256 } 237 }
257 238
258 /* this function assumes that all information is available locally */ 239 /* this function assumes that all information is available locally */
259 static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const char *ver, GList *ext) { 240 static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const char *ver, GList *ext) {
288 const char *feat = iter->data; 269 const char *feat = iter->data;
289 char *newfeat = g_strdup(feat); 270 char *newfeat = g_strdup(feat);
290 271
291 result->features = g_list_append(result->features,newfeat); 272 result->features = g_list_append(result->features,newfeat);
292 } 273 }
293 274 #if 0
294 for(iter = ext; iter; iter = g_list_next(iter)) { 275 for(iter = ext; iter; iter = g_list_next(iter)) {
295 const char *extname = iter->data; 276 const char *extname = iter->data;
296 JabberCapsValueExt *extinfo = g_hash_table_lookup(caps->ext,extname); 277 JabberCapsValueExt *extinfo = g_hash_table_lookup(caps->ext,extname);
297 278
298 if(extinfo) { 279 if(extinfo) {
312 293
313 result->features = g_list_append(result->features,newfeat); 294 result->features = g_list_append(result->features,newfeat);
314 } 295 }
315 } 296 }
316 } 297 }
298 #endif
317 return result; 299 return result;
318 } 300 }
319 301
320 void jabber_caps_free_clientinfo(JabberCapsClientInfo *clientinfo) { 302 void jabber_caps_free_clientinfo(JabberCapsClientInfo *clientinfo) {
321 if(!clientinfo) 303 if(!clientinfo)
344 gpointer user_data; 326 gpointer user_data;
345 327
346 char *who; 328 char *who;
347 char *node; 329 char *node;
348 char *ver; 330 char *ver;
349 GList *ext; 331 char *hash;
350 unsigned extOutstanding; 332 unsigned extOutstanding;
351 } jabber_caps_cbplususerdata; 333 } jabber_caps_cbplususerdata;
352 334
353 typedef struct jabber_ext_userdata { 335 typedef struct jabber_ext_userdata {
354 jabber_caps_cbplususerdata *userdata; 336 jabber_caps_cbplususerdata *userdata;
355 char *node; 337 char *node;
356 } jabber_ext_userdata; 338 } jabber_ext_userdata;
357 339
340 #if 0
358 static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *userdata) { 341 static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *userdata) {
359 if(userdata->extOutstanding == 0) { 342 if(userdata->extOutstanding == 0) {
360 userdata->cb(jabber_caps_collect_info(userdata->node, userdata->ver, userdata->ext), userdata->user_data); 343 userdata->cb(jabber_caps_collect_info(userdata->node, userdata->ver, userdata->ext), userdata->user_data);
361 g_free(userdata->who); 344 g_free(userdata->who);
362 g_free(userdata->node); 345 g_free(userdata->node);
366 userdata->ext = g_list_delete_link(userdata->ext,userdata->ext); 349 userdata->ext = g_list_delete_link(userdata->ext,userdata->ext);
367 } 350 }
368 g_free(userdata); 351 g_free(userdata);
369 } 352 }
370 } 353 }
371 354 #endif
355 #if 0
372 static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { 356 static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer data) {
373 /* collect data and fetch all exts */ 357 /* collect data and fetch all exts */
374 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info"); 358 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info");
375 jabber_ext_userdata *extuserdata = data; 359 jabber_ext_userdata *extuserdata = data;
376 jabber_caps_cbplususerdata *userdata = extuserdata->userdata; 360 jabber_caps_cbplususerdata *userdata = extuserdata->userdata;
377 const char *node = extuserdata->node; 361 const char *node = extuserdata->node;
378 362
379 --userdata->extOutstanding; 363 --userdata->extOutstanding;
380 364
381 /* TODO: Better error handling */ 365 /* TODO: Better error handling */
382 366 printf("\n\tjabber_caps_ext_iqcb for %s", xmlnode_get_attrib(packet, "from"));
383 if(node && query) { 367 if(node && query) {
384 const char *key; 368 const char *key;
385 JabberCapsValue *client; 369 JabberCapsValue *client;
386 xmlnode *child; 370 xmlnode *child;
387 JabberCapsValueExt *value = g_new0(JabberCapsValueExt, 1); 371 JabberCapsValueExt *value = g_new0(JabberCapsValueExt, 1);
421 id->name = g_strdup(name); 405 id->name = g_strdup(name);
422 406
423 value->identities = g_list_append(value->identities,id); 407 value->identities = g_list_append(value->identities,id);
424 } 408 }
425 } 409 }
426 g_hash_table_replace(client->ext, g_strdup(key), value);
427
428 jabber_caps_store(); 410 jabber_caps_store();
429 } 411 }
430 412
431 g_free(extuserdata->node); 413 g_free(extuserdata->node);
432 g_free(extuserdata); 414 g_free(extuserdata);
433 jabber_caps_get_info_check_completion(userdata); 415 jabber_caps_get_info_check_completion(userdata);
434 } 416 }
435 417 #endif
436 static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { 418 static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) {
437 /* collect data and fetch all exts */ 419 /* collect data and fetch all exts */
438 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", 420 xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
439 "http://jabber.org/protocol/disco#info"); 421 "http://jabber.org/protocol/disco#info");
440 xmlnode *child; 422 xmlnode *child;
441 GList *iter;
442 jabber_caps_cbplususerdata *userdata = data; 423 jabber_caps_cbplususerdata *userdata = data;
443 424
444 /* TODO: Better error checking! */ 425 /* TODO: Better error checking! */
445 426 if (!strcmp(xmlnode_get_attrib(packet, "type"), "error"))return;
446 if (query) { 427 if (query) {
428 // check hash
429 JabberCapsClientInfo *info = jabber_caps_parse_client_info(query);
430 gchar *hash = 0;
431 if (!strcmp(userdata->hash, "sha-1")) {
432 hash = jabber_caps_calcualte_hash(info, "sha1");
433 } else if (!strcmp(userdata->hash, "md5")) {
434 hash = jabber_caps_calcualte_hash(info, "md5");
435 } else {
436 // clean up
437 return;
438 }
439
440 printf("\n\tfrom: %s", xmlnode_get_attrib(packet, "from"));
441 printf("\n\tnode: %s", xmlnode_get_attrib(query, "node"));
442 printf("\n\tcalculated key: %s", hash);
443 printf("\n\thash: %s", userdata->hash);
444 printf("\n");
445
446 if (strcmp(hash, userdata->ver)) {
447 g_free(info);
448 g_free(hash);
449 printf("\n! ! ! invalid hash ! ! !");
450 return;
451 }
452
453 g_free(hash);
454
447 JabberCapsValue *value = g_new0(JabberCapsValue, 1); 455 JabberCapsValue *value = g_new0(JabberCapsValue, 1);
448 JabberCapsKey *key = g_new0(JabberCapsKey, 1); 456 JabberCapsKey *key = g_new0(JabberCapsKey, 1);
449 457 #if 0
450 value->ext = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_ext_destroy_value); 458 value->ext = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_ext_destroy_value);
451 459 #endif
452 key->node = g_strdup(userdata->node); 460 key->node = g_strdup(userdata->node);
453 key->ver = g_strdup(userdata->ver); 461 key->ver = g_strdup(userdata->ver);
454 462 key->hash = g_strdup(userdata->hash);
463
464 /* check whether it's stil not in the table */
465 if (g_hash_table_lookup(capstable, key)) {
466 jabber_caps_destroy_key(key);
467 g_free(value);
468 return;
469 }
470
471
455 for(child = query->child; child; child = child->next) { 472 for(child = query->child; child; child = child->next) {
456 if(child->type != XMLNODE_TYPE_TAG) 473 if(child->type != XMLNODE_TYPE_TAG)
457 continue; 474 continue;
458 if(!strcmp(child->name,"feature")) { 475 if(!strcmp(child->name,"feature")) {
459 const char *var = xmlnode_get_attrib(child, "var"); 476 const char *var = xmlnode_get_attrib(child, "var");
462 value->features = g_list_append(value->features, g_strdup(var)); 479 value->features = g_list_append(value->features, g_strdup(var));
463 } else if(!strcmp(child->name,"identity")) { 480 } else if(!strcmp(child->name,"identity")) {
464 const char *category = xmlnode_get_attrib(child, "category"); 481 const char *category = xmlnode_get_attrib(child, "category");
465 const char *type = xmlnode_get_attrib(child, "type"); 482 const char *type = xmlnode_get_attrib(child, "type");
466 const char *name = xmlnode_get_attrib(child, "name"); 483 const char *name = xmlnode_get_attrib(child, "name");
467 484
468 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); 485 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1);
469 id->category = g_strdup(category); 486 id->category = g_strdup(category);
470 id->type = g_strdup(type); 487 id->type = g_strdup(type);
471 id->name = g_strdup(name); 488 id->name = g_strdup(name);
472 489
473 value->identities = g_list_append(value->identities,id); 490 value->identities = g_list_append(value->identities,id);
474 } 491 } else if(!strcmp(child->name, "x")) {
475 } 492 value->forms = g_list_append(value->forms, xmlnode_copy(child));
493 }
494 }
495
476 g_hash_table_replace(capstable, key, value); 496 g_hash_table_replace(capstable, key, value);
477 jabber_caps_store(); 497 jabber_caps_store();
478 } 498 }
479 499
500 #if 0
480 /* fetch all exts */ 501 /* fetch all exts */
481 for(iter = userdata->ext; iter; iter = g_list_next(iter)) { 502 for(iter = userdata->ext; iter; iter = g_list_next(iter)) {
482 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info"); 503 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
483 xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#info"); 504 xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#info");
484 char *node = g_strdup_printf("%s#%s", userdata->node, (const char*)iter->data); 505 char *node = g_strdup_printf("%s#%s", userdata->node, (const char*)iter->data);
492 jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, ext_data); 513 jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, ext_data);
493 jabber_iq_send(iq); 514 jabber_iq_send(iq);
494 } 515 }
495 516
496 jabber_caps_get_info_check_completion(userdata); 517 jabber_caps_get_info_check_completion(userdata);
497 } 518 #endif
498 519 }
499 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *ext, jabber_caps_get_info_cb cb, gpointer user_data) { 520
521 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) {
500 JabberCapsValue *client; 522 JabberCapsValue *client;
501 JabberCapsKey *key = g_new0(JabberCapsKey, 1); 523 JabberCapsKey *key = g_new0(JabberCapsKey, 1);
502 char *originalext = g_strdup(ext);
503 jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1); 524 jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1);
504 userdata->cb = cb; 525 userdata->cb = cb;
505 userdata->user_data = user_data; 526 userdata->user_data = user_data;
506 userdata->who = g_strdup(who); 527 userdata->who = g_strdup(who);
507 userdata->node = g_strdup(node); 528 userdata->node = g_strdup(node);
508 userdata->ver = g_strdup(ver); 529 userdata->ver = g_strdup(ver);
509 530 userdata->hash = g_strdup(hash);
510 if(originalext) { 531
511 int i; 532 key->node = g_strdup(node);
512 gchar **splat = g_strsplit(originalext, " ", 0); 533 key->ver = g_strdup(ver);
513 for(i =0; splat[i]; i++) { 534 key->hash = g_strdup(hash);
514 userdata->ext = g_list_append(userdata->ext, splat[i]); 535
515 ++userdata->extOutstanding;
516 }
517 g_free(splat);
518 }
519 g_free(originalext);
520
521 key->node = (char *)node;
522 key->ver = (char *)ver;
523
524 client = g_hash_table_lookup(capstable, key); 536 client = g_hash_table_lookup(capstable, key);
525 537
526 g_free(key); 538 g_hash_table_replace(jabber_contact_info, g_strdup(who), key);
527 539
528 if(!client) { 540 if(!client) {
529 JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#info"); 541 JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#info");
530 xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#info"); 542 xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#info");
531 char *nodever = g_strdup_printf("%s#%s", node, ver); 543 char *nodever = g_strdup_printf("%s#%s", node, ver);
532 xmlnode_set_attrib(query, "node", nodever); 544 xmlnode_set_attrib(query, "node", nodever);
533 g_free(nodever); 545 g_free(nodever);
534 xmlnode_set_attrib(iq->node, "to", who); 546 xmlnode_set_attrib(iq->node, "to", who);
535 547
536 jabber_iq_set_callback(iq,jabber_caps_client_iqcb,userdata); 548 jabber_iq_set_callback(iq,jabber_caps_client_iqcb,userdata);
537 jabber_iq_send(iq); 549 jabber_iq_send(iq);
550 }
551 #if 0
538 } else { 552 } else {
539 GList *iter; 553 GList *iter;
540 /* fetch unknown exts only */ 554 /* fetch unknown exts only */
541 for(iter = userdata->ext; iter; iter = g_list_next(iter)) { 555 for(iter = userdata->ext; iter; iter = g_list_next(iter)) {
542 JabberCapsValueExt *extvalue = g_hash_table_lookup(client->ext, (const char*)iter->data); 556 JabberCapsValueExt *extvalue = g_hash_table_lookup(client->ext, (const char*)iter->data);
566 jabber_iq_send(iq); 580 jabber_iq_send(iq);
567 } 581 }
568 /* maybe we have all data available anyways? This is the ideal case where no network traffic is necessary */ 582 /* maybe we have all data available anyways? This is the ideal case where no network traffic is necessary */
569 jabber_caps_get_info_check_completion(userdata); 583 jabber_caps_get_info_check_completion(userdata);
570 } 584 }
571 } 585 #endif
572 586 }
587
588 static gint jabber_caps_jabber_identity_compare(gconstpointer a, gconstpointer b) {
589 const JabberIdentity *ac;
590 const JabberIdentity *bc;
591 gint cat_cmp;
592 gint typ_cmp;
593
594 ac = a;
595 bc = b;
596
597 if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) {
598 if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) {
599 return strcmp(ac->lang, bc->lang);
600 } else {
601 return typ_cmp;
602 }
603 } else {
604 return cat_cmp;
605 }
606 }
607
608 static gint jabber_caps_jabber_feature_compare(gconstpointer a, gconstpointer b) {
609 const JabberFeature *ac;
610 const JabberFeature *bc;
611
612 ac = a;
613 bc = b;
614
615 return strcmp(ac->namespace, bc->namespace);
616 }
617
618 static gint jabber_caps_string_compare(gconstpointer a, gconstpointer b) {
619 const gchar *ac;
620 const gchar *bc;
621
622 ac = a;
623 bc = b;
624
625 return strcmp(ac, bc);
626 }
627
628 gchar *jabber_caps_get_formtype(const xmlnode *x) {
629 xmlnode *formtypefield;
630 formtypefield = xmlnode_get_child(x, "field");
631 while (formtypefield && strcmp(xmlnode_get_attrib(formtypefield, "var"), "FORM_TYPE")) formtypefield = xmlnode_get_next_twin(formtypefield);
632 formtypefield = xmlnode_get_child(formtypefield, "value");
633 return xmlnode_get_data(formtypefield);;
634 }
635
636 static gint jabber_caps_jabber_xdata_compare(gconstpointer a, gconstpointer b) {
637 const xmlnode *aformtypefield = a;
638 const xmlnode *bformtypefield = b;
639 char *aformtype;
640 char *bformtype;
641 int result;
642
643 aformtype = jabber_caps_get_formtype(aformtypefield);
644 bformtype = jabber_caps_get_formtype(bformtypefield);
645
646 result = strcmp(aformtype, bformtype);
647 g_free(aformtype);
648 g_free(bformtype);
649 return result;
650 }
651
652 JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) {
653 xmlnode *child;
654
655 if (!query) return 0;
656 if (strcmp(query->xmlns,"http://jabber.org/protocol/disco#info")) return 0;
657
658 JabberCapsClientInfo *info = g_new0(JabberCapsClientInfo, 1);
659
660 for(child = query->child; child; child = child->next) {
661 if (!strcmp(child->name,"identity")) {
662 /* parse identity */
663 const char *category = xmlnode_get_attrib(child, "category");
664 const char *type = xmlnode_get_attrib(child, "type");
665 const char *name = xmlnode_get_attrib(child, "name");
666
667 JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1);
668 id->category = g_strdup(category);
669 id->type = g_strdup(type);
670 id->name = g_strdup(name);
671
672 info->identities = g_list_append(info->identities, id);
673 } else if (!strcmp(child->name, "feature")) {
674 /* parse feature */
675 const char *var = xmlnode_get_attrib(child, "var");
676 if(!var)
677 continue;
678 info->features = g_list_append(info->features, g_strdup(var));
679 } else if (!strcmp(child->name, "x")) {
680 if (!strcmp(child->xmlns, "jabber:x:data")) {
681 /* x-data form */
682 xmlnode *dataform = xmlnode_copy(child);
683 info->forms = g_list_append(info->forms, dataform);
684 }
685 }
686 }
687 return info;
688 }
689
690 static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) {
691 const JabberDataFormField *ac;
692 const JabberDataFormField *bc;
693
694 ac = a;
695 bc = b;
696
697 return strcmp(ac->var, bc->var);
698 }
699
700 GList *jabber_caps_xdata_get_fields(const xmlnode *x) {
701 GList *fields = 0;
702 xmlnode *field;
703 xmlnode *value;
704 JabberDataFormField *xdatafield;
705
706 if(!x) return 0;
707
708 for(field = xmlnode_get_child(x, "field"); field != 0; field = xmlnode_get_next_twin(field)) {
709 xdatafield = g_new0(JabberDataFormField, 1);
710 xdatafield->var = g_strdup(xmlnode_get_attrib(field, "var"));
711 for(value = xmlnode_get_child(field, "value"); value != 0; value = xmlnode_get_next_twin(value)) {
712 gchar *val = xmlnode_get_data(value);
713 xdatafield->values = g_list_append(xdatafield->values, val);
714 }
715 xdatafield->values = g_list_sort(xdatafield->values, jabber_caps_string_compare);
716 fields = g_list_append(fields, xdatafield);
717 }
718 fields = g_list_sort(fields, jabber_caps_xdata_field_compare);
719 return fields;
720 }
721
722 gchar *jabber_caps_verification_append(gchar *verification_string, gchar *string) {
723 gchar *verification;
724 verification = g_strconcat(verification_string, string, "<", NULL);
725 g_free(verification_string);
726 return verification;
727 }
728
729 gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info, const char *hash) {
730 GList *identities;
731 GList *features;
732 GList *xdata;
733 gchar *verification = 0;
734 gchar *feature_string;
735 gchar *free_verification;
736 gchar *identity_string;
737 PurpleCipherContext *context;
738 guint8 checksum[20];
739 gsize checksum_size = 20;
740
741 if (!info) return 0;
742
743 /* sort identities, features and x-data forms */
744 info->identities = g_list_sort(info->identities, jabber_caps_jabber_identity_compare);
745 info->features = g_list_sort(info->features, jabber_caps_string_compare);
746 info->forms = g_list_sort(info->forms, jabber_caps_jabber_xdata_compare);
747
748 /* concat identities to the verification string */
749 for(identities = info->identities; identities; identities = identities->next) {
750 JabberIdentity *ident = (JabberIdentity*)identities->data;
751 identity_string = g_strdup_printf("%s/%s//%s<", ident->category, ident->type, ident->name);
752 free_verification = verification;
753 if(verification == 0) verification = g_strdup(identity_string);
754 else verification = g_strconcat(verification, identity_string, NULL);
755 g_free(identity_string);
756 g_free(free_verification);
757 }
758
759 /* concat features to the verification string */
760 for(features = info->features; features; features = features->next) {
761 feature_string = g_strdup_printf("%s", (gchar*)features->data);
762 verification = jabber_caps_verification_append(verification, feature_string);
763 g_free(feature_string);
764 }
765
766 /* concat x-data forms to the verification string */
767 for(xdata = info->forms; xdata; xdata = xdata->next) {
768 gchar *formtype = 0;
769 GList *fields;
770 /* append FORM_TYPE's field value to the verification string */
771 formtype = jabber_caps_get_formtype((xmlnode*)xdata->data);
772 verification = jabber_caps_verification_append(verification, formtype);
773 g_free(formtype);
774
775 for(fields = jabber_caps_xdata_get_fields((xmlnode*)(xdata->data)); fields != 0; fields = fields->next) {
776 GList *value;
777 JabberDataFormField *field = (JabberDataFormField*)fields->data;
778 if(strcmp(field->var, "FORM_TYPE")) {
779 /* Append the value of the "var" attribute, followed by the '<' character. */
780 verification = jabber_caps_verification_append(verification, field->var);
781 /* For each <value/> element, append the XML character data, followed by the '<' character. */
782 for(value = field->values; value != 0; value = value->next) {
783 verification = jabber_caps_verification_append(verification, value->data);
784 }
785 }
786 for(value = field->values; value != 0; value = value->next) {
787 g_free(value->data);
788 }
789 g_free(field->var);
790 g_list_free(field->values);
791 }
792 g_list_free(fields);
793 }
794
795 /* generate hash */
796 context = purple_cipher_context_new_by_name(hash, NULL);
797 if (context == NULL) {
798 //purple_debug_error("jabber", "Could not find cipher\n");
799 return 0;
800 }
801 purple_cipher_context_append(context, (guchar*)verification, strlen(verification));
802
803 if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) {
804 //purple_debug_error("util", "Failed to get digest.\n");
805 }
806 purple_cipher_context_destroy(context);
807
808 /* apply Base64 on hash */
809
810 g_free(verification);
811 verification = purple_base64_encode(checksum, checksum_size);
812
813 return verification;
814 }
815
816 void jabber_caps_calculate_own_hash(JabberStream *js) {
817 JabberCapsClientInfo *info;
818 GList *iter = 0;
819 GList *features = 0;
820
821 if (jabber_identities == 0 && jabber_features == 0) return;
822
823 /* sort features */
824 if (jabber_features) {
825 for (iter = jabber_features; iter; iter = iter->next) {
826 JabberFeature *feat = iter->data;
827 if(feat->is_enabled == NULL || feat->is_enabled(js, feat->namespace) == TRUE) {
828 features = g_list_append(features, feat->namespace);
829 }
830 }
831 }
832
833 info = g_new0(JabberCapsClientInfo, 1);
834 info->features = features;
835 info->identities = jabber_identities;
836 info->forms = 0;
837
838 if (caps_hash) g_free(caps_hash);
839 caps_hash = jabber_caps_calcualte_hash(info, "sha1");
840 g_free(info);
841 g_list_free(features);
842 }
843
844 const gchar* jabber_caps_get_own_hash() {
845 return caps_hash;
846 }
847
848 void jabber_caps_broadcast_change() {
849 GList *active_accounts = purple_accounts_get_all_active();
850 for (active_accounts = purple_accounts_get_all_active(); active_accounts; active_accounts = active_accounts->next) {
851 PurpleAccount *account = active_accounts->data;
852 if (!strcmp(account->protocol_id, "jabber")) {
853 PurpleConnection *conn = account->gc;
854 JabberStream *js = conn->proto_data;
855 xmlnode *presence = jabber_presence_create_js(js, JABBER_BUDDY_STATE_UNKNOWN, 0, 0);
856 jabber_send(js, presence);
857 }
858 }
859 }
860