1
0

xmlrpc_expat.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
  2. **
  3. ** Redistribution and use in source and binary forms, with or without
  4. ** modification, are permitted provided that the following conditions
  5. ** are met:
  6. ** 1. Redistributions of source code must retain the above copyright
  7. ** notice, this list of conditions and the following disclaimer.
  8. ** 2. Redistributions in binary form must reproduce the above copyright
  9. ** notice, this list of conditions and the following disclaimer in the
  10. ** documentation and/or other materials provided with the distribution.
  11. ** 3. The name of the author may not be used to endorse or promote products
  12. ** derived from this software without specific prior written permission.
  13. **
  14. ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15. ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18. ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. ** SUCH DAMAGE. */
  25. #include "xmlrpc_config.h"
  26. #include <stddef.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <expat.h>
  30. #include "xmlrpc.h"
  31. #include "xmlrpc_int.h"
  32. #include "xmlrpc_xmlparser.h"
  33. /* Define the contents of our internal structure. */
  34. struct _xml_element {
  35. struct _xml_element *_parent;
  36. char *_name;
  37. xmlrpc_mem_block _cdata; /* char */
  38. xmlrpc_mem_block _children; /* xml_element* */
  39. };
  40. /* Check that we're using expat in UTF-8 mode, not wchar_t mode.
  41. ** If you need to use expat in wchar_t mode, write a subroutine to
  42. ** copy a wchar_t string to a char string & return an error for
  43. ** any non-ASCII characters. Then call this subroutine on all
  44. ** XML_Char strings passed to our event handlers before using the
  45. ** data. */
  46. /* #if sizeof(char) != sizeof(XML_Char)
  47. ** #error expat must define XML_Char to be a regular char.
  48. ** #endif
  49. */
  50. #define XMLRPC_ASSERT_ELEM_OK(elem) \
  51. XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
  52. /*=========================================================================
  53. ** xml_element_new
  54. **=========================================================================
  55. ** Create a new xml_element. This routine isn't exported, because the
  56. ** arguments are implementation-dependent.
  57. */
  58. static xml_element *xml_element_new (xmlrpc_env *env, char *name)
  59. {
  60. xml_element *retval;
  61. int name_valid, cdata_valid, children_valid;
  62. XMLRPC_ASSERT_ENV_OK(env);
  63. XMLRPC_ASSERT(name != NULL);
  64. /* Set up our error-handling preconditions. */
  65. name_valid = cdata_valid = children_valid = 0;
  66. /* Allocate our xml_element structure. */
  67. retval = (xml_element*) malloc(sizeof(xml_element));
  68. XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
  69. "Couldn't allocate memory for XML element");
  70. /* Set our parent field to NULL. */
  71. retval->_parent = NULL;
  72. /* Copy over the element name. */
  73. retval->_name = (char*) malloc(strlen(name) + 1);
  74. XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
  75. "Couldn't allocate memory for XML element");
  76. name_valid = 1;
  77. strcpy(retval->_name, name);
  78. /* Initialize a block to hold our CDATA. */
  79. XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
  80. XMLRPC_FAIL_IF_FAULT(env);
  81. cdata_valid = 1;
  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. cleanup:
  87. if (env->fault_occurred) {
  88. if (retval) {
  89. if (name_valid)
  90. free(retval->_name);
  91. if (cdata_valid)
  92. xmlrpc_mem_block_clean(&retval->_cdata);
  93. if (children_valid)
  94. xmlrpc_mem_block_clean(&retval->_children);
  95. free(retval);
  96. }
  97. return NULL;
  98. } else {
  99. return retval;
  100. }
  101. }
  102. /*=========================================================================
  103. ** xml_element_free
  104. **=========================================================================
  105. ** Blow away an existing element & all of its child elements.
  106. */
  107. void xml_element_free (xml_element *elem)
  108. {
  109. xmlrpc_mem_block *children;
  110. int size, i;
  111. xml_element **contents;
  112. XMLRPC_ASSERT_ELEM_OK(elem);
  113. free(elem->_name);
  114. elem->_name = XMLRPC_BAD_POINTER;
  115. xmlrpc_mem_block_clean(&elem->_cdata);
  116. /* Deallocate all of our children recursively. */
  117. children = &elem->_children;
  118. contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, children);
  119. size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, children);
  120. for (i = 0; i < size; i++)
  121. xml_element_free(contents[i]);
  122. xmlrpc_mem_block_clean(&elem->_children);
  123. free(elem);
  124. }
  125. /*=========================================================================
  126. ** Miscellaneous Accessors
  127. **=========================================================================
  128. ** Return the fields of the xml_element. See the header for more
  129. ** documentation on each function works.
  130. */
  131. char *xml_element_name (xml_element *elem)
  132. {
  133. XMLRPC_ASSERT_ELEM_OK(elem);
  134. return elem->_name;
  135. }
  136. /* The result of this function is NOT VALID until the end_element handler
  137. ** has been called! */
  138. size_t xml_element_cdata_size (xml_element *elem)
  139. {
  140. XMLRPC_ASSERT_ELEM_OK(elem);
  141. return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
  142. }
  143. char *xml_element_cdata (xml_element *elem)
  144. {
  145. XMLRPC_ASSERT_ELEM_OK(elem);
  146. return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
  147. }
  148. size_t xml_element_children_size (xml_element *elem)
  149. {
  150. XMLRPC_ASSERT_ELEM_OK(elem);
  151. return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
  152. }
  153. xml_element **xml_element_children (xml_element *elem)
  154. {
  155. XMLRPC_ASSERT_ELEM_OK(elem);
  156. return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
  157. }
  158. /*=========================================================================
  159. ** Internal xml_element Utility Functions
  160. **=========================================================================
  161. */
  162. static void xml_element_append_cdata (xmlrpc_env *env,
  163. xml_element *elem,
  164. char *cdata,
  165. size_t size)
  166. {
  167. XMLRPC_ASSERT_ENV_OK(env);
  168. XMLRPC_ASSERT_ELEM_OK(elem);
  169. XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
  170. }
  171. /* Whether or not this function succeeds, it takes ownership of the 'child'
  172. ** argument.
  173. ** WARNING - This is the exact opposite of the usual memory ownership
  174. ** rules for xmlrpc_value! So please pay attention. */
  175. static void xml_element_append_child (xmlrpc_env *env,
  176. xml_element *elem,
  177. xml_element *child)
  178. {
  179. XMLRPC_ASSERT_ENV_OK(env);
  180. XMLRPC_ASSERT_ELEM_OK(elem);
  181. XMLRPC_ASSERT_ELEM_OK(child);
  182. XMLRPC_ASSERT(child->_parent == NULL);
  183. XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
  184. &child, 1);
  185. if (!env->fault_occurred)
  186. child->_parent = elem;
  187. else
  188. xml_element_free(child);
  189. }
  190. /*=========================================================================
  191. ** Our parse context. We pass this around as expat user data.
  192. **=========================================================================
  193. */
  194. typedef struct {
  195. xmlrpc_env *env;
  196. xml_element *root;
  197. xml_element *current;
  198. } parse_context;
  199. /*=========================================================================
  200. ** Expat Event Handler Functions
  201. **=========================================================================
  202. */
  203. static void
  204. start_element (void *user_data, XML_Char *name, XML_Char **atts ATTR_UNUSED)
  205. {
  206. parse_context *context;
  207. xml_element *elem, *new_current;
  208. XMLRPC_ASSERT(user_data != NULL && name != NULL);
  209. /* Get our context and see if an error has already occured. */
  210. context = (parse_context*) user_data;
  211. if (!context->env->fault_occurred) {
  212. /* Build a new element. */
  213. elem = xml_element_new(context->env, name);
  214. XMLRPC_FAIL_IF_FAULT(context->env);
  215. /* Insert it in the appropriate place. */
  216. if (!context->root) {
  217. context->root = elem;
  218. context->current = elem;
  219. elem = NULL;
  220. } else {
  221. XMLRPC_ASSERT(context->current != NULL);
  222. /* (We need to watch our error handling invariants very carefully
  223. ** here. Read the docs for xml_element_append_child. */
  224. new_current = elem;
  225. xml_element_append_child(context->env, context->current, elem);
  226. elem = NULL;
  227. XMLRPC_FAIL_IF_FAULT(context->env);
  228. context->current = new_current;
  229. }
  230. cleanup:
  231. if (elem)
  232. xml_element_free(elem);
  233. }
  234. }
  235. static void end_element (void *user_data, XML_Char *name)
  236. {
  237. parse_context *context;
  238. XMLRPC_ASSERT(user_data != NULL && name != NULL);
  239. /* Get our context and see if an error has already occured. */
  240. context = (parse_context*) user_data;
  241. if (!context->env->fault_occurred) {
  242. /* XXX - I think expat enforces these facts, but I want to be sure.
  243. ** If one of these assertion ever fails, it should be replaced by a
  244. ** non-assertion runtime error check. */
  245. XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
  246. XMLRPC_ASSERT(context->current->_parent != NULL ||
  247. context->current == context->root);
  248. /* Add a trailing '\0' to our cdata. */
  249. xml_element_append_cdata(context->env, context->current, "\0", 1);
  250. XMLRPC_FAIL_IF_FAULT(context->env);
  251. /* Pop our "stack" of elements. */
  252. context->current = context->current->_parent;
  253. cleanup:
  254. return;
  255. }
  256. }
  257. static void character_data (void *user_data, XML_Char *s, int len)
  258. {
  259. parse_context *context;
  260. XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
  261. /* Get our context and see if an error has already occured. */
  262. context = (parse_context*) user_data;
  263. if (!context->env->fault_occurred) {
  264. XMLRPC_ASSERT(context->current != NULL);
  265. xml_element_append_cdata(context->env, context->current, s, len);
  266. XMLRPC_FAIL_IF_FAULT(context->env);
  267. cleanup:
  268. return;
  269. }
  270. }
  271. /*=========================================================================
  272. ** Expat Driver
  273. **=========================================================================
  274. ** XXX - We should allow the user to specify the encoding of our xml_data.
  275. */
  276. xml_element *xml_parse (xmlrpc_env *env, const char *xml_data, int xml_len)
  277. {
  278. parse_context context;
  279. XML_Parser parser;
  280. int ok;
  281. XMLRPC_ASSERT_ENV_OK(env);
  282. XMLRPC_ASSERT(xml_data != NULL && xml_len >= 0);
  283. /* Set up our error-handling preconditions. */
  284. context.root = NULL;
  285. /* Set up the rest of our parse context. */
  286. context.env = env;
  287. context.current = NULL;
  288. /* Set up our XML parser. */
  289. parser = XML_ParserCreate(NULL);
  290. XMLRPC_FAIL_IF_NULL(parser, env, XMLRPC_INTERNAL_ERROR,
  291. "Could not create expat parser");
  292. XML_SetUserData(parser, &context);
  293. XML_SetElementHandler(parser,
  294. (XML_StartElementHandler) start_element,
  295. (XML_EndElementHandler) end_element);
  296. XML_SetCharacterDataHandler(parser,
  297. (XML_CharacterDataHandler) character_data);
  298. /* Parse our data. */
  299. ok = XML_Parse(parser, xml_data, xml_len, 1);
  300. if (!ok)
  301. XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
  302. (char*) XML_ErrorString(XML_GetErrorCode(parser)));
  303. XMLRPC_FAIL_IF_FAULT(env);
  304. /* Perform some sanity checks. */
  305. XMLRPC_ASSERT(context.root != NULL);
  306. XMLRPC_ASSERT(context.current == NULL);
  307. cleanup:
  308. if (parser)
  309. XML_ParserFree(parser);
  310. if (env->fault_occurred) {
  311. if (context.root)
  312. xml_element_free(context.root);
  313. return NULL;
  314. } else {
  315. return context.root;
  316. }
  317. }