freeswitch/libs/xmlrpc-c/src/xmlrpc_serialize.c

715 lines
24 KiB
C
Raw Normal View History

/* Copyright information is at end of file */
/* Implementation note:
The printf format specifiers we use appear to be entirely standard,
except for the "long long" one, which is %I64 on Windows and %lld
everywhere else. So for that, we use the C99 standard macro PRId64,
which is defined by inttypes.h. Ironically, Windows doesn't have
inttypes.h either, but we have int.h instead.
*/
#include "xmlrpc_config.h"
#include <assert.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <float.h>
#include "int.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/string_int.h"
#include "double.h"
#define CRLF "\015\012"
#define SMALL_BUFFER_SZ (128)
#define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF
static void
addString(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
const char * const string) {
XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, string, strlen(string));
}
static void
formatOut(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
const char * const formatString,
...) {
/*----------------------------------------------------------------------------
A lightweight print routine for use with various serialization
functions.
Use this routine only for printing small objects -- it uses a
fixed-size internal buffer and returns an error on overflow. In
particular, do NOT use this routine to print XML-RPC string values!
-----------------------------------------------------------------------------*/
va_list args;
char buffer[SMALL_BUFFER_SZ];
int count;
XMLRPC_ASSERT_ENV_OK(envP);
va_start(args, formatString);
count = XMLRPC_VSNPRINTF(buffer, SMALL_BUFFER_SZ, formatString, args);
/* Old C libraries return -1 if vsnprintf overflows its buffer.
** New C libraries return the number of characters which *would* have
** been printed if the error did not occur. This is impressively vile.
** Thank the C99 committee for this bright idea. But wait! We also
** need to keep track of the trailing NUL. */
if (count < 0 || count >= (SMALL_BUFFER_SZ - 1))
xmlrpc_faultf(envP, "formatOut() overflowed internal buffer");
else
XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, buffer, count);
va_end(args);
}
static void
assertValidUtf8(const char * const str ATTR_UNUSED,
size_t const len ATTR_UNUSED) {
/*----------------------------------------------------------------------------
Assert that the string 'str' of length 'len' is valid UTF-8.
-----------------------------------------------------------------------------*/
#if !defined NDEBUG
/* Check the assertion; if it's false, issue a message to
Standard Error, but otherwise ignore it.
*/
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_validate_utf8(&env, str, len);
if (env.fault_occurred)
fprintf(stderr, "*** xmlrpc-c WARNING ***: %s (%s)\n",
"Xmlrpc-c sending corrupted UTF-8 data to network",
env.fault_string);
xmlrpc_env_clean(&env);
#endif
}
static size_t
escapedSize(const char * const chars,
size_t const len) {
size_t size;
size_t i;
size = 0;
for (i = 0; i < len; ++i) {
if (chars[i] == '<')
size += 4; /* &lt; */
else if (chars[i] == '>')
size += 4; /* &gt; */
else if (chars[i] == '&')
size += 5; /* &amp; */
else if (chars[i] == '\r')
size += 6; /* &#x0d; */
else
size += 1;
}
return size;
}
static void
escapeForXml(xmlrpc_env * const envP,
const char * const chars,
size_t const len,
xmlrpc_mem_block ** const outputPP) {
/*----------------------------------------------------------------------------
Escape & and < in a UTF-8 string so as to make it suitable for the
content of an XML element. I.e. turn them into entity references
&amp; and &lt;.
Also change > to &gt;, even though not required for XML, for
symmetry.
&lt; etc. are known in XML as "entity references."
Also Escape CR as &#x0d; . While raw CR _is_ allowed in the content
of an XML element, it has a special meaning -- it means line ending.
Our input uses LF for for line endings. Since it also means line ending
in XML, we just pass it through to our output like it were a regular
character.
&#x0d; is known in XML as a "character reference."
We assume chars[] is is ASCII. That isn't right -- we should
handle all valid UTF-8. Someday, we must do something more complex
and copy over multibyte characters verbatim. (The code here could
erroneously find that e.g. the 2nd byte of a UTF-8 character is a
CR).
-----------------------------------------------------------------------------*/
xmlrpc_mem_block * outputP;
size_t outputSize;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(chars != NULL);
assertValidUtf8(chars, len);
/* Note that in UTF-8, any byte that has high bit of zero is a
character all by itself (every byte of a multi-byte UTF-8 character
has the high bit set). Also, the Unicode code points < 128 are
identical to the ASCII ones.
*/
outputSize = escapedSize(chars, len);
outputP = XMLRPC_MEMBLOCK_NEW(char, envP, outputSize);
if (!envP->fault_occurred) {
char * p;
size_t i;
p = XMLRPC_MEMBLOCK_CONTENTS(char, outputP); /* Start at beginning */
for (i = 0; i < len; i++) {
if (chars[i] == '<') {
memcpy(p, "&lt;", 4);
p += 4;
} else if (chars[i] == '>') {
memcpy(p, "&gt;", 4);
p += 4;
} else if (chars[i] == '&') {
memcpy(p, "&amp;", 5);
p += 5;
} else if (chars[i] == '\r') {
memcpy(p, "&#x0d;", 6);
p += 6;
} else {
/* Either a plain character or a LF line delimiter */
*p = chars[i];
p += 1;
}
}
*outputPP = outputP;
assert(p == XMLRPC_MEMBLOCK_CONTENTS(char, outputP) + outputSize);
if (envP->fault_occurred)
XMLRPC_MEMBLOCK_FREE(char, outputP);
}
}
static void
serializeUtf8MemBlock(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_mem_block * const inputP) {
/*----------------------------------------------------------------------------
Append the characters in *inputP to the XML stream in *outputP.
*inputP contains Unicode characters in UTF-8.
We assume *inputP ends with a NUL character that marks end of
string, and we ignore that. (There might also be NUL characters
inside the string, though).
-----------------------------------------------------------------------------*/
xmlrpc_mem_block * escapedP;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(outputP != NULL);
XMLRPC_ASSERT(inputP != NULL);
escapeForXml(envP,
XMLRPC_MEMBLOCK_CONTENTS(const char, inputP),
XMLRPC_MEMBLOCK_SIZE(const char, inputP) - 1,
/* -1 is for the terminating NUL */
&escapedP);
if (!envP->fault_occurred) {
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(const char, escapedP);
size_t const size = XMLRPC_MEMBLOCK_SIZE(char, escapedP);
XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, contents, size);
XMLRPC_MEMBLOCK_FREE(const char, escapedP);
}
}
static void
xmlrpc_serialize_base64_data(xmlrpc_env * const envP,
xmlrpc_mem_block * const output,
unsigned char * const data,
size_t const len) {
/*----------------------------------------------------------------------------
Encode the 'len' bytes at 'data' in base64 ASCII and append the result to
'output'.
-----------------------------------------------------------------------------*/
xmlrpc_mem_block * encoded;
encoded = xmlrpc_base64_encode(envP, data, len);
if (!envP->fault_occurred) {
unsigned char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(unsigned char, encoded);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(unsigned char, encoded);
XMLRPC_MEMBLOCK_APPEND(char, envP, output, contents, size);
XMLRPC_MEMBLOCK_FREE(char, encoded);
}
}
static void
serializeStruct(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const structP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Add to *outputP the content of a <value> element to represent
the structure value *valueP. I.e. "<struct> ... </struct>".
-----------------------------------------------------------------------------*/
size_t size;
size_t i;
xmlrpc_value * memberKeyP;
xmlrpc_value * memberValueP;
addString(envP, outputP, "<struct>"CRLF);
XMLRPC_FAIL_IF_FAULT(envP);
size = xmlrpc_struct_size(envP, structP);
XMLRPC_FAIL_IF_FAULT(envP);
for (i = 0; i < size; ++i) {
xmlrpc_struct_get_key_and_value(envP, structP, i,
&memberKeyP, &memberValueP);
XMLRPC_FAIL_IF_FAULT(envP);
addString(envP, outputP, "<member><name>");
XMLRPC_FAIL_IF_FAULT(envP);
serializeUtf8MemBlock(envP, outputP, &memberKeyP->_block);
XMLRPC_FAIL_IF_FAULT(envP);
addString(envP, outputP, "</name>"CRLF);
XMLRPC_FAIL_IF_FAULT(envP);
xmlrpc_serialize_value2(envP, outputP, memberValueP, dialect);
XMLRPC_FAIL_IF_FAULT(envP);
addString(envP, outputP, "</member>"CRLF);
XMLRPC_FAIL_IF_FAULT(envP);
}
addString(envP, outputP, "</struct>");
XMLRPC_FAIL_IF_FAULT(envP);
cleanup:
return;
}
static void
serializeArray(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const valueP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Add to *outputP the content of a <value> element to represent
the array value *valueP. I.e. "<array> ... </array>".
-----------------------------------------------------------------------------*/
int const size = xmlrpc_array_size(envP, valueP);
if (!envP->fault_occurred) {
addString(envP, outputP, "<array><data>"CRLF);
if (!envP->fault_occurred) {
int i;
/* Serialize each item. */
for (i = 0; i < size && !envP->fault_occurred; ++i) {
xmlrpc_value * const itemP =
xmlrpc_array_get_item(envP, valueP, i);
if (!envP->fault_occurred) {
xmlrpc_serialize_value2(envP, outputP, itemP, dialect);
if (!envP->fault_occurred)
addString(envP, outputP, CRLF);
}
}
}
}
if (!envP->fault_occurred)
addString(envP, outputP, "</data></array>");
}
static void
formatValueContent(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const valueP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Add to *outputP the content of a <value> element to represent
value *valueP. E.g. "<int>42</int>"
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
switch (valueP->_type) {
case XMLRPC_TYPE_INT:
formatOut(envP, outputP, "<i4>%d</i4>", valueP->_value.i);
break;
case XMLRPC_TYPE_I8: {
const char * const elemName =
dialect == xmlrpc_dialect_apache ? "ex.i8" : "i8";
formatOut(envP, outputP, "<%s>%" PRId64 "</%s>",
elemName, valueP->_value.i8, elemName);
} break;
case XMLRPC_TYPE_BOOL:
formatOut(envP, outputP, "<boolean>%s</boolean>",
valueP->_value.b ? "1" : "0");
break;
case XMLRPC_TYPE_DOUBLE: {
const char * serializedValue;
xmlrpc_formatFloat(envP, valueP->_value.d, &serializedValue);
if (!envP->fault_occurred) {
addString(envP, outputP, "<double>");
if (!envP->fault_occurred) {
addString(envP, outputP, serializedValue);
if (!envP->fault_occurred)
addString(envP, outputP, "</double>");
}
xmlrpc_strfree(serializedValue);
}
} break;
case XMLRPC_TYPE_DATETIME:
addString(envP, outputP, "<dateTime.iso8601>");
if (!envP->fault_occurred) {
serializeUtf8MemBlock(envP, outputP, &valueP->_block);
if (!envP->fault_occurred) {
addString(envP, outputP, "</dateTime.iso8601>");
}
}
break;
case XMLRPC_TYPE_STRING:
addString(envP, outputP, "<string>");
if (!envP->fault_occurred) {
serializeUtf8MemBlock(envP, outputP, &valueP->_block);
if (!envP->fault_occurred)
addString(envP, outputP, "</string>");
}
break;
case XMLRPC_TYPE_BASE64: {
unsigned char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(unsigned char, &valueP->_block);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(unsigned char, &valueP->_block);
addString(envP, outputP, "<base64>"CRLF);
if (!envP->fault_occurred) {
xmlrpc_serialize_base64_data(envP, outputP, contents, size);
if (!envP->fault_occurred)
addString(envP, outputP, "</base64>");
}
} break;
case XMLRPC_TYPE_ARRAY:
serializeArray(envP, outputP, valueP, dialect);
break;
case XMLRPC_TYPE_STRUCT:
serializeStruct(envP, outputP, valueP, dialect);
break;
case XMLRPC_TYPE_C_PTR:
xmlrpc_faultf(envP, "Tried to serialize a C pointer value.");
break;
case XMLRPC_TYPE_NIL: {
const char * const elemName =
dialect == xmlrpc_dialect_apache ? "ex.nil" : "nil";
formatOut(envP, outputP, "<%s/>", elemName);
} break;
case XMLRPC_TYPE_DEAD:
xmlrpc_faultf(envP, "Tried to serialize a dead value.");
break;
default:
xmlrpc_faultf(envP, "Invalid xmlrpc_value type: %d", valueP->_type);
}
}
void
xmlrpc_serialize_value2(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const valueP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Generate the XML to represent XML-RPC value 'valueP' in XML-RPC.
Add it to *outputP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(outputP != NULL);
XMLRPC_ASSERT_VALUE_OK(valueP);
addString(envP, outputP, "<value>");
if (!envP->fault_occurred) {
formatValueContent(envP, outputP, valueP, dialect);
if (!envP->fault_occurred)
addString(envP, outputP, "</value>");
}
}
void
xmlrpc_serialize_value(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const valueP) {
xmlrpc_serialize_value2(envP, outputP, valueP, xmlrpc_dialect_i8);
}
void
xmlrpc_serialize_params2(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const paramArrayP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Serialize the parameter list of an XML-RPC call.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(outputP != NULL);
XMLRPC_ASSERT_VALUE_OK(paramArrayP);
addString(envP, outputP, "<params>"CRLF);
if (!envP->fault_occurred) {
/* Serialize each parameter. */
size_t const paramCount = xmlrpc_array_size(envP, paramArrayP);
if (!envP->fault_occurred) {
size_t paramSeq;
for (paramSeq = 0;
paramSeq < paramCount && !envP->fault_occurred;
++paramSeq) {
addString(envP, outputP, "<param>");
if (!envP->fault_occurred) {
xmlrpc_value * const itemP =
xmlrpc_array_get_item(envP, paramArrayP, paramSeq);
if (!envP->fault_occurred) {
xmlrpc_serialize_value2(envP, outputP, itemP, dialect);
if (!envP->fault_occurred)
addString(envP, outputP, "</param>"CRLF);
}
}
}
}
}
if (!envP->fault_occurred)
addString(envP, outputP, "</params>"CRLF);
}
void
xmlrpc_serialize_params(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const paramArrayP) {
/*----------------------------------------------------------------------------
Serialize the parameter list of an XML-RPC call in the original
"i8" dialect.
-----------------------------------------------------------------------------*/
xmlrpc_serialize_params2(envP, outputP, paramArrayP, xmlrpc_dialect_i8);
}
/*=========================================================================
** xmlrpc_serialize_call
**=========================================================================
** Serialize an XML-RPC call.
*/
void
xmlrpc_serialize_call2(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
const char * const methodName,
xmlrpc_value * const paramArrayP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Serialize an XML-RPC call of method named 'methodName' with parameter
list *paramArrayP. Use XML-RPC dialect 'dialect'.
Append the call XML ot *outputP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(outputP != NULL);
XMLRPC_ASSERT(methodName != NULL);
XMLRPC_ASSERT_VALUE_OK(paramArrayP);
addString(envP, outputP, XML_PROLOGUE);
if (!envP->fault_occurred) {
addString(envP, outputP, "<methodCall>"CRLF"<methodName>");
if (!envP->fault_occurred) {
xmlrpc_mem_block * encodedP;
escapeForXml(envP, methodName, strlen(methodName), &encodedP);
if (!envP->fault_occurred) {
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, encodedP);
size_t const size = XMLRPC_MEMBLOCK_SIZE(char, encodedP);
XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, contents, size);
if (!envP->fault_occurred) {
addString(envP, outputP, "</methodName>"CRLF);
if (!envP->fault_occurred) {
xmlrpc_serialize_params2(envP, outputP, paramArrayP,
dialect);
if (!envP->fault_occurred)
addString(envP, outputP, "</methodCall>"CRLF);
}
}
XMLRPC_MEMBLOCK_FREE(char, encodedP);
}
}
}
}
void
xmlrpc_serialize_call(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
const char * const methodName,
xmlrpc_value * const paramArrayP) {
xmlrpc_serialize_call2(envP, outputP, methodName, paramArrayP,
xmlrpc_dialect_i8);
}
void
xmlrpc_serialize_response2(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const valueP,
xmlrpc_dialect const dialect) {
/*----------------------------------------------------------------------------
Serialize a result response to an XML-RPC call.
The result is 'valueP'.
Add the response XML to *outputP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(outputP != NULL);
XMLRPC_ASSERT_VALUE_OK(valueP);
addString(envP, outputP, XML_PROLOGUE);
if (!envP->fault_occurred) {
addString(envP, outputP,
"<methodResponse>"CRLF"<params>"CRLF"<param>");
if (!envP->fault_occurred) {
xmlrpc_serialize_value2(envP, outputP, valueP, dialect);
if (!envP->fault_occurred) {
addString(envP, outputP,
"</param>"CRLF"</params>"CRLF
"</methodResponse>"CRLF);
}
}
}
}
void
xmlrpc_serialize_response(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
xmlrpc_value * const valueP) {
xmlrpc_serialize_response2(envP, outputP, valueP, xmlrpc_dialect_i8);
}
void
xmlrpc_serialize_fault(xmlrpc_env * const envP,
xmlrpc_mem_block * const outputP,
const xmlrpc_env * const faultP) {
/*----------------------------------------------------------------------------
Serialize a fault response to an XML-RPC call.
'faultP' is the fault.
Add the response XML to *outputP.
-----------------------------------------------------------------------------*/
xmlrpc_value * faultStructP;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(outputP != NULL);
XMLRPC_ASSERT(faultP != NULL);
XMLRPC_ASSERT(faultP->fault_occurred);
faultStructP = xmlrpc_build_value(envP, "{s:i,s:s}",
"faultCode",
(xmlrpc_int32) faultP->fault_code,
"faultString", faultP->fault_string);
if (!envP->fault_occurred) {
addString(envP, outputP, XML_PROLOGUE);
if (!envP->fault_occurred) {
addString(envP, outputP, "<methodResponse>"CRLF"<fault>"CRLF);
if (!envP->fault_occurred) {
xmlrpc_serialize_value(envP, outputP, faultStructP);
if (!envP->fault_occurred) {
addString(envP, outputP,
CRLF"</fault>"CRLF"</methodResponse>"CRLF);
}
}
}
xmlrpc_DECREF(faultStructP);
}
}
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */