xref: /PHP-7.2/ext/xmlrpc/libxmlrpc/xml_element.c (revision 9c62b95e)
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