freeswitch/libs/xmlrpc-c/src/parse_value.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);
}
}
}
}