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); }
void xml_element_free(xml_element * const elemP) { /*---------------------------------------------------------------------------- Blow away an existing element & all of its child elements. -----------------------------------------------------------------------------*/ xmlrpc_mem_block * children; unsigned int size; unsigned int i; xml_element ** contents; XMLRPC_ASSERT_ELEM_OK(elemP); xmlrpc_strfree(elemP->name); elemP->name = XMLRPC_BAD_POINTER; xmlrpc_mem_block_clean(&elemP->cdata); /* Deallocate all of our children recursively. */ children = &elemP->children; contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element *, children); size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element *, children); for (i = 0; i < size; ++i) xml_element_free(contents[i]); xmlrpc_mem_block_clean(&elemP->children); free(elemP); }
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 test_parse_xml_call(void) { xmlrpc_env env; const char *method_name; xmlrpc_value *params; int i1, i2; const char **bad_call; xml_element *elem; xmlrpc_env_init(&env); /* Parse a valid call. */ xmlrpc_parse_call(&env, serialized_call, strlen(serialized_call), &method_name, ¶ms); TEST_NO_FAULT(&env); TEST(params != NULL); xmlrpc_decompose_value(&env, params, "(ii)", &i1, &i2); xmlrpc_DECREF(params); TEST_NO_FAULT(&env); TEST(streq(method_name, "gloom&doom")); TEST(i1 == 10 && i2 == 20); strfree(method_name); /* Test some poorly-formed XML data. */ xmlrpc_parse_call(&env, unparseable_value, strlen(unparseable_value), &method_name, ¶ms); TEST_FAULT(&env, XMLRPC_PARSE_ERROR); TEST(method_name == NULL && params == NULL); /* Next, check for bogus values. These are all well-formed XML, but they aren't legal XML-RPC. */ for (bad_call = bad_calls; *bad_call != NULL; ++bad_call) { /* First, check to make sure that our test case is well-formed XML. ** (It's easy to make mistakes when writing the test cases!) */ xml_parse(&env, *bad_call, strlen(*bad_call), &elem); TEST_NO_FAULT(&env); xml_element_free(elem); /* Now, make sure the higher-level routine barfs appropriately. */ xmlrpc_parse_call(&env, *bad_call, strlen(*bad_call), &method_name, ¶ms); TEST_FAULT(&env, XMLRPC_PARSE_ERROR); TEST(method_name == NULL && params == NULL); } xmlrpc_env_clean(&env); }
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 testParseBadResult(void) { /*---------------------------------------------------------------------------- Test parsing of data that is supposed to be a response, but is not valid. It looks like a valid success response, but the result value is not valid XML-RPC. -----------------------------------------------------------------------------*/ unsigned int i; for (i = 0; bad_values[i] != NULL; ++i) { const char * const bad_resp = bad_values[i]; xmlrpc_env env; xmlrpc_value * valueP; xml_element *elem; int faultCode; const char * faultString; xmlrpc_env_init(&env); /* First, check to make sure that our test case is well-formed XML. ** (It's easy to make mistakes when writing the test cases!) */ xml_parse(&env, bad_resp, strlen(bad_resp), &elem); TEST_NO_FAULT(&env); xml_element_free(elem); /* Now, make sure the higher-level routine barfs appropriately. */ xmlrpc_parse_response2(&env, bad_resp, strlen(bad_resp), &valueP, &faultCode, &faultString); TEST_FAULT(&env, XMLRPC_PARSE_ERROR); xmlrpc_env_clean(&env); xmlrpc_env_init(&env); /* And again with the old interface */ valueP = xmlrpc_parse_response(&env, bad_resp, strlen(bad_resp)); TEST_FAULT(&env, XMLRPC_PARSE_ERROR); TEST(valueP == NULL); xmlrpc_env_clean(&env); } }
static void test_expat (void) { xmlrpc_env env; xml_element *elem, *array, *data, *value1, *i4; char *cdata; size_t size; xmlrpc_env_init(&env); /* Parse a moderately complex XML document. */ xml_parse(&env, expat_data, strlen(expat_data), &elem); TEST_NO_FAULT(&env); TEST(elem != NULL); /* Verify our results. */ TEST(streq(xml_element_name(elem), "value")); TEST(xml_element_children_size(elem) == 1); array = xml_element_children(elem)[0]; TEST(streq(xml_element_name(array), "array")); TEST(xml_element_children_size(array) == 1); data = xml_element_children(array)[0]; TEST(streq(xml_element_name(data), "data")); TEST(xml_element_children_size(data) > 1); value1 = xml_element_children(data)[0]; TEST(streq(xml_element_name(value1), "value")); TEST(xml_element_children_size(value1) == 1); i4 = xml_element_children(value1)[0]; TEST(streq(xml_element_name(i4), "i4")); TEST(xml_element_children_size(i4) == 0); cdata = xml_element_cdata(i4); size = xml_element_cdata_size(i4); TEST(size == strlen("2147483647")); TEST(memcmp(cdata, "2147483647", strlen("2147483647")) == 0); /* Test cleanup code (w/memprof). */ xml_element_free(elem); /* Test broken XML */ xml_parse(&env, expat_error_data, strlen(expat_error_data), &elem); TEST(env.fault_occurred); xmlrpc_env_clean(&env); }
void xmlrpc_parse_call(xmlrpc_env * const envP, const char * const xmlData, size_t const xmlDataLen, const char ** const methodNameP, xmlrpc_value ** const paramArrayPP) { /*---------------------------------------------------------------------------- Given some XML text, attempt to parse it as an XML-RPC call. Return as *methodNameP the name of the method identified in the call and as *paramArrayPP the parameter list as an XML-RPC array. Caller must free() and xmlrpc_DECREF() these, respectively). -----------------------------------------------------------------------------*/ 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 (xmlDataLen > 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", (unsigned)xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)); else { xml_element * callElemP; parseCallXml(envP, xmlData, xmlDataLen, &callElemP); if (!envP->fault_occurred) { parseCallChildren(envP, callElemP, methodNameP, paramArrayPP); xml_element_free(callElemP); } } if (envP->fault_occurred) { /* Should not be necessary, but for backward compatibility: */ *methodNameP = NULL; *paramArrayPP = NULL; } }
static void xmlElementAppendChild(xmlrpc_env * const envP, xml_element * const elemP, xml_element * const childP) { /* Whether or not this function succeeds, it takes ownership of the 'child' argument. WARNING - This is the exact opposite of the usual memory ownership rules for xmlrpc_value! So please pay attention. */ XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_ELEM_OK(elemP); XMLRPC_ASSERT_ELEM_OK(childP); assert(childP->parentP == NULL); XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element *, envP, &elemP->children, &childP, 1); if (!envP->fault_occurred) childP->parentP = elemP; else xml_element_free(childP); }
static void testParseBadResponseXmlRpc(void) { /*---------------------------------------------------------------------------- Test parsing of data that is supposed to be a response, and is valid XML, but is not valid XML-RPC. -----------------------------------------------------------------------------*/ unsigned int i; /* For this test, we test up to but not including the <value> in a successful RPC response. */ /* Next, check for bogus responses. These are all well-formed XML, but ** they aren't legal XML-RPC. */ for (i = 15; bad_responses[i] != NULL; ++i) { const char * const bad_resp = bad_responses[i]; xmlrpc_env env; xmlrpc_value * v; xml_element *elem; xmlrpc_env_init(&env); /* First, check to make sure that our test case is well-formed XML. ** (It's easy to make mistakes when writing the test cases!) */ xml_parse(&env, bad_resp, strlen(bad_resp), &elem); TEST_NO_FAULT(&env); xml_element_free(elem); /* Now, make sure the higher-level routine barfs appropriately. */ v = xmlrpc_parse_response(&env, bad_resp, strlen(bad_resp)); TEST(env.fault_occurred); TEST(env.fault_code != 0); /* We use 0 as a code in our bad faults. */ TEST(v == NULL); xmlrpc_env_clean(&env); } }