--- /dev/null
+/* Copyright information is at end of file. */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "bool.h"
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/base_int.h"
+#include "xmlrpc-c/string_int.h"
+#include "xmlrpc-c/xmlparser.h"
+
+
+/*=========================================================================
+** Data Format
+**=========================================================================
+** An XML-RPC document consists of 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|
+** nil|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 (!xmlrpc_streq((name), xml_element_name(elem))) \
+ 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 (xmlrpc_streq(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 * const envP,
+ char * const cdata,
+ size_t const cdata_size) {
+#if HAVE_UNICODE_WCHAR
+ xmlrpc_validate_utf8(envP, cdata, cdata_size);
+#endif
+
+ if (envP->fault_occurred)
+ return NULL;
+ return xmlrpc_build_value(envP, "s#", cdata, cdata_size);
+}
+
+
+
+/* Forward declaration for recursion */
+static xmlrpc_value *
+convert_value(xmlrpc_env * const envP,
+ unsigned int const maxRecursion,
+ xml_element * const elemP);
+
+
+
+static void
+convertBase64(xmlrpc_env * const envP,
+ const char * const cdata,
+ size_t const cdata_size,
+ xmlrpc_value ** const valuePP) {
+
+ xmlrpc_mem_block *decoded;
+
+ decoded = xmlrpc_base64_decode(envP, cdata, cdata_size);
+ if (!envP->fault_occurred) {
+ unsigned char * const asciiData =
+ XMLRPC_MEMBLOCK_CONTENTS(unsigned char, decoded);
+ size_t const asciiLen =
+ XMLRPC_MEMBLOCK_SIZE(unsigned char, decoded);
+
+ *valuePP = xmlrpc_build_value(envP, "6", asciiData, asciiLen);
+
+ XMLRPC_MEMBLOCK_FREE(unsigned char, decoded);
+ }
+}
+
+
+
+/*=========================================================================
+** convert_array
+**=========================================================================
+** Convert an XML element representing an array into an xmlrpc_value.
+*/
+
+static xmlrpc_value *
+convert_array(xmlrpc_env * const envP,
+ unsigned int const maxRecursion,
+ xml_element * const elemP) {
+
+ xml_element *data, **values, *value;
+ xmlrpc_value *array, *item;
+ int size, i;
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT(elemP != NULL);
+
+ /* Set up our error-handling preconditions. */
+ array = item = NULL;
+
+ /* Allocate an array to hold our values. */
+ array = xmlrpc_build_value(envP, "()");
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ /* We don't need to check our element name--our callers do that. */
+ CHECK_CHILD_COUNT(envP, elemP, 1);
+ data = xml_element_children(elemP)[0];
+ CHECK_NAME(envP, 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(envP, maxRecursion-1, value);
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ xmlrpc_array_append_item(envP, array, item);
+ xmlrpc_DECREF(item);
+ item = NULL;
+ XMLRPC_FAIL_IF_FAULT(envP);
+ }
+
+ cleanup:
+ if (item)
+ xmlrpc_DECREF(item);
+ if (envP->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 * const envP,
+ unsigned int const maxRecursion,
+ xml_element * const elemP) {
+
+ xmlrpc_value *strct, *key, *value;
+ xml_element **members, *member, *name_elemP, *value_elemP;
+ int size, i;
+ char *cdata;
+ size_t cdata_size;
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT(elemP != NULL);
+
+ /* Set up our error-handling preconditions. */
+ strct = key = value = NULL;
+
+ /* Allocate an array to hold our members. */
+ strct = xmlrpc_struct_new(envP);
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ /* 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(elemP);
+ size = xml_element_children_size(elemP);
+ for (i = 0; i < size; i++) {
+ member = members[i];
+ CHECK_NAME(envP, member, "member");
+ CHECK_CHILD_COUNT(envP, member, 2);
+
+ /* Get our key. */
+ name_elemP = get_child_by_name(envP, member, "name");
+ XMLRPC_FAIL_IF_FAULT(envP);
+ CHECK_CHILD_COUNT(envP, name_elemP, 0);
+ cdata = xml_element_cdata(name_elemP);
+ cdata_size = xml_element_cdata_size(name_elemP);
+ key = make_string(envP, cdata, cdata_size);
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ /* Get our value. */
+ value_elemP = get_child_by_name(envP, member, "value");
+ XMLRPC_FAIL_IF_FAULT(envP);
+ value = convert_value(envP, maxRecursion-1, value_elemP);
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ /* Add the key/value pair to our struct. */
+ xmlrpc_struct_set_value_v(envP, strct, key, value);
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ /* Release our references & memory, and restore our invariants. */
+ xmlrpc_DECREF(key);
+ key = NULL;
+ xmlrpc_DECREF(value);
+ value = NULL;
+ }
+
+ cleanup:
+ if (key)
+ xmlrpc_DECREF(key);
+ if (value)
+ xmlrpc_DECREF(value);
+ if (envP->fault_occurred) {
+ if (strct)
+ xmlrpc_DECREF(strct);
+ return NULL;
+ }
+ return strct;
+}
+
+
+
+static xmlrpc_value *
+convert_value(xmlrpc_env * const envP,
+ unsigned int const maxRecursion,
+ xml_element * const elemP) {
+/*----------------------------------------------------------------------------
+ Compute the xmlrpc_value represented by the XML <value> element 'elem'.
+ Return that xmlrpc_value.
+
+ We call convert_array() and convert_struct(), which may ultimately
+ call us recursively. Don't recurse any more than 'maxRecursion'
+ times.
+-----------------------------------------------------------------------------*/
+ int child_count;
+ xmlrpc_value * retval;
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT(elemP != NULL);
+
+ /* Error-handling preconditions.
+ ** If we haven't changed any of these from their default state, we're
+ ** allowed to tail-call xmlrpc_build_value. */
+ retval = NULL;
+
+ /* Assume we'll need to recurse, make sure we're allowed */
+ if (maxRecursion < 1)
+ XMLRPC_FAIL(envP, XMLRPC_PARSE_ERROR,
+ "Nested data structure too deep.");
+
+ /* Validate our structure, and see whether we have a child element. */
+ CHECK_NAME(envP, elemP, "value");
+ child_count = xml_element_children_size(elemP);
+
+ if (child_count == 0) {
+ /* We have no type element, so treat the value as a string. */
+ char * const cdata = xml_element_cdata(elemP);
+ size_t const cdata_size = xml_element_cdata_size(elemP);
+ retval = make_string(envP, cdata, cdata_size);
+ } else {
+ /* We should have a type tag inside our value tag. */
+ xml_element * child;
+ const char * child_name;
+
+ CHECK_CHILD_COUNT(envP, elemP, 1);
+ child = xml_element_children(elemP)[0];
+
+ /* Parse our value-containing element. */
+ child_name = xml_element_name(child);
+ if (xmlrpc_streq(child_name, "struct")) {
+ retval = convert_struct(envP, maxRecursion, child);
+ } else if (xmlrpc_streq(child_name, "array")) {
+ CHECK_CHILD_COUNT(envP, child, 1);
+ retval = convert_array(envP, maxRecursion, child);
+ } else {
+ char * cdata;
+ size_t cdata_size;
+
+ CHECK_CHILD_COUNT(envP, child, 0);
+ cdata = xml_element_cdata(child);
+ cdata_size = xml_element_cdata_size(child);
+ if (xmlrpc_streq(child_name, "i4") ||
+ xmlrpc_streq(child_name, "int")) {
+ xmlrpc_int32 const i =
+ xmlrpc_atoi(envP, cdata, strlen(cdata),
+ XMLRPC_INT32_MIN, XMLRPC_INT32_MAX);
+ XMLRPC_FAIL_IF_FAULT(envP);
+ retval = xmlrpc_build_value(envP, "i", i);
+ } else if (xmlrpc_streq(child_name, "string")) {
+ retval = make_string(envP, cdata, cdata_size);
+ } else if (xmlrpc_streq(child_name, "boolean")) {
+ xmlrpc_int32 const i =
+ xmlrpc_atoi(envP, cdata, strlen(cdata), 0, 1);
+ XMLRPC_FAIL_IF_FAULT(envP);
+ retval = xmlrpc_build_value(envP, "b", (xmlrpc_bool) i);
+ } else if (xmlrpc_streq(child_name, "double")) {
+ double const d = xmlrpc_atod(envP, cdata, strlen(cdata));
+ XMLRPC_FAIL_IF_FAULT(envP);
+ retval = xmlrpc_build_value(envP, "d", d);
+ } else if (xmlrpc_streq(child_name, "dateTime.iso8601")) {
+ retval = xmlrpc_build_value(envP, "8", cdata);
+ } else if (xmlrpc_streq(child_name, "nil")) {
+ retval = xmlrpc_build_value(envP, "n");
+ } else if (xmlrpc_streq(child_name, "base64")) {
+ /* No more tail calls once we do this! */
+
+ convertBase64(envP, cdata, cdata_size, &retval);
+ if (envP->fault_occurred)
+ /* Just for cleanup code: */
+ retval = NULL;
+ } else {
+ XMLRPC_FAIL1(envP, XMLRPC_PARSE_ERROR,
+ "Unknown value type -- XML element is named "
+ "<%s>", child_name);
+ }
+ }
+ }
+
+ cleanup:
+ if (envP->fault_occurred) {
+ if (retval)
+ xmlrpc_DECREF(retval);
+ retval = NULL;
+ }
+ return retval;
+}
+
+
+
+/*=========================================================================
+** 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 * const envP,
+ const xml_element * const elemP) {
+/*----------------------------------------------------------------------------
+ Convert an XML element representing a list of parameters (i.e. a
+ <params> element) to an xmlrpc_value of type array. Note that an
+ array is normally represented in XML by a <value> element. We use
+ type xmlrpc_value to represent the parameter list just for convenience.
+-----------------------------------------------------------------------------*/
+ xmlrpc_value *array, *item;
+ int size, i;
+ xml_element **params, *param, *value;
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT(elemP != NULL);
+
+ /* Set up our error-handling preconditions. */
+ array = item = NULL;
+
+ /* Allocate an array to hold our parameters. */
+ array = xmlrpc_build_value(envP, "()");
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ /* We're responsible for checking our own element name. */
+ CHECK_NAME(envP, elemP, "params");
+
+ /* Iterate over our children. */
+ size = xml_element_children_size(elemP);
+ params = xml_element_children(elemP);
+ for (i = 0; i < size; ++i) {
+ unsigned int const maxNest = xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID);
+
+ param = params[i];
+ CHECK_NAME(envP, param, "param");
+ CHECK_CHILD_COUNT(envP, param, 1);
+
+ value = xml_element_children(param)[0];
+ item = convert_value(envP, maxNest, value);
+ XMLRPC_FAIL_IF_FAULT(envP);
+
+ xmlrpc_array_append_item(envP, array, item);
+ xmlrpc_DECREF(item);
+ item = NULL;
+ XMLRPC_FAIL_IF_FAULT(envP);
+ }
+
+ cleanup:
+ if (envP->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 callElemPP) {
+
+ xml_element * callElemP;
+ xmlrpc_env env;
+
+ xmlrpc_env_init(&env);
+ xml_parse(&env, xmlData, xmlLen, &callElemP);
+ if (env.fault_occurred)
+ xmlrpc_env_set_fault_formatted(
+ envP, env.fault_code, "Call is not valid XML. %s",
+ env.fault_string);
+ else {
+ if (!xmlrpc_streq(xml_element_name(callElemP), "methodCall"))
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "XML-RPC call should be a <methodCall> element. "
+ "Instead, we have a <%s> element.",
+ xml_element_name(callElemP));
+
+ if (!envP->fault_occurred)
+ *callElemPP = callElemP;
+
+ if (envP->fault_occurred)
+ xml_element_free(callElemP);
+ }
+ xmlrpc_env_clean(&env);
+}
+
+
+
+static void
+parseMethodNameElement(xmlrpc_env * const envP,
+ xml_element * const nameElemP,
+ const char ** const methodNameP) {
+
+ XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(nameElemP), "methodName"));
+
+ if (xml_element_children_size(nameElemP) > 0)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "A <methodName> element should not have children. "
+ "This one has %u of them.", xml_element_children_size(nameElemP));
+ else {
+ const char * const cdata = xml_element_cdata(nameElemP);
+
+ xmlrpc_validate_utf8(envP, cdata, strlen(cdata));
+
+ if (!envP->fault_occurred) {
+ *methodNameP = strdup(cdata);
+ if (*methodNameP == NULL)
+ xmlrpc_faultf(envP,
+ "Could not allocate memory for method name");
+ }
+ }
+}
+
+
+
+static void
+parseCallChildren(xmlrpc_env * const envP,
+ xml_element * const callElemP,
+ const char ** const methodNameP,
+ xmlrpc_value ** const paramArrayPP ) {
+/*----------------------------------------------------------------------------
+ Parse the children of a <methodCall> XML element *callElemP. They should
+ be <methodName> and <params>.
+-----------------------------------------------------------------------------*/
+ size_t const callChildCount = xml_element_children_size(callElemP);
+
+ xml_element * nameElemP;
+
+ XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(callElemP), "methodCall"));
+
+ nameElemP = get_child_by_name(envP, callElemP, "methodName");
+
+ if (!envP->fault_occurred) {
+ parseMethodNameElement(envP, nameElemP, methodNameP);
+
+ if (!envP->fault_occurred) {
+ /* Convert our parameters. */
+ if (callChildCount > 1) {
+ xml_element * paramsElemP;
+
+ paramsElemP = get_child_by_name(envP, callElemP, "params");
+
+ if (!envP->fault_occurred)
+ *paramArrayPP = convert_params(envP, paramsElemP);
+ } else {
+ /* Workaround for Ruby XML-RPC and old versions of
+ xmlrpc-epi. Future improvement: Instead of looking
+ at child count, we should just check for existence
+ of <params>.
+ */
+ *paramArrayPP = xmlrpc_array_new(envP);
+ }
+ if (!envP->fault_occurred) {
+ if (callChildCount > 2)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "<methodCall> has extraneous children, other than "
+ "<methodName> and <params>. Total child count = %u",
+ callChildCount);
+
+ if (envP->fault_occurred)
+ xmlrpc_DECREF(*paramArrayPP);
+ }
+ if (envP->fault_occurred)
+ xmlrpc_strfree(*methodNameP);
+ }
+ }
+}
+
+
+
+/*=========================================================================
+** 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 xmlData,
+ size_t const xmlLen,
+ const char ** const methodNameP,
+ xmlrpc_value ** const paramArrayPP) {
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT(xmlData != NULL);
+ XMLRPC_ASSERT(methodNameP != NULL && paramArrayPP != 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, so you should try to
+ enforce it *before* reading any data off the network.
+ */
+ if (xmlLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_LIMIT_EXCEEDED_ERROR,
+ "XML-RPC request too large. Max allowed is %u bytes",
+ xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID));
+ else {
+ xml_element * callElemP;
+ parseCallXml(envP, xmlData, xmlLen, &callElemP);
+ if (!envP->fault_occurred) {
+ parseCallChildren(envP, callElemP, methodNameP, paramArrayPP);
+
+ xml_element_free(callElemP);
+ }
+ }
+ if (envP->fault_occurred) {
+ *methodNameP = NULL;
+ *paramArrayPP = NULL;
+ }
+}
+
+
+
+static void
+interpretFaultValue(xmlrpc_env * const envP,
+ xmlrpc_value * const faultVP,
+ int * const faultCodeP,
+ const char ** const faultStringP) {
+
+ if (faultVP->_type != XMLRPC_TYPE_STRUCT)
+ xmlrpc_env_set_fault(
+ envP, XMLRPC_PARSE_ERROR,
+ "<value> element of <fault> response contains is not "
+ "of structure type");
+ else {
+ xmlrpc_value * faultCodeVP;
+ xmlrpc_env fvEnv;
+
+ xmlrpc_env_init(&fvEnv);
+
+ xmlrpc_struct_read_value(&fvEnv, faultVP, "faultCode", &faultCodeVP);
+ if (!fvEnv.fault_occurred) {
+ xmlrpc_read_int(&fvEnv, faultCodeVP, faultCodeP);
+ if (!fvEnv.fault_occurred) {
+ xmlrpc_value * faultStringVP;
+
+ xmlrpc_struct_read_value(&fvEnv, faultVP, "faultString",
+ &faultStringVP);
+ if (!fvEnv.fault_occurred) {
+ xmlrpc_read_string(&fvEnv, faultStringVP, faultStringP);
+ xmlrpc_DECREF(faultStringVP);
+ }
+ }
+ xmlrpc_DECREF(faultCodeVP);
+ }
+ if (fvEnv.fault_occurred)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "Invalid struct for <fault> value. %s", fvEnv.fault_string);
+
+ xmlrpc_env_clean(&fvEnv);
+ }
+}
+
+
+
+static void
+parseFaultElement(xmlrpc_env * const envP,
+ const xml_element * const faultElement,
+ int * const faultCodeP,
+ const char ** const faultStringP) {
+
+ unsigned int const maxRecursion =
+ xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID);
+
+ XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(faultElement), "fault"));
+
+ if (xml_element_children_size(faultElement) != 1)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "<fault> element should have 1 child, but it has %u.",
+ xml_element_children_size(faultElement));
+ else {
+ xml_element * const faultValueP =
+ xml_element_children(faultElement)[0];
+
+ xmlrpc_value * faultVP;
+
+ faultVP = convert_value(envP, maxRecursion, faultValueP);
+
+ if (!envP->fault_occurred) {
+ interpretFaultValue(envP, faultVP, faultCodeP, faultStringP);
+
+ xmlrpc_DECREF(faultVP);
+ }
+ }
+}
+
+
+
+static void
+parseParamsElement(xmlrpc_env * const envP,
+ const xml_element * const paramsElementP,
+ xmlrpc_value ** const resultPP) {
+
+ xmlrpc_value * paramsVP;
+ xmlrpc_env env;
+
+ xmlrpc_env_init(&env);
+
+ XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(paramsElementP), "params"));
+
+ paramsVP = convert_params(envP, paramsElementP);
+
+ if (!envP->fault_occurred) {
+ int arraySize;
+ xmlrpc_env sizeEnv;
+
+ XMLRPC_ASSERT_ARRAY_OK(paramsVP);
+
+ xmlrpc_env_init(&sizeEnv);
+
+ arraySize = xmlrpc_array_size(&sizeEnv, paramsVP);
+ /* Since it's a valid array, as asserted above, can't fail */
+ XMLRPC_ASSERT(!sizeEnv.fault_occurred);
+
+ if (arraySize != 1)
+ xmlrpc_env_set_fault_formatted(
+ &env, XMLRPC_PARSE_ERROR,
+ "Contains %d items. It should have 1.",
+ arraySize);
+ else {
+ xmlrpc_array_read_item(envP, paramsVP, 0, resultPP);
+ }
+ xmlrpc_DECREF(paramsVP);
+ xmlrpc_env_clean(&sizeEnv);
+ }
+ if (env.fault_occurred)
+ xmlrpc_env_set_fault_formatted(
+ envP, env.fault_code,
+ "Invalid <params> element. %s", env.fault_string);
+
+ xmlrpc_env_clean(&env);
+}
+
+
+
+static void
+parseMethodResponseElt(xmlrpc_env * const envP,
+ const xml_element * const methodResponseEltP,
+ xmlrpc_value ** const resultPP,
+ int * const faultCodeP,
+ const char ** const faultStringP) {
+
+ XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(methodResponseEltP),
+ "methodResponse"));
+
+ if (xml_element_children_size(methodResponseEltP) == 1) {
+ xml_element * const child =
+ xml_element_children(methodResponseEltP)[0];
+
+ if (xmlrpc_streq(xml_element_name(child), "params")) {
+ /* It's a successful response */
+ parseParamsElement(envP, child, resultPP);
+ *faultStringP = NULL;
+ } else if (xmlrpc_streq(xml_element_name(child), "fault")) {
+ /* It's a failure response */
+ parseFaultElement(envP, child, faultCodeP, faultStringP);
+ } else
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "<methodResponse> must contain <params> or <fault>, "
+ "but contains <%s>.", xml_element_name(child));
+ } else
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "<methodResponse> has %u children, should have 1.",
+ xml_element_children_size(methodResponseEltP));
+}
+
+
+
+void
+xmlrpc_parse_response2(xmlrpc_env * const envP,
+ const char * const xmlData,
+ size_t const xmlDataLen,
+ xmlrpc_value ** const resultPP,
+ int * const faultCodeP,
+ const char ** const faultStringP) {
+/*----------------------------------------------------------------------------
+ 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 as *resultP and return NULL as
+ *faultStringP and nothing as *faultCodeP.
+
+ If the response valid, but indicates a failure of the RPC, return the
+ fault string in newly malloc'ed space as *faultStringP and the fault
+ code as *faultCodeP and nothing as *resultP.
+
+ If the XML text is not a valid response or something prevents us from
+ parsing it, return a description of the error as *envP and nothing else.
+-----------------------------------------------------------------------------*/
+ xml_element * response;
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT(xmlData != 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, so you should try to enforce it
+ ** *before* reading any data off the network. */
+ if (xmlDataLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_LIMIT_EXCEEDED_ERROR,
+ "XML-RPC response too large. Our limit is %u characters. "
+ "We got %u characters",
+ xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID), xmlDataLen);
+ else {
+ xml_parse(envP, xmlData, xmlDataLen, &response);
+ if (!envP->fault_occurred) {
+ /* Pick apart and verify our structure. */
+ if (xmlrpc_streq(xml_element_name(response), "methodResponse")) {
+ parseMethodResponseElt(envP, response,
+ resultPP, faultCodeP, faultStringP);
+ } else
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_PARSE_ERROR,
+ "XML-RPC response must consist of a "
+ "<methodResponse> element. This has a <%s> instead.",
+ xml_element_name(response));
+
+ xml_element_free(response);
+ }
+ }
+}
+
+
+
+xmlrpc_value *
+xmlrpc_parse_response(xmlrpc_env * const envP,
+ const char * const xmlData,
+ size_t const xmlDataLen) {
+/*----------------------------------------------------------------------------
+ This exists for backward compatibility. It is like
+ xmlrpc_parse_response2(), except that it merges the concepts of a
+ failed RPC and an error in executing the RPC.
+-----------------------------------------------------------------------------*/
+ xmlrpc_value * retval;
+ xmlrpc_value * result;
+ const char * faultString;
+ int faultCode;
+
+ xmlrpc_parse_response2(envP, xmlData, xmlDataLen,
+ &result, &faultCode, &faultString);
+
+ if (envP->fault_occurred)
+ retval = NULL;
+ else {
+ if (faultString) {
+ xmlrpc_env_set_fault(envP, faultCode, faultString);
+ xmlrpc_strfree(faultString);
+ retval = NULL;
+ } else
+ retval = result; /* transfer reference */
+ }
+ return retval;
+}
+
+
+
+/* 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. */