/* Copyright information is at end of file */ /* Implementation note: The printf format specifiers we use appear to be entirely standard, except for the "long long" one, which is %I64 on Windows and %lld everywhere else. So for that, we use the C99 standard macro PRId64, which is defined by inttypes.h. Ironically, Windows doesn't have inttypes.h either, but we have int.h instead. */ #include "xmlrpc_config.h" #include #include #include #include #include #include #include #include "int.h" #include "xmlrpc-c/base.h" #include "xmlrpc-c/base_int.h" #include "xmlrpc-c/string_int.h" #include "double.h" #define CRLF "\015\012" #define SMALL_BUFFER_SZ (128) #define XML_PROLOGUE ""CRLF static void addString(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, const char * const string) { XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, string, strlen(string)); } static void formatOut(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, const char * const formatString, ...) { /*---------------------------------------------------------------------------- A lightweight print routine for use with various serialization functions. Use this routine only for printing small objects -- it uses a fixed-size internal buffer and returns an error on overflow. In particular, do NOT use this routine to print XML-RPC string values! -----------------------------------------------------------------------------*/ va_list args; char buffer[SMALL_BUFFER_SZ]; int count; XMLRPC_ASSERT_ENV_OK(envP); va_start(args, formatString); count = XMLRPC_VSNPRINTF(buffer, SMALL_BUFFER_SZ, formatString, args); /* Old C libraries return -1 if vsnprintf overflows its buffer. ** New C libraries return the number of characters which *would* have ** been printed if the error did not occur. This is impressively vile. ** Thank the C99 committee for this bright idea. But wait! We also ** need to keep track of the trailing NUL. */ if (count < 0 || count >= (SMALL_BUFFER_SZ - 1)) xmlrpc_faultf(envP, "formatOut() overflowed internal buffer"); else XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, buffer, count); va_end(args); } static void assertValidUtf8(const char * const str ATTR_UNUSED, size_t const len ATTR_UNUSED) { /*---------------------------------------------------------------------------- Assert that the string 'str' of length 'len' is valid UTF-8. -----------------------------------------------------------------------------*/ #if !defined NDEBUG /* Check the assertion; if it's false, issue a message to Standard Error, but otherwise ignore it. */ xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_validate_utf8(&env, str, len); if (env.fault_occurred) fprintf(stderr, "*** xmlrpc-c WARNING ***: %s (%s)\n", "Xmlrpc-c sending corrupted UTF-8 data to network", env.fault_string); xmlrpc_env_clean(&env); #endif } static size_t escapedSize(const char * const chars, size_t const len) { size_t size; size_t i; size = 0; for (i = 0; i < len; ++i) { if (chars[i] == '<') size += 4; /* < */ else if (chars[i] == '>') size += 4; /* > */ else if (chars[i] == '&') size += 5; /* & */ else if (chars[i] == '\r') size += 6; /* */ else size += 1; } return size; } static void escapeForXml(xmlrpc_env * const envP, const char * const chars, size_t const len, xmlrpc_mem_block ** const outputPP) { /*---------------------------------------------------------------------------- Escape & and < in a UTF-8 string so as to make it suitable for the content of an XML element. I.e. turn them into entity references & and <. Also change > to >, even though not required for XML, for symmetry. < etc. are known in XML as "entity references." Also Escape CR as . While raw CR _is_ allowed in the content of an XML element, it has a special meaning -- it means line ending. Our input uses LF for for line endings. Since it also means line ending in XML, we just pass it through to our output like it were a regular character. is known in XML as a "character reference." We assume chars[] is is ASCII. That isn't right -- we should handle all valid UTF-8. Someday, we must do something more complex and copy over multibyte characters verbatim. (The code here could erroneously find that e.g. the 2nd byte of a UTF-8 character is a CR). -----------------------------------------------------------------------------*/ xmlrpc_mem_block * outputP; size_t outputSize; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(chars != NULL); assertValidUtf8(chars, len); /* Note that in UTF-8, any byte that has high bit of zero is a character all by itself (every byte of a multi-byte UTF-8 character has the high bit set). Also, the Unicode code points < 128 are identical to the ASCII ones. */ outputSize = escapedSize(chars, len); outputP = XMLRPC_MEMBLOCK_NEW(char, envP, outputSize); if (!envP->fault_occurred) { char * p; size_t i; p = XMLRPC_MEMBLOCK_CONTENTS(char, outputP); /* Start at beginning */ for (i = 0; i < len; i++) { if (chars[i] == '<') { memcpy(p, "<", 4); p += 4; } else if (chars[i] == '>') { memcpy(p, ">", 4); p += 4; } else if (chars[i] == '&') { memcpy(p, "&", 5); p += 5; } else if (chars[i] == '\r') { memcpy(p, " ", 6); p += 6; } else { /* Either a plain character or a LF line delimiter */ *p = chars[i]; p += 1; } } *outputPP = outputP; assert(p == XMLRPC_MEMBLOCK_CONTENTS(char, outputP) + outputSize); if (envP->fault_occurred) XMLRPC_MEMBLOCK_FREE(char, outputP); } } static void serializeUtf8MemBlock(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_mem_block * const inputP) { /*---------------------------------------------------------------------------- Append the characters in *inputP to the XML stream in *outputP. *inputP contains Unicode characters in UTF-8. We assume *inputP ends with a NUL character that marks end of string, and we ignore that. (There might also be NUL characters inside the string, though). -----------------------------------------------------------------------------*/ xmlrpc_mem_block * escapedP; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(outputP != NULL); XMLRPC_ASSERT(inputP != NULL); escapeForXml(envP, XMLRPC_MEMBLOCK_CONTENTS(const char, inputP), XMLRPC_MEMBLOCK_SIZE(const char, inputP) - 1, /* -1 is for the terminating NUL */ &escapedP); if (!envP->fault_occurred) { const char * const contents = XMLRPC_MEMBLOCK_CONTENTS(const char, escapedP); size_t const size = XMLRPC_MEMBLOCK_SIZE(char, escapedP); XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, contents, size); XMLRPC_MEMBLOCK_FREE(const char, escapedP); } } static void xmlrpc_serialize_base64_data(xmlrpc_env * const envP, xmlrpc_mem_block * const output, unsigned char * const data, size_t const len) { /*---------------------------------------------------------------------------- Encode the 'len' bytes at 'data' in base64 ASCII and append the result to 'output'. -----------------------------------------------------------------------------*/ xmlrpc_mem_block * encoded; encoded = xmlrpc_base64_encode(envP, data, len); if (!envP->fault_occurred) { unsigned char * const contents = XMLRPC_MEMBLOCK_CONTENTS(unsigned char, encoded); size_t const size = XMLRPC_MEMBLOCK_SIZE(unsigned char, encoded); XMLRPC_MEMBLOCK_APPEND(char, envP, output, contents, size); XMLRPC_MEMBLOCK_FREE(char, encoded); } } static void serializeStruct(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const structP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Add to *outputP the content of a element to represent the structure value *valueP. I.e. " ... ". -----------------------------------------------------------------------------*/ size_t size; size_t i; xmlrpc_value * memberKeyP; xmlrpc_value * memberValueP; addString(envP, outputP, ""CRLF); XMLRPC_FAIL_IF_FAULT(envP); size = xmlrpc_struct_size(envP, structP); XMLRPC_FAIL_IF_FAULT(envP); for (i = 0; i < size; ++i) { xmlrpc_struct_get_key_and_value(envP, structP, i, &memberKeyP, &memberValueP); XMLRPC_FAIL_IF_FAULT(envP); addString(envP, outputP, ""); XMLRPC_FAIL_IF_FAULT(envP); serializeUtf8MemBlock(envP, outputP, &memberKeyP->_block); XMLRPC_FAIL_IF_FAULT(envP); addString(envP, outputP, ""CRLF); XMLRPC_FAIL_IF_FAULT(envP); xmlrpc_serialize_value2(envP, outputP, memberValueP, dialect); XMLRPC_FAIL_IF_FAULT(envP); addString(envP, outputP, ""CRLF); XMLRPC_FAIL_IF_FAULT(envP); } addString(envP, outputP, ""); XMLRPC_FAIL_IF_FAULT(envP); cleanup: return; } static void serializeArray(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const valueP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Add to *outputP the content of a element to represent the array value *valueP. I.e. " ... ". -----------------------------------------------------------------------------*/ int const size = xmlrpc_array_size(envP, valueP); if (!envP->fault_occurred) { addString(envP, outputP, ""CRLF); if (!envP->fault_occurred) { int i; /* Serialize each item. */ for (i = 0; i < size && !envP->fault_occurred; ++i) { xmlrpc_value * const itemP = xmlrpc_array_get_item(envP, valueP, i); if (!envP->fault_occurred) { xmlrpc_serialize_value2(envP, outputP, itemP, dialect); if (!envP->fault_occurred) addString(envP, outputP, CRLF); } } } } if (!envP->fault_occurred) addString(envP, outputP, ""); } static void formatValueContent(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const valueP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Add to *outputP the content of a element to represent value *valueP. E.g. "42" -----------------------------------------------------------------------------*/ XMLRPC_ASSERT_ENV_OK(envP); switch (valueP->_type) { case XMLRPC_TYPE_INT: formatOut(envP, outputP, "%d", valueP->_value.i); break; case XMLRPC_TYPE_I8: { const char * const elemName = dialect == xmlrpc_dialect_apache ? "ex.i8" : "i8"; formatOut(envP, outputP, "<%s>%" PRId64 "", elemName, valueP->_value.i8, elemName); } break; case XMLRPC_TYPE_BOOL: formatOut(envP, outputP, "%s", valueP->_value.b ? "1" : "0"); break; case XMLRPC_TYPE_DOUBLE: { const char * serializedValue; xmlrpc_formatFloat(envP, valueP->_value.d, &serializedValue); if (!envP->fault_occurred) { addString(envP, outputP, ""); if (!envP->fault_occurred) { addString(envP, outputP, serializedValue); if (!envP->fault_occurred) addString(envP, outputP, ""); } xmlrpc_strfree(serializedValue); } } break; case XMLRPC_TYPE_DATETIME: addString(envP, outputP, ""); if (!envP->fault_occurred) { serializeUtf8MemBlock(envP, outputP, &valueP->_block); if (!envP->fault_occurred) { addString(envP, outputP, ""); } } break; case XMLRPC_TYPE_STRING: addString(envP, outputP, ""); if (!envP->fault_occurred) { serializeUtf8MemBlock(envP, outputP, &valueP->_block); if (!envP->fault_occurred) addString(envP, outputP, ""); } break; case XMLRPC_TYPE_BASE64: { unsigned char * const contents = XMLRPC_MEMBLOCK_CONTENTS(unsigned char, &valueP->_block); size_t const size = XMLRPC_MEMBLOCK_SIZE(unsigned char, &valueP->_block); addString(envP, outputP, ""CRLF); if (!envP->fault_occurred) { xmlrpc_serialize_base64_data(envP, outputP, contents, size); if (!envP->fault_occurred) addString(envP, outputP, ""); } } break; case XMLRPC_TYPE_ARRAY: serializeArray(envP, outputP, valueP, dialect); break; case XMLRPC_TYPE_STRUCT: serializeStruct(envP, outputP, valueP, dialect); break; case XMLRPC_TYPE_C_PTR: xmlrpc_faultf(envP, "Tried to serialize a C pointer value."); break; case XMLRPC_TYPE_NIL: { const char * const elemName = dialect == xmlrpc_dialect_apache ? "ex.nil" : "nil"; formatOut(envP, outputP, "<%s/>", elemName); } break; case XMLRPC_TYPE_DEAD: xmlrpc_faultf(envP, "Tried to serialize a dead value."); break; default: xmlrpc_faultf(envP, "Invalid xmlrpc_value type: %d", valueP->_type); } } void xmlrpc_serialize_value2(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const valueP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Generate the XML to represent XML-RPC value 'valueP' in XML-RPC. Add it to *outputP. -----------------------------------------------------------------------------*/ XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(outputP != NULL); XMLRPC_ASSERT_VALUE_OK(valueP); addString(envP, outputP, ""); if (!envP->fault_occurred) { formatValueContent(envP, outputP, valueP, dialect); if (!envP->fault_occurred) addString(envP, outputP, ""); } } void xmlrpc_serialize_value(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const valueP) { xmlrpc_serialize_value2(envP, outputP, valueP, xmlrpc_dialect_i8); } void xmlrpc_serialize_params2(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const paramArrayP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Serialize the parameter list of an XML-RPC call. -----------------------------------------------------------------------------*/ XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(outputP != NULL); XMLRPC_ASSERT_VALUE_OK(paramArrayP); addString(envP, outputP, ""CRLF); if (!envP->fault_occurred) { /* Serialize each parameter. */ size_t const paramCount = xmlrpc_array_size(envP, paramArrayP); if (!envP->fault_occurred) { size_t paramSeq; for (paramSeq = 0; paramSeq < paramCount && !envP->fault_occurred; ++paramSeq) { addString(envP, outputP, ""); if (!envP->fault_occurred) { xmlrpc_value * const itemP = xmlrpc_array_get_item(envP, paramArrayP, paramSeq); if (!envP->fault_occurred) { xmlrpc_serialize_value2(envP, outputP, itemP, dialect); if (!envP->fault_occurred) addString(envP, outputP, ""CRLF); } } } } } if (!envP->fault_occurred) addString(envP, outputP, ""CRLF); } void xmlrpc_serialize_params(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const paramArrayP) { /*---------------------------------------------------------------------------- Serialize the parameter list of an XML-RPC call in the original "i8" dialect. -----------------------------------------------------------------------------*/ xmlrpc_serialize_params2(envP, outputP, paramArrayP, xmlrpc_dialect_i8); } /*========================================================================= ** xmlrpc_serialize_call **========================================================================= ** Serialize an XML-RPC call. */ void xmlrpc_serialize_call2(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, const char * const methodName, xmlrpc_value * const paramArrayP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Serialize an XML-RPC call of method named 'methodName' with parameter list *paramArrayP. Use XML-RPC dialect 'dialect'. Append the call XML ot *outputP. -----------------------------------------------------------------------------*/ XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(outputP != NULL); XMLRPC_ASSERT(methodName != NULL); XMLRPC_ASSERT_VALUE_OK(paramArrayP); addString(envP, outputP, XML_PROLOGUE); if (!envP->fault_occurred) { addString(envP, outputP, ""CRLF""); if (!envP->fault_occurred) { xmlrpc_mem_block * encodedP; escapeForXml(envP, methodName, strlen(methodName), &encodedP); if (!envP->fault_occurred) { const char * const contents = XMLRPC_MEMBLOCK_CONTENTS(char, encodedP); size_t const size = XMLRPC_MEMBLOCK_SIZE(char, encodedP); XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, contents, size); if (!envP->fault_occurred) { addString(envP, outputP, ""CRLF); if (!envP->fault_occurred) { xmlrpc_serialize_params2(envP, outputP, paramArrayP, dialect); if (!envP->fault_occurred) addString(envP, outputP, ""CRLF); } } XMLRPC_MEMBLOCK_FREE(char, encodedP); } } } } void xmlrpc_serialize_call(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, const char * const methodName, xmlrpc_value * const paramArrayP) { xmlrpc_serialize_call2(envP, outputP, methodName, paramArrayP, xmlrpc_dialect_i8); } void xmlrpc_serialize_response2(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const valueP, xmlrpc_dialect const dialect) { /*---------------------------------------------------------------------------- Serialize a result response to an XML-RPC call. The result is 'valueP'. Add the response XML to *outputP. -----------------------------------------------------------------------------*/ XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(outputP != NULL); XMLRPC_ASSERT_VALUE_OK(valueP); addString(envP, outputP, XML_PROLOGUE); if (!envP->fault_occurred) { addString(envP, outputP, ""CRLF""CRLF""); if (!envP->fault_occurred) { xmlrpc_serialize_value2(envP, outputP, valueP, dialect); if (!envP->fault_occurred) { addString(envP, outputP, ""CRLF""CRLF ""CRLF); } } } } void xmlrpc_serialize_response(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, xmlrpc_value * const valueP) { xmlrpc_serialize_response2(envP, outputP, valueP, xmlrpc_dialect_i8); } void xmlrpc_serialize_fault(xmlrpc_env * const envP, xmlrpc_mem_block * const outputP, const xmlrpc_env * const faultP) { /*---------------------------------------------------------------------------- Serialize a fault response to an XML-RPC call. 'faultP' is the fault. Add the response XML to *outputP. -----------------------------------------------------------------------------*/ xmlrpc_value * faultStructP; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT(outputP != NULL); XMLRPC_ASSERT(faultP != NULL); XMLRPC_ASSERT(faultP->fault_occurred); faultStructP = xmlrpc_build_value(envP, "{s:i,s:s}", "faultCode", (xmlrpc_int32) faultP->fault_code, "faultString", faultP->fault_string); if (!envP->fault_occurred) { addString(envP, outputP, XML_PROLOGUE); if (!envP->fault_occurred) { addString(envP, outputP, ""CRLF""CRLF); if (!envP->fault_occurred) { xmlrpc_serialize_value(envP, outputP, faultStructP); if (!envP->fault_occurred) { addString(envP, outputP, CRLF""CRLF""CRLF); } } } xmlrpc_DECREF(faultStructP); } } /* Copyright (C) 2001 by First Peer, Inc. All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. */