summaryrefslogtreecommitdiffstats
path: root/Utilities/cmxmlrpc/xmlrpc_parse.c
diff options
context:
space:
mode:
authorAndy Cedilnik <andy.cedilnik@kitware.com>2005-02-22 18:08:27 (GMT)
committerAndy Cedilnik <andy.cedilnik@kitware.com>2005-02-22 18:08:27 (GMT)
commitb9b4ea0f7bbf0313d8aa5e354ee56f912969763c (patch)
treeba5e857cda91949be14f508e6df490e1e2176f40 /Utilities/cmxmlrpc/xmlrpc_parse.c
parentbfcb4b693763bb11f841094d2ca9852a64e5d33b (diff)
downloadCMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.zip
CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.gz
CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.bz2
ENH: Initial import
Diffstat (limited to 'Utilities/cmxmlrpc/xmlrpc_parse.c')
-rw-r--r--Utilities/cmxmlrpc/xmlrpc_parse.c767
1 files changed, 767 insertions, 0 deletions
diff --git a/Utilities/cmxmlrpc/xmlrpc_parse.c b/Utilities/cmxmlrpc/xmlrpc_parse.c
new file mode 100644
index 0000000..8a1f172
--- /dev/null
+++ b/Utilities/cmxmlrpc/xmlrpc_parse.c
@@ -0,0 +1,767 @@
+/* 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 strlen,
+ 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(str[0]))
+ XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+ "\"%s\" must not contain whitespace", str);
+
+ /* Convert the value. */
+ end = str + strlen;
+ 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 + strlen)
+ 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 strlen)
+{
+ 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(str[0]))
+ XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+ "\"%s\" must not contain whitespace", str);
+
+ /* Convert the value. */
+ end = str + strlen;
+ 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 + strlen)
+ 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;
+}