745 lines
24 KiB
C
745 lines
24 KiB
C
#include "xmlrpc_config.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
|
|
#include "bool.h"
|
|
|
|
#include "xmlrpc-c/base.h"
|
|
#include "xmlrpc-c/base_int.h"
|
|
#include "xmlrpc-c/string_int.h"
|
|
#include "xmlrpc-c/string_number.h"
|
|
#include "xmlrpc-c/util.h"
|
|
#include "xmlrpc-c/xmlparser.h"
|
|
#include "parse_datetime.h"
|
|
|
|
#include "parse_value.h"
|
|
|
|
|
|
|
|
static void
|
|
setParseFault(xmlrpc_env * const envP,
|
|
const char * const format,
|
|
...) {
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
xmlrpc_set_fault_formatted_v(envP, XMLRPC_PARSE_ERROR, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseArrayDataChild(xmlrpc_env * const envP,
|
|
xml_element * const childP,
|
|
unsigned int const maxRecursion,
|
|
xmlrpc_value * const arrayP) {
|
|
|
|
const char * const elemName = xml_element_name(childP);
|
|
|
|
if (!xmlrpc_streq(elemName, "value"))
|
|
setParseFault(envP, "<data> element has <%s> child. "
|
|
"Only <value> makes sense.", elemName);
|
|
else {
|
|
xmlrpc_value * itemP;
|
|
|
|
xmlrpc_parseValue(envP, maxRecursion-1, childP, &itemP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_array_append_item(envP, arrayP, itemP);
|
|
|
|
xmlrpc_DECREF(itemP);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseArray(xmlrpc_env * const envP,
|
|
unsigned int const maxRecursion,
|
|
xml_element * const arrayElemP,
|
|
xmlrpc_value ** const arrayPP) {
|
|
|
|
xmlrpc_value * arrayP;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT(arrayElemP != NULL);
|
|
|
|
arrayP = xmlrpc_array_new(envP);
|
|
if (!envP->fault_occurred) {
|
|
size_t const childCount = xml_element_children_size(arrayElemP);
|
|
|
|
if (childCount != 1)
|
|
setParseFault(envP,
|
|
"<array> element has %u children. Only one <data> "
|
|
"makes sense.", (unsigned int)childCount);
|
|
else {
|
|
xml_element * const dataElemP =
|
|
xml_element_children(arrayElemP)[0];
|
|
const char * const elemName = xml_element_name(dataElemP);
|
|
|
|
if (!xmlrpc_streq(elemName, "data"))
|
|
setParseFault(envP,
|
|
"<array> element has <%s> child. Only <data> "
|
|
"makes sense.", elemName);
|
|
else {
|
|
xml_element ** const values = xml_element_children(dataElemP);
|
|
unsigned int const size = xml_element_children_size(dataElemP);
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < size && !envP->fault_occurred; ++i)
|
|
parseArrayDataChild(envP, values[i], maxRecursion, arrayP);
|
|
}
|
|
}
|
|
if (envP->fault_occurred)
|
|
xmlrpc_DECREF(arrayP);
|
|
else
|
|
*arrayPP = arrayP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseName(xmlrpc_env * const envP,
|
|
xml_element * const nameElemP,
|
|
xmlrpc_value ** const valuePP) {
|
|
|
|
size_t const childCount = xml_element_children_size(nameElemP);
|
|
|
|
if (childCount > 0)
|
|
setParseFault(envP, "<name> element has %u children. "
|
|
"Should have none.", (unsigned int)childCount);
|
|
else {
|
|
const char * const cdata = xml_element_cdata(nameElemP);
|
|
size_t const cdataSize = xml_element_cdata_size(nameElemP);
|
|
|
|
*valuePP = xmlrpc_string_new_lp(envP, cdataSize, cdata);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
getNameChild(xmlrpc_env * const envP,
|
|
xml_element * const parentP,
|
|
xml_element * * const childPP) {
|
|
|
|
xml_element ** const children = xml_element_children(parentP);
|
|
size_t const childCount = xml_element_children_size(parentP);
|
|
|
|
xml_element * childP;
|
|
unsigned int i;
|
|
|
|
for (i = 0, childP = NULL; i < childCount && !childP; ++i) {
|
|
if (xmlrpc_streq(xml_element_name(children[i]), "name"))
|
|
childP = children[i];
|
|
}
|
|
if (!childP)
|
|
xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR,
|
|
"<member> has no <name> child");
|
|
else
|
|
*childPP = childP;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
getValueChild(xmlrpc_env * const envP,
|
|
xml_element * const parentP,
|
|
xml_element * * const childPP) {
|
|
|
|
xml_element ** const children = xml_element_children(parentP);
|
|
size_t const childCount = xml_element_children_size(parentP);
|
|
|
|
xml_element * childP;
|
|
unsigned int i;
|
|
|
|
for (i = 0, childP = NULL; i < childCount && !childP; ++i) {
|
|
if (xmlrpc_streq(xml_element_name(children[i]), "value"))
|
|
childP = children[i];
|
|
}
|
|
if (!childP)
|
|
xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR,
|
|
"<member> has no <value> child");
|
|
else
|
|
*childPP = childP;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseMember(xmlrpc_env * const envP,
|
|
xml_element * const memberP,
|
|
unsigned int const maxRecursion,
|
|
xmlrpc_value ** const keyPP,
|
|
xmlrpc_value ** const valuePP) {
|
|
|
|
size_t const childCount = xml_element_children_size(memberP);
|
|
|
|
if (childCount != 2)
|
|
setParseFault(envP,
|
|
"<member> element has %u children. Only one <name> and "
|
|
"one <value> make sense.", (unsigned int)childCount);
|
|
else {
|
|
xml_element * nameElemP = NULL;
|
|
|
|
getNameChild(envP, memberP, &nameElemP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
parseName(envP, nameElemP, keyPP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xml_element * valueElemP = NULL;
|
|
|
|
getValueChild(envP, memberP, &valueElemP);
|
|
|
|
if (!envP->fault_occurred)
|
|
xmlrpc_parseValue(envP, maxRecursion-1, valueElemP,
|
|
valuePP);
|
|
|
|
if (envP->fault_occurred)
|
|
xmlrpc_DECREF(*keyPP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseStruct(xmlrpc_env * const envP,
|
|
unsigned int const maxRecursion,
|
|
xml_element * const elemP,
|
|
xmlrpc_value ** const structPP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse the <struct> element 'elemP'.
|
|
-----------------------------------------------------------------------------*/
|
|
xmlrpc_value * structP;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT(elemP != NULL);
|
|
|
|
structP = xmlrpc_struct_new(envP);
|
|
if (!envP->fault_occurred) {
|
|
/* Iterate over our children, extracting key/value pairs. */
|
|
|
|
xml_element ** const members = xml_element_children(elemP);
|
|
unsigned int const size = xml_element_children_size(elemP);
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < size && !envP->fault_occurred; ++i) {
|
|
const char * const elemName = xml_element_name(members[i]);
|
|
|
|
if (!xmlrpc_streq(elemName, "member"))
|
|
setParseFault(envP, "<%s> element found where only <member> "
|
|
"makes sense", elemName);
|
|
else {
|
|
xmlrpc_value * keyP = NULL;
|
|
xmlrpc_value * valueP;
|
|
|
|
parseMember(envP, members[i], maxRecursion, &keyP, &valueP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_struct_set_value_v(envP, structP, keyP, valueP);
|
|
|
|
xmlrpc_DECREF(keyP);
|
|
xmlrpc_DECREF(valueP);
|
|
}
|
|
}
|
|
}
|
|
if (envP->fault_occurred)
|
|
xmlrpc_DECREF(structP);
|
|
else
|
|
*structPP = structP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseInt(xmlrpc_env * const envP,
|
|
const char * const str,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse the content of a <int> XML-RPC XML element, e.g. "34".
|
|
|
|
'str' is that content.
|
|
-----------------------------------------------------------------------------*/
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(str);
|
|
|
|
if (str[0] == '\0')
|
|
setParseFault(envP, "<int> XML element content is empty");
|
|
else if (isspace(str[0]))
|
|
setParseFault(envP, "<int> content '%s' starts with white space",
|
|
str);
|
|
else {
|
|
long i;
|
|
char * tail;
|
|
|
|
errno = 0;
|
|
i = strtol(str, &tail, 10);
|
|
|
|
/* Look for ERANGE. */
|
|
if (errno == ERANGE)
|
|
setParseFault(envP, "<int> XML element value '%s' represents a "
|
|
"number beyond the range that "
|
|
"XML-RPC allows (%d - %d)", str,
|
|
XMLRPC_INT32_MIN, XMLRPC_INT32_MAX);
|
|
else if (errno != 0)
|
|
setParseFault(envP, "unexpected error parsing <int> XML element "
|
|
"value '%s'. strtol() failed with errno %d (%s)",
|
|
str, errno, strerror(errno));
|
|
else {
|
|
/* Look for out-of-range errors which didn't produce ERANGE. */
|
|
if (i < XMLRPC_INT32_MIN)
|
|
setParseFault(envP,
|
|
"<int> value %ld is below the range allowed "
|
|
"by XML-RPC (minimum is %d)",
|
|
i, XMLRPC_INT32_MIN);
|
|
else if (i > XMLRPC_INT32_MAX)
|
|
setParseFault(envP,
|
|
"<int> value %ld is above the range allowed "
|
|
"by XML-RPC (maximum is %d)",
|
|
i, XMLRPC_INT32_MAX);
|
|
else {
|
|
if (tail[0] != '\0')
|
|
setParseFault(envP,
|
|
"<int> value '%s' contains non-numerical "
|
|
"junk: '%s'", str, tail);
|
|
else
|
|
*valuePP = xmlrpc_int_new(envP, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseBoolean(xmlrpc_env * const envP,
|
|
const char * const str,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse the content of a <boolean> XML-RPC XML element, e.g. "1".
|
|
|
|
'str' is that content.
|
|
-----------------------------------------------------------------------------*/
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(str);
|
|
|
|
if (xmlrpc_streq(str, "0") || xmlrpc_streq(str, "1"))
|
|
*valuePP = xmlrpc_bool_new(envP, xmlrpc_streq(str, "1") ? 1 : 0);
|
|
else
|
|
setParseFault(envP, "<boolean> XML element content must be either "
|
|
"'0' or '1' according to XML-RPC. This one has '%s'",
|
|
str);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
scanAndValidateDoubleString(xmlrpc_env * const envP,
|
|
const char * const string,
|
|
const char ** const mantissaP,
|
|
const char ** const mantissaEndP,
|
|
const char ** const fractionP,
|
|
const char ** const fractionEndP) {
|
|
|
|
const char * mantissa;
|
|
const char * dp;
|
|
const char * p;
|
|
|
|
if (string[0] == '-' || string[0] == '+')
|
|
mantissa = &string[1];
|
|
else
|
|
mantissa = &string[0];
|
|
|
|
for (p = mantissa, dp = NULL; *p; ++p) {
|
|
char const c = *p;
|
|
if (c == '.') {
|
|
if (dp) {
|
|
setParseFault(envP, "Two decimal points");
|
|
return;
|
|
} else
|
|
dp = p;
|
|
} else if (c < '0' || c > '9') {
|
|
setParseFault(envP, "Garbage (not sign, digit, or period) "
|
|
"starting at '%s'", p);
|
|
return;
|
|
}
|
|
}
|
|
*mantissaP = mantissa;
|
|
if (dp) {
|
|
*mantissaEndP = dp;
|
|
*fractionP = dp+1;
|
|
*fractionEndP = p;
|
|
} else {
|
|
*mantissaEndP = p;
|
|
*fractionP = p;
|
|
*fractionEndP = p;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
isInfinite(double const value) {
|
|
|
|
return value > DBL_MAX;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseDoubleString(xmlrpc_env * const envP,
|
|
const char * const string,
|
|
double * const valueP) {
|
|
/*----------------------------------------------------------------------------
|
|
Turn e.g. "4.3" into 4.3 .
|
|
-----------------------------------------------------------------------------*/
|
|
/* strtod() is no good for this because it is designed for human
|
|
interfaces; it parses according to locale. As a practical
|
|
matter that sometimes means that it does not recognize "." as a
|
|
decimal point. In XML-RPC, "." is a decimal point.
|
|
|
|
Design note: in my experiments, using strtod() was 10 times
|
|
slower than using this function.
|
|
*/
|
|
const char * mantissa = NULL;
|
|
const char * mantissaEnd = NULL;
|
|
const char * fraction = NULL;
|
|
const char * fractionEnd = NULL;
|
|
|
|
scanAndValidateDoubleString(envP, string, &mantissa, &mantissaEnd,
|
|
&fraction, &fractionEnd);
|
|
|
|
if (!envP->fault_occurred) {
|
|
double accum;
|
|
|
|
accum = 0.0;
|
|
|
|
if (mantissa == mantissaEnd && fraction == fractionEnd) {
|
|
setParseFault(envP, "No digits");
|
|
return;
|
|
}
|
|
{
|
|
/* Add in the whole part */
|
|
const char * p;
|
|
|
|
for (p = mantissa; p < mantissaEnd; ++p) {
|
|
accum *= 10;
|
|
accum += (*p - '0');
|
|
}
|
|
}
|
|
{
|
|
/* Add in the fractional part */
|
|
double significance;
|
|
const char * p;
|
|
for (significance = 0.1, p = fraction;
|
|
p < fractionEnd;
|
|
++p, significance *= 0.1) {
|
|
|
|
accum += (*p - '0') * significance;
|
|
}
|
|
}
|
|
if (isInfinite(accum))
|
|
setParseFault(envP, "Value exceeds the size allowed by XML-RPC");
|
|
else
|
|
*valueP = string[0] == '-' ? (- accum) : accum;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseDoubleStringStrtod(const char * const str,
|
|
bool * const failedP,
|
|
double * const valueP) {
|
|
|
|
if (strlen(str) == 0) {
|
|
/* strtod() happily interprets empty string as 0.0. We don't think
|
|
the user will appreciate that XML-RPC extension.
|
|
*/
|
|
*failedP = true;
|
|
} else {
|
|
char * tail;
|
|
|
|
errno = 0;
|
|
|
|
*valueP = strtod(str, &tail);
|
|
|
|
if (errno != 0)
|
|
*failedP = true;
|
|
else {
|
|
if (tail[0] != '\0')
|
|
*failedP = true;
|
|
else
|
|
*failedP = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseDouble(xmlrpc_env * const envP,
|
|
const char * const str,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse the content of a <double> XML-RPC XML element, e.g. "34.5".
|
|
|
|
'str' is that content.
|
|
-----------------------------------------------------------------------------*/
|
|
xmlrpc_env parseEnv;
|
|
double valueDouble = 0;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(str);
|
|
|
|
xmlrpc_env_init(&parseEnv);
|
|
|
|
parseDoubleString(&parseEnv, str, &valueDouble);
|
|
|
|
if (parseEnv.fault_occurred) {
|
|
/* As an alternative, try a strtod() parsing. strtod()
|
|
accepts other forms, e.g. "3.4E6"; "3,4"; " 3.4". These
|
|
are not permitted by XML-RPC, but an almost-XML-RPC partner
|
|
might use one. In fact, for many years, Xmlrpc-c generated
|
|
such alternatives (by mistake).
|
|
*/
|
|
bool failed;
|
|
parseDoubleStringStrtod(str, &failed, &valueDouble);
|
|
if (failed)
|
|
setParseFault(envP, "<double> element value '%s' is not a valid "
|
|
"floating point number. %s",
|
|
str, parseEnv.fault_string);
|
|
}
|
|
|
|
if (!envP->fault_occurred)
|
|
*valuePP = xmlrpc_double_new(envP, valueDouble);
|
|
|
|
xmlrpc_env_clean(&parseEnv);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseBase64(xmlrpc_env * const envP,
|
|
const char * const str,
|
|
size_t const strLength,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse the content of a <base64> XML-RPC XML element, e.g. "FD32YY".
|
|
|
|
'str' is that content.
|
|
-----------------------------------------------------------------------------*/
|
|
xmlrpc_mem_block * decoded;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(str);
|
|
|
|
decoded = xmlrpc_base64_decode(envP, str, strLength);
|
|
if (!envP->fault_occurred) {
|
|
unsigned char * const bytes =
|
|
XMLRPC_MEMBLOCK_CONTENTS(unsigned char, decoded);
|
|
size_t const byteCount =
|
|
XMLRPC_MEMBLOCK_SIZE(unsigned char, decoded);
|
|
|
|
*valuePP = xmlrpc_base64_new(envP, byteCount, bytes);
|
|
|
|
XMLRPC_MEMBLOCK_FREE(unsigned char, decoded);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseI8(xmlrpc_env * const envP,
|
|
const char * const str,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse the content of a <i8> XML-RPC XML element, e.g. "34".
|
|
|
|
'str' is that content.
|
|
-----------------------------------------------------------------------------*/
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(str);
|
|
|
|
if (str[0] == '\0')
|
|
setParseFault(envP, "<i8> XML element content is empty");
|
|
else if (isspace(str[0]))
|
|
setParseFault(envP,
|
|
"<i8> content '%s' starts with white space", str);
|
|
else {
|
|
xmlrpc_int64 i;
|
|
xmlrpc_env env;
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
xmlrpc_parse_int64(&env, str, &i);
|
|
|
|
if (env.fault_occurred)
|
|
setParseFault(envP, "<i8> XML element value '%s' is invalid "
|
|
"because it does not represent "
|
|
"a 64 bit integer. %s", env.fault_string);
|
|
else
|
|
*valuePP = xmlrpc_i8_new(envP, i);
|
|
|
|
xmlrpc_env_clean(&env);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseSimpleValueCdata(xmlrpc_env * const envP,
|
|
const char * const elementName,
|
|
const char * const cdata,
|
|
size_t const cdataLength,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
Parse an XML element is supposedly a data type element such as
|
|
<string>. Its name is 'elementName', and it has no children, but
|
|
contains cdata 'cdata', which is 'dataLength' characters long.
|
|
-----------------------------------------------------------------------------*/
|
|
/* We need to straighten out the whole character set / encoding thing
|
|
some day. What is 'cdata', and what should it be? Does it have
|
|
embedded NUL? Some of the code here assumes it doesn't. Is it
|
|
text?
|
|
|
|
The <string> parser assumes it's UTF 8 with embedded NULs.
|
|
But the <int> parser will get terribly confused if there are any
|
|
UTF-8 multibyte sequences or NUL characters. So will most of the
|
|
others.
|
|
|
|
The "ex:XXX" element names are what the Apache XML-RPC facility
|
|
uses: http://ws.apache.org/xmlrpc/types.html. (Technically, it
|
|
isn't "ex" but an arbitrary prefix that identifies a namespace
|
|
declared earlier in the XML document -- this is an XML thing.
|
|
But we aren't nearly sophisticated enough to use real XML
|
|
namespaces, so we exploit the fact that XML-RPC actually uses
|
|
"ex").
|
|
|
|
"i1" and "i2" are just from my imagination.
|
|
*/
|
|
|
|
if (xmlrpc_streq(elementName, "int") ||
|
|
xmlrpc_streq(elementName, "i4") ||
|
|
xmlrpc_streq(elementName, "i1") ||
|
|
xmlrpc_streq(elementName, "i2") ||
|
|
xmlrpc_streq(elementName, "ex:i1") ||
|
|
xmlrpc_streq(elementName, "ex:i2"))
|
|
parseInt(envP, cdata, valuePP);
|
|
else if (xmlrpc_streq(elementName, "boolean"))
|
|
parseBoolean(envP, cdata, valuePP);
|
|
else if (xmlrpc_streq(elementName, "double"))
|
|
parseDouble(envP, cdata, valuePP);
|
|
else if (xmlrpc_streq(elementName, "dateTime.iso8601"))
|
|
xmlrpc_parseDatetime(envP, cdata, valuePP);
|
|
else if (xmlrpc_streq(elementName, "string"))
|
|
*valuePP = xmlrpc_string_new_lp(envP, cdataLength, cdata);
|
|
else if (xmlrpc_streq(elementName, "base64"))
|
|
parseBase64(envP, cdata, cdataLength, valuePP);
|
|
else if (xmlrpc_streq(elementName, "nil") ||
|
|
xmlrpc_streq(elementName, "ex:nil"))
|
|
*valuePP = xmlrpc_nil_new(envP);
|
|
else if (xmlrpc_streq(elementName, "i8") ||
|
|
xmlrpc_streq(elementName, "ex:i8"))
|
|
parseI8(envP, cdata, valuePP);
|
|
else
|
|
setParseFault(envP, "Unknown value type -- XML element is named "
|
|
"<%s>", elementName);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseSimpleValue(xmlrpc_env * const envP,
|
|
xml_element * const elemP,
|
|
xmlrpc_value ** const valuePP) {
|
|
|
|
size_t childCount = xml_element_children_size(elemP);
|
|
|
|
if (childCount > 0)
|
|
setParseFault(envP, "The child of a <value> element "
|
|
"is neither <array> nor <struct>, "
|
|
"but has %u child elements of its own.",
|
|
(unsigned int)childCount);
|
|
else {
|
|
const char * const elemName = xml_element_name(elemP);
|
|
const char * const cdata = xml_element_cdata(elemP);
|
|
size_t const cdataSize = xml_element_cdata_size(elemP);
|
|
|
|
parseSimpleValueCdata(envP, elemName, cdata, cdataSize, valuePP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_parseValue(xmlrpc_env * const envP,
|
|
unsigned int const maxRecursion,
|
|
xml_element * const elemP,
|
|
xmlrpc_value ** const valuePP) {
|
|
/*----------------------------------------------------------------------------
|
|
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.
|
|
-----------------------------------------------------------------------------*/
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT(elemP != NULL);
|
|
|
|
/* Assume we'll need to recurse, make sure we're allowed */
|
|
if (maxRecursion < 1)
|
|
xmlrpc_env_set_fault(envP, XMLRPC_PARSE_ERROR,
|
|
"Nested data structure too deep.");
|
|
else {
|
|
if (!xmlrpc_streq(xml_element_name(elemP), "value"))
|
|
setParseFault(envP,
|
|
"<%s> element where <value> expected",
|
|
xml_element_name(elemP));
|
|
else {
|
|
size_t const childCount = xml_element_children_size(elemP);
|
|
|
|
if (childCount == 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);
|
|
*valuePP = xmlrpc_string_new_lp(envP, cdata_size, cdata);
|
|
} else if (childCount > 1)
|
|
setParseFault(envP, "<value> has %u child elements. "
|
|
"Only zero or one make sense.",
|
|
(unsigned int)childCount);
|
|
else {
|
|
/* We should have a type tag inside our value tag. */
|
|
xml_element * const childP = xml_element_children(elemP)[0];
|
|
const char * const childName = xml_element_name(childP);
|
|
|
|
if (xmlrpc_streq(childName, "struct"))
|
|
parseStruct(envP, maxRecursion, childP, valuePP);
|
|
else if (xmlrpc_streq(childName, "array"))
|
|
parseArray(envP, maxRecursion, childP, valuePP);
|
|
else
|
|
parseSimpleValue(envP, childP, valuePP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|