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 setParseFault(envP, "<methodResponse> must contain <params> or <fault>, " "but contains <%s>.", xml_element_name(child)); } else setParseFault(envP, "<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 is 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 { xmlrpc_env env; xmlrpc_env_init(&env); xml_parse(&env, xmlData, xmlDataLen, &response); if (env.fault_occurred) setParseFault(envP, "Not valid XML. %s", env.fault_string); else { /* Pick apart and verify our structure. */ if (xmlrpc_streq(xml_element_name(response), "methodResponse")) { parseMethodResponseElt(envP, response, resultPP, faultCodeP, faultStringP); } else setParseFault(envP, "XML-RPC response must consist of a " "<methodResponse> element. " "This has a <%s> instead.", xml_element_name(response)); xml_element_free(response); } xmlrpc_env_clean(&env); } }
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; const char * mantissaEnd; const char * fraction; const char * fractionEnd; 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; } }
void xmlrpc_parse_value_xml(xmlrpc_env * const envP, const char * const xmlData, size_t const xmlDataLen, xmlrpc_value ** const valuePP) { /*---------------------------------------------------------------------------- Compute the xmlrpc_value represented by the XML document 'xmlData' (of length 'xmlDataLen' characters), which must consist of a single <value> element. 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. This isn't generally useful in XML-RPC programs, because such programs parse a whole XML-RPC call or response document, and never see the XML text of just a <value> element. But a program may do some weird form of XML-RPC processing or just borrow Xmlrpc-c's value serialization facilities for something unrelated to XML-RPC. In any case, it makes sense to have an inverse of xmlrpc_serialize_value2(), which generates XML text from an xmlrpc_value. -----------------------------------------------------------------------------*/ xmlrpc_env env; xml_element * valueEltP; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(xmlData != NULL); xmlrpc_env_init(&env); xml_parse(&env, xmlData, xmlDataLen, &valueEltP); if (env.fault_occurred) { setParseFault(envP, "Not valid XML. %s", env.fault_string); } else { if (xmlrpc_streq(xml_element_name(valueEltP), "value")) { unsigned int const maxRecursion = (unsigned int) xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID); xmlrpc_parseValue(envP, maxRecursion, valueEltP, valuePP); } else setParseFault(envP, "XML-RPC value XML document must consist of " "a <value> element. This has a <%s> instead.", xml_element_name(valueEltP)); xml_element_free(valueEltP); } xmlrpc_env_clean(&env); }
static void parseCallXml(xmlrpc_env * const envP, const char * const xmlData, size_t const xmlDataLen, xml_element ** const callElemPP) { /*---------------------------------------------------------------------------- Parse the XML of an XML-RPC call. -----------------------------------------------------------------------------*/ xml_element * callElemP; xmlrpc_env env; xmlrpc_env_init(&env); xml_parse(&env, xmlData, xmlDataLen, &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")) setParseFault(envP, "XML-RPC call should be a <methodCall> element. " "Instead, we have a <%s> element.", xml_element_name(callElemP)); if (envP->fault_occurred) xml_element_free(callElemP); } *callElemPP = callElemP; xmlrpc_env_clean(&env); }
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) { unsigned int const childCount = xml_element_children_size(arrayElemP); if (childCount != 1) setParseFault(envP, "<array> element has %u children. Only one <data> " "makes sense.", 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 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 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 void interpretFaultValue(xmlrpc_env * const envP, xmlrpc_value * const faultVP, int * const faultCodeP, const char ** const faultStringP) { if (faultVP->_type != XMLRPC_TYPE_STRUCT) setParseFault(envP, "<value> element of <fault> response 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) { interpretFaultCode(&fvEnv, faultCodeVP, faultCodeP); if (!fvEnv.fault_occurred) { xmlrpc_value * faultStringVP; xmlrpc_struct_read_value(&fvEnv, faultVP, "faultString", &faultStringVP); if (!fvEnv.fault_occurred) { interpretFaultString(&fvEnv, faultStringVP, faultStringP); xmlrpc_DECREF(faultStringVP); } } xmlrpc_DECREF(faultCodeVP); } if (fvEnv.fault_occurred) setParseFault(envP, "Invalid struct for <fault> value. %s", fvEnv.fault_string); xmlrpc_env_clean(&fvEnv); } }
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 = getChildByName(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 = getChildByName(envP, callElemP, "params"); if (!envP->fault_occurred) *paramArrayPP = convertParams(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) setParseFault(envP, "<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); } } }
static void parseFaultElement(xmlrpc_env * const envP, const xml_element * const faultElement, int * const faultCodeP, const char ** const faultStringP) { unsigned int const maxRecursion = (unsigned int) xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID); XMLRPC_ASSERT(xmlrpc_streq(xml_element_name(faultElement), "fault")); if (xml_element_children_size(faultElement) != 1) setParseFault(envP, "<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]; const char * const elemName = xml_element_name(faultValueP); if (!xmlrpc_streq(elemName, "value")) setParseFault(envP, "<fault> contains a <%s> element. " "Only <value> makes sense.", elemName); else { xmlrpc_value * faultVP; xmlrpc_parseValue(envP, maxRecursion, faultValueP, &faultVP); if (!envP->fault_occurred) { interpretFaultValue(envP, faultVP, faultCodeP, faultStringP); xmlrpc_DECREF(faultVP); } } } }
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; 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 parseName(xmlrpc_env * const envP, xml_element * const nameElemP, xmlrpc_value ** const valuePP) { unsigned int const childCount = xml_element_children_size(nameElemP); if (childCount > 0) setParseFault(envP, "<name> element has %u children. " "Should have none.", 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 xml_element * getChildByName (xmlrpc_env * const envP, xml_element * const parentP, const char * const name) { size_t const childCount = xml_element_children_size(parentP); xml_element ** const childrenP = xml_element_children(parentP); unsigned int i; for (i = 0; i < childCount; ++i) { if (xmlrpc_streq(xml_element_name(childrenP[i]), name)) return childrenP[i]; } setParseFault(envP, "Expected <%s> to have child <%s>", xml_element_name(parentP), name); return NULL; }
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 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 = convertParams(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) setParseFault(envP, "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 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; 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 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 parseMember(xmlrpc_env * const envP, xml_element * const memberP, unsigned int const maxRecursion, xmlrpc_value ** const keyPP, xmlrpc_value ** const valuePP) { unsigned int 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.", childCount); else { xml_element * nameElemP; getNameChild(envP, memberP, &nameElemP); if (!envP->fault_occurred) { parseName(envP, nameElemP, keyPP); if (!envP->fault_occurred) { xml_element * valueElemP; getValueChild(envP, memberP, &valueElemP); if (!envP->fault_occurred) xmlrpc_parseValue(envP, maxRecursion-1, valueElemP, valuePP); if (envP->fault_occurred) xmlrpc_DECREF(*keyPP); } } } }
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) setParseFault(envP, "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"); } } }