1 /*
2 This file is part of libXMLRPC - a C library for xml-encoded function calls.
3
4 Author: Dan Libby (dan@libby.com)
5 Epinions.com may be contacted at feedback@epinions-inc.com
6 */
7
8 /*
9 Copyright 2000 Epinions, Inc.
10
11 Subject to the following 3 conditions, Epinions, Inc. permits you, free
12 of charge, to (a) use, copy, distribute, modify, perform and display this
13 software and associated documentation files (the "Software"), and (b)
14 permit others to whom the Software is furnished to do so as well.
15
16 1) The above copyright notice and this permission notice shall be included
17 without modification in all copies or substantial portions of the
18 Software.
19
20 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
21 ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
22 IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
23 PURPOSE OR NONINFRINGEMENT.
24
25 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
26 SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
27 OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
28 NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
29 DAMAGES.
30
31 */
32
33
34 static const char rcsid[] = "#(@) $Id$";
35
36
37
38 /****h* ABOUT/xml_element
39 * NAME
40 * xml_element
41 * AUTHOR
42 * Dan Libby, aka danda (dan@libby.com)
43 * CREATION DATE
44 * 06/2000
45 * HISTORY
46 * $Log$
47 * Revision 1.9.4.1.2.1 2008/12/09 17:22:12 iliaa
48 *
49 * MFH: Fixed bug #46746 (xmlrpc_decode_request outputs non-suppressable error
50 * when given bad data).
51 *
52 * Revision 1.9.4.1 2006/07/30 11:34:02 tony2001
53 * MFH: fix compile warnings (#38257)
54 *
55 * Revision 1.9 2005/04/22 11:06:53 jorton
56 * Fixed bug #32797 (invalid C code in xmlrpc extension).
57 *
58 * Revision 1.8 2005/03/28 00:07:24 edink
59 * Reshufle includes to make it compile on windows
60 *
61 * Revision 1.7 2005/03/26 03:13:58 sniper
62 * - Made it possible to build ext/xmlrpc with libxml2
63 *
64 * Revision 1.6 2004/06/01 20:16:06 iliaa
65 * Fixed bug #28597 (xmlrpc_encode_request() incorrectly encodes chars in
66 * 200-210 range).
67 * Patch by: fernando dot nemec at folha dot com dot br
68 *
69 * Revision 1.5 2003/12/16 21:00:21 sniper
70 * Fix some compile warnings (patch by Joe Orton)
71 *
72 * Revision 1.4 2002/11/26 23:01:16 fmk
73 * removing unused variables
74 *
75 * Revision 1.3 2002/07/05 04:43:53 danda
76 * merged in updates from SF project. bring php repository up to date with xmlrpc-epi version 0.51
77 *
78 * Revision 1.9 2002/07/03 20:54:30 danda
79 * root element should not have a parent. patch from anon SF user
80 *
81 * Revision 1.8 2002/05/23 17:46:51 danda
82 * patch from mukund - fix non utf-8 encoding conversions
83 *
84 * Revision 1.7 2002/02/13 20:58:50 danda
85 * patch to make source more windows friendly, contributed by Jeff Lawson
86 *
87 * Revision 1.6 2002/01/08 01:06:55 danda
88 * enable <?xml version="1.0"?> format for parsers that are very picky.
89 *
90 * Revision 1.5 2001/09/29 21:58:05 danda
91 * adding cvs log to history section
92 *
93 * 10/15/2000 -- danda -- adding robodoc documentation
94 * TODO
95 * Nicer external API. Get rid of macros. Make opaque types, etc.
96 * PORTABILITY
97 * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just
98 * about anything with minor mods.
99 * NOTES
100 * This code incorporates ideas from expat-ensor from http://xml.ensor.org.
101 *
102 * It was coded primarily to act as a go-between for expat and xmlrpc. To this
103 * end, it stores xml elements, their sub-elements, and their attributes in an
104 * in-memory tree. When expat is done parsing, the tree can be walked, thus
105 * retrieving the values. The code can also be used to build a tree via API then
106 * write out the tree to a buffer, thus "serializing" the xml.
107 *
108 * It turns out this is useful for other purposes, such as parsing config files.
109 * YMMV.
110 *
111 * Some Features:
112 * - output option for xml escaping data. Choices include no escaping, entity escaping,
113 * or CDATA sections.
114 * - output option for character encoding. Defaults to (none) utf-8.
115 * - output option for verbosity/readability. ultra-compact, newlines, pretty/level indented.
116 *
117 * BUGS
118 * there must be some.
119 ******/
120
121 #include "ext/xml/expat_compat.h"
122 #include <stdlib.h>
123 #include <string.h>
124 #include <ctype.h>
125
126 #include "xml_element.h"
127 #include "queue.h"
128 #include "encodings.h"
129
130 #define my_free(thing) if(thing) {efree(thing); thing = NULL;}
131
132 #define XML_DECL_START "<?xml"
133 #define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1
134 #define XML_DECL_VERSION "version=\"1.0\""
135 #define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1
136 #define XML_DECL_ENCODING_ATTR "encoding"
137 #define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1
138 #define XML_DECL_ENCODING_DEFAULT "utf-8"
139 #define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1
140 #define XML_DECL_END "?>"
141 #define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1
142 #define START_TOKEN_BEGIN "<"
143 #define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1
144 #define START_TOKEN_END ">"
145 #define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1
146 #define EMPTY_START_TOKEN_END "/>"
147 #define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1
148 #define END_TOKEN_BEGIN "</"
149 #define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1
150 #define END_TOKEN_END ">"
151 #define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1
152 #define ATTR_DELIMITER "\""
153 #define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1
154 #define CDATA_BEGIN "<![CDATA["
155 #define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1
156 #define CDATA_END "]]>"
157 #define CDATA_END_LEN sizeof(CDATA_END) - 1
158 #define EQUALS "="
159 #define EQUALS_LEN sizeof(EQUALS) - 1
160 #define WHITESPACE " "
161 #define WHITESPACE_LEN sizeof(WHITESPACE) - 1
162 #define NEWLINE "\n"
163 #define NEWLINE_LEN sizeof(NEWLINE) - 1
164 #define MAX_VAL_BUF 144
165 #define SCALAR_STR "SCALAR"
166 #define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1
167 #define VECTOR_STR "VECTOR"
168 #define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1
169 #define RESPONSE_STR "RESPONSE"
170 #define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1
171
172
173 /*-----------------------------
174 - Begin xml_element Functions -
175 -----------------------------*/
176
177 /****f* xml_element/xml_elem_free_non_recurse
178 * NAME
179 * xml_elem_free_non_recurse
180 * SYNOPSIS
181 * void xml_elem_free_non_recurse(xml_element* root)
182 * FUNCTION
183 * free a single xml element. child elements will not be freed.
184 * INPUTS
185 * root - the element to free
186 * RESULT
187 * void
188 * NOTES
189 * SEE ALSO
190 * xml_elem_free ()
191 * xml_elem_new ()
192 * SOURCE
193 */
xml_elem_free_non_recurse(xml_element * root)194 void xml_elem_free_non_recurse(xml_element* root) {
195 if(root) {
196 xml_element_attr* attrs = Q_Head(&root->attrs);
197 while(attrs) {
198 my_free(attrs->key);
199 my_free(attrs->val);
200 my_free(attrs);
201 attrs = Q_Next(&root->attrs);
202 }
203
204 Q_Destroy(&root->children);
205 Q_Destroy(&root->attrs);
206 if(root->name) {
207 efree((char *)root->name);
208 root->name = NULL;
209 }
210 simplestring_free(&root->text);
211 my_free(root);
212 }
213 }
214 /******/
215
216 /****f* xml_element/xml_elem_free
217 * NAME
218 * xml_elem_free
219 * SYNOPSIS
220 * void xml_elem_free(xml_element* root)
221 * FUNCTION
222 * free an xml element and all of its child elements
223 * INPUTS
224 * root - the root of an xml tree you would like to free
225 * RESULT
226 * void
227 * NOTES
228 * SEE ALSO
229 * xml_elem_free_non_recurse ()
230 * xml_elem_new ()
231 * SOURCE
232 */
xml_elem_free(xml_element * root)233 void xml_elem_free(xml_element* root) {
234 if(root) {
235 xml_element* kids = Q_Head(&root->children);
236 while(kids) {
237 xml_elem_free(kids);
238 kids = Q_Next(&root->children);
239 }
240 xml_elem_free_non_recurse(root);
241 }
242 }
243 /******/
244
245 /****f* xml_element/xml_elem_new
246 * NAME
247 * xml_elem_new
248 * SYNOPSIS
249 * xml_element* xml_elem_new()
250 * FUNCTION
251 * allocates and initializes a new xml_element
252 * INPUTS
253 * none
254 * RESULT
255 * xml_element* or NULL. NULL indicates an out-of-memory condition.
256 * NOTES
257 * SEE ALSO
258 * xml_elem_free ()
259 * xml_elem_free_non_recurse ()
260 * SOURCE
261 */
xml_elem_new()262 xml_element* xml_elem_new() {
263 xml_element* elem = ecalloc(1, sizeof(xml_element));
264 if(elem) {
265 Q_Init(&elem->children);
266 Q_Init(&elem->attrs);
267 simplestring_init(&elem->text);
268
269 /* init empty string in case we don't find any char data */
270 simplestring_addn(&elem->text, "", 0);
271 }
272 return elem;
273 }
274 /******/
275
xml_elem_writefunc(int (* fptr)(void * data,const char * text,int size),const char * text,void * data,int len)276 static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len)
277 {
278 return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0;
279 }
280
281
282
create_xml_escape(char * pString,unsigned char c)283 static int create_xml_escape(char *pString, unsigned char c)
284 {
285 int counter = 0;
286
287 pString[counter++] = '&';
288 pString[counter++] = '#';
289 if(c >= 100) {
290 pString[counter++] = c / 100 + '0';
291 c = c % 100;
292 }
293 pString[counter++] = c / 10 + '0';
294 c = c % 10;
295
296 pString[counter++] = c + '0';
297 pString[counter++] = ';';
298 return counter;
299 }
300
301 #define non_ascii(c) (c > 127)
302 #define non_print(c) (!isprint(c))
303 #define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<')
304 #define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */
305
306 /*
307 * xml_elem_entity_escape
308 *
309 * Purpose:
310 * escape reserved xml chars and non utf-8 chars as xml entities
311 * Comments:
312 * The return value may be a new string, or null if no
313 * conversion was performed. In the latter case, *newlen will
314 * be 0.
315 * Flags (to escape)
316 * xml_elem_no_escaping = 0x000,
317 * xml_elem_entity_escaping = 0x002, // escape xml special chars as entities
318 * xml_elem_non_ascii_escaping = 0x008, // escape chars above 127
319 * xml_elem_cdata_escaping = 0x010, // wrap in cdata
320 */
xml_elem_entity_escape(const char * buf,int old_len,int * newlen,XML_ELEM_ESCAPING flags)321 static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) {
322 char *pRetval = 0;
323 int iNewBufLen=0;
324
325 #define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \
326 ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \
327 ((flag & xml_elem_non_print_escaping) && non_print(c)) )
328
329 if(buf && *buf) {
330 const unsigned char *bufcopy;
331 char *NewBuffer;
332 int ToBeXmlEscaped=0;
333 int iLength;
334 bufcopy = buf;
335 iLength= old_len ? old_len : strlen(buf);
336 while(*bufcopy) {
337 if( should_escape(*bufcopy, flags) ) {
338 /* the length will increase by length of xml escape - the character length */
339 iLength += entity_length(*bufcopy);
340 ToBeXmlEscaped=1;
341 }
342 bufcopy++;
343 }
344
345 if(ToBeXmlEscaped) {
346
347 NewBuffer= emalloc(iLength+1);
348 if(NewBuffer) {
349 bufcopy=buf;
350 while(*bufcopy) {
351 if(should_escape(*bufcopy, flags)) {
352 iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy);
353 }
354 else {
355 NewBuffer[iNewBufLen++]=*bufcopy;
356 }
357 bufcopy++;
358 }
359 NewBuffer[iNewBufLen] = 0;
360 pRetval = NewBuffer;
361 }
362 }
363 }
364
365 if(newlen) {
366 *newlen = iNewBufLen;
367 }
368
369 return pRetval;
370 }
371
372
xml_element_serialize(xml_element * el,int (* fptr)(void * data,const char * text,int size),void * data,XML_ELEM_OUTPUT_OPTIONS options,int depth)373 static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth)
374 {
375 int i;
376 static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT};
377 static char whitespace[] = " "
378 " "
379 " ";
380 depth++;
381
382 if(!el) {
383 /* fprintf(stderr, "Nothing to write\n"); */
384 return;
385 }
386 if(!options) {
387 options = &default_opts;
388 }
389
390 /* print xml declaration if at root level */
391 if(depth == 1) {
392 xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN);
393 xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
394 xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN);
395 if(options->encoding && *options->encoding) {
396 xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
397 xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN);
398 xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
399 xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
400 xml_elem_writefunc(fptr, options->encoding, data, 0);
401 xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
402 }
403 xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN);
404 if(options->verbosity != xml_elem_no_white_space) {
405 xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
406 }
407 }
408
409 if(options->verbosity == xml_elem_pretty && depth > 2) {
410 xml_elem_writefunc(fptr, whitespace, data, depth - 2);
411 }
412 /* begin element */
413 xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN);
414 if(el->name) {
415 xml_elem_writefunc(fptr, el->name, data, 0);
416
417 /* write attrs, if any */
418 if(Q_Size(&el->attrs)) {
419 xml_element_attr* iter = Q_Head(&el->attrs);
420 while( iter ) {
421 xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
422 xml_elem_writefunc(fptr, iter->key, data, 0);
423 xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
424 xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
425 xml_elem_writefunc(fptr, iter->val, data, 0);
426 xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
427
428 iter = Q_Next(&el->attrs);
429 }
430 }
431 }
432 else {
433 xml_elem_writefunc(fptr, "None", data, 0);
434 }
435 /* if no text and no children, use abbreviated form, eg: <foo/> */
436 if(!el->text.len && !Q_Size(&el->children)) {
437 xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN);
438 }
439 /* otherwise, print element contents */
440 else {
441 xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN);
442
443 /* print text, if any */
444 if(el->text.len) {
445 char* escaped_str = el->text.str;
446 int buflen = el->text.len;
447
448 if(options->escaping && options->escaping != xml_elem_cdata_escaping) {
449 escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping );
450 if(!escaped_str) {
451 escaped_str = el->text.str;
452 }
453 }
454
455 if(options->escaping & xml_elem_cdata_escaping) {
456 xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN);
457 }
458
459 xml_elem_writefunc(fptr, escaped_str, data, buflen);
460
461 if(escaped_str != el->text.str) {
462 my_free(escaped_str);
463 }
464
465 if(options->escaping & xml_elem_cdata_escaping) {
466 xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN);
467 }
468 }
469 /* no text, so print child elems */
470 else {
471 xml_element *kids = Q_Head(&el->children);
472 i = 0;
473 while( kids ) {
474 if(i++ == 0) {
475 if(options->verbosity != xml_elem_no_white_space) {
476 xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
477 }
478 }
479 xml_element_serialize(kids, fptr, data, options, depth);
480 kids = Q_Next(&el->children);
481 }
482 if(i) {
483 if(options->verbosity == xml_elem_pretty && depth > 2) {
484 xml_elem_writefunc(fptr, whitespace, data, depth - 2);
485 }
486 }
487 }
488
489 xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN);
490 xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0);
491 xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN);
492 }
493 if(options->verbosity != xml_elem_no_white_space) {
494 xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
495 }
496 }
497
498 /* print buf to file */
file_out_fptr(void * f,const char * text,int size)499 static int file_out_fptr(void *f, const char *text, int size)
500 {
501 fputs(text, (FILE *)f);
502 return 0;
503 }
504
505 /* print buf to simplestring */
simplestring_out_fptr(void * f,const char * text,int size)506 static int simplestring_out_fptr(void *f, const char *text, int size)
507 {
508 simplestring* buf = (simplestring*)f;
509 if(buf) {
510 simplestring_addn(buf, text, size);
511 }
512 return 0;
513 }
514
515 /****f* xml_element/xml_elem_serialize_to_string
516 * NAME
517 * xml_elem_serialize_to_string
518 * SYNOPSIS
519 * void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
520 * FUNCTION
521 * writes element tree as XML into a newly allocated buffer
522 * INPUTS
523 * el - root element of tree
524 * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS
525 * buf_len - length of returned buffer, if not null.
526 * RESULT
527 * char* or NULL. Must be free'd by caller.
528 * NOTES
529 * SEE ALSO
530 * xml_elem_serialize_to_stream ()
531 * xml_elem_parse_buf ()
532 * SOURCE
533 */
xml_elem_serialize_to_string(xml_element * el,XML_ELEM_OUTPUT_OPTIONS options,int * buf_len)534 char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
535 {
536 simplestring buf;
537 simplestring_init(&buf);
538
539 xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0);
540
541 if(buf_len) {
542 *buf_len = buf.len;
543 }
544
545 return buf.str;
546 }
547 /******/
548
549 /****f* xml_element/xml_elem_serialize_to_stream
550 * NAME
551 * xml_elem_serialize_to_stream
552 * SYNOPSIS
553 * void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
554 * FUNCTION
555 * writes element tree as XML into a stream (typically an opened file)
556 * INPUTS
557 * el - root element of tree
558 * output - stream handle
559 * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS
560 * RESULT
561 * void
562 * NOTES
563 * SEE ALSO
564 * xml_elem_serialize_to_string ()
565 * xml_elem_parse_buf ()
566 * SOURCE
567 */
xml_elem_serialize_to_stream(xml_element * el,FILE * output,XML_ELEM_OUTPUT_OPTIONS options)568 void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
569 {
570 xml_element_serialize(el, file_out_fptr, (void *)output, options, 0);
571 }
572 /******/
573
574 /*--------------------------*
575 * End xml_element Functions *
576 *--------------------------*/
577
578
579 /*----------------------
580 * Begin Expat Handlers *
581 *---------------------*/
582
583 typedef struct _xml_elem_data {
584 xml_element* root;
585 xml_element* current;
586 XML_ELEM_INPUT_OPTIONS input_options;
587 int needs_enc_conversion;
588 } xml_elem_data;
589
590
591 /* expat start of element handler */
_xmlrpc_startElement(void * userData,const char * name,const char ** attrs)592 static void _xmlrpc_startElement(void *userData, const char *name, const char **attrs)
593 {
594 xml_element *c;
595 xml_elem_data* mydata = (xml_elem_data*)userData;
596 const char** p = attrs;
597
598 if(mydata) {
599 c = mydata->current;
600
601 mydata->current = xml_elem_new();
602 mydata->current->name = (char*)estrdup(name);
603 mydata->current->parent = c;
604
605 /* init attrs */
606 while(p && *p) {
607 xml_element_attr* attr = emalloc(sizeof(xml_element_attr));
608 if(attr) {
609 attr->key = estrdup(*p);
610 attr->val = estrdup(*(p+1));
611 Q_PushTail(&mydata->current->attrs, attr);
612
613 p += 2;
614 }
615 }
616 }
617 }
618
619 /* expat end of element handler */
_xmlrpc_endElement(void * userData,const char * name)620 static void _xmlrpc_endElement(void *userData, const char *name)
621 {
622 xml_elem_data* mydata = (xml_elem_data*)userData;
623
624 if(mydata && mydata->current && mydata->current->parent) {
625 Q_PushTail(&mydata->current->parent->children, mydata->current);
626
627 mydata->current = mydata->current->parent;
628 }
629 }
630
631 /* expat char data handler */
_xmlrpc_charHandler(void * userData,const char * s,int len)632 static void _xmlrpc_charHandler(void *userData,
633 const char *s,
634 int len)
635 {
636 xml_elem_data* mydata = (xml_elem_data*)userData;
637 if(mydata && mydata->current) {
638
639 /* Check if we need to decode utf-8 parser output to another encoding */
640 if(mydata->needs_enc_conversion && mydata->input_options->encoding) {
641 int new_len = 0;
642 char* add_text = utf8_decode(s, len, &new_len, mydata->input_options->encoding);
643 if(add_text) {
644 len = new_len;
645 simplestring_addn(&mydata->current->text, add_text, len);
646 efree(add_text);
647 return;
648 }
649 }
650 simplestring_addn(&mydata->current->text, s, len);
651 }
652 }
653 /******/
654
655 /*-------------------*
656 * End Expat Handlers *
657 *-------------------*/
658
659 /*-------------------*
660 * xml_elem_parse_buf *
661 *-------------------*/
662
663 /****f* xml_element/xml_elem_parse_buf
664 * NAME
665 * xml_elem_parse_buf
666 * SYNOPSIS
667 * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
668 * FUNCTION
669 * parse a buffer containing XML into an xml_element in-memory tree
670 * INPUTS
671 * in_buf - buffer containing XML document
672 * len - length of buffer
673 * options - input options. optional
674 * error - error result data. optional. check if result is null.
675 * RESULT
676 * void
677 * NOTES
678 * The returned data must be free'd by caller
679 * SEE ALSO
680 * xml_elem_serialize_to_string ()
681 * xml_elem_free ()
682 * SOURCE
683 */
xml_elem_parse_buf(const char * in_buf,int len,XML_ELEM_INPUT_OPTIONS options,XML_ELEM_ERROR error)684 xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
685 {
686 xml_element* xReturn = NULL;
687 char buf[100] = "";
688 static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8};
689
690 if(!options) {
691 options = &default_opts;
692 }
693
694 if(in_buf) {
695 XML_Parser parser;
696 xml_elem_data mydata = {0};
697
698 parser = XML_ParserCreate(NULL);
699
700 mydata.root = xml_elem_new();
701 mydata.current = mydata.root;
702 mydata.input_options = options;
703 mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8);
704
705 XML_SetElementHandler(parser, (XML_StartElementHandler)_xmlrpc_startElement, (XML_EndElementHandler)_xmlrpc_endElement);
706 XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler)_xmlrpc_charHandler);
707
708 /* pass the xml_elem_data struct along */
709 XML_SetUserData(parser, (void*)&mydata);
710
711 if(!len) {
712 len = strlen(in_buf);
713 }
714
715 /* parse the XML */
716 if(XML_Parse(parser, in_buf, len, 1) == 0) {
717 enum XML_Error err_code = XML_GetErrorCode(parser);
718 int line_num = XML_GetCurrentLineNumber(parser);
719 int col_num = XML_GetCurrentColumnNumber(parser);
720 long byte_idx = XML_GetCurrentByteIndex(parser);
721 /* int byte_total = XML_GetCurrentByteCount(parser); */
722 const char * error_str = XML_ErrorString(err_code);
723 if(byte_idx > len) {
724 byte_idx = len;
725 }
726 if(byte_idx >= 0) {
727 snprintf(buf,
728 sizeof(buf),
729 "\n\tdata beginning %ld before byte index: %s\n",
730 byte_idx > 10 ? 10 : byte_idx,
731 in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx));
732 }
733 /*
734 fprintf(stderr, "expat reports error code %i\n"
735 "\tdescription: %s\n"
736 "\tline: %i\n"
737 "\tcolumn: %i\n"
738 "\tbyte index: %ld\n"
739 "\ttotal bytes: %i\n%s ",
740 err_code, error_str, line_num,
741 col_num, byte_idx, byte_total, buf);
742 */
743
744 /* error condition */
745 if(error) {
746 error->parser_code = (long)err_code;
747 error->line = line_num;
748 error->column = col_num;
749 error->byte_index = byte_idx;
750 error->parser_error = error_str;
751 }
752 }
753 else {
754 xReturn = (xml_element*)Q_Head(&mydata.root->children);
755 xReturn->parent = NULL;
756 }
757
758 XML_ParserFree(parser);
759
760
761 xml_elem_free_non_recurse(mydata.root);
762 }
763
764 return xReturn;
765 }
766
767 /******/
768