xmlrpc_serialize.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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 <stdarg.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include "xmlrpc.h"
  31. #include "xmlrpc_int.h"
  32. #define CRLF "\015\012"
  33. #define SMALL_BUFFER_SZ (128)
  34. #define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF
  35. /*=========================================================================
  36. ** format_out
  37. **=========================================================================
  38. ** A lightweight print routine for use with various serialization
  39. ** functions. Only use this routine for printing small objects--it uses
  40. ** a fixed-size internal buffer and returns an error on overflow.
  41. ** In particular, do NOT use this routine to print XML-RPC string values!
  42. */
  43. static void
  44. format_out(xmlrpc_env *env,
  45. xmlrpc_mem_block *output,
  46. char *format_string,
  47. ...) {
  48. va_list args;
  49. char buffer[SMALL_BUFFER_SZ];
  50. int count;
  51. XMLRPC_ASSERT_ENV_OK(env);
  52. va_start(args, format_string);
  53. /* We assume that this function is present and works correctly. Right. */
  54. count = vsnprintf(buffer, SMALL_BUFFER_SZ, format_string, args);
  55. /* Old C libraries return -1 if vsnprintf overflows its buffer.
  56. ** New C libraries return the number of characters which *would* have
  57. ** been printed if the error did not occur. This is impressively vile.
  58. ** Thank the C99 committee for this bright idea. But wait! We also
  59. ** need to keep track of the trailing NULL. */
  60. if (count < 0 || count >= (SMALL_BUFFER_SZ - 1))
  61. XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
  62. "format_out overflowed internal buffer");
  63. /* Append our new data to our output. */
  64. XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, buffer, count);
  65. XMLRPC_FAIL_IF_FAULT(env);
  66. cleanup:
  67. va_end(args);
  68. }
  69. /*=========================================================================
  70. ** Warnings About Invalid UTF-8
  71. **=========================================================================
  72. ** We claim to send UTF-8 data to the network. But we rely on application
  73. ** programs to pass us correctly-formed UTF-8 data, which is very naive
  74. ** and optimistic.
  75. **
  76. ** In debudding mode, we call this routine to issue dire-sounding
  77. ** warnings. For the sake of safety, this routine never exits the
  78. ** program or does anything else drastic.
  79. **
  80. ** This routine almost certainly slows down our output.
  81. */
  82. #if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
  83. static void
  84. sanity_check_utf8(const char * const str,
  85. size_t const len) {
  86. xmlrpc_env env;
  87. xmlrpc_env_init(&env);
  88. xmlrpc_validate_utf8(&env, str, len);
  89. if (env.fault_occurred)
  90. fprintf(stderr, "*** xmlrpc-c WARNING ***: %s (%s)\n",
  91. "Application sending corrupted UTF-8 data to network",
  92. env.fault_string);
  93. xmlrpc_env_clean(&env);
  94. }
  95. #endif
  96. /*=========================================================================
  97. ** Escaping Strings
  98. **=========================================================================
  99. */
  100. static xmlrpc_mem_block *
  101. escape_string(xmlrpc_env * const env,
  102. const char * const str,
  103. size_t const len) {
  104. xmlrpc_mem_block *retval;
  105. size_t i, needed;
  106. char *out;
  107. XMLRPC_ASSERT_ENV_OK(env);
  108. XMLRPC_ASSERT(str != NULL);
  109. /* Set up our error-handling preconditions. */
  110. retval = NULL;
  111. /* Sanity-check this string before we print it. */
  112. #if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
  113. sanity_check_utf8(str, len);
  114. #endif
  115. /* Calculate the amount of space we'll need. */
  116. needed = 0;
  117. for (i = 0; i < len; i++) {
  118. if (str[i] == '<')
  119. needed += 4; /* &lt; */
  120. else if (str[i] == '>')
  121. needed += 4; /* &gt; */
  122. else if (str[i] == '&')
  123. needed += 5; /* &amp; */
  124. else
  125. needed++;
  126. }
  127. /* Allocate our memory block. */
  128. retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
  129. XMLRPC_FAIL_IF_FAULT(env);
  130. /* Copy over the newly-allocated data. */
  131. out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
  132. for (i = 0; i < len; i++) {
  133. if (str[i] == '<') {
  134. *out++ = '&';
  135. *out++ = 'l';
  136. *out++ = 't';
  137. *out++ = ';';
  138. } else if (str[i] == '>') {
  139. *out++ = '&';
  140. *out++ = 'g';
  141. *out++ = 't';
  142. *out++ = ';';
  143. } else if (str[i] == '&') {
  144. *out++ = '&';
  145. *out++ = 'a';
  146. *out++ = 'm';
  147. *out++ = 'p';
  148. *out++ = ';';
  149. } else {
  150. *out++ = str[i];
  151. }
  152. }
  153. cleanup:
  154. if (env->fault_occurred) {
  155. if (retval)
  156. XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
  157. retval = NULL;
  158. }
  159. return retval;
  160. }
  161. static xmlrpc_mem_block*
  162. escape_block (xmlrpc_env *env,
  163. xmlrpc_mem_block *block) {
  164. XMLRPC_ASSERT_ENV_OK(env);
  165. XMLRPC_ASSERT(block != NULL);
  166. return escape_string(env,
  167. XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
  168. XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
  169. }
  170. /*=========================================================================
  171. ** xmlrpc_serialize_string_data
  172. **=========================================================================
  173. ** Escape and print the contents of a string.
  174. */
  175. static void
  176. xmlrpc_serialize_string_data(xmlrpc_env *env,
  177. xmlrpc_mem_block *output,
  178. xmlrpc_value *string) {
  179. xmlrpc_mem_block *escaped;
  180. char *contents;
  181. size_t size;
  182. /* Since this routine can only be called internally, we only need
  183. ** an assertion here, not a runtime type check.
  184. ** XXX - Temporarily disabled because we're using this code to
  185. ** print <dateTime.iso8601> values as well. */
  186. /* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
  187. /* Escape any '&' and '<' characters in the string. */
  188. escaped = escape_block(env, &string->_block);
  189. XMLRPC_FAIL_IF_FAULT(env);
  190. contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
  191. size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
  192. /* Print the string. */
  193. XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
  194. XMLRPC_FAIL_IF_FAULT(env);
  195. cleanup:
  196. if (escaped)
  197. XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
  198. }
  199. /*=========================================================================
  200. ** xmlrpc_serialize_base64_data
  201. **=========================================================================
  202. ** Print the contents of a memory block as well-formed Base64 data.
  203. */
  204. static void
  205. xmlrpc_serialize_base64_data (xmlrpc_env *env,
  206. xmlrpc_mem_block *output,
  207. unsigned char* data, size_t len) {
  208. xmlrpc_mem_block *encoded;
  209. unsigned char *contents;
  210. size_t size;
  211. /* Encode the data. */
  212. encoded = xmlrpc_base64_encode(env, data, len);
  213. XMLRPC_FAIL_IF_FAULT(env);
  214. contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, encoded);
  215. size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, encoded);
  216. /* Print the data. */
  217. XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
  218. XMLRPC_FAIL_IF_FAULT(env);
  219. cleanup:
  220. if (encoded)
  221. XMLRPC_TYPED_MEM_BLOCK_FREE(char, encoded);
  222. }
  223. /*=========================================================================
  224. ** xmlrpc_serialize_struct
  225. **=========================================================================
  226. ** Dump the contents of a struct.
  227. */
  228. static void
  229. xmlrpc_serialize_struct(xmlrpc_env *env,
  230. xmlrpc_mem_block *output,
  231. xmlrpc_value *strct) {
  232. size_t size;
  233. size_t i;
  234. xmlrpc_value *key, *value;
  235. format_out(env, output, "<struct>"CRLF);
  236. XMLRPC_FAIL_IF_FAULT(env);
  237. size = xmlrpc_struct_size(env, strct);
  238. XMLRPC_FAIL_IF_FAULT(env);
  239. for (i = 0; i < size; i++) {
  240. xmlrpc_struct_get_key_and_value(env, strct, i, &key, &value);
  241. XMLRPC_FAIL_IF_FAULT(env);
  242. format_out(env, output, "<member><name>");
  243. XMLRPC_FAIL_IF_FAULT(env);
  244. xmlrpc_serialize_string_data(env, output, key);
  245. XMLRPC_FAIL_IF_FAULT(env);
  246. format_out(env, output, "</name>"CRLF);
  247. XMLRPC_FAIL_IF_FAULT(env);
  248. xmlrpc_serialize_value(env, output, value);
  249. XMLRPC_FAIL_IF_FAULT(env);
  250. format_out(env, output, "</member>"CRLF);
  251. XMLRPC_FAIL_IF_FAULT(env);
  252. }
  253. format_out(env, output, "</struct>");
  254. XMLRPC_FAIL_IF_FAULT(env);
  255. cleanup:
  256. return;
  257. }
  258. /*=========================================================================
  259. ** xmlrpc_serialize_value
  260. **=========================================================================
  261. ** Dump a value in the appropriate fashion.
  262. */
  263. void
  264. xmlrpc_serialize_value(xmlrpc_env *env,
  265. xmlrpc_mem_block *output,
  266. xmlrpc_value *value) {
  267. xmlrpc_value *item;
  268. size_t size;
  269. unsigned char* contents;
  270. size_t i;
  271. XMLRPC_ASSERT_ENV_OK(env);
  272. XMLRPC_ASSERT(output != NULL);
  273. XMLRPC_ASSERT_VALUE_OK(value);
  274. /* Print our ubiquitous header. */
  275. format_out(env, output, "<value>");
  276. XMLRPC_FAIL_IF_FAULT(env);
  277. switch (value->_type) {
  278. case XMLRPC_TYPE_INT:
  279. /* XXX - We assume that '%i' is the appropriate format specifier
  280. ** for an xmlrpc_int32 value. We should add some test cases to
  281. ** make sure this works. */
  282. format_out(env, output, "<i4>%i</i4>", value->_value.i);
  283. break;
  284. case XMLRPC_TYPE_BOOL:
  285. /* XXX - We assume that '%i' is the appropriate format specifier
  286. ** for an xmlrpc_bool value. */
  287. format_out(env, output, "<boolean>%i</boolean>",
  288. (value->_value.b) ? 1 : 0);
  289. break;
  290. case XMLRPC_TYPE_DOUBLE:
  291. /* We must output a number of the form [+-]?\d*.\d*. */
  292. format_out(env, output, "<double>%.17g</double>", value->_value.d);
  293. break;
  294. case XMLRPC_TYPE_STRING:
  295. format_out(env, output, "<string>");
  296. XMLRPC_FAIL_IF_FAULT(env);
  297. xmlrpc_serialize_string_data(env, output, value);
  298. XMLRPC_FAIL_IF_FAULT(env);
  299. format_out(env, output, "</string>");
  300. break;
  301. case XMLRPC_TYPE_ARRAY:
  302. format_out(env, output, "<array><data>"CRLF);
  303. XMLRPC_FAIL_IF_FAULT(env);
  304. /* Serialize each item. */
  305. size = xmlrpc_array_size(env, value);
  306. XMLRPC_FAIL_IF_FAULT(env);
  307. for (i = 0; i < size; i++) {
  308. item = xmlrpc_array_get_item(env, value, i);
  309. XMLRPC_FAIL_IF_FAULT(env);
  310. xmlrpc_serialize_value(env, output, item);
  311. XMLRPC_FAIL_IF_FAULT(env);
  312. format_out(env, output, CRLF);
  313. XMLRPC_FAIL_IF_FAULT(env);
  314. }
  315. format_out(env, output, "</data></array>");
  316. break;
  317. case XMLRPC_TYPE_STRUCT:
  318. xmlrpc_serialize_struct(env, output, value);
  319. break;
  320. case XMLRPC_TYPE_BASE64:
  321. format_out(env, output, "<base64>"CRLF);
  322. XMLRPC_FAIL_IF_FAULT(env);
  323. contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
  324. &value->_block);
  325. size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, &value->_block);
  326. xmlrpc_serialize_base64_data(env, output, contents, size);
  327. XMLRPC_FAIL_IF_FAULT(env);
  328. format_out(env, output, "</base64>");
  329. break;
  330. case XMLRPC_TYPE_DATETIME:
  331. format_out(env, output, "<dateTime.iso8601>");
  332. XMLRPC_FAIL_IF_FAULT(env);
  333. xmlrpc_serialize_string_data(env, output, value);
  334. XMLRPC_FAIL_IF_FAULT(env);
  335. format_out(env, output, "</dateTime.iso8601>");
  336. break;
  337. case XMLRPC_TYPE_C_PTR:
  338. xmlrpc_env_set_fault_formatted(
  339. env, XMLRPC_INTERNAL_ERROR,
  340. "Tried to serialize a C pointer value.");
  341. break;
  342. case XMLRPC_TYPE_DEAD:
  343. xmlrpc_env_set_fault_formatted(
  344. env, XMLRPC_INTERNAL_ERROR,
  345. "Tried to serialize a deaad value.");
  346. break;
  347. default:
  348. xmlrpc_env_set_fault_formatted(
  349. env, XMLRPC_INTERNAL_ERROR,
  350. "Invalid xmlrpc_value type: %d", value->_type);
  351. }
  352. XMLRPC_FAIL_IF_FAULT(env);
  353. /* Print our ubiquitous footer. */
  354. format_out(env, output, "</value>");
  355. XMLRPC_FAIL_IF_FAULT(env);
  356. cleanup:
  357. return;
  358. }
  359. /*=========================================================================
  360. ** xmlrpc_serialize_params
  361. **=========================================================================
  362. ** Serialize a list as a set of parameters.
  363. */
  364. void
  365. xmlrpc_serialize_params(xmlrpc_env *env,
  366. xmlrpc_mem_block *output,
  367. xmlrpc_value *param_array) {
  368. size_t size, i;
  369. xmlrpc_value *item;
  370. XMLRPC_ASSERT_ENV_OK(env);
  371. XMLRPC_ASSERT(output != NULL);
  372. XMLRPC_ASSERT_VALUE_OK(param_array);
  373. format_out(env, output, "<params>"CRLF);
  374. XMLRPC_FAIL_IF_FAULT(env);
  375. /* Dump each parameter. */
  376. size = xmlrpc_array_size(env, param_array);
  377. XMLRPC_FAIL_IF_FAULT(env);
  378. for (i = 0; i < size; i++) {
  379. format_out(env, output, "<param>");
  380. XMLRPC_FAIL_IF_FAULT(env);
  381. item = xmlrpc_array_get_item(env, param_array, i);
  382. XMLRPC_FAIL_IF_FAULT(env);
  383. xmlrpc_serialize_value(env, output, item);
  384. XMLRPC_FAIL_IF_FAULT(env);
  385. format_out(env, output, "</param>"CRLF);
  386. XMLRPC_FAIL_IF_FAULT(env);
  387. }
  388. format_out(env, output, "</params>"CRLF);
  389. XMLRPC_FAIL_IF_FAULT(env);
  390. cleanup:
  391. return;
  392. }
  393. /*=========================================================================
  394. ** xmlrpc_serialize_call
  395. **=========================================================================
  396. ** Serialize an XML-RPC call.
  397. */
  398. void
  399. xmlrpc_serialize_call(xmlrpc_env * const env,
  400. xmlrpc_mem_block * const output,
  401. const char * const method_name,
  402. xmlrpc_value * const param_array) {
  403. xmlrpc_mem_block *escaped;
  404. char *contents;
  405. size_t size;
  406. XMLRPC_ASSERT_ENV_OK(env);
  407. XMLRPC_ASSERT(output != NULL);
  408. XMLRPC_ASSERT(method_name != NULL);
  409. XMLRPC_ASSERT_VALUE_OK(param_array);
  410. /* Set up our error-handling preconditions. */
  411. escaped = NULL;
  412. /* Dump our header. */
  413. format_out(env, output, XML_PROLOGUE);
  414. XMLRPC_FAIL_IF_FAULT(env);
  415. format_out(env, output, "<methodCall>"CRLF"<methodName>");
  416. XMLRPC_FAIL_IF_FAULT(env);
  417. /* Dump the method name. */
  418. escaped = escape_string(env, method_name, strlen(method_name));
  419. XMLRPC_FAIL_IF_FAULT(env);
  420. contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
  421. size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped);
  422. XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
  423. XMLRPC_FAIL_IF_FAULT(env);
  424. /* Dump our parameters and footer. */
  425. format_out(env, output, "</methodName>"CRLF);
  426. XMLRPC_FAIL_IF_FAULT(env);
  427. xmlrpc_serialize_params(env, output, param_array);
  428. XMLRPC_FAIL_IF_FAULT(env);
  429. format_out(env, output, "</methodCall>"CRLF);
  430. XMLRPC_FAIL_IF_FAULT(env);
  431. cleanup:
  432. if (escaped)
  433. xmlrpc_mem_block_free(escaped);
  434. }
  435. /*=========================================================================
  436. ** xmlrpc_serialize_response
  437. **=========================================================================
  438. ** Serialize the (non-fault) response to an XML-RPC call.
  439. */
  440. void
  441. xmlrpc_serialize_response (xmlrpc_env *env,
  442. xmlrpc_mem_block *output,
  443. xmlrpc_value *value) {
  444. XMLRPC_ASSERT_ENV_OK(env);
  445. XMLRPC_ASSERT(output != NULL);
  446. XMLRPC_ASSERT_VALUE_OK(value);
  447. format_out(env, output, XML_PROLOGUE);
  448. XMLRPC_FAIL_IF_FAULT(env);
  449. format_out(env, output, "<methodResponse>"CRLF"<params>"CRLF"<param>");
  450. XMLRPC_FAIL_IF_FAULT(env);
  451. xmlrpc_serialize_value(env, output, value);
  452. XMLRPC_FAIL_IF_FAULT(env);
  453. format_out(env, output,
  454. "</param>"CRLF"</params>"CRLF"</methodResponse>"CRLF);
  455. XMLRPC_FAIL_IF_FAULT(env);
  456. cleanup:
  457. return;
  458. }
  459. /*=========================================================================
  460. ** xmlrpc_serialize_fault
  461. **=========================================================================
  462. ** Serialize an XML-RPC fault.
  463. **
  464. ** If this function fails, it will set up the first env argument. You'll
  465. ** need to take some other drastic action to produce a serialized fault
  466. ** of your own. (This function should only fail in an out-of-memory
  467. ** situation, AFAIK.)
  468. */
  469. void
  470. xmlrpc_serialize_fault(xmlrpc_env *env,
  471. xmlrpc_mem_block *output,
  472. xmlrpc_env *fault) {
  473. xmlrpc_value *strct;
  474. XMLRPC_ASSERT_ENV_OK(env);
  475. XMLRPC_ASSERT(output != NULL);
  476. XMLRPC_ASSERT(fault != NULL && fault->fault_occurred);
  477. /* Set up our error-handling preconditions. */
  478. strct = NULL;
  479. /* Build a fault structure. */
  480. strct = xmlrpc_build_value(env, "{s:i,s:s}",
  481. "faultCode", (xmlrpc_int32) fault->fault_code,
  482. "faultString", fault->fault_string);
  483. XMLRPC_FAIL_IF_FAULT(env);
  484. /* Output our header. */
  485. format_out(env, output, XML_PROLOGUE);
  486. XMLRPC_FAIL_IF_FAULT(env);
  487. format_out(env, output, "<methodResponse>"CRLF"<fault>"CRLF);
  488. XMLRPC_FAIL_IF_FAULT(env);
  489. /* Serialize our fault structure. */
  490. xmlrpc_serialize_value(env, output, strct);
  491. XMLRPC_FAIL_IF_FAULT(env);
  492. /* Output our footer. */
  493. format_out(env, output, CRLF"</fault>"CRLF"</methodResponse>"CRLF);
  494. XMLRPC_FAIL_IF_FAULT(env);
  495. cleanup:
  496. if (strct)
  497. xmlrpc_DECREF(strct);
  498. }