initial load of upstream version 1.06.32
[xmlrpc-c] / src / xmlrpc_libxml2.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
3 ** Copyright (C) 2002 Ximian, Inc.
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions
7 ** are met:
8 ** 1. Redistributions of source code must retain the above copyright
9 **    notice, this list of conditions and the following disclaimer.
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 **    notice, this list of conditions and the following disclaimer in the
12 **    documentation and/or other materials provided with the distribution.
13 ** 3. The name of the author may not be used to endorse or promote products
14 **    derived from this software without specific prior written permission. 
15 **  
16 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 ** SUCH DAMAGE. */
27
28 #include "xmlrpc_config.h"
29
30 #include <stddef.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #ifdef WIN32
34 #include <xmlparser.h>
35 #else
36 #include <libxml/parser.h>
37 #endif
38
39 #include "xmlrpc-c/base.h"
40 #include "xmlrpc-c/base_int.h"
41 #include "xmlrpc-c/xmlparser.h"
42
43 /* Define the contents of our internal structure. */
44 struct _xml_element {
45     struct _xml_element *_parent;
46     char *_name;
47     xmlrpc_mem_block _cdata;    /* char */
48     xmlrpc_mem_block _children; /* xml_element* */
49 };
50
51 #define XMLRPC_ASSERT_ELEM_OK(elem) \
52     XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
53
54
55 /*=========================================================================
56 **  xml_element_new
57 **=========================================================================
58 **  Create a new xml_element. This routine isn't exported, because the
59 **  arguments are implementation-dependent.
60 */
61
62 static xml_element *xml_element_new (xmlrpc_env *env, char *name)
63 {
64     xml_element *retval;
65     int name_valid, cdata_valid, children_valid;
66
67     XMLRPC_ASSERT_ENV_OK(env);
68     XMLRPC_ASSERT(name != NULL);
69
70     /* Set up our error-handling preconditions. */
71     retval = NULL;
72     name_valid = cdata_valid = children_valid = 0;
73
74     /* Allocate our xml_element structure. */
75     retval = (xml_element*) malloc(sizeof(xml_element));
76     XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
77                         "Couldn't allocate memory for XML element");
78
79     /* Set our parent field to NULL. */
80     retval->_parent = NULL;
81     
82     /* Copy over the element name. */
83     retval->_name = (char*) malloc(strlen(name) + 1);
84     XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
85                         "Couldn't allocate memory for XML element");
86     name_valid = 1;
87     strcpy(retval->_name, name);
88
89     /* Initialize a block to hold our CDATA. */
90     XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
91     XMLRPC_FAIL_IF_FAULT(env);
92     cdata_valid = 1;
93
94     /* Initialize a block to hold our child elements. */
95     XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
96     XMLRPC_FAIL_IF_FAULT(env);
97     children_valid = 1;
98
99  cleanup:
100     if (env->fault_occurred) {
101         if (retval) {
102             if (name_valid)
103                 free(retval->_name);
104             if (cdata_valid)
105                 xmlrpc_mem_block_clean(&retval->_cdata);
106             if (children_valid)
107                 xmlrpc_mem_block_clean(&retval->_children);
108             free(retval);
109         }
110         return NULL;
111     } else {
112         return retval;
113     }
114 }
115
116
117 /*=========================================================================
118 **  xml_element_free
119 **=========================================================================
120 **  Blow away an existing element & all of its child elements.
121 */
122
123 void xml_element_free (xml_element *elem)
124 {
125     xmlrpc_mem_block *children;
126     int size, i;
127     xml_element **contents;
128
129     XMLRPC_ASSERT_ELEM_OK(elem);
130
131     free(elem->_name);
132     elem->_name = XMLRPC_BAD_POINTER;
133     xmlrpc_mem_block_clean(&elem->_cdata);
134
135     /* Deallocate all of our children recursively. */
136     children = &elem->_children;
137     contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, children);
138     size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, children);
139     for (i = 0; i < size; i++)
140         xml_element_free(contents[i]);
141
142     xmlrpc_mem_block_clean(&elem->_children);
143     free(elem);
144 }
145
146
147 /*=========================================================================
148 **  Miscellaneous Accessors
149 **=========================================================================
150 **  Return the fields of the xml_element. See the header for more
151 **  documentation on each function works.
152 */
153
154 const char *xml_element_name (const xml_element * const elem)
155 {
156     XMLRPC_ASSERT_ELEM_OK(elem);
157     return elem->_name;
158 }
159
160 /* The result of this function is NOT VALID until the end_element handler
161 ** has been called! */
162 size_t xml_element_cdata_size (xml_element *elem)
163 {
164     XMLRPC_ASSERT_ELEM_OK(elem);
165     return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
166 }
167
168 char *xml_element_cdata (xml_element *elem)
169 {
170     XMLRPC_ASSERT_ELEM_OK(elem);
171     return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
172 }
173
174 size_t xml_element_children_size (const xml_element *const elem)
175 {
176     XMLRPC_ASSERT_ELEM_OK(elem);
177     return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
178 }
179
180 xml_element **xml_element_children (const xml_element *const elem)
181 {
182     XMLRPC_ASSERT_ELEM_OK(elem);
183     return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
184 }
185
186
187 /*=========================================================================
188 **  Internal xml_element Utility Functions
189 **=========================================================================
190 */
191
192 static void xml_element_append_cdata (xmlrpc_env *env,
193                                       xml_element *elem,
194                                       char *cdata,
195                                       size_t size)
196 {
197     XMLRPC_ASSERT_ENV_OK(env);
198     XMLRPC_ASSERT_ELEM_OK(elem);    
199
200     XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
201 }
202
203 /* Whether or not this function succeeds, it takes ownership of the 'child'
204 ** argument.
205 ** WARNING - This is the exact opposite of the usual memory ownership
206 ** rules for xmlrpc_value! So please pay attention. */
207 static void xml_element_append_child (xmlrpc_env *env,
208                                       xml_element *elem,
209                                       xml_element *child)
210 {
211     XMLRPC_ASSERT_ENV_OK(env);
212     XMLRPC_ASSERT_ELEM_OK(elem);
213     XMLRPC_ASSERT_ELEM_OK(child);
214     XMLRPC_ASSERT(child->_parent == NULL);
215
216     XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
217                                   &child, 1);
218     if (!env->fault_occurred)
219         child->_parent = elem;
220     else
221         xml_element_free(child);
222 }
223
224
225 /*=========================================================================
226 **  Our parse context. We pass this around as libxml user data.
227 **=========================================================================
228 */
229
230 typedef struct {
231     xmlrpc_env *env;
232     xml_element *root;
233     xml_element *current;
234 } parse_context;
235
236
237 /*=========================================================================
238 **  LibXML Event Handler Functions
239 **=========================================================================
240 */
241
242 static void
243 start_element (void *user_data, const xmlChar *name, const xmlChar **attrs)
244 {
245     parse_context *context;
246     xml_element *elem, *new_current;
247
248     XMLRPC_ASSERT(user_data != NULL && name != NULL);
249
250     /* Get our context and see if an error has already occured. */
251     context = (parse_context*) user_data;
252     if (!context->env->fault_occurred) {
253
254         /* Set up our error-handling preconditions. */
255         elem = NULL;
256
257         /* Build a new element. */
258         elem = xml_element_new(context->env, (char *) name);
259         XMLRPC_FAIL_IF_FAULT(context->env);
260
261         /* Insert it in the appropriate place. */
262         if (!context->root) {
263             context->root = elem;
264             context->current = elem;
265             elem = NULL;
266         } else {
267             XMLRPC_ASSERT(context->current != NULL);
268
269             /* (We need to watch our error handling invariants very carefully
270             ** here. Read the docs for xml_element_append_child. */
271             new_current = elem;
272             xml_element_append_child(context->env, context->current, elem);
273             elem = NULL;
274             XMLRPC_FAIL_IF_FAULT(context->env);
275             context->current = new_current;
276         }
277
278  cleanup:
279         if (elem)
280             xml_element_free(elem);
281     }
282 }
283
284 static void
285 end_element (void *user_data, const xmlChar *name)
286 {
287     parse_context *context;
288
289     XMLRPC_ASSERT(user_data != NULL && name != NULL);
290
291     /* Get our context and see if an error has already occured. */
292     context = (parse_context*) user_data;
293     if (!context->env->fault_occurred) {
294
295         /* XXX - I think expat enforces these facts, but I want to be sure.
296         ** If one of these assertion ever fails, it should be replaced by a
297         ** non-assertion runtime error check. */
298         XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
299         XMLRPC_ASSERT(context->current->_parent != NULL ||
300                       context->current == context->root);
301
302         /* Add a trailing '\0' to our cdata. */
303         xml_element_append_cdata(context->env, context->current, "\0", 1);
304         XMLRPC_FAIL_IF_FAULT(context->env);     
305
306         /* Pop our "stack" of elements. */
307         context->current = context->current->_parent;
308
309  cleanup:
310         return;
311     }
312 }
313
314 static void character_data (void *user_data, const xmlChar *s, int len)
315 {
316     parse_context *context;
317
318     XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
319
320     /* Get our context and see if an error has already occured. */
321     context = (parse_context*) user_data;
322     if (!context->env->fault_occurred) {
323
324         XMLRPC_ASSERT(context->current != NULL);
325         
326         xml_element_append_cdata(context->env, context->current, (char *) s, len);
327         XMLRPC_FAIL_IF_FAULT(context->env);
328
329  cleanup:
330         return;
331     }
332 }
333
334
335 /*=========================================================================
336 **  LibXML Driver
337 **=========================================================================
338 **  XXX - We should allow the user to specify the encoding of our xml_data.
339 */
340
341 static xmlSAXHandler sax_handler = {
342     NULL,      /* internalSubset */
343     NULL,      /* isStandalone */
344     NULL,      /* hasInternalSubset */
345     NULL,      /* hasExternalSubset */
346     NULL,      /* resolveEntity */
347     NULL,      /* getEntity */
348     NULL,      /* entityDecl */
349     NULL,      /* notationDecl */
350     NULL,      /* attributeDecl */
351     NULL,      /* elementDecl */
352     NULL,      /* unparsedEntityDecl */
353     NULL,      /* setDocumentLocator */
354     NULL,      /* startDocument */
355     NULL,      /* endDocument */
356     start_element,       /* startElement */
357     end_element,         /* endElement */
358     NULL,      /* reference */
359     character_data,      /* characters */
360     NULL,      /* ignorableWhitespace */
361     NULL,      /* processingInstruction */
362     NULL,      /* comment */
363     NULL,      /* warning */
364     NULL,      /* error */
365     NULL,      /* fatalError */
366     NULL,      /* getParameterEntity */
367     NULL,      /* cdataBlock */
368     NULL,      /* externalSubset */
369     1          /* initialized */
370     
371     /* Following are SAX2 fields. Any ifdef here? */ 
372
373     ,NULL,     /* _private */
374     NULL,      /* startElementNs */
375     NULL,      /* endElementNs */
376     NULL       /* serror */
377 };
378
379
380
381 void
382 xml_parse(xmlrpc_env *   const envP,
383           const char *   const xmlData,
384           size_t         const xmlDataLen,
385           xml_element ** const resultPP) {
386
387     parse_context context;
388     xmlParserCtxt *parser;
389     int err;
390
391     XMLRPC_ASSERT_ENV_OK(envP);
392     XMLRPC_ASSERT(xmlData != NULL && xmlDataLen >= 0);
393
394     /* Set up our error-handling preconditions. */
395     parser = NULL;
396     context.root = NULL;
397     
398     /* Set up the rest of our parse context. */
399     context.env     = envP;
400     context.current = NULL;
401
402     /* Set up our XML parser. */
403     parser = xmlCreatePushParserCtxt(&sax_handler, &context, NULL, 0, NULL);
404     XMLRPC_FAIL_IF_NULL(parser, envP, XMLRPC_INTERNAL_ERROR,
405                         "Could not create expat parser");
406
407     /* Parse our data. */
408     err = xmlParseChunk(parser, xmlData, xmlDataLen, 1);
409     if (err)
410         XMLRPC_FAIL(envP, XMLRPC_PARSE_ERROR, "XML parsing failed");
411     XMLRPC_FAIL_IF_FAULT(envP);
412
413     /* Perform some sanity checks. */
414     XMLRPC_ASSERT(context.root != NULL);
415     XMLRPC_ASSERT(context.current == NULL);
416
417     *resultPP = context.root;
418
419  cleanup:
420     if (parser)
421         xmlFreeParserCtxt(parser);
422
423     if (envP->fault_occurred) {
424         if (context.root)
425             xml_element_free(context.root);
426     }
427 }