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); } } }
void xmlrpc_registry_free(xmlrpc_registry * registry) { XMLRPC_ASSERT_PTR_OK(registry); XMLRPC_ASSERT(registry->_methods != XMLRPC_BAD_POINTER); xmlrpc_DECREF(registry->_methods); registry->_methods = XMLRPC_BAD_POINTER; if (registry->_default_method != NULL) xmlrpc_DECREF(registry->_default_method); if (registry->_preinvoke_method != NULL) xmlrpc_DECREF(registry->_preinvoke_method); free(registry); }
void xmlrpc_server_abyss_global_term(void) { /* Note that this is specified as not thread safe; user calls it at the end of his program, when it is only one thread. */ XMLRPC_ASSERT(globallyInitialized > 0); --globallyInitialized; if (globallyInitialized == 0) termAbyss(); }
void xmlrpc_client_cleanup() { /*---------------------------------------------------------------------------- This function is not thread-safe -----------------------------------------------------------------------------*/ XMLRPC_ASSERT(globalClientExists); xmlrpc_client_destroy(globalClientP); globalClientExists = false; /* The following call is not thread-safe */ xmlrpc_client_teardown_global_const(); }
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 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; } }
void xmlrpc_mem_block_append(xmlrpc_env * const envP, xmlrpc_mem_block * const blockP, const void * const data, size_t const len) { size_t const originalSize = blockP->_size; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(blockP != NULL); xmlrpc_mem_block_resize(envP, blockP, originalSize + len); if (!envP->fault_occurred) { memcpy(((unsigned char*) blockP->_block) + originalSize, data, len); } }
static uint32_t hashStructKey(const char * const key, size_t const keyLen) { uint32_t hash; size_t i; XMLRPC_ASSERT(key != NULL); /* This is the Bernstein hash, optimized for lower case ASCII keys. Note that the bytes of such a key differ only in their lower 5 bits. */ for (hash = 0, i = 0; i < keyLen; ++i) hash = hash + key[i] + (hash << 5); return hash; }
/* Resize an xmlrpc_mem_block, preserving as much of the contents as possible. */ void xmlrpc_mem_block_resize (xmlrpc_env * const envP, xmlrpc_mem_block * const blockP, size_t const size) { size_t proposed_alloc; void* new_block; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(blockP != NULL); /* Check to see if we already have enough space. Maybe we'll get lucky. */ if (size <= blockP->_allocated) { blockP->_size = size; return; } /* Calculate a new allocation size. */ #ifdef EFENCE proposed_alloc = size; #else proposed_alloc = blockP->_allocated; while (proposed_alloc < size && proposed_alloc <= BLOCK_ALLOC_MAX) proposed_alloc *= 2; #endif /* DEBUG_MEM_ERRORS */ if (proposed_alloc > BLOCK_ALLOC_MAX) XMLRPC_FAIL(envP, XMLRPC_INTERNAL_ERROR, "Memory block too large"); /* Allocate our new memory block. */ new_block = (void*) malloc(proposed_alloc); XMLRPC_FAIL_IF_NULL(new_block, envP, XMLRPC_INTERNAL_ERROR, "Can't resize memory block"); /* Copy over our data and update the xmlrpc_mem_block struct. */ memcpy(new_block, blockP->_block, blockP->_size); free(blockP->_block); blockP->_block = new_block; blockP->_size = size; blockP->_allocated = proposed_alloc; cleanup: return; }
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; } }
/* Initialize the contents of the provided xmlrpc_mem_block. */ void xmlrpc_mem_block_init(xmlrpc_env * const envP, xmlrpc_mem_block * const blockP, size_t const size) { XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(blockP != NULL); blockP->_size = size; if (size < BLOCK_ALLOC_MIN) blockP->_allocated = BLOCK_ALLOC_MIN; else blockP->_allocated = size; blockP->_block = (void*) malloc(blockP->_allocated); if (!blockP->_block) xmlrpc_faultf(envP, "Can't allocate %u-byte memory block", (unsigned)blockP->_allocated); }
void xmlrpc_server_info_free(xmlrpc_server_info * const serverInfoP) { XMLRPC_ASSERT_PTR_OK(serverInfoP); XMLRPC_ASSERT(serverInfoP->serverUrl != XMLRPC_BAD_POINTER); if (serverInfoP->userNamePw) xmlrpc_strfree(serverInfoP->userNamePw); serverInfoP->userNamePw = XMLRPC_BAD_POINTER; if (serverInfoP->basicAuthHdrValue) xmlrpc_strfree(serverInfoP->basicAuthHdrValue); serverInfoP->basicAuthHdrValue = XMLRPC_BAD_POINTER; xmlrpc_strfree(serverInfoP->serverUrl); serverInfoP->serverUrl = XMLRPC_BAD_POINTER; free(serverInfoP); }
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 parsearray(xmlrpc_env *const envP, const xmlrpc_value *const arrayP, struct arrayDecomp const arrayDecomp, bool const oldstyleMemMgmt) { validateArraySize(envP, arrayP, arrayDecomp); if (!envP->fault_occurred) { unsigned int doneCnt; doneCnt = 0; while (doneCnt < arrayDecomp.itemCnt && !envP->fault_occurred) { xmlrpc_value *itemP; xmlrpc_array_read_item(envP, arrayP, doneCnt, &itemP); if (!envP->fault_occurred) { XMLRPC_ASSERT(doneCnt < ARRAY_SIZE(arrayDecomp.itemArray)); decomposeValueWithTree(envP, itemP, oldstyleMemMgmt, arrayDecomp.itemArray[doneCnt]); if (!envP->fault_occurred) ++doneCnt; xmlrpc_DECREF(itemP); } } if (envP->fault_occurred) { if (!oldstyleMemMgmt) { /* Release the items we completed before we failed. */ unsigned int i; for (i = 0; i < doneCnt; ++i) releaseDecomposition(arrayDecomp.itemArray[i]); } } } }
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"); } } }
static void decodeUtf8(xmlrpc_env *const envP, const char *const utf8_data, size_t const utf8_len, wchar_t *const ioBuff, size_t *const outBuffLenP) { /*---------------------------------------------------------------------------- Decode to UCS-2 (or validate as UTF-8 that can be decoded to UCS-2) a UTF-8 string. To validate, set ioBuff and outBuffLenP to NULL. To decode, allocate a sufficiently large buffer, pass it as ioBuff, and pass a pointer as as outBuffLenP. The data will be written to the buffer, and the length to outBuffLenP. We assume that wchar_t holds a single UCS-2 character in native-endian byte ordering. -----------------------------------------------------------------------------*/ size_t utf8Cursor; size_t outPos; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_PTR_OK(utf8_data); XMLRPC_ASSERT((!ioBuff && !outBuffLenP) || (ioBuff && outBuffLenP)); for (utf8Cursor = 0, outPos = 0; utf8Cursor < utf8_len && !envP->fault_occurred; ) { char const init = utf8_data[utf8Cursor]; /* Initial byte of the UTF-8 sequence */ wchar_t wc; if ((init & 0x80) == 0x00) { /* Convert ASCII character to wide character. */ wc = init; ++utf8Cursor; } else { /* Look up the length of this UTF-8 sequence. */ size_t const length = utf8SeqLength[(unsigned char) init]; /* Special value 0 means no length could be determined because it is not a valid initial byte for a UTF-8 sequence. */ if (length == 0) xmlrpc_env_set_fault_formatted( envP, XMLRPC_INVALID_UTF8_ERROR, "Unrecognized UTF-8 initial byte value 0x%02x", (unsigned char) init); else { /* Make sure we have enough bytes to convert. */ if (utf8Cursor + length > utf8_len) { xmlrpc_env_set_fault_formatted( envP, XMLRPC_INVALID_UTF8_ERROR, "Invalid UTF-8 sequence indicates a %u-byte sequence " "when only %u bytes are left in the string", (unsigned) length, (unsigned) (utf8_len - utf8Cursor)); } else { decodeMultibyte(envP, &utf8_data[utf8Cursor], length, &wc); /* Advance to the end of the sequence. */ utf8Cursor += length; } } } if (!envP->fault_occurred) { /* If we have a buffer, write our character to it. */ if (ioBuff) ioBuff[outPos++] = wc; } } if (outBuffLenP) *outBuffLenP = envP->fault_occurred ? 0 : outPos; }
static void decomposeValueWithTree(xmlrpc_env * const envP, xmlrpc_value * const valueP, bool const oldstyleMemMgmt, const struct decompTreeNode * const decompRootP) { /*---------------------------------------------------------------------------- Decompose XML-RPC value *valueP, given the decomposition tree *decompRootP. The decomposition tree tells what structure *valueP is expected to have and where to put the various components of it (e.g. it says "it's an array of 3 integers. Put their values at locations x, y, and z") -----------------------------------------------------------------------------*/ switch (decompRootP->formatSpecChar) { case '-': /* There's nothing to validate or return */ break; case 'i': xmlrpc_read_int(envP, valueP, decompRootP->store.Tinteger.valueP); break; case 'b': xmlrpc_read_bool(envP, valueP, decompRootP->store.Tbool.valueP); break; case 'd': xmlrpc_read_double(envP, valueP, decompRootP->store.Tdouble.valueP); break; case 't': xmlrpc_read_datetime_sec(envP, valueP, decompRootP->store.TdatetimeT.valueP); break; case '8': readDatetime8Str(envP, valueP, decompRootP->store.Tdatetime8.valueP, oldstyleMemMgmt); break; case 's': if (decompRootP->store.Tstring.sizeP) readStringLp(envP, valueP, decompRootP->store.Tstring.sizeP, decompRootP->store.Tstring.valueP, oldstyleMemMgmt); else readString(envP, valueP, decompRootP->store.Tstring.valueP, oldstyleMemMgmt); break; case 'w': #if HAVE_UNICODE_WCHAR if (decompRootP->store.Tstring.sizeP) readStringWLp(envP, valueP, decompRootP->store.TwideString.sizeP, decompRootP->store.TwideString.valueP, oldstyleMemMgmt); else readStringW(envP, valueP, decompRootP->store.TwideString.valueP, oldstyleMemMgmt); #else XMLRPC_ASSERT(false); #endif /* HAVE_UNICODE_WCHAR */ break; case '6': readBase64(envP, valueP, decompRootP->store.TbitString.sizeP, decompRootP->store.TbitString.valueP, oldstyleMemMgmt); break; case 'n': xmlrpc_read_nil(envP, valueP); break; case 'I': xmlrpc_read_i8(envP, valueP, decompRootP->store.Ti8.valueP); break; case 'p': xmlrpc_read_cptr(envP, valueP, decompRootP->store.Tcptr.valueP); break; case 'V': *decompRootP->store.Tvalue.valueP = valueP; if (!oldstyleMemMgmt) xmlrpc_INCREF(valueP); break; case 'A': if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_ARRAY) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type " "%s, but the 'A' specifier requires type ARRAY", xmlrpc_type_name(xmlrpc_value_type(valueP))); else { *decompRootP->store.TarrayVal.valueP = valueP; if (!oldstyleMemMgmt) xmlrpc_INCREF(valueP); } break; case 'S': if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_STRUCT) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type " "%s, but the 'S' specifier requires type STRUCT.", xmlrpc_type_name(xmlrpc_value_type(valueP))); else { *decompRootP->store.TstructVal.valueP = valueP; if (!oldstyleMemMgmt) xmlrpc_INCREF(valueP); } break; case '(': if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_ARRAY) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type " "%s, but the '(...)' specifier requires type ARRAY", xmlrpc_type_name(xmlrpc_value_type(valueP))); else parsearray(envP, valueP, decompRootP->store.Tarray, oldstyleMemMgmt); break; case '{': if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_STRUCT) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TYPE_ERROR, "Value to be decomposed is of type " "%s, but the '{...}' specifier requires type STRUCT", xmlrpc_type_name(xmlrpc_value_type(valueP))); else parsestruct(envP, valueP, decompRootP->store.Tstruct, oldstyleMemMgmt); break; default: /* Every format character that is allowed in a decomposition tree node is handled above. */ XMLRPC_ASSERT(false); } }
static xmlrpc_value * convertParams(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 xmlrpc_value is normally represented in XML by a <value> element, not a <params> element. We use type xmlrpc_value to represent the parameter list just for convenience. -----------------------------------------------------------------------------*/ xmlrpc_value * arrayP; xmlrpc_value * itemP; unsigned int i; unsigned int size; xml_element ** params; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(elemP != NULL); /* Set up our error-handling preconditions. */ arrayP = itemP = NULL; /* Allocate an array to hold our parameters. */ arrayP = 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) { xml_element * const paramP = params[i]; unsigned int const maxNest = (unsigned int) xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID); xml_element * valueEltP; CHECK_NAME(envP, paramP, "param"); CHECK_CHILD_COUNT(envP, paramP, 1); valueEltP = xml_element_children(paramP)[0]; CHECK_NAME(envP, valueEltP, "value"); xmlrpc_parseValue(envP, maxNest, valueEltP, &itemP); XMLRPC_FAIL_IF_FAULT(envP); xmlrpc_array_append_item(envP, arrayP, itemP); xmlrpc_DECREF(itemP); itemP = NULL; XMLRPC_FAIL_IF_FAULT(envP); } cleanup: if (envP->fault_occurred) { if (arrayP) xmlrpc_DECREF(arrayP); if (itemP) xmlrpc_DECREF(itemP); return NULL; } return arrayP; }
/* Get the contents of the xmlrpc_mem_block. */ void * xmlrpc_mem_block_contents(const xmlrpc_mem_block * const blockP) { XMLRPC_ASSERT(blockP != NULL); return blockP->_block; }
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 * responseEltP; 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", (unsigned)xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID), (unsigned)xmlDataLen); else { xmlrpc_env env; xmlrpc_env_init(&env); xml_parse(&env, xmlData, xmlDataLen, &responseEltP); 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(responseEltP), "methodResponse")) { parseMethodResponseElt(envP, responseEltP, resultPP, faultCodeP, faultStringP); } else setParseFault(envP, "XML-RPC response must consist of a " "<methodResponse> element. " "This has a <%s> instead.", xml_element_name(responseEltP)); xml_element_free(responseEltP); } xmlrpc_env_clean(&env); } }
/* Get the size of the xmlrpc_mem_block. */ size_t xmlrpc_mem_block_size(const xmlrpc_mem_block * const blockP) { XMLRPC_ASSERT(blockP != NULL); return blockP->_size; }