initial load of upstream version 1.06.32
[xmlrpc-c] / src / xmlrpc_expat.c
1 /* Copyright information is at end of file */
2
3 #include "xmlrpc_config.h"
4
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include <xmlparse.h> /* Expat */
10
11 #include "bool.h"
12
13 #include "xmlrpc-c/base.h"
14 #include "xmlrpc-c/base_int.h"
15 #include "xmlrpc-c/string_int.h"
16 #include "xmlrpc-c/xmlparser.h"
17
18 /* Define the contents of our internal structure. */
19 struct _xml_element {
20     struct _xml_element *_parent;
21     char *_name;
22     xmlrpc_mem_block _cdata;    /* char */
23     xmlrpc_mem_block _children; /* xml_element* */
24 };
25
26 /* Check that we're using expat in UTF-8 mode, not wchar_t mode.
27 ** If you need to use expat in wchar_t mode, write a subroutine to
28 ** copy a wchar_t string to a char string & return an error for
29 ** any non-ASCII characters. Then call this subroutine on all
30 ** XML_Char strings passed to our event handlers before using the
31 ** data. */
32 /* #if sizeof(char) != sizeof(XML_Char)
33 ** #error expat must define XML_Char to be a regular char. 
34 ** #endif
35 */
36
37 #define XMLRPC_ASSERT_ELEM_OK(elem) \
38     XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
39
40
41 /*=========================================================================
42 **  xml_element_new
43 **=========================================================================
44 **  Create a new xml_element. This routine isn't exported, because the
45 **  arguments are implementation-dependent.
46 */
47
48 static xml_element *
49 xml_element_new (xmlrpc_env * const env,
50                  const char * const name) {
51
52     xml_element *retval;
53     int name_valid, cdata_valid, children_valid;
54
55     XMLRPC_ASSERT_ENV_OK(env);
56     XMLRPC_ASSERT(name != NULL);
57
58     /* Set up our error-handling preconditions. */
59     retval = NULL;
60     name_valid = cdata_valid = children_valid = 0;
61
62     /* Allocate our xml_element structure. */
63     retval = (xml_element*) malloc(sizeof(xml_element));
64     XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
65                         "Couldn't allocate memory for XML element");
66
67     /* Set our parent field to NULL. */
68     retval->_parent = NULL;
69     
70     /* Copy over the element name. */
71     retval->_name = (char*) malloc(strlen(name) + 1);
72     XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
73                         "Couldn't allocate memory for XML element");
74     name_valid = 1;
75     strcpy(retval->_name, name);
76
77     /* Initialize a block to hold our CDATA. */
78     XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
79     XMLRPC_FAIL_IF_FAULT(env);
80     cdata_valid = 1;
81
82     /* Initialize a block to hold our child elements. */
83     XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
84     XMLRPC_FAIL_IF_FAULT(env);
85     children_valid = 1;
86
87  cleanup:
88     if (env->fault_occurred) {
89         if (retval) {
90             if (name_valid)
91                 free(retval->_name);
92             if (cdata_valid)
93                 xmlrpc_mem_block_clean(&retval->_cdata);
94             if (children_valid)
95                 xmlrpc_mem_block_clean(&retval->_children);
96             free(retval);
97         }
98         return NULL;
99     } else {
100         return retval;
101     }
102 }
103
104
105 /*=========================================================================
106 **  xml_element_free
107 **=========================================================================
108 **  Blow away an existing element & all of its child elements.
109 */
110 void
111 xml_element_free(xml_element * const elem) {
112
113     xmlrpc_mem_block *children;
114     int size, i;
115     xml_element **contents;
116
117     XMLRPC_ASSERT_ELEM_OK(elem);
118
119     free(elem->_name);
120     elem->_name = XMLRPC_BAD_POINTER;
121     xmlrpc_mem_block_clean(&elem->_cdata);
122
123     /* Deallocate all of our children recursively. */
124     children = &elem->_children;
125     contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, children);
126     size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, children);
127     for (i = 0; i < size; i++)
128         xml_element_free(contents[i]);
129
130     xmlrpc_mem_block_clean(&elem->_children);
131     free(elem);
132 }
133
134
135 /*=========================================================================
136 **  Miscellaneous Accessors
137 **=========================================================================
138 **  Return the fields of the xml_element. See the header for more
139 **  documentation on each function works.
140 */
141
142
143
144 const char *
145 xml_element_name(const xml_element * const elemP) {
146
147     XMLRPC_ASSERT_ELEM_OK(elemP);
148     return elemP->_name;
149 }
150
151
152
153 /* The result of this function is NOT VALID until the end_element handler
154 ** has been called! */
155 size_t xml_element_cdata_size (xml_element *elem)
156 {
157     XMLRPC_ASSERT_ELEM_OK(elem);
158     return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
159 }
160
161 char *xml_element_cdata (xml_element *elem)
162 {
163     XMLRPC_ASSERT_ELEM_OK(elem);
164     return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
165 }
166
167
168
169 size_t
170 xml_element_children_size(const xml_element * const elemP) {
171     XMLRPC_ASSERT_ELEM_OK(elemP);
172     return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element *, &elemP->_children);
173 }
174
175
176
177 xml_element **
178 xml_element_children(const xml_element * const elemP) {
179     XMLRPC_ASSERT_ELEM_OK(elemP);
180     return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element *, &elemP->_children);
181 }
182
183
184
185 /*=========================================================================
186 **  Internal xml_element Utility Functions
187 **=========================================================================
188 */
189
190 static void xml_element_append_cdata (xmlrpc_env *env,
191                                       xml_element *elem,
192                                       char *cdata,
193                                       size_t size)
194 {
195     XMLRPC_ASSERT_ENV_OK(env);
196     XMLRPC_ASSERT_ELEM_OK(elem);    
197
198     XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
199 }
200
201 /* Whether or not this function succeeds, it takes ownership of the 'child'
202 ** argument.
203 ** WARNING - This is the exact opposite of the usual memory ownership
204 ** rules for xmlrpc_value! So please pay attention. */
205 static void xml_element_append_child (xmlrpc_env *env,
206                                       xml_element *elem,
207                                       xml_element *child)
208 {
209     XMLRPC_ASSERT_ENV_OK(env);
210     XMLRPC_ASSERT_ELEM_OK(elem);
211     XMLRPC_ASSERT_ELEM_OK(child);
212     XMLRPC_ASSERT(child->_parent == NULL);
213
214     XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
215                                   &child, 1);
216     if (!env->fault_occurred)
217         child->_parent = elem;
218     else
219         xml_element_free(child);
220 }
221
222
223 /*=========================================================================
224 **  Our parse context. We pass this around as expat user data.
225 **=========================================================================
226 */
227
228 typedef struct {
229     xmlrpc_env env;
230     xml_element * rootP;
231     xml_element * currentP;
232 } parseContext;
233
234
235 /*=========================================================================
236 **  Expat Event Handler Functions
237 **=========================================================================
238 */
239
240 static void
241 startElement(void *      const userData,
242              XML_Char *  const name,
243              XML_Char ** const atts ATTR_UNUSED) {
244
245     parseContext * const contextP = userData;
246
247     XMLRPC_ASSERT(contextP != NULL);
248     XMLRPC_ASSERT(name != NULL);
249
250     if (!contextP->env.fault_occurred) {
251         xml_element * elemP;
252
253         elemP = xml_element_new(&contextP->env, name);
254         if (!contextP->env.fault_occurred) {
255             XMLRPC_ASSERT(elemP != NULL);
256
257             /* Insert the new element in the appropriate place. */
258             if (!contextP->rootP) {
259                 /* No root yet, so this element must be the root. */
260                 contextP->rootP = elemP;
261                 contextP->currentP = elemP;
262             } else {
263                 XMLRPC_ASSERT(contextP->currentP != NULL);
264
265                 /* (We need to watch our error handling invariants
266                    very carefully here. Read the docs for
267                    xml_element_append_child.
268                 */
269                 xml_element_append_child(&contextP->env, contextP->currentP,
270                                          elemP);
271                 if (!contextP->env.fault_occurred)
272                     contextP->currentP = elemP;
273             }
274             if (contextP->env.fault_occurred)
275                 xml_element_free(elemP);
276         }
277         if (contextP->env.fault_occurred) {
278             /* Having changed *contextP to reflect failure, we are responsible
279                for undoing everything that has been done so far in this
280                context.
281             */
282             if (contextP->rootP)
283                 xml_element_free(contextP->rootP);
284         }
285     }
286 }
287
288
289
290 static void
291 endElement(void *     const userData,
292            XML_Char * const name ATTR_UNUSED) {
293
294     parseContext * const contextP = userData;
295
296     XMLRPC_ASSERT(contextP != NULL);
297     XMLRPC_ASSERT(name != NULL);
298
299     if (!contextP->env.fault_occurred) {
300         /* I think Expat enforces these facts: */
301         XMLRPC_ASSERT(xmlrpc_streq(name, contextP->currentP->_name));
302         XMLRPC_ASSERT(contextP->currentP->_parent != NULL ||
303                       contextP->currentP == contextP->rootP);
304
305         /* Add a trailing NUL to our cdata. */
306         xml_element_append_cdata(&contextP->env, contextP->currentP, "\0", 1);
307         if (!contextP->env.fault_occurred)
308             /* Pop our "stack" of elements. */
309             contextP->currentP = contextP->currentP->_parent;
310
311         if (contextP->env.fault_occurred) {
312             /* Having changed *contextP to reflect failure, we are responsible
313                for undoing everything that has been done so far in this
314                context.
315             */
316             if (contextP->rootP)
317                 xml_element_free(contextP->rootP);
318         }
319     }
320 }
321
322
323
324 static void
325 characterData(void *     const userData,
326               XML_Char * const s,
327               int        const len) {
328
329     parseContext * const contextP = userData;
330
331     XMLRPC_ASSERT(contextP != NULL);
332     XMLRPC_ASSERT(s != NULL);
333     XMLRPC_ASSERT(len >= 0);
334
335     if (!contextP->env.fault_occurred) {
336         XMLRPC_ASSERT(contextP->currentP != NULL);
337     
338         xml_element_append_cdata(&contextP->env, contextP->currentP, s, len);
339     }
340 }
341
342
343
344 static void
345 createParser(xmlrpc_env *   const envP,
346              parseContext * const contextP,
347              XML_Parser *   const parserP) {
348 /*----------------------------------------------------------------------------
349    Create an Expat parser to parse our XML.
350 -----------------------------------------------------------------------------*/
351     XML_Parser parser;
352
353     parser = xmlrpc_XML_ParserCreate(NULL);
354     if (parser == NULL)
355         xmlrpc_faultf(envP, "Could not create expat parser");
356     else {
357         /* Initialize our parse context. */
358         xmlrpc_env_init(&contextP->env);
359         contextP->rootP    = NULL;
360         contextP->currentP = NULL;
361
362         xmlrpc_XML_SetUserData(parser, contextP);
363         xmlrpc_XML_SetElementHandler(
364             parser,
365             (XML_StartElementHandler) startElement,
366             (XML_EndElementHandler) endElement);
367         xmlrpc_XML_SetCharacterDataHandler(
368             parser,
369             (XML_CharacterDataHandler) characterData);
370         
371         *parserP = parser;
372     }
373 }
374
375
376
377 static void
378 destroyParser(XML_Parser     const parser,
379               parseContext * const contextP) {
380
381     xmlrpc_env_clean(&contextP->env);
382
383     xmlrpc_XML_ParserFree(parser);
384 }
385
386
387
388 void
389 xml_parse(xmlrpc_env *   const envP,
390           const char *   const xmlData,
391           size_t         const xmlDataLen,
392           xml_element ** const resultPP) {
393 /*----------------------------------------------------------------------------
394   Parse the XML text 'xmlData', of length 'xmlDataLen'.  Return the
395   description of the element that the XML text contains as *resultPP.
396 -----------------------------------------------------------------------------*/
397     /* 
398        This is an Expat driver.
399    
400        We set up event-based parser handlers for Expat and set Expat loose
401        on the XML.  Expat walks through the XML, calling our handlers along
402        the way.  Our handlers build up the element description in our
403        'context' variable, so that when Expat is finished, our results are
404        in 'context' and we just have to pluck them out.
405
406        We should allow the user to specify the encoding in 'xmlData', but
407        we don't.
408     */
409     XML_Parser parser;
410     parseContext context;
411
412     XMLRPC_ASSERT_ENV_OK(envP);
413     XMLRPC_ASSERT(xmlData != NULL);
414
415     createParser(envP, &context, &parser);
416
417     if (!envP->fault_occurred) {
418         bool ok;
419
420         ok = xmlrpc_XML_Parse(parser, xmlData, xmlDataLen, 1);
421             /* sets 'context', *envP */
422         if (!ok) {
423             /* Expat failed on its own to parse it -- this is not an error
424                that our handlers detected.
425             */
426             xmlrpc_env_set_fault(
427                 envP, XMLRPC_PARSE_ERROR,
428                 xmlrpc_XML_ErrorString(xmlrpc_XML_GetErrorCode(parser)));
429             if (!context.env.fault_occurred) {
430                 /* Have to clean up what our handlers built before Expat
431                    barfed.
432                 */
433                 if (context.rootP)
434                     xml_element_free(context.rootP);
435             }
436         } else {
437             /* Expat got through the XML OK, but when it called our handlers,
438                they might have detected a problem.  They would have noted
439                such a problem in *contextP.
440             */
441             if (context.env.fault_occurred)
442                 xmlrpc_env_set_fault_formatted(
443                     envP, context.env.fault_code,
444                     "XML doesn't parse.  %s", context.env.fault_string);
445             else {
446                 XMLRPC_ASSERT(context.rootP != NULL);
447                 XMLRPC_ASSERT(context.currentP == NULL);
448                 
449                 *resultPP = context.rootP;
450             }
451         }
452         destroyParser(parser, &context);
453     }
454 }
455
456
457 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
458 **
459 ** Redistribution and use in source and binary forms, with or without
460 ** modification, are permitted provided that the following conditions
461 ** are met:
462 ** 1. Redistributions of source code must retain the above copyright
463 **    notice, this list of conditions and the following disclaimer.
464 ** 2. Redistributions in binary form must reproduce the above copyright
465 **    notice, this list of conditions and the following disclaimer in the
466 **    documentation and/or other materials provided with the distribution.
467 ** 3. The name of the author may not be used to endorse or promote products
468 **    derived from this software without specific prior written permission. 
469 **  
470 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
471 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
472 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
473 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
474 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
475 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
476 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
477 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
478 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
479 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
480 ** SUCH DAMAGE. */