CMake/Utilities/cmxmlrpc/xmlrpc_parse.c
2005-03-02 11:04:32 -05:00

768 lines
25 KiB
C

/* 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. */
#include "xmlrpc_config.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "xmlrpc.h"
#include "xmlrpc_int.h"
#include "xmlrpc_xmlparser.h"
/*=========================================================================
** Data Format
**=========================================================================
** All XML-RPC documents contain a single methodCall or methodResponse
** element.
**
** methodCall methodName, params
** methodResponse (params|fault)
** params param*
** param value
** fault value
** value (i4|int|boolean|string|double|dateTime.iso8601|base64|
** struct|array)
** array data
** data value*
** struct member*
** member name, value
**
** Contain CDATA: methodName, i4, int, boolean, string, double,
** dateTime.iso8601, base64, name
**
** We attempt to validate the structure of the XML document carefully.
** We also try *very* hard to handle malicious data gracefully, and without
** leaking memory.
**
** The CHECK_NAME and CHECK_CHILD_COUNT macros examine an XML element, and
** invoke XMLRPC_FAIL if something looks wrong.
*/
#define CHECK_NAME(env,elem,name) \
do \
if (strcmp((name), xml_element_name(elem)) != 0) \
XMLRPC_FAIL2(env, XMLRPC_PARSE_ERROR, \
"Expected element of type <%s>, found <%s>", \
(name), xml_element_name(elem)); \
while (0)
#define CHECK_CHILD_COUNT(env,elem,count) \
do \
if (xml_element_children_size(elem) != (count)) \
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR, \
"Expected <%s> to have %d children, found %d", \
xml_element_name(elem), (count), \
xml_element_children_size(elem)); \
while (0)
static xml_element *
get_child_by_name (xmlrpc_env *env, xml_element *parent, char *name)
{
size_t child_count, i;
xml_element **children;
children = xml_element_children(parent);
child_count = xml_element_children_size(parent);
for (i = 0; i < child_count; i++) {
if (0 == strcmp(xml_element_name(children[i]), name))
return children[i];
}
xmlrpc_env_set_fault_formatted(env, XMLRPC_PARSE_ERROR,
"Expected <%s> to have child <%s>",
xml_element_name(parent), name);
return NULL;
}
/*=========================================================================
** Number-Parsing Functions
**=========================================================================
** These functions mirror atoi, atof, etc., but provide better
** error-handling. These routines may reset errno to zero.
*/
static xmlrpc_int32
xmlrpc_atoi(xmlrpc_env *env, char *str, size_t stringLength,
xmlrpc_int32 min, xmlrpc_int32 max)
{
long i;
char *end;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(str);
/* Suppress compiler warnings. */
i = 0;
/* Check for leading white space. */
if (isspace((int)(str[0])))
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" must not contain whitespace", str);
/* Convert the value. */
end = str + stringLength;
errno = 0;
i = strtol(str, &end, 10);
/* Look for ERANGE. */
if (errno != 0)
/* XXX - Do all operating systems have thread-safe strerror? */
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
"error parsing \"%s\": %s (%d)",
str, strerror(errno), errno);
/* Look for out-of-range errors which didn't produce ERANGE. */
if (i < min || i > max)
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
"\"%s\" must be in range %d to %d", str, min, max);
/* Check for unused characters. */
if (end != str + stringLength)
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" contained trailing data", str);
cleanup:
errno = 0;
if (env->fault_occurred)
return 0;
return (xmlrpc_int32) i;
}
static double
xmlrpc_atod(xmlrpc_env *env, char *str, size_t stringLength)
{
double d;
char *end;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(str);
/* Suppress compiler warnings. */
d = 0.0;
/* Check for leading white space. */
if (isspace((int)(str[0])))
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" must not contain whitespace", str);
/* Convert the value. */
end = str + stringLength;
errno = 0;
d = strtod(str, &end);
/* Look for ERANGE. */
if (errno != 0)
/* XXX - Do all operating systems have thread-safe strerror? */
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
"error parsing \"%s\": %s (%d)",
str, strerror(errno), errno);
/* Check for unused characters. */
if (end != str + stringLength)
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" contained trailing data", str);
cleanup:
errno = 0;
if (env->fault_occurred)
return 0.0;
return d;
}
/*=========================================================================
** make_string
**=========================================================================
** Make an XML-RPC string.
**
** SECURITY: We validate our UTF-8 first. This incurs a performance
** penalty, but ensures that we will never pass maliciously malformed
** UTF-8 data back up to the user layer, where it could wreak untold
** damange. Don't comment out this check unless you know *exactly* what
** you're doing. (Win32 developers who remove this check are *begging*
** to wind up on BugTraq, because many of the Win32 filesystem routines
** rely on an insecure UTF-8 decoder.)
**
** XXX - This validation is redundant if the user chooses to convert
** UTF-8 data into a wchar_t string.
*/
static xmlrpc_value *
make_string(xmlrpc_env *env, char *cdata, size_t cdata_size)
{
#ifdef HAVE_UNICODE_WCHAR
xmlrpc_validate_utf8(env, cdata, cdata_size);
#endif
if (env->fault_occurred)
return NULL;
return xmlrpc_build_value(env, "s#", cdata, cdata_size);
}
/*=========================================================================
** convert_value
**=========================================================================
** Convert an XML element representing a value into an xmlrpc_value.
*/
static xmlrpc_value *
convert_array (xmlrpc_env *env, unsigned *depth, xml_element *elem);
static xmlrpc_value *
convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem);
static xmlrpc_value *
convert_value(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xml_element *child;
int child_count;
char *cdata, *child_name;
size_t cdata_size, ascii_len;
xmlrpc_mem_block *decoded;
unsigned char *ascii_data;
xmlrpc_value *retval;
xmlrpc_int32 i;
double d;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Error-handling precoditions.
** If we haven't changed any of these from their default state, we're
** allowed to tail-call xmlrpc_build_value. */
retval = NULL;
decoded = NULL;
/* Make sure we haven't recursed too deeply. */
if (*depth > xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID))
XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
"Nested data structure too deep.");
/* Validate our structure, and see whether we have a child element. */
CHECK_NAME(env, elem, "value");
child_count = xml_element_children_size(elem);
if (child_count == 0) {
/* We have no type element, so treat the value as a string. */
cdata = xml_element_cdata(elem);
cdata_size = xml_element_cdata_size(elem);
return make_string(env, cdata, cdata_size);
} else {
/* We should have a type tag inside our value tag. */
CHECK_CHILD_COUNT(env, elem, 1);
child = xml_element_children(elem)[0];
/* Parse our value-containing element. */
child_name = xml_element_name(child);
if (strcmp(child_name, "struct") == 0) {
return convert_struct(env, depth, child);
} else if (strcmp(child_name, "array") == 0) {
CHECK_CHILD_COUNT(env, child, 1);
return convert_array(env, depth, child);
} else {
CHECK_CHILD_COUNT(env, child, 0);
cdata = xml_element_cdata(child);
cdata_size = xml_element_cdata_size(child);
if (strcmp(child_name, "i4") == 0 ||
strcmp(child_name, "int") == 0)
{
i = xmlrpc_atoi(env, cdata, strlen(cdata),
XMLRPC_INT32_MIN, XMLRPC_INT32_MAX);
XMLRPC_FAIL_IF_FAULT(env);
return xmlrpc_build_value(env, "i", i);
} else if (strcmp(child_name, "string") == 0) {
return make_string(env, cdata, cdata_size);
} else if (strcmp(child_name, "boolean") == 0) {
i = xmlrpc_atoi(env, cdata, strlen(cdata), 0, 1);
XMLRPC_FAIL_IF_FAULT(env);
return xmlrpc_build_value(env, "b", (xmlrpc_bool) i);
} else if (strcmp(child_name, "double") == 0) {
d = xmlrpc_atod(env, cdata, strlen(cdata));
XMLRPC_FAIL_IF_FAULT(env);
return xmlrpc_build_value(env, "d", d);
} else if (strcmp(child_name, "dateTime.iso8601") == 0) {
return xmlrpc_build_value(env, "8", cdata);
} else if (strcmp(child_name, "base64") == 0) {
/* No more tail calls once we do this! */
decoded = xmlrpc_base64_decode(env, cdata, cdata_size);
XMLRPC_FAIL_IF_FAULT(env);
ascii_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
decoded);
ascii_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char,
decoded);
retval = xmlrpc_build_value(env, "6", ascii_data, ascii_len);
XMLRPC_FAIL_IF_FAULT(env);
} else {
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"Unknown value type <%s>", child_name);
}
}
}
cleanup:
if (decoded)
xmlrpc_mem_block_free(decoded);
if (env->fault_occurred) {
if (retval)
xmlrpc_DECREF(retval);
return NULL;
}
return retval;
}
/*=========================================================================
** convert_array
**=========================================================================
** Convert an XML element representing an array into an xmlrpc_value.
*/
static xmlrpc_value *
convert_array(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xml_element *data, **values, *value;
xmlrpc_value *array, *item;
int size, i;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Set up our error-handling preconditions. */
array = item = NULL;
(*depth)++;
/* Allocate an array to hold our values. */
array = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
/* We don't need to check our element name--our callers do that. */
CHECK_CHILD_COUNT(env, elem, 1);
data = xml_element_children(elem)[0];
CHECK_NAME(env, data, "data");
/* Iterate over our children. */
values = xml_element_children(data);
size = xml_element_children_size(data);
for (i = 0; i < size; i++) {
value = values[i];
item = convert_value(env, depth, value);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, array, item);
xmlrpc_DECREF(item);
item = NULL;
XMLRPC_FAIL_IF_FAULT(env);
}
cleanup:
(*depth)--;
if (item)
xmlrpc_DECREF(item);
if (env->fault_occurred) {
if (array)
xmlrpc_DECREF(array);
return NULL;
}
return array;
}
/*=========================================================================
** convert_struct
**=========================================================================
** Convert an XML element representing a struct into an xmlrpc_value.
*/
static xmlrpc_value *
convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xmlrpc_value *strct, *key, *value;
xml_element **members, *member, *name_elem, *value_elem;
int size, i;
char *cdata;
size_t cdata_size;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Set up our error-handling preconditions. */
strct = key = value = NULL;
(*depth)++;
/* Allocate an array to hold our members. */
strct = xmlrpc_struct_new(env);
XMLRPC_FAIL_IF_FAULT(env);
/* Iterate over our children, extracting key/value pairs. */
/* We don't need to check our element name--our callers do that. */
members = xml_element_children(elem);
size = xml_element_children_size(elem);
for (i = 0; i < size; i++) {
member = members[i];
CHECK_NAME(env, member, "member");
CHECK_CHILD_COUNT(env, member, 2);
/* Get our key. */
name_elem = get_child_by_name(env, member, "name");
XMLRPC_FAIL_IF_FAULT(env);
CHECK_CHILD_COUNT(env, name_elem, 0);
cdata = xml_element_cdata(name_elem);
cdata_size = xml_element_cdata_size(name_elem);
key = make_string(env, cdata, cdata_size);
XMLRPC_FAIL_IF_FAULT(env);
/* Get our value. */
value_elem = get_child_by_name(env, member, "value");
XMLRPC_FAIL_IF_FAULT(env);
value = convert_value(env, depth, value_elem);
XMLRPC_FAIL_IF_FAULT(env);
/* Add the key/value pair to our struct. */
xmlrpc_struct_set_value_v(env, strct, key, value);
XMLRPC_FAIL_IF_FAULT(env);
/* Release our references & memory, and restore our invariants. */
xmlrpc_DECREF(key);
key = NULL;
xmlrpc_DECREF(value);
value = NULL;
}
cleanup:
(*depth)--;
if (key)
xmlrpc_DECREF(key);
if (value)
xmlrpc_DECREF(value);
if (env->fault_occurred) {
if (strct)
xmlrpc_DECREF(strct);
return NULL;
}
return strct;
}
/*=========================================================================
** convert_params
**=========================================================================
** Convert an XML element representing a list of params into an
** xmlrpc_value (of type array).
*/
static xmlrpc_value *
convert_params(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xmlrpc_value *array, *item;
int size, i;
xml_element **params, *param, *value;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Set up our error-handling preconditions. */
array = item = NULL;
/* Allocate an array to hold our parameters. */
array = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
/* We're responsible for checking our own element name. */
CHECK_NAME(env, elem, "params");
/* Iterate over our children. */
size = xml_element_children_size(elem);
params = xml_element_children(elem);
for (i = 0; i < size; i++) {
param = params[i];
CHECK_NAME(env, param, "param");
CHECK_CHILD_COUNT(env, param, 1);
value = xml_element_children(param)[0];
item = convert_value(env, depth, value);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, array, item);
xmlrpc_DECREF(item);
item = NULL;
XMLRPC_FAIL_IF_FAULT(env);
}
cleanup:
if (env->fault_occurred) {
if (array)
xmlrpc_DECREF(array);
if (item)
xmlrpc_DECREF(item);
return NULL;
}
return array;
}
static void
parseCallXml(xmlrpc_env * const envP,
const char * const xmlData,
size_t const xmlLen,
xml_element ** const callElemP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
*callElemP = xml_parse(&env, xmlData, xmlLen);
if (env.fault_occurred)
xmlrpc_env_set_fault_formatted(
envP, env.fault_code, "Call is not valid XML. %s",
env.fault_string);
xmlrpc_env_clean(&env);
}
/*=========================================================================
** xmlrpc_parse_call
**=========================================================================
** Given some XML text, attempt to parse it as an XML-RPC call. Return
** a newly allocated xmlrpc_call structure (or NULL, if an error occurs).
** The two output variables will contain either valid values (which
** must free() and xmlrpc_DECREF(), respectively) or NULLs (if an error
** occurs).
*/
void
xmlrpc_parse_call(xmlrpc_env * const envP,
const char * const xml_data,
size_t const xml_len,
const char ** const out_method_nameP,
xmlrpc_value ** const out_param_arrayPP) {
xml_element *call_elem, *name_elem, *params_elem;
char *cdata;
unsigned depth;
size_t call_child_count;
char * outMethodName;
xmlrpc_value * outParamArrayP;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(xml_data != NULL);
XMLRPC_ASSERT(out_method_nameP != NULL && out_param_arrayPP != NULL);
/* Set up our error-handling preconditions. */
outMethodName = NULL;
outParamArrayP = NULL;
call_elem = NULL;
/* SECURITY: Last-ditch attempt to make sure our content length is legal.
** XXX - This check occurs too late to prevent an attacker from creating
** an enormous memory block in RAM, so you should try to enforce it
** *before* reading any data off the network. */
if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
XMLRPC_FAIL(envP, XMLRPC_LIMIT_EXCEEDED_ERROR,
"XML-RPC request too large");
parseCallXml(envP, xml_data, xml_len, &call_elem);
XMLRPC_FAIL_IF_FAULT(envP);
/* Pick apart and verify our structure. */
CHECK_NAME(envP, call_elem, "methodCall");
call_child_count = xml_element_children_size(call_elem);
if (call_child_count != 2 && call_child_count != 1)
XMLRPC_FAIL1(envP, XMLRPC_PARSE_ERROR,
"Expected <methodCall> to have 1 or 2 children, found %d",
call_child_count);
/* Extract the method name.
** SECURITY: We make sure the method name is valid UTF-8. */
name_elem = get_child_by_name(envP, call_elem, "methodName");
XMLRPC_FAIL_IF_FAULT(envP);
CHECK_CHILD_COUNT(envP, name_elem, 0);
cdata = xml_element_cdata(name_elem);
#ifdef HAVE_UNICODE_WCHAR
xmlrpc_validate_utf8(envP, cdata, strlen(cdata));
XMLRPC_FAIL_IF_FAULT(envP);
#endif /* HAVE_UNICODE_WCHAR */
outMethodName = malloc(strlen(cdata) + 1);
XMLRPC_FAIL_IF_NULL(outMethodName, envP, XMLRPC_INTERNAL_ERROR,
"Could not allocate memory for method name");
strcpy(outMethodName, cdata);
/* Convert our parameters. */
if (call_child_count == 1) {
/* Workaround for Ruby XML-RPC and old versions of xmlrpc-epi. */
outParamArrayP = xmlrpc_build_value(envP, "()");
XMLRPC_FAIL_IF_FAULT(envP);
} else {
params_elem = get_child_by_name(envP, call_elem, "params");
XMLRPC_FAIL_IF_FAULT(envP);
depth = 0;
outParamArrayP = convert_params(envP, &depth, params_elem);
XMLRPC_ASSERT(depth == 0);
XMLRPC_FAIL_IF_FAULT(envP);
}
cleanup:
if (call_elem)
xml_element_free(call_elem);
if (envP->fault_occurred) {
if (outMethodName)
free(outMethodName);
if (outParamArrayP)
xmlrpc_DECREF(outParamArrayP);
outMethodName = NULL;
outParamArrayP = NULL;
}
*out_method_nameP = outMethodName;
*out_param_arrayPP = outParamArrayP;
}
/*=========================================================================
** xmlrpc_parse_response
**=========================================================================
** Given some XML text, attempt to parse it as an XML-RPC response.
** If the response is a regular, valid response, return a new reference
** to the appropriate value. If the response is a fault, or an error
** occurs during processing, return NULL and set up env appropriately.
*/
xmlrpc_value *
xmlrpc_parse_response(xmlrpc_env *env,
const char *xml_data,
size_t xml_len) {
xml_element *response, *child, *value;
unsigned depth;
xmlrpc_value *params, *retval, *fault;
int retval_incremented;
xmlrpc_value *fault_code_value, *fault_str_value;
xmlrpc_int32 fault_code;
char *fault_str;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(xml_data != NULL);
/* Set up our error-handling preconditions. */
response = NULL;
params = fault = NULL;
retval_incremented = 0;
/* SECURITY: Last-ditch attempt to make sure our content length is legal.
** XXX - This check occurs too late to prevent an attacker from creating
** an enormous memory block in RAM, so you should try to enforce it
** *before* reading any data off the network. */
if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
XMLRPC_FAIL(env, XMLRPC_LIMIT_EXCEEDED_ERROR,
"XML-RPC response too large");
/* SECURITY: Set up our recursion depth counter. */
depth = 0;
/* Parse our XML data. */
response = xml_parse(env, xml_data, xml_len);
XMLRPC_FAIL_IF_FAULT(env);
/* Pick apart and verify our structure. */
CHECK_NAME(env, response, "methodResponse");
CHECK_CHILD_COUNT(env, response, 1);
child = xml_element_children(response)[0];
/* Parse the response itself. */
if (strcmp("params", xml_element_name(child)) == 0) {
/* Convert our parameter list. */
params = convert_params(env, &depth, child);
XMLRPC_FAIL_IF_FAULT(env);
/* Extract the return value, and jiggle our reference counts. */
xmlrpc_parse_value(env, params, "(V)", &retval);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_INCREF(retval);
retval_incremented = 1;
} else if (strcmp("fault", xml_element_name(child)) == 0) {
/* Convert our fault structure. */
CHECK_CHILD_COUNT(env, child, 1);
value = xml_element_children(child)[0];
fault = convert_value(env, &depth, value);
XMLRPC_FAIL_IF_FAULT(env);
XMLRPC_TYPE_CHECK(env, fault, XMLRPC_TYPE_STRUCT);
/* Get our fault code. */
fault_code_value = xmlrpc_struct_get_value(env, fault, "faultCode");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_parse_value(env, fault_code_value, "i", &fault_code);
XMLRPC_FAIL_IF_FAULT(env);
/* Get our fault string. */
fault_str_value = xmlrpc_struct_get_value(env, fault, "faultString");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_parse_value(env, fault_str_value, "s", &fault_str);
XMLRPC_FAIL_IF_FAULT(env);
/* Return our fault. */
XMLRPC_FAIL(env, fault_code, fault_str);
} else {
XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
"Expected <params> or <fault> in <methodResponse>");
}
/* Sanity-check our depth-counting code. */
XMLRPC_ASSERT(depth == 0);
cleanup:
if (response)
xml_element_free(response);
if (params)
xmlrpc_DECREF(params);
if (fault)
xmlrpc_DECREF(fault);
if (env->fault_occurred) {
if (retval_incremented)
xmlrpc_DECREF(retval);
return NULL;
}
return retval;
}