xmlrpc_libxml2.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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. #include "xmlrpc_config.h"
  28. #include <stddef.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <libxml/parser.h>
  32. #include "xmlrpc.h"
  33. #include "xmlrpc_xmlparser.h"
  34. /* Define the contents of our internal structure. */
  35. struct _xml_element {
  36. struct _xml_element *_parent;
  37. char *_name;
  38. xmlrpc_mem_block _cdata; /* char */
  39. xmlrpc_mem_block _children; /* xml_element* */
  40. };
  41. #define XMLRPC_ASSERT_ELEM_OK(elem) \
  42. XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
  43. /*=========================================================================
  44. ** xml_element_new
  45. **=========================================================================
  46. ** Create a new xml_element. This routine isn't exported, because the
  47. ** arguments are implementation-dependent.
  48. */
  49. static xml_element *xml_element_new (xmlrpc_env *env, char *name)
  50. {
  51. xml_element *retval;
  52. int name_valid, cdata_valid, children_valid;
  53. XMLRPC_ASSERT_ENV_OK(env);
  54. XMLRPC_ASSERT(name != NULL);
  55. /* Set up our error-handling preconditions. */
  56. retval = NULL;
  57. name_valid = cdata_valid = children_valid = 0;
  58. /* Allocate our xml_element structure. */
  59. retval = (xml_element*) malloc(sizeof(xml_element));
  60. XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
  61. "Couldn't allocate memory for XML element");
  62. /* Set our parent field to NULL. */
  63. retval->_parent = NULL;
  64. /* Copy over the element name. */
  65. retval->_name = (char*) malloc(strlen(name) + 1);
  66. XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
  67. "Couldn't allocate memory for XML element");
  68. name_valid = 1;
  69. strcpy(retval->_name, name);
  70. /* Initialize a block to hold our CDATA. */
  71. XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
  72. XMLRPC_FAIL_IF_FAULT(env);
  73. cdata_valid = 1;
  74. /* Initialize a block to hold our child elements. */
  75. XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
  76. XMLRPC_FAIL_IF_FAULT(env);
  77. children_valid = 1;
  78. cleanup:
  79. if (env->fault_occurred) {
  80. if (retval) {
  81. if (name_valid)
  82. free(retval->_name);
  83. if (cdata_valid)
  84. xmlrpc_mem_block_clean(&retval->_cdata);
  85. if (children_valid)
  86. xmlrpc_mem_block_clean(&retval->_children);
  87. free(retval);
  88. }
  89. return NULL;
  90. } else {
  91. return retval;
  92. }
  93. }
  94. /*=========================================================================
  95. ** xml_element_free
  96. **=========================================================================
  97. ** Blow away an existing element & all of its child elements.
  98. */
  99. void xml_element_free (xml_element *elem)
  100. {
  101. xmlrpc_mem_block *children;
  102. int size, i;
  103. xml_element **contents;
  104. XMLRPC_ASSERT_ELEM_OK(elem);
  105. free(elem->_name);
  106. elem->_name = XMLRPC_BAD_POINTER;
  107. xmlrpc_mem_block_clean(&elem->_cdata);
  108. /* Deallocate all of our children recursively. */
  109. children = &elem->_children;
  110. contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, children);
  111. size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, children);
  112. for (i = 0; i < size; i++)
  113. xml_element_free(contents[i]);
  114. xmlrpc_mem_block_clean(&elem->_children);
  115. free(elem);
  116. }
  117. /*=========================================================================
  118. ** Miscellaneous Accessors
  119. **=========================================================================
  120. ** Return the fields of the xml_element. See the header for more
  121. ** documentation on each function works.
  122. */
  123. char *xml_element_name (xml_element *elem)
  124. {
  125. XMLRPC_ASSERT_ELEM_OK(elem);
  126. return elem->_name;
  127. }
  128. /* The result of this function is NOT VALID until the end_element handler
  129. ** has been called! */
  130. size_t xml_element_cdata_size (xml_element *elem)
  131. {
  132. XMLRPC_ASSERT_ELEM_OK(elem);
  133. return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
  134. }
  135. char *xml_element_cdata (xml_element *elem)
  136. {
  137. XMLRPC_ASSERT_ELEM_OK(elem);
  138. return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
  139. }
  140. size_t xml_element_children_size (xml_element *elem)
  141. {
  142. XMLRPC_ASSERT_ELEM_OK(elem);
  143. return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
  144. }
  145. xml_element **xml_element_children (xml_element *elem)
  146. {
  147. XMLRPC_ASSERT_ELEM_OK(elem);
  148. return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
  149. }
  150. /*=========================================================================
  151. ** Internal xml_element Utility Functions
  152. **=========================================================================
  153. */
  154. static void xml_element_append_cdata (xmlrpc_env *env,
  155. xml_element *elem,
  156. char *cdata,
  157. size_t size)
  158. {
  159. XMLRPC_ASSERT_ENV_OK(env);
  160. XMLRPC_ASSERT_ELEM_OK(elem);
  161. XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
  162. }
  163. /* Whether or not this function succeeds, it takes ownership of the 'child'
  164. ** argument.
  165. ** WARNING - This is the exact opposite of the usual memory ownership
  166. ** rules for xmlrpc_value! So please pay attention. */
  167. static void xml_element_append_child (xmlrpc_env *env,
  168. xml_element *elem,
  169. xml_element *child)
  170. {
  171. XMLRPC_ASSERT_ENV_OK(env);
  172. XMLRPC_ASSERT_ELEM_OK(elem);
  173. XMLRPC_ASSERT_ELEM_OK(child);
  174. XMLRPC_ASSERT(child->_parent == NULL);
  175. XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
  176. &child, 1);
  177. if (!env->fault_occurred)
  178. child->_parent = elem;
  179. else
  180. xml_element_free(child);
  181. }
  182. /*=========================================================================
  183. ** Our parse context. We pass this around as libxml user data.
  184. **=========================================================================
  185. */
  186. typedef struct {
  187. xmlrpc_env *env;
  188. xml_element *root;
  189. xml_element *current;
  190. } parse_context;
  191. /*=========================================================================
  192. ** LibXML Event Handler Functions
  193. **=========================================================================
  194. */
  195. static void
  196. start_element (void *user_data, const xmlChar *name, const xmlChar **attrs)
  197. {
  198. parse_context *context;
  199. xml_element *elem, *new_current;
  200. XMLRPC_ASSERT(user_data != NULL && name != NULL);
  201. /* Get our context and see if an error has already occured. */
  202. context = (parse_context*) user_data;
  203. if (!context->env->fault_occurred) {
  204. /* Set up our error-handling preconditions. */
  205. elem = NULL;
  206. /* Build a new element. */
  207. elem = xml_element_new(context->env, (char *) name);
  208. XMLRPC_FAIL_IF_FAULT(context->env);
  209. /* Insert it in the appropriate place. */
  210. if (!context->root) {
  211. context->root = elem;
  212. context->current = elem;
  213. elem = NULL;
  214. } else {
  215. XMLRPC_ASSERT(context->current != NULL);
  216. /* (We need to watch our error handling invariants very carefully
  217. ** here. Read the docs for xml_element_append_child. */
  218. new_current = elem;
  219. xml_element_append_child(context->env, context->current, elem);
  220. elem = NULL;
  221. XMLRPC_FAIL_IF_FAULT(context->env);
  222. context->current = new_current;
  223. }
  224. cleanup:
  225. if (elem)
  226. xml_element_free(elem);
  227. }
  228. }
  229. static void
  230. end_element (void *user_data, const xmlChar *name)
  231. {
  232. parse_context *context;
  233. XMLRPC_ASSERT(user_data != NULL && name != NULL);
  234. /* Get our context and see if an error has already occured. */
  235. context = (parse_context*) user_data;
  236. if (!context->env->fault_occurred) {
  237. /* XXX - I think expat enforces these facts, but I want to be sure.
  238. ** If one of these assertion ever fails, it should be replaced by a
  239. ** non-assertion runtime error check. */
  240. XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
  241. XMLRPC_ASSERT(context->current->_parent != NULL ||
  242. context->current == context->root);
  243. /* Add a trailing '\0' to our cdata. */
  244. xml_element_append_cdata(context->env, context->current, "\0", 1);
  245. XMLRPC_FAIL_IF_FAULT(context->env);
  246. /* Pop our "stack" of elements. */
  247. context->current = context->current->_parent;
  248. cleanup:
  249. return;
  250. }
  251. }
  252. static void character_data (void *user_data, const xmlChar *s, int len)
  253. {
  254. parse_context *context;
  255. XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
  256. /* Get our context and see if an error has already occured. */
  257. context = (parse_context*) user_data;
  258. if (!context->env->fault_occurred) {
  259. XMLRPC_ASSERT(context->current != NULL);
  260. xml_element_append_cdata(context->env, context->current, (char *) s, len);
  261. XMLRPC_FAIL_IF_FAULT(context->env);
  262. cleanup:
  263. return;
  264. }
  265. }
  266. /*=========================================================================
  267. ** LibXML Driver
  268. **=========================================================================
  269. ** XXX - We should allow the user to specify the encoding of our xml_data.
  270. */
  271. static xmlSAXHandler sax_handler = {
  272. NULL, /* internalSubset */
  273. NULL, /* isStandalone */
  274. NULL, /* hasInternalSubset */
  275. NULL, /* hasExternalSubset */
  276. NULL, /* resolveEntity */
  277. NULL, /* getEntity */
  278. NULL, /* entityDecl */
  279. NULL, /* notationDecl */
  280. NULL, /* attributeDecl */
  281. NULL, /* elementDecl */
  282. NULL, /* unparsedEntityDecl */
  283. NULL, /* setDocumentLocator */
  284. NULL, /* startDocument */
  285. NULL, /* endDocument */
  286. start_element, /* startElement */
  287. end_element, /* endElement */
  288. NULL, /* reference */
  289. character_data, /* characters */
  290. NULL, /* ignorableWhitespace */
  291. NULL, /* processingInstruction */
  292. NULL, /* comment */
  293. NULL, /* warning */
  294. NULL, /* error */
  295. NULL, /* fatalError */
  296. };
  297. xml_element *xml_parse (xmlrpc_env *env, char *xml_data, int xml_len)
  298. {
  299. parse_context context;
  300. xmlParserCtxt *parser;
  301. int err;
  302. XMLRPC_ASSERT_ENV_OK(env);
  303. XMLRPC_ASSERT(xml_data != NULL && xml_len >= 0);
  304. /* Set up our error-handling preconditions. */
  305. parser = NULL;
  306. context.root = NULL;
  307. /* Set up the rest of our parse context. */
  308. context.env = env;
  309. context.current = NULL;
  310. /* Set up our XML parser. */
  311. parser = xmlCreatePushParserCtxt(&sax_handler, &context, NULL, 0, NULL);
  312. XMLRPC_FAIL_IF_NULL(parser, env, XMLRPC_INTERNAL_ERROR,
  313. "Could not create expat parser");
  314. /* Parse our data. */
  315. err = xmlParseChunk(parser, xml_data, xml_len, 1);
  316. if (err)
  317. XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR, "XML parsing failed");
  318. XMLRPC_FAIL_IF_FAULT(env);
  319. /* Perform some sanity checks. */
  320. XMLRPC_ASSERT(context.root != NULL);
  321. XMLRPC_ASSERT(context.current == NULL);
  322. cleanup:
  323. if (parser)
  324. xmlFreeParserCtxt(parser);
  325. if (env->fault_occurred) {
  326. if (context.root)
  327. xml_element_free(context.root);
  328. return NULL;
  329. } else {
  330. return context.root;
  331. }
  332. }