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.
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions
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.
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
28 #include "xmlrpc_config.h"
34 #include <xmlparser.h>
36 #include <libxml/parser.h>
39 #include "xmlrpc-c/base.h"
40 #include "xmlrpc-c/base_int.h"
41 #include "xmlrpc-c/xmlparser.h"
43 /* Define the contents of our internal structure. */
45 struct _xml_element *_parent;
47 xmlrpc_mem_block _cdata; /* char */
48 xmlrpc_mem_block _children; /* xml_element* */
51 #define XMLRPC_ASSERT_ELEM_OK(elem) \
52 XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
55 /*=========================================================================
57 **=========================================================================
58 ** Create a new xml_element. This routine isn't exported, because the
59 ** arguments are implementation-dependent.
62 static xml_element *xml_element_new (xmlrpc_env *env, char *name)
65 int name_valid, cdata_valid, children_valid;
67 XMLRPC_ASSERT_ENV_OK(env);
68 XMLRPC_ASSERT(name != NULL);
70 /* Set up our error-handling preconditions. */
72 name_valid = cdata_valid = children_valid = 0;
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");
79 /* Set our parent field to NULL. */
80 retval->_parent = NULL;
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");
87 strcpy(retval->_name, name);
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);
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);
100 if (env->fault_occurred) {
105 xmlrpc_mem_block_clean(&retval->_cdata);
107 xmlrpc_mem_block_clean(&retval->_children);
117 /*=========================================================================
119 **=========================================================================
120 ** Blow away an existing element & all of its child elements.
123 void xml_element_free (xml_element *elem)
125 xmlrpc_mem_block *children;
127 xml_element **contents;
129 XMLRPC_ASSERT_ELEM_OK(elem);
132 elem->_name = XMLRPC_BAD_POINTER;
133 xmlrpc_mem_block_clean(&elem->_cdata);
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]);
142 xmlrpc_mem_block_clean(&elem->_children);
147 /*=========================================================================
148 ** Miscellaneous Accessors
149 **=========================================================================
150 ** Return the fields of the xml_element. See the header for more
151 ** documentation on each function works.
154 const char *xml_element_name (const xml_element * const elem)
156 XMLRPC_ASSERT_ELEM_OK(elem);
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)
164 XMLRPC_ASSERT_ELEM_OK(elem);
165 return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
168 char *xml_element_cdata (xml_element *elem)
170 XMLRPC_ASSERT_ELEM_OK(elem);
171 return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
174 size_t xml_element_children_size (const xml_element *const elem)
176 XMLRPC_ASSERT_ELEM_OK(elem);
177 return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
180 xml_element **xml_element_children (const xml_element *const elem)
182 XMLRPC_ASSERT_ELEM_OK(elem);
183 return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
187 /*=========================================================================
188 ** Internal xml_element Utility Functions
189 **=========================================================================
192 static void xml_element_append_cdata (xmlrpc_env *env,
197 XMLRPC_ASSERT_ENV_OK(env);
198 XMLRPC_ASSERT_ELEM_OK(elem);
200 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
203 /* Whether or not this function succeeds, it takes ownership of the 'child'
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,
211 XMLRPC_ASSERT_ENV_OK(env);
212 XMLRPC_ASSERT_ELEM_OK(elem);
213 XMLRPC_ASSERT_ELEM_OK(child);
214 XMLRPC_ASSERT(child->_parent == NULL);
216 XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
218 if (!env->fault_occurred)
219 child->_parent = elem;
221 xml_element_free(child);
225 /*=========================================================================
226 ** Our parse context. We pass this around as libxml user data.
227 **=========================================================================
233 xml_element *current;
237 /*=========================================================================
238 ** LibXML Event Handler Functions
239 **=========================================================================
243 start_element (void *user_data, const xmlChar *name, const xmlChar **attrs)
245 parse_context *context;
246 xml_element *elem, *new_current;
248 XMLRPC_ASSERT(user_data != NULL && name != NULL);
250 /* Get our context and see if an error has already occured. */
251 context = (parse_context*) user_data;
252 if (!context->env->fault_occurred) {
254 /* Set up our error-handling preconditions. */
257 /* Build a new element. */
258 elem = xml_element_new(context->env, (char *) name);
259 XMLRPC_FAIL_IF_FAULT(context->env);
261 /* Insert it in the appropriate place. */
262 if (!context->root) {
263 context->root = elem;
264 context->current = elem;
267 XMLRPC_ASSERT(context->current != NULL);
269 /* (We need to watch our error handling invariants very carefully
270 ** here. Read the docs for xml_element_append_child. */
272 xml_element_append_child(context->env, context->current, elem);
274 XMLRPC_FAIL_IF_FAULT(context->env);
275 context->current = new_current;
280 xml_element_free(elem);
285 end_element (void *user_data, const xmlChar *name)
287 parse_context *context;
289 XMLRPC_ASSERT(user_data != NULL && name != NULL);
291 /* Get our context and see if an error has already occured. */
292 context = (parse_context*) user_data;
293 if (!context->env->fault_occurred) {
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);
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);
306 /* Pop our "stack" of elements. */
307 context->current = context->current->_parent;
314 static void character_data (void *user_data, const xmlChar *s, int len)
316 parse_context *context;
318 XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
320 /* Get our context and see if an error has already occured. */
321 context = (parse_context*) user_data;
322 if (!context->env->fault_occurred) {
324 XMLRPC_ASSERT(context->current != NULL);
326 xml_element_append_cdata(context->env, context->current, (char *) s, len);
327 XMLRPC_FAIL_IF_FAULT(context->env);
335 /*=========================================================================
337 **=========================================================================
338 ** XXX - We should allow the user to specify the encoding of our xml_data.
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 */
365 NULL, /* fatalError */
366 NULL, /* getParameterEntity */
367 NULL, /* cdataBlock */
368 NULL, /* externalSubset */
371 /* Following are SAX2 fields. Any ifdef here? */
373 ,NULL, /* _private */
374 NULL, /* startElementNs */
375 NULL, /* endElementNs */
382 xml_parse(xmlrpc_env * const envP,
383 const char * const xmlData,
384 size_t const xmlDataLen,
385 xml_element ** const resultPP) {
387 parse_context context;
388 xmlParserCtxt *parser;
391 XMLRPC_ASSERT_ENV_OK(envP);
392 XMLRPC_ASSERT(xmlData != NULL && xmlDataLen >= 0);
394 /* Set up our error-handling preconditions. */
398 /* Set up the rest of our parse context. */
400 context.current = NULL;
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");
407 /* Parse our data. */
408 err = xmlParseChunk(parser, xmlData, xmlDataLen, 1);
410 XMLRPC_FAIL(envP, XMLRPC_PARSE_ERROR, "XML parsing failed");
411 XMLRPC_FAIL_IF_FAULT(envP);
413 /* Perform some sanity checks. */
414 XMLRPC_ASSERT(context.root != NULL);
415 XMLRPC_ASSERT(context.current == NULL);
417 *resultPP = context.root;
421 xmlFreeParserCtxt(parser);
423 if (envP->fault_occurred) {
425 xml_element_free(context.root);