Mercurial > pidgin
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 |