ENH: make this work for older versions of OSX
[cmake.git] / Utilities / cmxmlrpc / xmlrpc_serialize.c
blob5315ff91681ca197606fdfe3470825bd0e9c8ab4
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. */
26 #include "xmlrpc_config.h"
28 #include <stddef.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <string.h>
33 #include "xmlrpc.h"
34 #include "xmlrpc_int.h"
36 #define CRLF "\015\012"
37 #define SMALL_BUFFER_SZ (128)
38 #define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF
41 /*=========================================================================
42 ** format_out
43 **=========================================================================
44 ** A lightweight print routine for use with various serialization
45 ** functions. Only use this routine for printing small objects--it uses
46 ** a fixed-size internal buffer and returns an error on overflow.
47 ** In particular, do NOT use this routine to print XML-RPC string values!
50 static void
51 format_out(xmlrpc_env *env,
52 xmlrpc_mem_block *output,
53 char *format_string,
54 ...) {
56 va_list args;
57 char buffer[SMALL_BUFFER_SZ];
58 int count;
60 XMLRPC_ASSERT_ENV_OK(env);
62 va_start(args, format_string);
64 /* We assume that this function is present and works correctly. Right. */
65 count = vsnprintf(buffer, SMALL_BUFFER_SZ, format_string, args);
67 /* Old C libraries return -1 if vsnprintf overflows its buffer.
68 ** New C libraries return the number of characters which *would* have
69 ** been printed if the error did not occur. This is impressively vile.
70 ** Thank the C99 committee for this bright idea. But wait! We also
71 ** need to keep track of the trailing NULL. */
72 if (count < 0 || count >= (SMALL_BUFFER_SZ - 1))
73 XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
74 "format_out overflowed internal buffer");
76 /* Append our new data to our output. */
77 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, buffer, count);
78 XMLRPC_FAIL_IF_FAULT(env);
80 cleanup:
81 va_end(args);
85 /*=========================================================================
86 ** Warnings About Invalid UTF-8
87 **=========================================================================
88 ** We claim to send UTF-8 data to the network. But we rely on application
89 ** programs to pass us correctly-formed UTF-8 data, which is very naive
90 ** and optimistic.
92 ** In debudding mode, we call this routine to issue dire-sounding
93 ** warnings. For the sake of safety, this routine never exits the
94 ** program or does anything else drastic.
96 ** This routine almost certainly slows down our output.
99 #if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
101 static void
102 sanity_check_utf8(const char * const str,
103 size_t const len) {
105 xmlrpc_env env;
107 xmlrpc_env_init(&env);
108 xmlrpc_validate_utf8(&env, str, len);
109 if (env.fault_occurred)
110 fprintf(stderr, "*** xmlrpc-c WARNING ***: %s (%s)\n",
111 "Application sending corrupted UTF-8 data to network",
112 env.fault_string);
113 xmlrpc_env_clean(&env);
115 #endif
119 /*=========================================================================
120 ** Escaping Strings
121 **=========================================================================
124 static xmlrpc_mem_block *
125 escape_string(xmlrpc_env * const env,
126 const char * const str,
127 size_t const len) {
129 xmlrpc_mem_block *retval;
130 size_t i, needed;
131 char *out;
133 XMLRPC_ASSERT_ENV_OK(env);
134 XMLRPC_ASSERT(str != NULL);
136 /* Sanity-check this string before we print it. */
137 #if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
138 sanity_check_utf8(str, len);
139 #endif
141 /* Calculate the amount of space we'll need. */
142 needed = 0;
143 for (i = 0; i < len; i++) {
144 if (str[i] == '<')
145 needed += 4; /* &lt; */
146 else if (str[i] == '>')
147 needed += 4; /* &gt; */
148 else if (str[i] == '&')
149 needed += 5; /* &amp; */
150 else
151 needed++;
154 /* Allocate our memory block. */
155 retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
156 XMLRPC_FAIL_IF_FAULT(env);
158 /* Copy over the newly-allocated data. */
159 out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
160 for (i = 0; i < len; i++) {
161 if (str[i] == '<') {
162 *out++ = '&';
163 *out++ = 'l';
164 *out++ = 't';
165 *out++ = ';';
166 } else if (str[i] == '>') {
167 *out++ = '&';
168 *out++ = 'g';
169 *out++ = 't';
170 *out++ = ';';
171 } else if (str[i] == '&') {
172 *out++ = '&';
173 *out++ = 'a';
174 *out++ = 'm';
175 *out++ = 'p';
176 *out++ = ';';
177 } else {
178 *out++ = str[i];
182 cleanup:
183 if (env->fault_occurred) {
184 if (retval)
185 XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
186 retval = NULL;
188 return retval;
193 static xmlrpc_mem_block*
194 escape_block (xmlrpc_env *env,
195 xmlrpc_mem_block *block) {
197 XMLRPC_ASSERT_ENV_OK(env);
198 XMLRPC_ASSERT(block != NULL);
200 return escape_string(env,
201 XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
202 XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
207 /*=========================================================================
208 ** xmlrpc_serialize_string_data
209 **=========================================================================
210 ** Escape and print the contents of a string.
213 static void
214 xmlrpc_serialize_string_data(xmlrpc_env *env,
215 xmlrpc_mem_block *output,
216 xmlrpc_value *string) {
218 xmlrpc_mem_block *escaped;
219 char *contents;
220 size_t size;
222 /* Since this routine can only be called internally, we only need
223 ** an assertion here, not a runtime type check.
224 ** XXX - Temporarily disabled because we're using this code to
225 ** print <dateTime.iso8601> values as well. */
226 /* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
228 /* Escape any '&' and '<' characters in the string. */
229 escaped = escape_block(env, &string->_block);
230 XMLRPC_FAIL_IF_FAULT(env);
231 contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
232 size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
234 /* Print the string. */
235 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
236 XMLRPC_FAIL_IF_FAULT(env);
238 cleanup:
239 if (escaped)
240 XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
245 /*=========================================================================
246 ** xmlrpc_serialize_base64_data
247 **=========================================================================
248 ** Print the contents of a memory block as well-formed Base64 data.
251 static void
252 xmlrpc_serialize_base64_data (xmlrpc_env *env,
253 xmlrpc_mem_block *output,
254 unsigned char* data, size_t len) {
256 xmlrpc_mem_block *encoded;
257 unsigned char *contents;
258 size_t size;
260 /* Encode the data. */
261 encoded = xmlrpc_base64_encode_without_newlines(env, data, len);
262 XMLRPC_FAIL_IF_FAULT(env);
263 contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, encoded);
264 size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, encoded);
266 /* Print the data. */
267 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
268 XMLRPC_FAIL_IF_FAULT(env);
270 cleanup:
271 if (encoded)
272 XMLRPC_TYPED_MEM_BLOCK_FREE(char, encoded);
277 /*=========================================================================
278 ** xmlrpc_serialize_struct
279 **=========================================================================
280 ** Dump the contents of a struct.
283 static void
284 xmlrpc_serialize_struct(xmlrpc_env *env,
285 xmlrpc_mem_block *output,
286 xmlrpc_value *strct) {
288 size_t size;
289 size_t i;
290 xmlrpc_value *key, *value;
292 format_out(env, output, "<struct>"CRLF);
293 XMLRPC_FAIL_IF_FAULT(env);
295 size = xmlrpc_struct_size(env, strct);
296 XMLRPC_FAIL_IF_FAULT(env);
297 for (i = 0; i < size; i++) {
298 xmlrpc_struct_get_key_and_value(env, strct, (int)i, &key, &value);
299 XMLRPC_FAIL_IF_FAULT(env);
300 format_out(env, output, "<member><name>");
301 XMLRPC_FAIL_IF_FAULT(env);
302 xmlrpc_serialize_string_data(env, output, key);
303 XMLRPC_FAIL_IF_FAULT(env);
304 format_out(env, output, "</name>"CRLF);
305 XMLRPC_FAIL_IF_FAULT(env);
306 xmlrpc_serialize_value(env, output, value);
307 XMLRPC_FAIL_IF_FAULT(env);
308 format_out(env, output, "</member>"CRLF);
309 XMLRPC_FAIL_IF_FAULT(env);
312 format_out(env, output, "</struct>");
313 XMLRPC_FAIL_IF_FAULT(env);
315 cleanup:
316 return;
321 /*=========================================================================
322 ** xmlrpc_serialize_value
323 **=========================================================================
324 ** Dump a value in the appropriate fashion.
327 void
328 xmlrpc_serialize_value(xmlrpc_env *env,
329 xmlrpc_mem_block *output,
330 xmlrpc_value *value) {
332 xmlrpc_value *item;
333 size_t size;
334 unsigned char* contents;
335 size_t i;
337 XMLRPC_ASSERT_ENV_OK(env);
338 XMLRPC_ASSERT(output != NULL);
339 XMLRPC_ASSERT_VALUE_OK(value);
341 /* Print our ubiquitous header. */
342 format_out(env, output, "<value>");
343 XMLRPC_FAIL_IF_FAULT(env);
345 switch (value->_type) {
347 case XMLRPC_TYPE_INT:
348 /* XXX - We assume that '%i' is the appropriate format specifier
349 ** for an xmlrpc_int32 value. We should add some test cases to
350 ** make sure this works. */
351 format_out(env, output, "<i4>%i</i4>", value->_value.i);
352 break;
354 case XMLRPC_TYPE_BOOL:
355 /* XXX - We assume that '%i' is the appropriate format specifier
356 ** for an xmlrpc_bool value. */
357 format_out(env, output, "<boolean>%i</boolean>",
358 (value->_value.b) ? 1 : 0);
359 break;
361 case XMLRPC_TYPE_DOUBLE:
362 /* We must output a number of the form [+-]?\d*.\d*. */
363 format_out(env, output, "<double>%.17g</double>", value->_value.d);
364 break;
366 case XMLRPC_TYPE_STRING:
367 format_out(env, output, "<string>");
368 XMLRPC_FAIL_IF_FAULT(env);
369 xmlrpc_serialize_string_data(env, output, value);
370 XMLRPC_FAIL_IF_FAULT(env);
371 format_out(env, output, "</string>");
372 break;
374 case XMLRPC_TYPE_ARRAY:
375 format_out(env, output, "<array><data>"CRLF);
376 XMLRPC_FAIL_IF_FAULT(env);
378 /* Serialize each item. */
379 size = xmlrpc_array_size(env, value);
380 XMLRPC_FAIL_IF_FAULT(env);
381 for (i = 0; i < size; i++) {
382 item = xmlrpc_array_get_item(env, value, (int)i);
383 XMLRPC_FAIL_IF_FAULT(env);
384 xmlrpc_serialize_value(env, output, item);
385 XMLRPC_FAIL_IF_FAULT(env);
386 format_out(env, output, CRLF);
387 XMLRPC_FAIL_IF_FAULT(env);
390 format_out(env, output, "</data></array>");
391 break;
393 case XMLRPC_TYPE_STRUCT:
394 xmlrpc_serialize_struct(env, output, value);
395 break;
397 case XMLRPC_TYPE_BASE64:
398 format_out(env, output, "<base64>"CRLF);
399 XMLRPC_FAIL_IF_FAULT(env);
400 contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
401 &value->_block);
402 size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, &value->_block);
403 xmlrpc_serialize_base64_data(env, output, contents, size);
404 XMLRPC_FAIL_IF_FAULT(env);
405 format_out(env, output, "</base64>");
406 break;
408 case XMLRPC_TYPE_DATETIME:
409 format_out(env, output, "<dateTime.iso8601>");
410 XMLRPC_FAIL_IF_FAULT(env);
411 xmlrpc_serialize_string_data(env, output, value);
412 XMLRPC_FAIL_IF_FAULT(env);
413 format_out(env, output, "</dateTime.iso8601>");
414 break;
416 case XMLRPC_TYPE_C_PTR:
417 xmlrpc_env_set_fault_formatted(
418 env, XMLRPC_INTERNAL_ERROR,
419 "Tried to serialize a C pointer value.");
420 break;
422 case XMLRPC_TYPE_DEAD:
423 xmlrpc_env_set_fault_formatted(
424 env, XMLRPC_INTERNAL_ERROR,
425 "Tried to serialize a deaad value.");
426 break;
428 default:
429 xmlrpc_env_set_fault_formatted(
430 env, XMLRPC_INTERNAL_ERROR,
431 "Invalid xmlrpc_value type: %d", value->_type);
433 XMLRPC_FAIL_IF_FAULT(env);
435 /* Print our ubiquitous footer. */
436 format_out(env, output, "</value>");
437 XMLRPC_FAIL_IF_FAULT(env);
439 cleanup:
440 return;
445 /*=========================================================================
446 ** xmlrpc_serialize_params
447 **=========================================================================
448 ** Serialize a list as a set of parameters.
451 void
452 xmlrpc_serialize_params(xmlrpc_env *env,
453 xmlrpc_mem_block *output,
454 xmlrpc_value *param_array) {
456 size_t size, i;
457 xmlrpc_value *item;
459 XMLRPC_ASSERT_ENV_OK(env);
460 XMLRPC_ASSERT(output != NULL);
461 XMLRPC_ASSERT_VALUE_OK(param_array);
463 format_out(env, output, "<params>"CRLF);
464 XMLRPC_FAIL_IF_FAULT(env);
466 /* Dump each parameter. */
467 size = xmlrpc_array_size(env, param_array);
468 XMLRPC_FAIL_IF_FAULT(env);
469 for (i = 0; i < size; i++) {
470 format_out(env, output, "<param>");
471 XMLRPC_FAIL_IF_FAULT(env);
472 item = xmlrpc_array_get_item(env, param_array, (int)i);
473 XMLRPC_FAIL_IF_FAULT(env);
474 xmlrpc_serialize_value(env, output, item);
475 XMLRPC_FAIL_IF_FAULT(env);
476 format_out(env, output, "</param>"CRLF);
477 XMLRPC_FAIL_IF_FAULT(env);
480 format_out(env, output, "</params>"CRLF);
481 XMLRPC_FAIL_IF_FAULT(env);
483 cleanup:
484 return;
489 /*=========================================================================
490 ** xmlrpc_serialize_call
491 **=========================================================================
492 ** Serialize an XML-RPC call.
495 void
496 xmlrpc_serialize_call(xmlrpc_env * const env,
497 xmlrpc_mem_block * const output,
498 const char * const method_name,
499 xmlrpc_value * const param_array) {
501 xmlrpc_mem_block *escaped;
502 char *contents;
503 size_t size;
505 XMLRPC_ASSERT_ENV_OK(env);
506 XMLRPC_ASSERT(output != NULL);
507 XMLRPC_ASSERT(method_name != NULL);
508 XMLRPC_ASSERT_VALUE_OK(param_array);
510 /* Set up our error-handling preconditions. */
511 escaped = NULL;
513 /* Dump our header. */
514 format_out(env, output, XML_PROLOGUE);
515 XMLRPC_FAIL_IF_FAULT(env);
516 format_out(env, output, "<methodCall>"CRLF"<methodName>");
517 XMLRPC_FAIL_IF_FAULT(env);
519 /* Dump the method name. */
520 escaped = escape_string(env, method_name, strlen(method_name));
521 XMLRPC_FAIL_IF_FAULT(env);
522 contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
523 size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped);
524 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
525 XMLRPC_FAIL_IF_FAULT(env);
527 /* Dump our parameters and footer. */
528 format_out(env, output, "</methodName>"CRLF);
529 XMLRPC_FAIL_IF_FAULT(env);
530 xmlrpc_serialize_params(env, output, param_array);
531 XMLRPC_FAIL_IF_FAULT(env);
532 format_out(env, output, "</methodCall>"CRLF);
533 XMLRPC_FAIL_IF_FAULT(env);
535 cleanup:
536 if (escaped)
537 xmlrpc_mem_block_free(escaped);
542 /*=========================================================================
543 ** xmlrpc_serialize_response
544 **=========================================================================
545 ** Serialize the (non-fault) response to an XML-RPC call.
548 void
549 xmlrpc_serialize_response (xmlrpc_env *env,
550 xmlrpc_mem_block *output,
551 xmlrpc_value *value) {
553 XMLRPC_ASSERT_ENV_OK(env);
554 XMLRPC_ASSERT(output != NULL);
555 XMLRPC_ASSERT_VALUE_OK(value);
557 format_out(env, output, XML_PROLOGUE);
558 XMLRPC_FAIL_IF_FAULT(env);
559 format_out(env, output, "<methodResponse>"CRLF"<params>"CRLF"<param>");
560 XMLRPC_FAIL_IF_FAULT(env);
562 xmlrpc_serialize_value(env, output, value);
563 XMLRPC_FAIL_IF_FAULT(env);
565 format_out(env, output,
566 "</param>"CRLF"</params>"CRLF"</methodResponse>"CRLF);
567 XMLRPC_FAIL_IF_FAULT(env);
569 cleanup:
570 return;
575 /*=========================================================================
576 ** xmlrpc_serialize_fault
577 **=========================================================================
578 ** Serialize an XML-RPC fault.
580 ** If this function fails, it will set up the first env argument. You'll
581 ** need to take some other drastic action to produce a serialized fault
582 ** of your own. (This function should only fail in an out-of-memory
583 ** situation, AFAIK.)
586 void
587 xmlrpc_serialize_fault(xmlrpc_env *env,
588 xmlrpc_mem_block *output,
589 xmlrpc_env *fault) {
591 xmlrpc_value *strct;
593 XMLRPC_ASSERT_ENV_OK(env);
594 XMLRPC_ASSERT(output != NULL);
595 XMLRPC_ASSERT(fault != NULL && fault->fault_occurred);
597 /* Build a fault structure. */
598 strct = xmlrpc_build_value(env, "{s:i,s:s}",
599 "faultCode", (xmlrpc_int32) fault->fault_code,
600 "faultString", fault->fault_string);
601 XMLRPC_FAIL_IF_FAULT(env);
603 /* Output our header. */
604 format_out(env, output, XML_PROLOGUE);
605 XMLRPC_FAIL_IF_FAULT(env);
606 format_out(env, output, "<methodResponse>"CRLF"<fault>"CRLF);
607 XMLRPC_FAIL_IF_FAULT(env);
609 /* Serialize our fault structure. */
610 xmlrpc_serialize_value(env, output, strct);
611 XMLRPC_FAIL_IF_FAULT(env);
613 /* Output our footer. */
614 format_out(env, output, CRLF"</fault>"CRLF"</methodResponse>"CRLF);
615 XMLRPC_FAIL_IF_FAULT(env);
617 cleanup:
618 if (strct)
619 xmlrpc_DECREF(strct);