Mercurial > pidgin.yaz
annotate libgaim/xmlnode.c @ 15195:4934e7a03a98
[gaim-migrate @ 17983]
Move away from using MSG_PEEK when recv'ing data over oscar sockets.
It's possible this was causing problems with a small number of
Windows users? People seem to frown pretty heavily upon MSG_PEEK
in general, for some reason.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Wed, 13 Dec 2006 10:11:31 +0000 |
parents | 50f263712df1 |
children | f814b2df9cce |
rev | line source |
---|---|
14192 | 1 /** |
2 * @file xmlnode.c XML DOM functions | |
3 * | |
4 * gaim | |
5 * | |
6 * Gaim is the legal property of its developers, whose names are too numerous | |
7 * to list here. Please refer to the COPYRIGHT file distributed with this | |
8 * source distribution. | |
9 * | |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 */ | |
24 | |
25 /* A lot of this code at least resembles the code in libxode, but since | |
26 * libxode uses memory pools that we simply have no need for, I decided to | |
27 * write my own stuff. Also, re-writing this lets me be as lightweight | |
28 * as I want to be. Thank you libxode for giving me a good starting point */ | |
29 | |
30 #include "internal.h" | |
31 | |
32 #include <libxml/parser.h> | |
33 #include <string.h> | |
34 #include <glib.h> | |
35 | |
14324 | 36 #include "dbus-maybe.h" |
14192 | 37 #include "util.h" |
38 #include "xmlnode.h" | |
39 | |
40 #ifdef _WIN32 | |
41 # define NEWLINE_S "\r\n" | |
42 #else | |
43 # define NEWLINE_S "\n" | |
44 #endif | |
45 | |
46 static xmlnode* | |
47 new_node(const char *name, XMLNodeType type) | |
48 { | |
49 xmlnode *node = g_new0(xmlnode, 1); | |
50 | |
51 node->name = g_strdup(name); | |
52 node->type = type; | |
53 | |
14324 | 54 GAIM_DBUS_REGISTER_POINTER(node, xmlnode); |
55 | |
14192 | 56 return node; |
57 } | |
58 | |
59 xmlnode* | |
60 xmlnode_new(const char *name) | |
61 { | |
62 g_return_val_if_fail(name != NULL, NULL); | |
63 | |
64 return new_node(name, XMLNODE_TYPE_TAG); | |
65 } | |
66 | |
67 xmlnode * | |
68 xmlnode_new_child(xmlnode *parent, const char *name) | |
69 { | |
70 xmlnode *node; | |
71 | |
72 g_return_val_if_fail(parent != NULL, NULL); | |
73 g_return_val_if_fail(name != NULL, NULL); | |
74 | |
75 node = new_node(name, XMLNODE_TYPE_TAG); | |
76 | |
77 xmlnode_insert_child(parent, node); | |
78 | |
79 return node; | |
80 } | |
81 | |
82 void | |
83 xmlnode_insert_child(xmlnode *parent, xmlnode *child) | |
84 { | |
85 g_return_if_fail(parent != NULL); | |
86 g_return_if_fail(child != NULL); | |
87 | |
88 child->parent = parent; | |
89 | |
90 if(parent->lastchild) { | |
91 parent->lastchild->next = child; | |
92 } else { | |
93 parent->child = child; | |
94 } | |
95 | |
96 parent->lastchild = child; | |
97 } | |
98 | |
99 void | |
100 xmlnode_insert_data(xmlnode *node, const char *data, gssize size) | |
101 { | |
102 xmlnode *child; | |
103 gsize real_size; | |
104 | |
105 g_return_if_fail(node != NULL); | |
106 g_return_if_fail(data != NULL); | |
107 g_return_if_fail(size != 0); | |
108 | |
109 real_size = size == -1 ? strlen(data) : size; | |
110 | |
111 child = new_node(NULL, XMLNODE_TYPE_DATA); | |
112 | |
113 child->data = g_memdup(data, real_size); | |
114 child->data_sz = real_size; | |
115 | |
116 xmlnode_insert_child(node, child); | |
117 } | |
118 | |
119 void | |
120 xmlnode_remove_attrib(xmlnode *node, const char *attr) | |
121 { | |
122 xmlnode *attr_node, *sibling = NULL; | |
123 | |
124 g_return_if_fail(node != NULL); | |
125 g_return_if_fail(attr != NULL); | |
126 | |
127 for(attr_node = node->child; attr_node; attr_node = attr_node->next) | |
128 { | |
129 if(attr_node->type == XMLNODE_TYPE_ATTRIB && | |
130 !strcmp(attr_node->name, attr)) { | |
131 if(node->child == attr_node) { | |
132 node->child = attr_node->next; | |
133 } else if (node->lastchild == attr_node) { | |
134 node->lastchild = sibling; | |
135 } else { | |
136 sibling->next = attr_node->next; | |
137 } | |
138 xmlnode_free(attr_node); | |
139 return; | |
140 } | |
141 sibling = attr_node; | |
142 } | |
143 } | |
144 | |
145 void | |
146 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) | |
147 { | |
148 xmlnode *attrib_node; | |
149 | |
150 g_return_if_fail(node != NULL); | |
151 g_return_if_fail(attr != NULL); | |
152 g_return_if_fail(value != NULL); | |
153 | |
154 xmlnode_remove_attrib(node, attr); | |
155 | |
156 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); | |
157 | |
158 attrib_node->data = g_strdup(value); | |
159 | |
160 xmlnode_insert_child(node, attrib_node); | |
161 } | |
162 | |
163 const char * | |
164 xmlnode_get_attrib(xmlnode *node, const char *attr) | |
165 { | |
166 xmlnode *x; | |
167 | |
168 g_return_val_if_fail(node != NULL, NULL); | |
169 | |
170 for(x = node->child; x; x = x->next) { | |
171 if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { | |
172 return x->data; | |
173 } | |
174 } | |
175 | |
176 return NULL; | |
177 } | |
178 | |
179 | |
180 void xmlnode_set_namespace(xmlnode *node, const char *xmlns) | |
181 { | |
182 g_return_if_fail(node != NULL); | |
183 | |
15177 | 184 g_free(node->xmlns); |
185 node->xmlns = g_strdup(xmlns); | |
14192 | 186 } |
187 | |
188 const char *xmlnode_get_namespace(xmlnode *node) | |
189 { | |
190 g_return_val_if_fail(node != NULL, NULL); | |
191 | |
15177 | 192 return node->xmlns; |
14192 | 193 } |
194 | |
195 void | |
196 xmlnode_free(xmlnode *node) | |
197 { | |
198 xmlnode *x, *y; | |
199 | |
200 g_return_if_fail(node != NULL); | |
201 | |
202 x = node->child; | |
203 while(x) { | |
204 y = x->next; | |
205 xmlnode_free(x); | |
206 x = y; | |
207 } | |
208 | |
209 g_free(node->name); | |
210 g_free(node->data); | |
15177 | 211 g_free(node->xmlns); |
14324 | 212 |
213 GAIM_DBUS_UNREGISTER_POINTER(node); | |
14192 | 214 g_free(node); |
215 } | |
216 | |
217 xmlnode* | |
218 xmlnode_get_child(const xmlnode *parent, const char *name) | |
219 { | |
220 return xmlnode_get_child_with_namespace(parent, name, NULL); | |
221 } | |
222 | |
223 xmlnode * | |
224 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns) | |
225 { | |
226 xmlnode *x, *ret = NULL; | |
227 char **names; | |
228 char *parent_name, *child_name; | |
229 | |
230 g_return_val_if_fail(parent != NULL, NULL); | |
231 g_return_val_if_fail(name != NULL, NULL); | |
232 | |
233 names = g_strsplit(name, "/", 2); | |
234 parent_name = names[0]; | |
235 child_name = names[1]; | |
236 | |
237 for(x = parent->child; x; x = x->next) { | |
238 const char *xmlns = NULL; | |
239 if(ns) | |
240 xmlns = xmlnode_get_namespace(x); | |
241 | |
242 if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name) | |
243 && (!ns || (xmlns && !strcmp(ns, xmlns)))) { | |
244 ret = x; | |
245 break; | |
246 } | |
247 } | |
248 | |
249 if(child_name && ret) | |
250 ret = xmlnode_get_child(ret, child_name); | |
251 | |
252 g_strfreev(names); | |
253 return ret; | |
254 } | |
255 | |
256 char * | |
257 xmlnode_get_data(xmlnode *node) | |
258 { | |
259 GString *str = NULL; | |
260 xmlnode *c; | |
261 | |
262 g_return_val_if_fail(node != NULL, NULL); | |
263 | |
264 for(c = node->child; c; c = c->next) { | |
265 if(c->type == XMLNODE_TYPE_DATA) { | |
266 if(!str) | |
267 str = g_string_new(""); | |
268 str = g_string_append_len(str, c->data, c->data_sz); | |
269 } | |
270 } | |
271 | |
272 if (str == NULL) | |
273 return NULL; | |
274 | |
275 return g_string_free(str, FALSE); | |
276 } | |
277 | |
278 static char * | |
279 xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) | |
280 { | |
281 GString *text = g_string_new(""); | |
282 xmlnode *c; | |
283 char *node_name, *esc, *esc2, *tab = NULL; | |
284 gboolean need_end = FALSE, pretty = formatting; | |
285 | |
286 g_return_val_if_fail(node != NULL, NULL); | |
287 | |
288 if(pretty && depth) { | |
289 tab = g_strnfill(depth, '\t'); | |
290 text = g_string_append(text, tab); | |
291 } | |
292 | |
293 node_name = g_markup_escape_text(node->name, -1); | |
294 g_string_append_printf(text, "<%s", node_name); | |
295 | |
15177 | 296 if (node->xmlns) { |
297 if(!node->parent || !node->parent->xmlns || strcmp(node->xmlns, node->parent->xmlns)) | |
15123 | 298 { |
15177 | 299 char *xmlns = g_markup_escape_text(node->xmlns, -1); |
300 g_string_append_printf(text, " xmlns='%s'", xmlns); | |
301 g_free(xmlns); | |
15123 | 302 } |
14192 | 303 } |
304 for(c = node->child; c; c = c->next) | |
305 { | |
306 if(c->type == XMLNODE_TYPE_ATTRIB) { | |
307 esc = g_markup_escape_text(c->name, -1); | |
308 esc2 = g_markup_escape_text(c->data, -1); | |
309 g_string_append_printf(text, " %s='%s'", esc, esc2); | |
310 g_free(esc); | |
311 g_free(esc2); | |
312 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) { | |
313 if(c->type == XMLNODE_TYPE_DATA) | |
314 pretty = FALSE; | |
315 need_end = TRUE; | |
316 } | |
317 } | |
318 | |
319 if(need_end) { | |
320 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : ""); | |
321 | |
322 for(c = node->child; c; c = c->next) | |
323 { | |
324 if(c->type == XMLNODE_TYPE_TAG) { | |
325 int esc_len; | |
326 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1); | |
327 text = g_string_append_len(text, esc, esc_len); | |
328 g_free(esc); | |
329 } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) { | |
330 esc = g_markup_escape_text(c->data, c->data_sz); | |
331 text = g_string_append(text, esc); | |
332 g_free(esc); | |
333 } | |
334 } | |
335 | |
336 if(tab && pretty) | |
337 text = g_string_append(text, tab); | |
338 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : ""); | |
339 } else { | |
340 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : ""); | |
341 } | |
342 | |
343 g_free(node_name); | |
344 | |
345 g_free(tab); | |
346 | |
347 if(len) | |
348 *len = text->len; | |
349 | |
350 return g_string_free(text, FALSE); | |
351 } | |
352 | |
353 char * | |
354 xmlnode_to_str(xmlnode *node, int *len) | |
355 { | |
356 return xmlnode_to_str_helper(node, len, FALSE, 0); | |
357 } | |
358 | |
359 char * | |
360 xmlnode_to_formatted_str(xmlnode *node, int *len) | |
361 { | |
362 char *xml, *xml_with_declaration; | |
363 | |
364 g_return_val_if_fail(node != NULL, NULL); | |
365 | |
366 xml = xmlnode_to_str_helper(node, len, TRUE, 0); | |
367 xml_with_declaration = | |
368 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml); | |
369 g_free(xml); | |
370 | |
371 return xml_with_declaration; | |
372 } | |
373 | |
374 struct _xmlnode_parser_data { | |
375 xmlnode *current; | |
376 }; | |
377 | |
378 static void | |
379 xmlnode_parser_element_start_libxml(void *user_data, | |
15177 | 380 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns, |
14192 | 381 int nb_namespaces, const xmlChar **namespaces, |
382 int nb_attributes, int nb_defaulted, const xmlChar **attributes) | |
383 { | |
384 struct _xmlnode_parser_data *xpd = user_data; | |
385 xmlnode *node; | |
386 int i; | |
387 | |
388 if(!element_name) { | |
389 return; | |
390 } else { | |
391 if(xpd->current) | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
392 node = xmlnode_new_child(xpd->current, (const char*) element_name); |
14192 | 393 else |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
394 node = xmlnode_new((const char *) element_name); |
14192 | 395 |
15177 | 396 xmlnode_set_namespace(node, (const char *) xmlns); |
14192 | 397 |
398 for(i=0; i < nb_attributes * 5; i+=5) { | |
14228 | 399 char *txt; |
14192 | 400 int attrib_len = attributes[i+4] - attributes[i+3]; |
401 char *attrib = g_malloc(attrib_len + 1); | |
402 memcpy(attrib, attributes[i+3], attrib_len); | |
403 attrib[attrib_len] = '\0'; | |
14228 | 404 txt = attrib; |
14192 | 405 attrib = gaim_unescape_html(txt); |
406 g_free(txt); | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
407 xmlnode_set_attrib(node, (const char*) attributes[i], attrib); |
14192 | 408 g_free(attrib); |
409 } | |
410 | |
411 xpd->current = node; | |
412 } | |
413 } | |
414 | |
415 static void | |
416 xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name, | |
15177 | 417 const xmlChar *prefix, const xmlChar *xmlns) |
14192 | 418 { |
419 struct _xmlnode_parser_data *xpd = user_data; | |
420 | |
421 if(!element_name || !xpd->current) | |
422 return; | |
423 | |
424 if(xpd->current->parent) { | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
425 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name)) |
14192 | 426 xpd->current = xpd->current->parent; |
427 } | |
428 } | |
429 | |
430 static void | |
431 xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) | |
432 { | |
433 struct _xmlnode_parser_data *xpd = user_data; | |
434 | |
435 if(!xpd->current) | |
436 return; | |
437 | |
438 if(!text || !text_len) | |
439 return; | |
440 | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
441 xmlnode_insert_data(xpd->current, (const char*) text, text_len); |
14192 | 442 } |
443 | |
444 static xmlSAXHandler xmlnode_parser_libxml = { | |
445 .internalSubset = NULL, | |
446 .isStandalone = NULL, | |
447 .hasInternalSubset = NULL, | |
448 .hasExternalSubset = NULL, | |
449 .resolveEntity = NULL, | |
450 .getEntity = NULL, | |
451 .entityDecl = NULL, | |
452 .notationDecl = NULL, | |
453 .attributeDecl = NULL, | |
454 .elementDecl = NULL, | |
455 .unparsedEntityDecl = NULL, | |
456 .setDocumentLocator = NULL, | |
457 .startDocument = NULL, | |
458 .endDocument = NULL, | |
459 .startElement = NULL, | |
460 .endElement = NULL, | |
461 .reference = NULL, | |
462 .characters = xmlnode_parser_element_text_libxml, | |
463 .ignorableWhitespace = NULL, | |
464 .processingInstruction = NULL, | |
465 .comment = NULL, | |
466 .warning = NULL, | |
467 .error = NULL, | |
468 .fatalError = NULL, | |
469 .getParameterEntity = NULL, | |
470 .cdataBlock = NULL, | |
471 .externalSubset = NULL, | |
472 .initialized = XML_SAX2_MAGIC, | |
473 ._private = NULL, | |
474 .startElementNs = xmlnode_parser_element_start_libxml, | |
475 .endElementNs = xmlnode_parser_element_end_libxml, | |
476 .serror = NULL | |
477 }; | |
478 | |
479 xmlnode * | |
480 xmlnode_from_str(const char *str, gssize size) | |
481 { | |
482 struct _xmlnode_parser_data *xpd; | |
483 xmlnode *ret; | |
484 gsize real_size; | |
485 | |
486 g_return_val_if_fail(str != NULL, NULL); | |
487 | |
488 real_size = size < 0 ? strlen(str) : size; | |
489 xpd = g_new0(struct _xmlnode_parser_data, 1); | |
490 | |
14322 | 491 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, real_size) < 0) { |
14192 | 492 while(xpd->current && xpd->current->parent) |
493 xpd->current = xpd->current->parent; | |
494 if(xpd->current) | |
495 xmlnode_free(xpd->current); | |
496 xpd->current = NULL; | |
497 } | |
498 ret = xpd->current; | |
499 g_free(xpd); | |
500 return ret; | |
501 } | |
502 | |
503 xmlnode * | |
504 xmlnode_copy(xmlnode *src) | |
505 { | |
506 xmlnode *ret; | |
507 xmlnode *child; | |
508 xmlnode *sibling = NULL; | |
509 | |
510 g_return_val_if_fail(src != NULL, NULL); | |
511 | |
512 ret = new_node(src->name, src->type); | |
513 if(src->data) { | |
514 if(src->data_sz) { | |
515 ret->data = g_memdup(src->data, src->data_sz); | |
516 ret->data_sz = src->data_sz; | |
517 } else { | |
518 ret->data = g_strdup(src->data); | |
519 } | |
520 } | |
521 | |
522 for(child = src->child; child; child = child->next) { | |
523 if(sibling) { | |
524 sibling->next = xmlnode_copy(child); | |
525 sibling = sibling->next; | |
526 } else { | |
527 ret->child = xmlnode_copy(child); | |
528 sibling = ret->child; | |
529 } | |
530 sibling->parent = ret; | |
531 } | |
532 | |
533 ret->lastchild = sibling; | |
534 | |
535 return ret; | |
536 } | |
537 | |
538 xmlnode * | |
539 xmlnode_get_next_twin(xmlnode *node) | |
540 { | |
541 xmlnode *sibling; | |
542 const char *ns = xmlnode_get_namespace(node); | |
543 | |
544 g_return_val_if_fail(node != NULL, NULL); | |
545 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); | |
546 | |
547 for(sibling = node->next; sibling; sibling = sibling->next) { | |
548 const char *xmlns = NULL; | |
549 if(ns) | |
550 xmlns = xmlnode_get_namespace(sibling); | |
551 | |
552 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && | |
553 (!ns || (xmlns && !strcmp(ns, xmlns)))) | |
554 return sibling; | |
555 } | |
556 | |
557 return NULL; | |
558 } |