Sfoglia il codice sorgente

ENH: Initial import

Andy Cedilnik 21 anni fa
parent
commit
b9b4ea0f7b
40 ha cambiato i file con 12478 aggiunte e 0 eliminazioni
  1. 44 0
      Utilities/cmxmlrpc/CMake/TryCompileFromSource.cmake
  2. 76 0
      Utilities/cmxmlrpc/CMakeLists.txt
  3. 391 0
      Utilities/cmxmlrpc/XmlRpcCpp.cpp
  4. 428 0
      Utilities/cmxmlrpc/XmlRpcCpp.h
  5. 18 0
      Utilities/cmxmlrpc/bool.h
  6. 80 0
      Utilities/cmxmlrpc/mallocvar.h
  7. 5 0
      Utilities/cmxmlrpc/transport_config.h
  8. 108 0
      Utilities/cmxmlrpc/win32_pthreads.c
  9. 783 0
      Utilities/cmxmlrpc/xmlrpc.h
  10. 37 0
      Utilities/cmxmlrpc/xmlrpc_amconfig.h.in
  11. 206 0
      Utilities/cmxmlrpc/xmlrpc_array.c
  12. 69 0
      Utilities/cmxmlrpc/xmlrpc_authcookie.c
  13. 262 0
      Utilities/cmxmlrpc/xmlrpc_base64.c
  14. 295 0
      Utilities/cmxmlrpc/xmlrpc_cgi.c
  15. 81 0
      Utilities/cmxmlrpc/xmlrpc_cgi.h
  16. 981 0
      Utilities/cmxmlrpc/xmlrpc_client.c
  17. 261 0
      Utilities/cmxmlrpc/xmlrpc_client.h
  18. 105 0
      Utilities/cmxmlrpc/xmlrpc_client_int.h
  19. 46 0
      Utilities/cmxmlrpc/xmlrpc_config.h.in
  20. 651 0
      Utilities/cmxmlrpc/xmlrpc_curl_transport.c
  21. 8 0
      Utilities/cmxmlrpc/xmlrpc_curl_transport.h
  22. 1331 0
      Utilities/cmxmlrpc/xmlrpc_data.c
  23. 395 0
      Utilities/cmxmlrpc/xmlrpc_expat.c
  24. 112 0
      Utilities/cmxmlrpc/xmlrpc_int.h
  25. 406 0
      Utilities/cmxmlrpc/xmlrpc_libxml2.c
  26. 767 0
      Utilities/cmxmlrpc/xmlrpc_parse.c
  27. 66 0
      Utilities/cmxmlrpc/xmlrpc_pthreads.h
  28. 829 0
      Utilities/cmxmlrpc/xmlrpc_registry.c
  29. 626 0
      Utilities/cmxmlrpc/xmlrpc_serialize.c
  30. 179 0
      Utilities/cmxmlrpc/xmlrpc_server.h
  31. 799 0
      Utilities/cmxmlrpc/xmlrpc_server_abyss.c
  32. 182 0
      Utilities/cmxmlrpc/xmlrpc_server_abyss.h
  33. 79 0
      Utilities/cmxmlrpc/xmlrpc_server_abyss_int.h
  34. 609 0
      Utilities/cmxmlrpc/xmlrpc_struct.c
  35. 75 0
      Utilities/cmxmlrpc/xmlrpc_strutil.c
  36. 363 0
      Utilities/cmxmlrpc/xmlrpc_support.c
  37. 142 0
      Utilities/cmxmlrpc/xmlrpc_transport.c
  38. 125 0
      Utilities/cmxmlrpc/xmlrpc_transport.h
  39. 376 0
      Utilities/cmxmlrpc/xmlrpc_utf8.c
  40. 82 0
      Utilities/cmxmlrpc/xmlrpc_xmlparser.h

+ 44 - 0
Utilities/cmxmlrpc/CMake/TryCompileFromSource.cmake

@@ -0,0 +1,44 @@
+MACRO(TRY_COMPILE_FROM_SOURCE SOURCE VAR)
+  IF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
+    SET(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    IF(CMAKE_REQUIRED_LIBRARIES)
+      SET(TRY_COMPILE_FROM_SOURCE_ADD_LIBRARIES
+        "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
+    ENDIF(CMAKE_REQUIRED_LIBRARIES)
+    SET(src "")
+    FOREACH(def ${EXTRA_DEFINES})
+      SET(src "${src}#define ${def} 1\n")
+    ENDFOREACH(def)
+    FOREACH(inc ${HEADER_INCLUDES})
+      SET(src "${src}#include <${inc}>\n")
+    ENDFOREACH(inc)
+
+    SET(src "${src}\nint main() { ${SOURCE} ; return 0; }")
+    FILE(WRITE "${CMAKE_BINARY_DIR}/CMakeTmp/src.c"
+      "${src}")
+
+    MESSAGE(STATUS "Performing Test ${VAR}")
+    TRY_COMPILE(${VAR}
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}/CMakeTmp/src.c
+      CMAKE_FLAGS
+      "${TRY_COMPILE_FROM_SOURCE_ADD_LIBRARIES}"
+      OUTPUT_VARIABLE OUTPUT)
+    IF(${VAR})
+      SET(${VAR} 1 CACHE INTERNAL "Test ${FUNCTION}")
+      MESSAGE(STATUS "Performing Test ${VAR} - Success")
+      WRITE_FILE(${CMAKE_BINARY_DIR}/CMakeOutput.log
+        "Performing C SOURCE FILE Test ${VAR} succeded with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${src}\n" APPEND)
+    ELSE(${VAR})
+      MESSAGE(STATUS "Performing Test ${VAR} - Failed")
+      SET(${VAR} "" CACHE INTERNAL "Test ${FUNCTION}")
+      WRITE_FILE(${CMAKE_BINARY_DIR}/CMakeError.log
+        "Performing C SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${src}\n" APPEND)
+    ENDIF(${VAR})
+  ENDIF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
+ENDMACRO(TRY_COMPILE_FROM_SOURCE)

+ 76 - 0
Utilities/cmxmlrpc/CMakeLists.txt

@@ -0,0 +1,76 @@
+PROJECT(XMLRPC)
+
+# Include all the necessary files for macros
+SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
+
+# Check if header file exists and add it to the list.
+SET(CURRENT_INCLUDES)
+INCLUDE (CheckIncludeFiles)
+MACRO(CHECK_INCLUDE_FILE_CONCAT FILE VARIABLE)
+  CHECK_INCLUDE_FILES("${CURRENT_INCLUDES};${FILE}" ${VARIABLE})
+  IF(${VARIABLE})
+    SET(CURRENT_INCLUDES ${CURRENT_INCLUDES} ${FILE})
+  ENDIF(${VARIABLE})
+ENDMACRO(CHECK_INCLUDE_FILE_CONCAT)
+
+CHECK_INCLUDE_FILE_CONCAT("stdio.h" HAVE_STDIO_H)
+CHECK_INCLUDE_FILE_CONCAT("stdarg.h" HAVE_STDARG_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/filio.h" HAVE_SYS_FILIO_H)
+CHECK_INCLUDE_FILE_CONCAT("sys/ioctl.h" HAVE_SYS_IOCTL_H)
+CHECK_INCLUDE_FILE_CONCAT("wchar.h" HAVE_WCHAR_H)
+
+INCLUDE (CheckSymbolExists)
+CHECK_SYMBOL_EXISTS(asprintf "${CURRENT_INCLUDES}" HAVE_ASPRINTF)
+CHECK_SYMBOL_EXISTS(setgroups "${CURRENT_INCLUDES}" HAVE_SETGROUPS)
+
+INCLUDE (CheckTypeSize)
+CHECK_TYPE_SIZE(size_t  SIZEOF_SIZE_T)
+
+INCLUDE (TryCompileFromSource)
+SET(HEADER_INCLUDES "${CURRENT_INCLUDES}")
+TRY_COMPILE_FROM_SOURCE("va_list list1, list2; list1 = list2"
+  VA_LIST_IS_ARRAY_DEFINE)
+TRY_COMPILE_FROM_SOURCE("int x __attribute__((__unused__))"
+  ATTR_UNUSED_VAR)
+SET(ATTR_UNUSED)
+IF(ATTR_UNUSED_VAR)
+  SET(ATTR_UNUSED "__attribute__((__unused__))")
+ENDIF(ATTR_UNUSED_VAR)
+
+SET(HAVE_LIBWWW_SSL)
+SET(DIRECTORY_SEPARATOR "/")
+
+INCLUDE_DIRECTORIES(
+  "${CMAKE_CURRENT_SOURCE_DIR}"
+  "${CMAKE_CURRENT_BINARY_DIR}"
+  "${CMAKE_EXPAT_INCLUDES}"
+  )
+
+CONFIGURE_FILE(
+  "${CMAKE_CURRENT_SOURCE_DIR}/xmlrpc_config.h.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/xmlrpc_config.h"
+  )
+CONFIGURE_FILE(
+  "${CMAKE_CURRENT_SOURCE_DIR}/xmlrpc_amconfig.h.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/xmlrpc_amconfig.h"
+  )
+
+SET(xmlrpc_SRCS
+  #XmlRpcCpp.cpp
+  xmlrpc_array.c
+  xmlrpc_authcookie.c
+  xmlrpc_base64.c
+  xmlrpc_client.c
+  xmlrpc_data.c
+  xmlrpc_expat.c
+  xmlrpc_parse.c
+  xmlrpc_registry.c
+  xmlrpc_serialize.c
+  #xmlrpc_server_abyss.c
+  xmlrpc_struct.c
+  xmlrpc_strutil.c
+  xmlrpc_support.c
+  xmlrpc_utf8.c
+  )
+
+ADD_LIBRARY(cmXMLRPC ${xmlrpc_SRCS})

+ 391 - 0
Utilities/cmxmlrpc/XmlRpcCpp.cpp

@@ -0,0 +1,391 @@
+// Copyright (C) 2001 by Eric Kidd. 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.
+
+
+#include "XmlRpcCpp.h"
+
+
+//=========================================================================
+//  XmlRpcFault Methods
+//=========================================================================
+
+XmlRpcFault::XmlRpcFault (const XmlRpcFault &fault) {
+    xmlrpc_env_init(&mFault);
+    xmlrpc_env_set_fault(&mFault,
+                         fault.mFault.fault_code,
+                         fault.mFault.fault_string);
+}
+
+XmlRpcFault::XmlRpcFault (const int faultCode, const string faultString) {
+    xmlrpc_env_init(&mFault);
+    xmlrpc_env_set_fault(&mFault, faultCode,
+                         const_cast<char*>(faultString.c_str()));
+}
+ 
+XmlRpcFault::XmlRpcFault (const xmlrpc_env *env) {
+    if (!env->fault_string)
+        throw XmlRpcFault(XMLRPC_INTERNAL_ERROR,
+                          "Tried to create empty fault");
+    xmlrpc_env_init(&mFault);
+    xmlrpc_env_set_fault(&mFault, env->fault_code,
+                         const_cast<char*>(env->fault_string));
+}
+
+XmlRpcFault::~XmlRpcFault (void) {
+    xmlrpc_env_clean(&mFault);
+}
+
+string XmlRpcFault::getFaultString (void) const {
+    XMLRPC_ASSERT(mFault.fault_occurred);
+    return string(mFault.fault_string);
+}
+
+
+//=========================================================================
+//  XmlRpcEnv Methods
+//=========================================================================
+
+XmlRpcEnv::XmlRpcEnv (const XmlRpcEnv &env) {
+    xmlrpc_env_init(&mEnv);
+    if (env.hasFaultOccurred())
+        xmlrpc_env_set_fault(&mEnv,
+                             env.mEnv.fault_code,
+                             env.mEnv.fault_string);
+}
+
+XmlRpcFault XmlRpcEnv::getFault (void) const {
+    return XmlRpcFault(&mEnv);
+}
+
+void XmlRpcEnv::throwMe (void) const {
+    throw XmlRpcFault(&mEnv);
+}
+
+
+//=========================================================================
+//  XmlRpcValue Methods
+//=========================================================================
+
+// If the user doesn't tell us what kind of value to create, use
+// a false boolean value as the default.
+XmlRpcValue::XmlRpcValue (void) {
+    XmlRpcEnv env;
+    mValue = xmlrpc_build_value(env, "b", (xmlrpc_bool) 0);
+    env.throwIfFaultOccurred();
+}
+
+XmlRpcValue XmlRpcValue::makeInt (const XmlRpcValue::int32 i) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "i", i);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);
+}
+
+XmlRpcValue XmlRpcValue::makeBool (const bool b) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "b", (xmlrpc_bool) b);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);
+}
+
+XmlRpcValue XmlRpcValue::makeDouble (const double d) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "d", d);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);
+}
+
+XmlRpcValue XmlRpcValue::makeDateTime (const string& dateTime) {
+    XmlRpcEnv env;
+    xmlrpc_value *value;
+    const char *data = dateTime.c_str(); // Make sure we're not using wchar_t.
+    value = xmlrpc_build_value(env, "8", data);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);    
+}
+
+XmlRpcValue XmlRpcValue::makeString (const string& str) {
+    XmlRpcEnv env;
+    const char *data = str.data();      // Make sure we're not using wchar_t.
+    size_t size = str.size();
+    xmlrpc_value *value = xmlrpc_build_value(env, "s#", data, size);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);    
+}
+
+XmlRpcValue XmlRpcValue::makeString (const char *const str) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "s", str);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);    
+}
+
+XmlRpcValue XmlRpcValue::makeString (const char *const str, size_t len) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "s#", str, len);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);    
+}
+
+XmlRpcValue XmlRpcValue::makeArray (void) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "()");
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);    
+}
+
+XmlRpcValue XmlRpcValue::makeStruct (void) {
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_struct_new(env);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);
+}
+
+XmlRpcValue XmlRpcValue::makeBase64 (const unsigned char *const data,
+                                     size_t len)
+{
+    XmlRpcEnv env;
+    xmlrpc_value *value = xmlrpc_build_value(env, "6", data, len);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(value, CONSUME_REFERENCE);
+}
+
+XmlRpcValue::int32 XmlRpcValue::getInt (void) const {
+    XmlRpcEnv env;
+    XmlRpcValue::int32 result;
+    xmlrpc_parse_value(env, mValue, "i", &result);
+    env.throwIfFaultOccurred();
+    return result;
+}
+
+bool XmlRpcValue::getBool (void) const {
+    XmlRpcEnv env;
+    xmlrpc_bool result;
+    xmlrpc_parse_value(env, mValue, "b", &result);
+    env.throwIfFaultOccurred();
+    return result;
+}
+
+double XmlRpcValue::getDouble (void) const {
+    XmlRpcEnv env;
+    double result;
+    xmlrpc_parse_value(env, mValue, "d", &result);
+    env.throwIfFaultOccurred();
+    return result;
+}
+
+string XmlRpcValue::getRawDateTime (void) const {
+    XmlRpcEnv env;
+    char *result;
+    xmlrpc_parse_value(env, mValue, "8", &result);
+    env.throwIfFaultOccurred();
+    return string(result);
+}
+
+string XmlRpcValue::getString (void) const {
+    XmlRpcEnv env;
+    char *result;
+    size_t result_len;
+    xmlrpc_parse_value(env, mValue, "s#", &result, &result_len);
+    env.throwIfFaultOccurred();
+    return string(result, result_len);
+    
+}
+
+XmlRpcValue XmlRpcValue::getArray (void) const {
+    XmlRpcEnv env;
+    xmlrpc_value *result;
+    xmlrpc_parse_value(env, mValue, "A", &result);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(result);
+}
+
+XmlRpcValue XmlRpcValue::getStruct (void) const {
+    XmlRpcEnv env;
+    xmlrpc_value *result;
+    xmlrpc_parse_value(env, mValue, "S", &result);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(result);
+}
+
+void XmlRpcValue::getBase64 (const unsigned char *& out_data,
+                             size_t& out_len) const
+{
+    XmlRpcEnv env;
+    xmlrpc_parse_value(env, mValue, "6", &out_data, &out_len);
+    env.throwIfFaultOccurred();
+}
+
+size_t XmlRpcValue::arraySize (void) const {
+    XmlRpcEnv env;
+    size_t result = xmlrpc_array_size(env, mValue);
+    env.throwIfFaultOccurred();
+    return result;
+}
+
+void XmlRpcValue::arrayAppendItem (const XmlRpcValue& value) {
+    XmlRpcEnv env;
+    xmlrpc_array_append_item(env, mValue, value.borrowReference());
+    env.throwIfFaultOccurred();
+}
+
+XmlRpcValue XmlRpcValue::arrayGetItem (int index) const {
+    XmlRpcEnv env;
+    xmlrpc_value *result = xmlrpc_array_get_item(env, mValue, index);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(result);
+}
+
+size_t XmlRpcValue::structSize (void) const {
+    XmlRpcEnv env;
+    size_t result = xmlrpc_struct_size(env, mValue);
+    env.throwIfFaultOccurred();
+    return result;
+}
+
+bool XmlRpcValue::structHasKey (const string& key) const {
+    XmlRpcEnv env;
+    const char *keystr = key.data();
+    size_t keylen = key.size();
+    bool result = xmlrpc_struct_has_key_n(env, mValue,
+                                          const_cast<char*>(keystr), keylen);
+    env.throwIfFaultOccurred();
+    return result;
+}
+
+XmlRpcValue XmlRpcValue::structGetValue (const string& key) const {
+    XmlRpcEnv env;
+    const char *keystr = key.data();
+    size_t keylen = key.size();
+    xmlrpc_value *result =
+        xmlrpc_struct_get_value_n(env, mValue,
+                                  const_cast<char*>(keystr), keylen);
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(result);
+}
+
+void XmlRpcValue::structSetValue (const string& key, const XmlRpcValue& value)
+{
+    XmlRpcEnv env;
+    const char *keystr = key.data();
+    size_t keylen = key.size();
+    xmlrpc_struct_set_value_n(env, mValue, (char*) keystr, keylen,
+                              value.borrowReference());
+    env.throwIfFaultOccurred();
+}
+
+void XmlRpcValue::structGetKeyAndValue (const int index,
+                                        string& out_key,
+                                        XmlRpcValue& out_value) const
+{
+    XmlRpcEnv env;
+
+    xmlrpc_value *key, *value;
+    xmlrpc_struct_get_key_and_value(env, mValue, index, &key, &value);
+    env.throwIfFaultOccurred();
+
+    out_key = XmlRpcValue(key).getString();
+    out_value = XmlRpcValue(value);
+}
+
+XmlRpcGenSrv& XmlRpcGenSrv::addMethod (const string& name,
+                                                        xmlrpc_method method,
+                                                        void *data)
+{
+    XmlRpcEnv env;
+
+        xmlrpc_registry_add_method (env, mRegistry, NULL,
+                                name.c_str (),
+                                method, data);
+
+    env.throwIfFaultOccurred ();
+        return (*this);
+}
+
+XmlRpcGenSrv& XmlRpcGenSrv::addMethod (const string& name,
+                        xmlrpc_method method,
+                        void* data,
+                        const string& signature,
+                        const string& help)
+{
+    XmlRpcEnv env;
+
+        xmlrpc_registry_add_method_w_doc (env, mRegistry, NULL,
+                                name.c_str (),
+                                method, data,
+                                signature.c_str (),
+                                help.c_str ());
+
+    env.throwIfFaultOccurred ();
+        return (*this);
+}
+
+xmlrpc_mem_block* XmlRpcGenSrv::alloc (XmlRpcEnv& env, const string& body) const
+{
+        xmlrpc_mem_block*       result = NULL;
+        char*                           contents;
+
+        result          = xmlrpc_mem_block_new (env, body.length ());
+        env.throwIfFaultOccurred ();
+
+        contents        = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, result);
+
+        memcpy (contents, body.c_str (), body.length ());
+        return result;
+}
+
+string XmlRpcGenSrv::handle (const string& body) const
+{
+        XmlRpcEnv env;
+        string result;
+        xmlrpc_mem_block*       input = NULL, * output = NULL; 
+        char* input_data, * output_data;
+        size_t input_size, output_size;
+
+        if (body.length () > xmlrpc_limit_get (XMLRPC_XML_SIZE_LIMIT_ID))
+                throw XmlRpcFault (XMLRPC_LIMIT_EXCEEDED_ERROR, "XML-RPC request too large");
+
+        input   = alloc (env, body);
+        input_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, input);
+        input_size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, input);
+
+        output  = xmlrpc_registry_process_call (env, mRegistry, NULL,
+                        input_data, input_size);
+
+        if (output)
+        {
+        output_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output);
+                output_size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
+
+                result.assign (output_data, output_size);
+                xmlrpc_mem_block_free (output);
+        }
+
+        xmlrpc_mem_block_free (input);
+        if (!result.length ())
+                throw XmlRpcFault (env);
+
+        return result;
+}

+ 428 - 0
Utilities/cmxmlrpc/XmlRpcCpp.h

@@ -0,0 +1,428 @@
+// -*- C++ -*-   <-- an Emacs control
+
+// Copyright information is at the bottom of the file.
+
+//=========================================================================
+//  XML-RPC C++ API
+//=========================================================================
+
+
+#ifndef  _XMLRPCCPP_H_
+#define  _XMLRPCCPP_H_ 1
+
+// The C++ standard says we should either include <string.h> (which gets
+// us the wrong header), or say 'using namespace std;' (which doesn't
+// work with our version of g++). So this header name is technically wrong.
+// Tell me what your compiler does; I can provide some autoconf magic to the
+// Right Thing on most platforms.
+//
+// 2004.12.22 Bryan: This looks like a problem with whatever g++ he was
+// using, so if anything, the special case should be for that.  In any case,
+// we've already added using namespace std to other files, without knowing
+// there was an issue, so I'm just going to do the "using namespace" 
+// unconditionally and see who complains.  If there are complaints, we can
+// improve the documentation here.
+//
+// Formerly, the "using namespace std" was under
+// "#if defined(__GNUC__) && (__GNUC__ >= 3)".
+
+
+#include <string>
+
+using namespace std;
+
+#include <xmlrpc_config.h>
+#include <xmlrpc.h>
+#include <xmlrpc_client.h>
+#include <xmlrpc_server.h>
+
+#define XMLRPC_NO_ASSIGNMENT \
+    XMLRPC_FATAL_ERROR("Assignment operator not available"); return *this;
+
+
+//=========================================================================
+//  XmlRpcFault
+//=========================================================================
+//  A C++ exception class representing an XML-RPC fault.
+
+class XmlRpcFault {
+
+private:
+    xmlrpc_env   mFault;
+
+    XmlRpcFault& operator= (const XmlRpcFault& f)
+        { (void) f; XMLRPC_NO_ASSIGNMENT }
+
+public:
+    XmlRpcFault (const XmlRpcFault &fault);
+    XmlRpcFault (const int faultCode, const string faultString);
+    XmlRpcFault (const xmlrpc_env *env);
+    ~XmlRpcFault (void);
+
+    int          getFaultCode (void) const;
+    string       getFaultString (void) const;
+    xmlrpc_env  *getFaultEnv (void);
+};
+
+inline int XmlRpcFault::getFaultCode (void) const {
+    return mFault.fault_code;
+}
+
+inline xmlrpc_env *XmlRpcFault::getFaultEnv (void) {
+    return &mFault;
+}
+
+
+//=========================================================================
+//  XmlRpcEnv
+//=========================================================================
+//  This class can be used to wrap xmlrpc_env object. Use it as follows:
+//
+//    XmlRpcEnv env;
+//    xmlrpc_parse_value(env, v, "(i)", &i);
+//    env.throwIfFaultOccurred();        
+
+class XmlRpcEnv {
+
+private:
+    xmlrpc_env   mEnv;
+
+    void         throwMe (void) const;
+    XmlRpcEnv&   operator= (const XmlRpcEnv& e)
+        { (void) e; XMLRPC_NO_ASSIGNMENT }
+
+public:
+    XmlRpcEnv (const XmlRpcEnv &env);
+    XmlRpcEnv (void) { xmlrpc_env_init(&mEnv); }
+    ~XmlRpcEnv (void) { xmlrpc_env_clean(&mEnv); }
+    
+    bool         faultOccurred (void) const { return mEnv.fault_occurred; };
+    bool         hasFaultOccurred (void) const { return faultOccurred(); };
+        /* hasFaultOccurred() is for backward compatibility.
+           faultOccurred() is a superior name for this.
+        */
+    XmlRpcFault  getFault (void) const;
+
+    void         throwIfFaultOccurred (void) const;
+
+    operator xmlrpc_env * (void) { return &mEnv; }
+};
+
+inline void XmlRpcEnv::throwIfFaultOccurred (void) const {
+    if (faultOccurred())
+        throwMe();
+}
+
+
+//=========================================================================
+//  XmlRpcValue
+//=========================================================================
+//  An object in this class is an XML-RPC value.
+//
+//  We have a complex structure to allow C code mixed in with C++ code
+//  which uses this class to refer to the same XML-RPC value object.
+//  This is especially important because there aren't proper C++ facilities
+//  for much of Xmlrpc-c; you have to use the C facilities even if you'd
+//  rather use proper C++.
+//
+//  The XmlRpcValue object internally represents the value as an
+//  xmlrpc_value.  It hold one reference to the xmlrpc_value.  Users
+//  of XmlRpcValue never see that xmlrpc_value, but C code can.  the
+//  C code might create the xmlrpc_value and then bind it to an XmlRpcValue,
+//  or it might get the xmlrpc_value handle from the XmlRpcValue.  Finally,
+//  C code can simply use the XmlRpcValue where an xmlrpc_value handle is
+//  required and it gets converted automatically.
+//
+//  So reference counting for the xmlrpc_value is quite a nightmare.
+
+class XmlRpcValue {
+
+private:
+    xmlrpc_value *mValue;
+
+public:
+    enum ReferenceBehavior {
+        MAKE_REFERENCE,
+        CONSUME_REFERENCE
+    };
+
+    typedef xmlrpc_int32 int32;
+    
+    XmlRpcValue (void);
+    XmlRpcValue (xmlrpc_value *value,
+                 ReferenceBehavior behavior = MAKE_REFERENCE);
+    XmlRpcValue (const XmlRpcValue& value);
+    ~XmlRpcValue (void);
+    
+    XmlRpcValue&  operator= (const XmlRpcValue& value);
+
+    // Accessing the value's type (think of this as lightweight RTTI).
+    xmlrpc_type getType(void) const;
+    
+    // We don't supply an automatic conversion operator--you need to say
+    // whether you want to make or borrow this object's reference.
+    // XXX - Is it really OK for these to be const?
+    xmlrpc_value *makeReference (void) const;
+    xmlrpc_value *borrowReference (void) const;
+
+    // Some static "constructor" functions.
+    static XmlRpcValue makeInt      (const XmlRpcValue::int32 i);
+    static XmlRpcValue makeBool     (const bool b);
+    static XmlRpcValue makeDouble   (const double d);
+    static XmlRpcValue makeDateTime (const string& dateTime);
+    static XmlRpcValue makeString   (const string& str);
+    static XmlRpcValue makeString   (const char *const str);
+    static XmlRpcValue makeString   (const char *const str, size_t len);
+    static XmlRpcValue makeArray    (void);
+    static XmlRpcValue makeStruct   (void);
+    static XmlRpcValue makeBase64   (const unsigned char *const data,
+                                     size_t len);
+    /*
+    // An interface to xmlrpc_build_value.
+    static XmlRpcValue buildValue (const char *const format, ...);
+    */
+
+    // Some functions to get the underlying data.
+    // These will throw an XmlRpcFault if the data is the wrong type.
+    XmlRpcValue::int32 getInt   (void) const;
+    bool         getBool        (void) const;
+    double       getDouble      (void) const;
+    string       getRawDateTime (void) const;
+    string       getString      (void) const;
+    XmlRpcValue  getArray       (void) const;
+    XmlRpcValue  getStruct      (void) const;
+
+    // This returns an internal pointer which will become invalid when
+    // all references to the underlying value are destroyed.
+    void         getBase64      (const unsigned char *& out_data,
+                                 size_t& out_len) const;
+
+    /*
+    // An interface to xmlrpc_parse_value.
+    void parseValue (const char *const format, ...);
+    */
+
+    // Array functions. These will throw an XmlRpcFault if the value
+    // isn't an array.
+    size_t       arraySize (void) const;
+    void         arrayAppendItem (const XmlRpcValue& value);
+    XmlRpcValue  arrayGetItem (int index) const;
+    
+    // Struct functions. These will throw an XmlRpcFault if the value
+    // isn't a struct.
+    size_t       structSize (void) const;
+    bool         structHasKey (const string& key) const;
+    XmlRpcValue  structGetValue (const string& key) const;
+    void         structSetValue (const string& key, const XmlRpcValue& value);
+    void         structGetKeyAndValue (const int index,
+                                       string& out_key,
+                                       XmlRpcValue& out_value) const;
+};
+
+inline XmlRpcValue::XmlRpcValue (xmlrpc_value *value,
+                                 ReferenceBehavior behavior) 
+{
+    mValue = value;
+
+    if (behavior == MAKE_REFERENCE)
+        xmlrpc_INCREF(value);
+}
+
+inline XmlRpcValue::XmlRpcValue (const XmlRpcValue& value) {
+    mValue = value.mValue;
+    xmlrpc_INCREF(mValue);
+}
+
+inline XmlRpcValue::~XmlRpcValue (void) {
+    xmlrpc_DECREF(mValue);
+}
+
+inline XmlRpcValue& XmlRpcValue::operator= (const XmlRpcValue& value) {
+    // Must increment before we decrement, in case of assignment to self.
+    xmlrpc_INCREF(value.mValue);
+    xmlrpc_DECREF(mValue);
+    mValue = value.mValue;
+    return *this;
+}
+
+inline xmlrpc_type XmlRpcValue::getType (void) const {
+    return xmlrpc_value_type(mValue);
+}
+
+inline xmlrpc_value *XmlRpcValue::makeReference (void) const {
+    xmlrpc_INCREF(mValue);
+    return mValue;
+}
+
+inline xmlrpc_value *XmlRpcValue::borrowReference (void) const {
+    return mValue;
+}
+
+
+//=========================================================================
+//  XmlRpcClient
+//=========================================================================
+
+class XmlRpcClient {
+
+private:
+    string mServerUrl;
+
+public:
+    static void Initialize (string appname, string appversion);
+    static void Terminate (void);
+
+    XmlRpcClient (const string& server_url) : mServerUrl(server_url) {}
+    ~XmlRpcClient (void) {}
+
+    XmlRpcClient (const XmlRpcClient& client);
+    XmlRpcClient& operator= (const XmlRpcClient& client);
+
+    XmlRpcValue call (string method_name, XmlRpcValue param_array);
+    void call_asynch (string method_name,
+                      XmlRpcValue param_array,
+                      xmlrpc_response_handler callback,
+                      void* user_data);
+    void event_loop_asynch (unsigned long milliseconds);
+};
+
+inline void XmlRpcClient::call_asynch(string method_name,
+                                      XmlRpcValue param_array,
+                                      xmlrpc_response_handler callback,
+                                      void* user_data)
+{
+    xmlrpc_client_call_asynch_params(
+        mServerUrl.c_str(),
+        method_name.c_str(),
+        callback,
+        user_data,
+        param_array.borrowReference());
+}
+
+inline void XmlRpcClient::event_loop_asynch(unsigned long milliseconds)
+{
+    xmlrpc_client_event_loop_finish_asynch_timeout(milliseconds);
+}
+
+
+//=========================================================================
+//  XmlRpcClient Methods
+//=========================================================================
+//  These are inline for now, so we don't need to screw with linker issues
+//  and build a separate client library.
+
+inline XmlRpcClient::XmlRpcClient (const XmlRpcClient& client)
+    : mServerUrl(client.mServerUrl)
+{
+}
+
+inline XmlRpcClient& XmlRpcClient::operator= (const XmlRpcClient& client) {
+    if (this != &client)
+        mServerUrl = client.mServerUrl;
+    return *this;
+}
+
+inline void XmlRpcClient::Initialize (string appname, string appversion) {
+    xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS,
+                       appname.c_str(),
+                       appversion.c_str());
+}
+
+inline void XmlRpcClient::Terminate (void) {
+    xmlrpc_client_cleanup();
+}
+
+inline XmlRpcValue XmlRpcClient::call (string method_name,
+                       XmlRpcValue param_array)
+{
+    XmlRpcEnv env;
+    xmlrpc_value *result =
+    xmlrpc_client_call_params(env,
+                              mServerUrl.c_str(),
+                              method_name.c_str(),
+                              param_array.borrowReference());
+    env.throwIfFaultOccurred();
+    return XmlRpcValue(result, XmlRpcValue::CONSUME_REFERENCE);
+}
+
+//=========================================================================
+//  XmlRpcGenSrv
+//=========================================================================
+
+class XmlRpcGenSrv {
+
+private:
+
+    xmlrpc_registry*    mRegistry;
+
+    xmlrpc_mem_block* alloc (XmlRpcEnv& env, const string& body) const; 
+
+public:
+
+    XmlRpcGenSrv (int flags);
+    ~XmlRpcGenSrv (void);
+
+    xmlrpc_registry* getRegistry (void) const;
+
+    XmlRpcGenSrv&   addMethod (const string& name,
+                               xmlrpc_method method,
+                               void *data);
+    XmlRpcGenSrv&   addMethod (const string& name,
+                               xmlrpc_method method,
+                               void* data,
+                               const string& signature,
+                               const string& help);
+
+    string handle (const string& body) const;
+};
+
+inline XmlRpcGenSrv::XmlRpcGenSrv (int flags) {
+
+    XmlRpcEnv env;
+
+    if (flags == flags){};
+
+    mRegistry = xmlrpc_registry_new (env);
+    env.throwIfFaultOccurred();        
+}
+
+inline XmlRpcGenSrv::~XmlRpcGenSrv (void) {
+
+    xmlrpc_registry_free (mRegistry);
+}
+
+inline xmlrpc_registry* XmlRpcGenSrv::getRegistry () const {
+
+    return mRegistry;
+}
+
+#undef XMLRPC_NO_ASSIGNMENT
+
+
+// Copyright (C) 2001 by Eric Kidd. 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.
+
+
+#endif /* _XMLRPCCPP_H_ */

+ 18 - 0
Utilities/cmxmlrpc/bool.h

@@ -0,0 +1,18 @@
+#ifndef BOOL_H_INCLUDED
+#define BOOL_H_INCLUDED
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef __cplusplus
+#ifndef HAVE_BOOL
+#define HAVE_BOOL
+typedef int bool;
+#endif
+#endif
+
+#endif

+ 80 - 0
Utilities/cmxmlrpc/mallocvar.h

@@ -0,0 +1,80 @@
+/* These are some dynamic memory allocation facilities.  They are essentially
+   an extension to C, as they do allocations with a cognizance of C 
+   variables.  You can use them to make C read more like a high level
+   language.
+*/
+
+#ifndef MALLOCVAR_INCLUDED
+#define MALLOCVAR_INCLUDED
+
+#include <limits.h>
+#include <stdlib.h>
+
+static __inline__ void
+mallocProduct(void **      const resultP, 
+              unsigned int const factor1,
+              unsigned int const factor2) {
+/*----------------------------------------------------------------------------
+   malloc a space whose size in bytes is the product of 'factor1' and
+   'factor2'.  But if that size cannot be represented as an unsigned int,
+   return NULL without allocating anything.  Also return NULL if the malloc
+   fails.
+
+   Note that malloc() actually takes a size_t size argument, so the
+   proper test would be whether the size can be represented by size_t,
+   not unsigned int.  But there is no reliable indication available to
+   us, like UINT_MAX, of what the limitations of size_t are.  We
+   assume size_t is at least as expressive as unsigned int and that
+   nobody really needs to allocate more than 4GB of memory.
+-----------------------------------------------------------------------------*/
+    if (UINT_MAX / factor2 < factor1) 
+        *resultP = NULL; \
+    else 
+        *resultP = malloc(factor1 * factor2); 
+}
+
+
+
+static __inline__ void
+reallocProduct(void **      const blockP,
+               unsigned int const factor1,
+               unsigned int const factor2) {
+    
+    if (UINT_MAX / factor2 < factor1) 
+        *blockP = NULL; \
+    else 
+        *blockP = realloc(*blockP, factor1 * factor2); 
+}
+
+
+
+#define MALLOCARRAY(arrayName, nElements) \
+    mallocProduct((void **)&arrayName, nElements, sizeof(arrayName[0]))
+
+#define REALLOCARRAY(arrayName, nElements) \
+    reallocProduct((void **)&arrayName, nElements, sizeof(arrayName[0]))
+
+
+#define MALLOCARRAY_NOFAIL(arrayName, nElements) \
+do { \
+    MALLOCARRAY(arrayName, nElements); \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while(0)
+
+#define REALLOCARRAY_NOFAIL(arrayName, nElements) \
+do { \
+    REALLOCARRAY(arrayName, nElements); \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while(0)
+
+
+#define MALLOCVAR(varName) \
+    varName = malloc(sizeof(*varName))
+
+#define MALLOCVAR_NOFAIL(varName) \
+    do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0)
+
+#endif
+

+ 5 - 0
Utilities/cmxmlrpc/transport_config.h

@@ -0,0 +1,5 @@
+/* This file was generated by a make rule */
+#define MUST_BUILD_WININET_CLIENT 0
+#define MUST_BUILD_CURL_CLIENT 1
+#define MUST_BUILD_LIBWWW_CLIENT 0
+static const char * const XMLRPC_DEFAULT_TRANSPORT = "libcurl";

+ 108 - 0
Utilities/cmxmlrpc/win32_pthreads.c

@@ -0,0 +1,108 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#if WIN32
+
+#include "xmlrpc_pthreads.h"
+
+#include <process.h>
+
+#undef PACKAGE
+#undef VERSION
+
+#include "xmlrpc.h"
+
+int pthread_create(pthread_t *new_thread_ID,
+          const pthread_attr_t * attr,
+          pthread_func start_func, void *arg)
+{
+        HANDLE hThread;
+        DWORD dwThreadID;
+
+        XMLRPC_ASSERT (attr == NULL); /* unimplemented. */
+    XMLRPC_ASSERT_PTR_OK(new_thread_ID);
+    XMLRPC_ASSERT_PTR_OK(start_func);
+    XMLRPC_ASSERT_PTR_OK(arg);
+
+        hThread = (HANDLE) _beginthreadex (NULL, 0, 
+                start_func, (LPVOID)arg, CREATE_SUSPENDED, &dwThreadID);
+
+        SetThreadPriority (hThread, THREAD_PRIORITY_NORMAL); 
+        ResumeThread (hThread);
+
+        *new_thread_ID = hThread;
+
+        return hThread ? 0 : -1;
+}
+
+/* Just kill it. */
+int pthread_cancel(pthread_t target_thread)
+{
+        CloseHandle (target_thread);
+        return 0;
+}
+
+/* Waits for the thread to exit before continuing. */
+int pthread_join(pthread_t target_thread, void **status)
+{
+        DWORD dwResult = WaitForSingleObject(target_thread, INFINITE);
+        (*status) = (void *)dwResult;
+        return 0;
+}
+
+/* Stubbed. Do nothing. */
+int pthread_detach(pthread_t target_thread)
+{
+        return 0;
+}
+
+int pthread_mutex_init(pthread_mutex_t *mp,
+                                        const pthread_mutexattr_t * attr)
+{
+        InitializeCriticalSection(mp);
+        return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mp)
+{
+        EnterCriticalSection(mp);
+        return 0;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mp)
+{
+        LeaveCriticalSection(mp);
+        return 0;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mp)
+{
+        DeleteCriticalSection(mp);
+        return 0;
+}
+
+#endif

+ 783 - 0
Utilities/cmxmlrpc/xmlrpc.h

@@ -0,0 +1,783 @@
+/* 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. */
+
+
+#ifndef  _XMLRPC_H_
+#define  _XMLRPC_H_ 1
+
+#include <stddef.h>
+#include <stdarg.h>
+
+#ifdef HAVE_UNICODE_WCHAR
+#include <wchar.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*=========================================================================
+**  Typedefs
+**=========================================================================
+**  We define names for these types, because they may change from platform
+**  to platform.
+*/
+
+typedef signed int xmlrpc_int;  
+    /* An integer of the type defined by XML-RPC <int>; i.e. 32 bit */
+typedef signed int xmlrpc_int32;
+    /* An integer of the type defined by XML-RPC <int4>; i.e. 32 bit */
+typedef int xmlrpc_bool;
+    /* A boolean (of the type defined by XML-RPC <boolean>, but there's
+       really only one kind)
+    */
+typedef double xmlrpc_double;
+    /* A double precision floating point number as defined by
+       XML-RPC <float>.  But the C "double" type is universally the same,
+       so it's probably clearer just to use that.  This typedef is here 
+       for mathematical completeness.
+    */
+
+#define XMLRPC_INT32_MAX (2147483647)
+#define XMLRPC_INT32_MIN (-XMLRPC_INT32_MAX - 1)
+
+
+
+/*=========================================================================
+**  C struct size computations
+**=======================================================================*/
+
+/* Use XMLRPC_STRUCT_MEMBER_SIZE() to determine how big a structure is
+   up to and including a specified member.  E.g. if you have
+   struct mystruct {int red; int green; int blue};, then
+   XMLRPC_STRUCT_MEMBER_SIZE(mystruct, green) is (8).
+*/
+
+#define _XMLRPC_STRUCT_MEMBER_OFFSET(TYPE, MBRNAME) \
+  ((unsigned int)(char*)&((TYPE *)0)->MBRNAME)
+#define _XMLRPC_STRUCT_MEMBER_SIZE(TYPE, MBRNAME) \
+  sizeof(((TYPE *)0)->MBRNAME)
+#define XMLRPC_STRUCTSIZE(TYPE, MBRNAME) \
+  (_XMLRPC_STRUCT_MEMBER_OFFSET(TYPE, MBRNAME) + \
+  _XMLRPC_STRUCT_MEMBER_SIZE(TYPE, MBRNAME))
+
+/*=========================================================================
+**  Assertions and Debugging
+**=========================================================================
+**  We use xmlrpc_assert for internal sanity checks. For example:
+**
+**    xmlrpc_assert(ptr != NULL);
+**
+**  Assertions are only evaluated when debugging code is turned on. (To
+**  turn debugging off, define NDEBUG.) Some rules for using assertions:
+**
+**    1) Assertions should never have side effects.
+**    2) Assertions should never be used for run-time error checking.
+**       Instead, they should be used to check for "can't happen" errors.
+*/
+
+#ifndef NDEBUG
+
+#define XMLRPC_ASSERT(cond) \
+    do \
+        if (!(cond)) \
+            xmlrpc_assertion_failed(__FILE__, __LINE__); \
+    while (0)
+
+#else
+#define XMLRPC_ASSERT(cond) (0)
+#endif
+
+extern void xmlrpc_assertion_failed (char* file, int line);
+
+/* Validate a pointer. */
+#define XMLRPC_ASSERT_PTR_OK(ptr) \
+    XMLRPC_ASSERT((ptr) != NULL)
+
+/* We only call this if something truly drastic happens. */
+#define XMLRPC_FATAL_ERROR(msg) xmlrpc_fatal_error(__FILE__, __LINE__, (msg))
+
+extern void xmlrpc_fatal_error (char* file, int line, char* msg);
+
+
+/*=========================================================================
+**  Strings
+**=======================================================================*/
+
+/* Traditional C strings are char *, because they come from a time before
+   there was 'const'.  Now, const char * makes a lot more sense.  Also,
+   in modern times, we tend to dynamically allocate memory for strings.
+   We need this free function accordingly.  Ordinary free() doesn't check
+   the type, and can generate a warning due to the 'const'.
+*/
+void
+xmlrpc_strfree(const char * const string);
+
+
+
+/*=========================================================================
+**  xmlrpc_env
+**=========================================================================
+**  XML-RPC represents runtime errors as <fault> elements. These contain
+**  <faultCode> and <faultString> elements.
+**
+**  Since we need as much thread-safety as possible, we borrow an idea from
+**  CORBA--we store exception information in an "environment" object.
+**  You'll pass this to many different functions, and it will get filled
+**  out appropriately.
+**
+**  For example:
+**
+**    xmlrpc_env env;
+**
+**    xmlrpc_env_init(&env);
+**
+**    xmlrpc_do_something(&env);
+**    if (env.fault_occurred)
+**        report_error_appropriately();
+**
+**    xmlrpc_env_clean(&env);
+*/
+
+#define XMLRPC_INTERNAL_ERROR               (-500)
+#define XMLRPC_TYPE_ERROR                   (-501)
+#define XMLRPC_INDEX_ERROR                  (-502)
+#define XMLRPC_PARSE_ERROR                  (-503)
+#define XMLRPC_NETWORK_ERROR                (-504)
+#define XMLRPC_TIMEOUT_ERROR                (-505)
+#define XMLRPC_NO_SUCH_METHOD_ERROR         (-506)
+#define XMLRPC_REQUEST_REFUSED_ERROR        (-507)
+#define XMLRPC_INTROSPECTION_DISABLED_ERROR (-508)
+#define XMLRPC_LIMIT_EXCEEDED_ERROR         (-509)
+#define XMLRPC_INVALID_UTF8_ERROR           (-510)
+
+typedef struct _xmlrpc_env {
+    int   fault_occurred;
+    xmlrpc_int32 fault_code;
+    char* fault_string;
+} xmlrpc_env;
+
+/* Initialize and destroy the contents of the provided xmlrpc_env object.
+** These functions will never fail. */
+void xmlrpc_env_init (xmlrpc_env* env);
+void xmlrpc_env_clean (xmlrpc_env* env);
+
+/* Fill out an xmlrpc_fault with the specified values, and set the
+** fault_occurred flag. This function will make a private copy of 'string',
+** so you retain responsibility for your copy. */
+void 
+xmlrpc_env_set_fault(xmlrpc_env * const env, 
+                     int          const faultCode, 
+                     const char * const faultDescription);
+
+/* The same as the above, but using a printf-style format string. */
+void 
+xmlrpc_env_set_fault_formatted (xmlrpc_env * const envP, 
+                                int          const code,
+                                const char * const format, 
+                                ...);
+
+/* A simple debugging assertion. */
+#define XMLRPC_ASSERT_ENV_OK(env) \
+    XMLRPC_ASSERT((env) != NULL && !(env)->fault_occurred)
+
+/* This version must *not* interpret 'str' as a format string, to avoid
+** several evil attacks. */
+#define XMLRPC_FAIL(env,code,str) \
+    do { xmlrpc_env_set_fault((env),(code),(str)); goto cleanup; } while (0)
+
+#define XMLRPC_FAIL1(env,code,str,arg1) \
+    do { \
+        xmlrpc_env_set_fault_formatted((env),(code),(str),(arg1)); \
+        goto cleanup; \
+    } while (0)
+
+#define XMLRPC_FAIL2(env,code,str,arg1,arg2) \
+    do { \
+        xmlrpc_env_set_fault_formatted((env),(code),(str),(arg1),(arg2)); \
+        goto cleanup; \
+    } while (0)
+
+#define XMLRPC_FAIL3(env,code,str,arg1,arg2,arg3) \
+    do { \
+        xmlrpc_env_set_fault_formatted((env),(code), \
+                                       (str),(arg1),(arg2),(arg3)); \
+        goto cleanup; \
+    } while (0)
+
+#define XMLRPC_FAIL_IF_NULL(ptr,env,code,str) \
+    do { \
+        if ((ptr) == NULL) \
+            XMLRPC_FAIL((env),(code),(str)); \
+    } while (0)
+
+#define XMLRPC_FAIL_IF_FAULT(env) \
+    do { if ((env)->fault_occurred) goto cleanup; } while (0)
+
+
+/*=========================================================================
+**  Resource Limits
+**=========================================================================
+**  To discourage denial-of-service attacks, we provide several adjustable
+**  resource limits. These functions are *not* re-entrant.
+*/
+
+/* Limit IDs. There will be more of these as time goes on. */
+#define XMLRPC_NESTING_LIMIT_ID   (0)
+#define XMLRPC_XML_SIZE_LIMIT_ID  (1)
+#define XMLRPC_LAST_LIMIT_ID      (XMLRPC_XML_SIZE_LIMIT_ID)
+
+/* By default, deserialized data may be no more than 64 levels deep. */
+#define XMLRPC_NESTING_LIMIT_DEFAULT  (64)
+
+/* By default, XML data from the network may be no larger than 512K.
+** Some client and server modules may fail to enforce this properly. */
+#define XMLRPC_XML_SIZE_LIMIT_DEFAULT (512*1024)
+
+/* Set a specific limit to the specified value. */
+extern void xmlrpc_limit_set (int limit_id, size_t value);
+
+/* Get the value of a specified limit. */
+extern size_t xmlrpc_limit_get (int limit_id);
+
+
+/*=========================================================================
+**  xmlrpc_mem_block
+**=========================================================================
+**  A resizable chunk of memory. This is mostly used internally, but it is
+**  also used by the public API in a few places.
+**  The struct fields are private!
+*/
+
+typedef struct _xmlrpc_mem_block {
+    size_t _size;
+    size_t _allocated;
+    void*  _block;
+} xmlrpc_mem_block;
+
+/* Allocate a new xmlrpc_mem_block. */
+xmlrpc_mem_block* xmlrpc_mem_block_new (xmlrpc_env* env, size_t size);
+
+/* Destroy an existing xmlrpc_mem_block, and everything it contains. */
+void xmlrpc_mem_block_free (xmlrpc_mem_block* block);
+
+/* Initialize the contents of the provided xmlrpc_mem_block. */
+void xmlrpc_mem_block_init
+    (xmlrpc_env* env, xmlrpc_mem_block* block, size_t size);
+
+/* Deallocate the contents of the provided xmlrpc_mem_block, but not the
+** block itself. */
+void xmlrpc_mem_block_clean (xmlrpc_mem_block* block);
+
+/* Get the size and contents of the xmlrpc_mem_block. */
+size_t 
+xmlrpc_mem_block_size(const xmlrpc_mem_block * const block);
+
+void * 
+xmlrpc_mem_block_contents(const xmlrpc_mem_block * const block);
+
+/* Resize an xmlrpc_mem_block, preserving as much of the contents as
+** possible. */
+void xmlrpc_mem_block_resize
+    (xmlrpc_env* env, xmlrpc_mem_block* block, size_t size);
+
+/* Append data to an existing xmlrpc_mem_block. */
+void xmlrpc_mem_block_append
+    (xmlrpc_env* env, xmlrpc_mem_block* block, void *data, size_t len);
+
+#define XMLRPC_MEMBLOCK_NEW(type,env,size) \
+    xmlrpc_mem_block_new((env), sizeof(type) * (size))
+#define XMLRPC_MEMBLOCK_FREE(type,block) \
+    xmlrpc_mem_block_free(block)
+#define XMLRPC_MEMBLOCK_INIT(type,env,block,size) \
+    xmlrpc_mem_block_init((env), (block), sizeof(type) * (size))
+#define XMLRPC_MEMBLOCK_CLEAN(type,block) \
+    xmlrpc_mem_block_clean(block)
+#define XMLRPC_MEMBLOCK_SIZE(type,block) \
+    (xmlrpc_mem_block_size(block) / sizeof(type))
+#define XMLRPC_MEMBLOCK_CONTENTS(type,block) \
+    ((type*) xmlrpc_mem_block_contents(block))
+#define XMLRPC_MEMBLOCK_RESIZE(type,env,block,size) \
+    xmlrpc_mem_block_resize(env, block, sizeof(type) * (size))
+#define XMLRPC_MEMBLOCK_APPEND(type,env,block,data,size) \
+    xmlrpc_mem_block_append(env, block, data, sizeof(type) * (size))
+
+/* Here are some backward compatibility definitions.  These longer names
+   used to be the only ones and typed memory blocks were considered
+   special.
+*/
+#define XMLRPC_TYPED_MEM_BLOCK_NEW(type,env,size) \
+    XMLRPC_MEMBLOCK_NEW(type,env,size)
+#define XMLRPC_TYPED_MEM_BLOCK_FREE(type,block) \
+    XMLRPC_MEMBLOCK_FREE(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_INIT(type,env,block,size) \
+    XMLRPC_MEMBLOCK_INIT(type,env,block,size)
+#define XMLRPC_TYPED_MEM_BLOCK_CLEAN(type,block) \
+    XMLRPC_MEMBLOCK_CLEAN(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_SIZE(type,block) \
+    XMLRPC_MEMBLOCK_SIZE(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_CONTENTS(type,block) \
+    XMLRPC_MEMBLOCK_CONTENTS(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_RESIZE(type,env,block,size) \
+    XMLRPC_MEMBLOCK_RESIZE(type,env,block,size)
+#define XMLRPC_TYPED_MEM_BLOCK_APPEND(type,env,block,data,size) \
+    XMLRPC_MEMBLOCK_APPEND(type,env,block,data,size)
+
+
+
+/*=========================================================================
+**  xmlrpc_value
+**=========================================================================
+**  An XML-RPC value (of any type).
+*/
+
+typedef enum {
+    XMLRPC_TYPE_INT      = 0,
+    XMLRPC_TYPE_BOOL     = 1,
+    XMLRPC_TYPE_DOUBLE   = 2,
+    XMLRPC_TYPE_DATETIME = 3,
+    XMLRPC_TYPE_STRING   = 4,
+    XMLRPC_TYPE_BASE64   = 5,
+    XMLRPC_TYPE_ARRAY    = 6,
+    XMLRPC_TYPE_STRUCT   = 7,
+    XMLRPC_TYPE_C_PTR    = 8,
+    XMLRPC_TYPE_DEAD     = 0xDEAD,
+} xmlrpc_type;
+
+/* These are *always* allocated on the heap. No exceptions. */
+typedef struct _xmlrpc_value xmlrpc_value;
+
+#define XMLRPC_ASSERT_VALUE_OK(val) \
+    XMLRPC_ASSERT((val) != NULL && (val)->_type != XMLRPC_TYPE_DEAD)
+
+/* A handy type-checking routine. */
+#define XMLRPC_TYPE_CHECK(env,v,t) \
+    do \
+        if ((v)->_type != (t)) \
+            XMLRPC_FAIL(env, XMLRPC_TYPE_ERROR, "Expected " #t); \
+    while (0)
+
+void
+xmlrpc_abort_if_array_bad(xmlrpc_value * const arrayP);
+
+#define XMLRPC_ASSERT_ARRAY_OK(val) \
+    xmlrpc_abort_if_array_bad(val)
+
+/* Increment the reference count of an xmlrpc_value. */
+extern void xmlrpc_INCREF (xmlrpc_value* value);
+
+/* Decrement the reference count of an xmlrpc_value. If there
+** are no more references, free it. */
+extern void xmlrpc_DECREF (xmlrpc_value* value);
+
+/* Get the type of an XML-RPC value. */
+extern xmlrpc_type xmlrpc_value_type (xmlrpc_value* value);
+
+/* Build an xmlrpc_value from a format string.
+** Increments the reference counts of input arguments if necessary.
+** See the xmlrpc-c documentation for more information. */
+xmlrpc_value * 
+xmlrpc_build_value(xmlrpc_env * const env,
+                   const char * const format, 
+                   ...);
+
+/* The same as the above, but using a va_list and more general */
+void
+xmlrpc_build_value_va(xmlrpc_env *    const env,
+                      const char *    const format,
+                      va_list               args,
+                      xmlrpc_value ** const valPP,
+                      const char **   const tailP);
+
+/* Extract values from an xmlrpc_value and store them into C variables.
+** Does not increment the reference counts of output values.
+** See the xmlrpc-c documentation for more information. */
+void 
+xmlrpc_parse_value(xmlrpc_env *   const envP,
+                   xmlrpc_value * const value,
+                   const char *   const format, 
+                   ...);
+
+/* The same as the above, but using a va_list. */
+void 
+xmlrpc_parse_value_va(xmlrpc_env *   const envP,
+                      xmlrpc_value * const value,
+                      const char *   const format,
+                      va_list              args);
+
+void 
+xmlrpc_read_int(xmlrpc_env *         const envP,
+                const xmlrpc_value * const valueP,
+                int *                const intValueP);
+
+void
+xmlrpc_read_double(xmlrpc_env *         const envP,
+                   const xmlrpc_value * const valueP,
+                   xmlrpc_double *      const doubleValueP);
+
+void
+xmlrpc_read_bool(xmlrpc_env *         const envP,
+                 const xmlrpc_value * const valueP,
+                 xmlrpc_bool *        const boolValueP);
+
+void
+xmlrpc_read_string(xmlrpc_env *         const envP,
+                   const xmlrpc_value * const valueP,
+                   const char **        const stringValueP);
+
+
+void
+xmlrpc_read_string_lp(xmlrpc_env *         const envP,
+                      const xmlrpc_value * const valueP,
+                      unsigned int *       const lengthP,
+                      const char **        const stringValueP);
+
+/* Return the number of elements in an XML-RPC array.
+** Sets XMLRPC_TYPE_ERROR if 'array' is not an array. */
+int 
+xmlrpc_array_size(xmlrpc_env *         const env, 
+                  const xmlrpc_value * const array);
+
+/* Append an item to an XML-RPC array.
+** Increments the reference count of 'value' if no fault occurs.
+** Sets XMLRPC_TYPE_ERROR if 'array' is not an array. */
+extern void
+xmlrpc_array_append_item (xmlrpc_env*   env,
+                          xmlrpc_value* array,
+                          xmlrpc_value* value);
+
+void
+xmlrpc_array_read_item(xmlrpc_env *         const envP,
+                       const xmlrpc_value * const arrayP,
+                       unsigned int         const index,
+                       xmlrpc_value **      const valuePP);
+
+/* Get an item from an XML-RPC array.
+** Does not increment the reference count of the returned value.
+** Sets XMLRPC_TYPE_ERROR if 'array' is not an array.
+** Sets XMLRPC_INDEX_ERROR if 'index' is out of bounds. */
+xmlrpc_value * 
+xmlrpc_array_get_item(xmlrpc_env *         const env,
+                      const xmlrpc_value * const array,
+                      int                  const index);
+
+/* Not implemented--we don't need it yet.
+extern 
+int xmlrpc_array_set_item (xmlrpc_env* env,
+xmlrpc_value* array,
+int index,
+                                  xmlrpc_value* value);
+*/
+
+/* Create a new struct.  Deprecated.  xmlrpc_build_value() is the
+   general way to create an xmlrpc_value, including an empty struct.
+*/
+xmlrpc_value *
+xmlrpc_struct_new(xmlrpc_env * env);
+
+/* Return the number of key/value pairs in a struct.
+** Sets XMLRPC_TYPE_ERROR if 'strct' is not a struct. */
+int
+xmlrpc_struct_size (xmlrpc_env   * env, 
+                    xmlrpc_value * strct);
+
+/* Returns true iff 'strct' contains 'key'.
+** Sets XMLRPC_TYPE_ERROR if 'strct' is not a struct. */
+int 
+xmlrpc_struct_has_key(xmlrpc_env *   const envP,
+                      xmlrpc_value * const strctP,
+                      const char *   const key);
+
+/* The same as the above, but the key may contain zero bytes.
+   Deprecated.  xmlrpc_struct_get_value_v() is more general, and this
+   case is not common enough to warrant a shortcut.
+*/
+int 
+xmlrpc_struct_has_key_n(xmlrpc_env   * const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key, 
+                        size_t         const key_len);
+
+#if 0
+/* Not implemented yet, but needed for completeness. */
+int
+xmlrpc_struct_has_key_v(xmlrpc_env *   env, 
+                        xmlrpc_value * strct,
+                        xmlrpc_value * const keyval);
+#endif
+
+
+void
+xmlrpc_struct_find_value(xmlrpc_env *    const envP,
+                         xmlrpc_value *  const structP,
+                         const char *    const key,
+                         xmlrpc_value ** const valuePP);
+
+
+void
+xmlrpc_struct_find_value_v(xmlrpc_env *    const envP,
+                           xmlrpc_value *  const structP,
+                           xmlrpc_value *  const keyP,
+                           xmlrpc_value ** const valuePP);
+
+void
+xmlrpc_struct_read_value_v(xmlrpc_env *    const envP,
+                           xmlrpc_value *  const structP,
+                           xmlrpc_value *  const keyP,
+                           xmlrpc_value ** const valuePP);
+
+void
+xmlrpc_struct_read_value(xmlrpc_env *    const envP,
+                         xmlrpc_value *  const strctP,
+                         const char *    const key,
+                         xmlrpc_value ** const valuePP);
+
+/* The "get_value" functions are deprecated.  Use the "find_value"
+   and "read_value" functions instead.
+*/
+xmlrpc_value * 
+xmlrpc_struct_get_value(xmlrpc_env *   const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key);
+
+/* The same as above, but the key may contain zero bytes. 
+   Deprecated.  xmlrpc_struct_get_value_v() is more general, and this
+   case is not common enough to warrant a shortcut.
+*/
+xmlrpc_value * 
+xmlrpc_struct_get_value_n(xmlrpc_env *   const envP,
+                          xmlrpc_value * const strctP,
+                          const char *   const key, 
+                          size_t         const key_len);
+
+/* Set the value associated with 'key' in 'strct' to 'value'.
+** Increments the reference count of value.
+** Sets XMLRPC_TYPE_ERROR if 'strct' is not a struct. */
+void 
+xmlrpc_struct_set_value(xmlrpc_env *   const env,
+                        xmlrpc_value * const strct,
+                        const char *   const key,
+                        xmlrpc_value * const value);
+
+/* The same as above, but the key may contain zero bytes.  Deprecated.
+   The general way to set a structure value is xmlrpc_struct_set_value_v(),
+   and this case is not common enough to deserve a shortcut.
+*/
+void 
+xmlrpc_struct_set_value_n(xmlrpc_env *    const env,
+                          xmlrpc_value *  const strct,
+                          const char *    const key, 
+                          size_t          const key_len,
+                          xmlrpc_value *  const value);
+
+/* The same as above, but the key must be an XML-RPC string.
+** Fails with XMLRPC_TYPE_ERROR if 'keyval' is not a string. */
+void 
+xmlrpc_struct_set_value_v(xmlrpc_env *   const env,
+                          xmlrpc_value * const strct,
+                          xmlrpc_value * const keyval,
+                          xmlrpc_value * const value);
+
+/* Given a zero-based index, return the matching key and value. This
+** is normally used in conjunction with xmlrpc_struct_size.
+** Fails with XMLRPC_TYPE_ERROR if 'struct' is not a struct.
+** Fails with XMLRPC_INDEX_ERROR if 'index' is out of bounds. */
+
+void 
+xmlrpc_struct_read_member(xmlrpc_env *    const envP,
+                          xmlrpc_value *  const structP,
+                          unsigned int    const index,
+                          xmlrpc_value ** const keyvalP,
+                          xmlrpc_value ** const valueP);
+
+/* The same as above, but does not increment the reference count of the
+   two values it returns, and return NULL for both if it fails, and
+   takes a signed integer for the index (but fails if it is negative).
+
+   Deprecated.
+*/
+void
+xmlrpc_struct_get_key_and_value(xmlrpc_env *    env,
+                                xmlrpc_value *  strct,
+                                int             index,
+                                xmlrpc_value ** out_keyval,
+                                xmlrpc_value ** out_value);
+
+
+/*=========================================================================
+**  Encoding XML
+**=======================================================================*/
+
+/* Serialize an XML value without any XML header. This is primarily used
+** for testing purposes. */
+void
+xmlrpc_serialize_value(xmlrpc_env *       env,
+                       xmlrpc_mem_block * output,
+                       xmlrpc_value *     value);
+
+/* Serialize a list of parameters without any XML header. This is
+** primarily used for testing purposes. */
+void
+xmlrpc_serialize_params(xmlrpc_env *       env,
+                        xmlrpc_mem_block * output,
+                        xmlrpc_value *     param_array);
+
+/* Serialize an XML-RPC call. */
+void 
+xmlrpc_serialize_call (xmlrpc_env *       const env,
+                       xmlrpc_mem_block * const output,
+                       const char *       const method_name,
+                       xmlrpc_value *     const param_array);
+
+/* Serialize an XML-RPC return value. */
+extern void
+xmlrpc_serialize_response(xmlrpc_env *       env,
+                          xmlrpc_mem_block * output,
+                          xmlrpc_value *     value);
+
+/* Serialize an XML-RPC fault (as specified by 'fault'). */
+extern void
+xmlrpc_serialize_fault(xmlrpc_env *       env,
+                       xmlrpc_mem_block * output,
+                       xmlrpc_env *       fault);
+
+
+/*=========================================================================
+**  Decoding XML
+**=======================================================================*/
+
+/* Parse an XML-RPC call. If an error occurs, set a fault and set
+** the output variables to NULL.
+** The caller is responsible for calling free(*out_method_name) and
+** xmlrpc_DECREF(*out_param_array). */
+void 
+xmlrpc_parse_call(xmlrpc_env *    const envP,
+                  const char *    const xml_data,
+                  size_t          const xml_len,
+                  const char **   const out_method_name,
+                  xmlrpc_value ** const out_param_array);
+
+/* Parse an XML-RPC response. If a fault occurs (or was received over the
+** wire), return NULL and set up 'env'. The calling is responsible for
+** calling xmlrpc_DECREF on the return value (if it isn't NULL). */
+xmlrpc_value *
+xmlrpc_parse_response(xmlrpc_env * env, 
+                      const char * xml_data, 
+                      size_t       xml_len);
+
+
+/*=========================================================================
+**  XML-RPC Base64 Utilities
+**=========================================================================
+**  Here are some lightweight utilities which can be used to encode and
+**  decode Base64 data. These are exported mainly for testing purposes.
+*/
+
+/* This routine inserts newlines every 76 characters, as required by the
+** Base64 specification. */
+xmlrpc_mem_block *
+xmlrpc_base64_encode(xmlrpc_env *    env,
+                     unsigned char * bin_data,
+                     size_t          bin_len);
+
+/* This routine encodes everything in one line. This is needed for HTTP
+** authentication and similar tasks. */
+xmlrpc_mem_block *
+xmlrpc_base64_encode_without_newlines(xmlrpc_env *    env,
+                                      unsigned char * bin_data,
+                                      size_t          bin_len);
+
+/* This decodes Base64 data with or without newlines. */
+extern xmlrpc_mem_block *
+xmlrpc_base64_decode(xmlrpc_env * env,
+                     char *       ascii_data,
+                     size_t       ascii_len);
+
+
+/*=========================================================================
+**  UTF-8 Encoding and Decoding
+**=========================================================================
+**  We need a correct, reliable and secure UTF-8 decoder. This decoder
+**  raises a fault if it encounters invalid UTF-8.
+**
+**  Note that ANSI C does not precisely define the representation used
+**  by wchar_t--it may be UCS-2, UTF-16, UCS-4, or something from outer
+**  space. If your platform does something especially bizarre, you may
+**  need to reimplement these routines.
+*/
+
+#ifdef HAVE_UNICODE_WCHAR
+
+/* Ensure that a string contains valid, legally-encoded UTF-8 data.
+** (Incorrectly-encoded UTF-8 strings are often used to bypass security
+** checks.) */
+void 
+xmlrpc_validate_utf8 (xmlrpc_env * const env,
+                      const char * const utf8_data,
+                      size_t       const utf8_len);
+
+/* Decode a UTF-8 string. */
+xmlrpc_mem_block *
+xmlrpc_utf8_to_wcs(xmlrpc_env * env,
+                   char *       utf8_data,
+                   size_t       utf8_len);
+
+/* Encode a UTF-8 string. */
+xmlrpc_mem_block *
+xmlrpc_wcs_to_utf8(xmlrpc_env * env,
+                   wchar_t *    wcs_data,
+                   size_t       wcs_len);
+
+#endif /* HAVE_UNICODE_WCHAR */
+
+/*=========================================================================
+**  Authorization Cookie Handling
+**=========================================================================
+**  Routines to get and set values for authorizing via authorization
+**  cookies. Both the client and server use HTTP_COOKIE_AUTH to store
+**  the representation of the authorization value, which is actually
+**  just a base64 hash of username:password. (This entire method is
+**  a cookie replacement of basic authentication.)
+**/
+
+extern void xmlrpc_authcookie_set(xmlrpc_env * env,
+                                  const char * username,
+                                  const char * password);
+
+char *xmlrpc_authcookie(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* In the days before xmlrpc_server.h existed, some of what's in it was
+   in here.  For backward compatibility, we need to include it here, even
+   though it really isn't logical to do so.
+*/
+#include <xmlrpc_server.h>
+
+#endif
+

+ 37 - 0
Utilities/cmxmlrpc/xmlrpc_amconfig.h.in

@@ -0,0 +1,37 @@
+/* xmlrpc_amconfig.h is generated by 'configure' from the template
+   xmlrpc_amconfig.h.in, by virtue of the AM_CONFIG_HEADER() macro in
+   configure.in.
+
+   We'd like to replace it some day with something that doesn't use
+   such a special tool, to make the build understandable by dumber
+   developers.  
+*/
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#cmakedefine size_t @size_t@
+
+/* Define if you have the setgroups function.  */
+#cmakedefine HAVE_SETGROUPS @HAVE_SETGROUPS@
+
+#cmakedefine HAVE_ASPRINTF @HAVE_ASPRINTF@
+
+/* Define if you have the wcsncmp function.  */
+#cmakedefine HAVE_WCSNCMP @HAVE_WCSNCMP@
+
+/* Define if you have the <stdarg.h> header file.  */
+#cmakedefine HAVE_STDARG_H @HAVE_STDARG_H@
+
+/* Define if you have the <sys/filio.h> header file.  */
+#cmakedefine HAVE_SYS_FILIO_H @HAVE_SYS_FILIO_H@
+
+/* Define if you have the <sys/ioctl.h> header file.  */
+#cmakedefine HAVE_SYS_IOCTL_H @HAVE_SYS_IOCTL_H@
+
+/* Define if you have the <wchar.h> header file.  */
+#cmakedefine HAVE_WCHAR_H @HAVE_WCHAR_H@
+
+/* Define if you have the socket library (-lsocket).  */
+#cmakedefine HAVE_LIBSOCKET @HAVE_LIBSOCKET@
+
+/* Name of package */
+#cmakedefine PACKAGE @PACKAGE@

+ 206 - 0
Utilities/cmxmlrpc/xmlrpc_array.c

@@ -0,0 +1,206 @@
+/* Copyright information is at the end of the file */
+
+/*=========================================================================
+**  XML-RPC Array Functions
+**=========================================================================
+*/
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+
+
+void
+xmlrpc_abort_if_array_bad(xmlrpc_value * const arrayP) {
+
+    if (arrayP == NULL)
+        abort();
+    else if (arrayP->_type != XMLRPC_TYPE_ARRAY)
+        abort();
+    else {
+        unsigned int const arraySize =
+            XMLRPC_MEMBLOCK_SIZE(xmlrpc_value*, &arrayP->_block);
+        xmlrpc_value ** const contents = 
+            XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
+        
+        if (contents == NULL)
+            abort();
+        else {
+            unsigned int index;
+            
+            for (index = 0; index < arraySize; ++index) {
+                xmlrpc_value * const itemP = contents[index];
+                if (itemP == NULL)
+                    abort();
+                else if (itemP->_refcount < 1)
+                    abort();
+            }
+        }
+    }
+}
+
+
+
+void
+xmlrpc_destroyArrayContents(xmlrpc_value * const arrayP) {
+/*----------------------------------------------------------------------------
+   Dispose of the contents of an array (but not the array value itself).
+   The value is not valid after this.
+-----------------------------------------------------------------------------*/
+    unsigned int const arraySize =
+        XMLRPC_MEMBLOCK_SIZE(xmlrpc_value*, &arrayP->_block);
+    xmlrpc_value ** const contents = 
+        XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
+
+    unsigned int index;
+    
+    XMLRPC_ASSERT_ARRAY_OK(arrayP);
+
+    /* Release our reference to each item in the array */
+    for (index = 0; index < arraySize; ++index) {
+        xmlrpc_value * const itemP = contents[index];
+        xmlrpc_DECREF(itemP);
+    }
+    XMLRPC_MEMBLOCK_CLEAN(xmlrpc_value *, &arrayP->_block);
+}
+
+
+
+int 
+xmlrpc_array_size(xmlrpc_env *         const env, 
+                  const xmlrpc_value * const array) {
+
+    int retval;
+
+    /* Suppress a compiler warning about uninitialized variables. */
+    retval = 0;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_VALUE_OK(array);
+    XMLRPC_TYPE_CHECK(env, array, XMLRPC_TYPE_ARRAY);
+
+    retval = XMLRPC_TYPED_MEM_BLOCK_SIZE(xmlrpc_value*, &array->_block);
+
+                  cleanup:
+    if (env->fault_occurred)
+        return -1;
+    else
+        return retval;
+}
+
+
+
+void 
+xmlrpc_array_append_item(xmlrpc_env *   const envP,
+                         xmlrpc_value * const arrayP,
+                         xmlrpc_value * const valueP) {
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(arrayP);
+    
+    if (xmlrpc_value_type(arrayP) != XMLRPC_TYPE_ARRAY)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TYPE_ERROR, "Value is not an array");
+    else {
+        size_t const size = 
+            XMLRPC_MEMBLOCK_SIZE(xmlrpc_value *, &arrayP->_block);
+
+        XMLRPC_MEMBLOCK_RESIZE(xmlrpc_value *, envP, &arrayP->_block, size+1);
+
+        if (!envP->fault_occurred) {
+            xmlrpc_value ** const contents =
+                XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
+            xmlrpc_INCREF(valueP);
+            contents[size] = valueP;
+        }
+    }
+}
+
+
+
+void
+xmlrpc_array_read_item(xmlrpc_env *         const envP,
+                       const xmlrpc_value * const arrayP,
+                       unsigned int         const index,
+                       xmlrpc_value **      const valuePP) {
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(arrayP);
+    XMLRPC_ASSERT_PTR_OK(valuePP);
+
+    if (arrayP->_type != XMLRPC_TYPE_ARRAY)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TYPE_ERROR, "Attempt to read array item from "
+            "a value that is not an array");
+    else {
+        xmlrpc_value ** const contents = 
+            XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value *, &arrayP->_block);
+        size_t const size = 
+            XMLRPC_MEMBLOCK_SIZE(xmlrpc_value *, &arrayP->_block);
+
+        if (index >= size)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INDEX_ERROR, "Array index %u is beyond end "
+                "of %u-item array", index, (unsigned int)size);
+        else {
+            *valuePP = contents[index];
+            xmlrpc_INCREF(*valuePP);
+        }
+    }
+}
+
+
+
+xmlrpc_value * 
+xmlrpc_array_get_item(xmlrpc_env *         const envP,
+                      const xmlrpc_value * const arrayP,
+                      int                  const index) {
+
+    xmlrpc_value * valueP;
+
+    if (index < 0)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INDEX_ERROR, "Index %d is negative.");
+    else {
+        xmlrpc_array_read_item(envP, arrayP, index, &valueP);
+
+        if (!envP->fault_occurred)
+            xmlrpc_DECREF(valueP);
+    }
+    if (envP->fault_occurred)
+        valueP = NULL;
+        
+    return valueP;
+}
+
+
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+** Copyright (C) 2001 by Eric Kidd. 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. */

+ 69 - 0
Utilities/cmxmlrpc/xmlrpc_authcookie.c

@@ -0,0 +1,69 @@
+/* Copyright (C) 2002 by [email protected]. 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "xmlrpc.h"
+
+/* set cookie function */
+void xmlrpc_authcookie_set ( xmlrpc_env *env, 
+                        const char *username, 
+                        const char *password ) {
+    char *unencoded;
+    xmlrpc_mem_block *token;
+
+    /* Check asserts. */
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(username);
+    XMLRPC_ASSERT_PTR_OK(password);
+
+    /* Clear out memory. */
+    unencoded = (char *) malloc ( sizeof ( char * ) );
+    token = NULL;
+
+    /* Create unencoded string/hash. */
+    sprintf(unencoded, "%s:%s", username, password);
+
+    /* Create encoded string. */
+    token = xmlrpc_base64_encode_without_newlines(env, unencoded,
+                                strlen(unencoded));
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Set HTTP_COOKIE_AUTH to the character representation of the
+    ** encoded string. */
+    setenv("HTTP_COOKIE_AUTH", XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, token),
+                1);
+
+ cleanup:
+    if (token) xmlrpc_mem_block_free(token);
+}
+
+char *xmlrpc_authcookie ( void ) {
+    return getenv("HTTP_COOKIE_AUTH");
+}

+ 262 - 0
Utilities/cmxmlrpc/xmlrpc_base64.c

@@ -0,0 +1,262 @@
+/* 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.
+**
+** There is more copyright information in the bottom half of this file. 
+** Please see it for more details. */
+
+
+/*=========================================================================
+**  XML-RPC Base64 Utilities
+**=========================================================================
+**  This code was swiped from Jack Jansen's code in Python 1.5.2 and
+**  modified to work with our data types.
+*/
+
+#include "xmlrpc_config.h"
+
+#include "xmlrpc.h"
+
+#define CRLF    "\015\012"
+#define CR      '\015'
+#define LF      '\012'
+
+
+/***********************************************************
+Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
+Amsterdam, The Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI or Corporation for National Research Initiatives or
+CNRI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+While CWI is the initial source for this software, a modified version
+is made available by the Corporation for National Research Initiatives
+(CNRI) at the Internet address ftp://ftp.python.org.
+
+STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
+CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+static char table_a2b_base64[] = {
+        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+        52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */
+        -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+        15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+        -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+        41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+
+#define BASE64_PAD '='
+#define BASE64_MAXBIN 57        /* Max binary chunk size (76 char line) */
+#define BASE64_LINE_SZ 128      /* Buffer size for a single line. */    
+
+static unsigned char table_b2a_base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static xmlrpc_mem_block *
+xmlrpc_base64_encode_internal (xmlrpc_env *env,
+                               unsigned char *bin_data,
+                               size_t bin_len,
+                               int want_newlines)
+{
+    size_t chunk_start, chunk_left;
+    unsigned char *ascii_data;
+    int leftbits;
+    unsigned char this_ch;
+    unsigned int leftchar;
+    xmlrpc_mem_block *output;
+    unsigned char line_buffer[BASE64_LINE_SZ];
+
+    /* Create a block to hold our lines when we finish them. */
+    output = xmlrpc_mem_block_new(env, 0);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Deal with empty data blocks gracefully. Yuck. */
+    if (bin_len == 0) {
+        if (want_newlines)
+            XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, CRLF, 2);
+        goto cleanup;
+    }
+
+    /* Process our binary data in line-sized chunks. */
+    for (chunk_start=0; chunk_start < bin_len; chunk_start += BASE64_MAXBIN) {
+
+        /* Set up our per-line state. */
+        ascii_data = &line_buffer[0];
+        chunk_left = bin_len - chunk_start;
+        if (chunk_left > BASE64_MAXBIN)
+            chunk_left = BASE64_MAXBIN;
+        leftbits = 0;
+        leftchar = 0;
+
+        for(; chunk_left > 0; chunk_left--, bin_data++) {
+            /* Shift the data into our buffer */
+            leftchar = (leftchar << 8) | *bin_data;
+            leftbits += 8;
+
+            /* See if there are 6-bit groups ready */
+            while (leftbits >= 6) {
+                this_ch = (leftchar >> (leftbits-6)) & 0x3f;
+                leftbits -= 6;
+                *ascii_data++ = table_b2a_base64[this_ch];
+            }
+        }
+        if (leftbits == 2) {
+            *ascii_data++ = table_b2a_base64[(leftchar&3) << 4];
+            *ascii_data++ = BASE64_PAD;
+            *ascii_data++ = BASE64_PAD;
+        } else if (leftbits == 4) {
+            *ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2];
+            *ascii_data++ = BASE64_PAD;
+        } 
+
+        /* Append a courtesy CRLF. */
+        if (want_newlines) {
+            *ascii_data++ = CR;
+            *ascii_data++ = LF;
+        }
+        
+        /* Save our line. */
+        XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, line_buffer,
+                                      ascii_data - &line_buffer[0]);
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (output)
+            xmlrpc_mem_block_free(output);
+        return NULL;
+    }
+    return output;
+}
+
+
+xmlrpc_mem_block *
+xmlrpc_base64_encode (xmlrpc_env *env, unsigned char *bin_data, size_t bin_len)
+{
+    return xmlrpc_base64_encode_internal(env, bin_data, bin_len, 1);
+}
+
+
+xmlrpc_mem_block *
+xmlrpc_base64_encode_without_newlines (xmlrpc_env *env,
+                                       unsigned char *bin_data,
+                                       size_t bin_len)
+{
+    return xmlrpc_base64_encode_internal(env, bin_data, bin_len, 0);
+}
+
+
+xmlrpc_mem_block *
+xmlrpc_base64_decode (xmlrpc_env *env,
+                      char *ascii_data,
+                      size_t ascii_len)
+{
+    unsigned char *bin_data;
+    int leftbits;
+    unsigned char this_ch;
+    unsigned int leftchar;
+    size_t npad;
+    size_t bin_len, buffer_size;
+    xmlrpc_mem_block *output;
+
+    /* Create a block to hold our chunks when we finish them.
+    ** We overestimate the size now, and fix it later. */
+    buffer_size = ((ascii_len+3)/4)*3;
+    output = xmlrpc_mem_block_new(env, buffer_size);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Set up our decoder state. */
+    leftbits = 0;
+    leftchar = 0;
+    npad = 0;
+    bin_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, output);
+    bin_len = 0;
+
+    for( ; ascii_len > 0 ; ascii_len--, ascii_data++ ) {
+
+        /* Skip some punctuation. */
+        this_ch = (*ascii_data & 0x7f);
+        if ( this_ch == '\r' || this_ch == '\n' || this_ch == ' ' )
+            continue;
+        if ( this_ch == BASE64_PAD )
+            npad++;
+        this_ch = table_a2b_base64[(*ascii_data) & 0x7f];
+
+        /* XXX - We just throw away invalid characters. Is this right? */
+        if ( this_ch == (unsigned char) -1 ) continue;
+
+        /* Shift it in on the low end, and see if there's
+        ** a byte ready for output. */
+        leftchar = (leftchar << 6) | (this_ch);
+        leftbits += 6;
+        if ( leftbits >= 8 ) {
+            leftbits -= 8;
+            XMLRPC_ASSERT(bin_len < buffer_size);
+            *bin_data++ = (leftchar >> leftbits) & 0xFF;
+            leftchar &= ((1 << leftbits) - 1);
+            bin_len++;
+        }
+    }
+
+    /* Check that no bits are left. */
+    if ( leftbits )
+        XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR, "Incorrect Base64 padding");
+
+    /* Check to make sure we have a sane amount of padding. */
+    if (npad > bin_len || npad > 2)
+        XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR, "Malformed Base64 data");
+
+    /* Remove any padding and set the correct size. */
+    bin_len -= npad;
+    XMLRPC_TYPED_MEM_BLOCK_RESIZE(char, env, output, bin_len);
+    XMLRPC_ASSERT(!env->fault_occurred);
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (output)
+            xmlrpc_mem_block_free(output);
+        return NULL;
+    }
+    return output;
+}

+ 295 - 0
Utilities/cmxmlrpc/xmlrpc_cgi.c

@@ -0,0 +1,295 @@
+/* Copyright (C) 2001 by Eric Kidd. 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. */
+
+
+#include "xmlrpc_config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Windows NT stdout binary mode fix. */
+#ifdef _WIN32 
+#include <io.h> 
+#include <fcntl.h> 
+#endif 
+
+#include "xmlrpc.h"
+#include "xmlrpc_server.h"
+#include "xmlrpc_cgi.h"
+
+
+/*=========================================================================
+**  Output Routines
+**=========================================================================
+**  These routines send various kinds of responses to the server.
+*/
+
+static void send_xml (char *xml_data, size_t xml_len)
+{
+    /* Send our CGI headers back to the server.
+    ** XXX - Coercing 'size_t' to 'unsigned long' might be unsafe under
+    ** really weird circumstances. */
+    fprintf(stdout, "Status: 200 OK\n");
+    /* Handle authentication cookie being sent back. */
+    if (getenv("HTTP_COOKIE_AUTH") != NULL)
+        fprintf(stdout, "Set-Cookie: auth=%s\n", getenv("HTTP_COOKIE_AUTH"));
+    fprintf(stdout, "Content-type: text/xml; charset=\"utf-8\"\n");
+    fprintf(stdout, "Content-length: %ld\n\n", (unsigned long) xml_len);
+
+    /* Blast out our data. */
+    fwrite(xml_data, sizeof(char), xml_len, stdout);
+}
+
+static void send_error (int code, char *message, xmlrpc_env *env)
+{
+    /* Send an error header. */
+    fprintf(stdout, "Status: %d %s\n", code, message);
+    fprintf(stdout, "Content-type: text/html\n\n");
+    
+    /* Send an error message. */
+    fprintf(stdout, "<title>%d %s</title>\n", code, message);
+    fprintf(stdout, "<h1>%d %s</h1>\n", code, message);
+    fprintf(stdout, "<p>An error occurred processing your request.</p>\n");
+
+    /* Print out the XML-RPC fault, if present. */
+    if (env && env->fault_occurred)
+        fprintf(stdout, "<p>XML-RPC Fault #%d: %s</p>\n",
+                env->fault_code, env->fault_string);
+}
+
+
+/*=========================================================================
+**  die_if_fault_occurred
+**=========================================================================
+**  Certain kinds of errors aren't worth the trouble of generating
+**  an XML-RPC fault. For these, we just send status 500 to our web server
+**  and log the fault to our server log.
+*/
+
+static void die_if_fault_occurred (xmlrpc_env *env)
+{
+    if (env->fault_occurred) {
+        fprintf(stderr, "Unexpected XML-RPC fault: %s (%d)\n",
+                env->fault_string, env->fault_code);
+        send_error(500, "Internal Server Error", env);
+        exit(1);
+    }
+}
+
+
+/*=========================================================================
+**  Initialization, Cleanup & Method Registry
+**=========================================================================
+**  These are all related, so we group them together.
+*/
+
+static xmlrpc_registry *registry;
+
+void xmlrpc_cgi_init (int flags ATTR_UNUSED)
+{
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+    registry = xmlrpc_registry_new(&env);
+    die_if_fault_occurred(&env);
+    xmlrpc_env_clean(&env);    
+
+#ifdef _WIN32 
+    /* Fix from Jeff Stewart: NT opens stdin and stdout in text mode
+    ** by default, badly confusing our length calculations.  So we need
+    ** to set these file handles to binary. */
+    _setmode(_fileno(stdout), _O_BINARY); 
+    _setmode(_fileno(stdin), _O_BINARY); 
+#endif 
+}
+
+void xmlrpc_cgi_cleanup (void)
+{
+    xmlrpc_registry_free(registry);
+}
+
+xmlrpc_registry *xmlrpc_cgi_registry (void)
+{
+    return registry;
+}
+
+void xmlrpc_cgi_add_method (char *method_name,
+                            xmlrpc_method method,
+                            void *user_data)
+{
+    xmlrpc_env env;
+    xmlrpc_env_init(&env);
+    xmlrpc_registry_add_method(&env, registry, NULL, method_name,
+                               method, user_data);
+    die_if_fault_occurred(&env);
+    xmlrpc_env_clean(&env);    
+}
+
+extern void
+xmlrpc_cgi_add_method_w_doc (char *method_name,
+                             xmlrpc_method method,
+                             void *user_data,
+                             char *signature,
+                             char *help)
+{
+    xmlrpc_env env;
+    xmlrpc_env_init(&env);
+    xmlrpc_registry_add_method_w_doc(&env, registry, NULL, method_name,
+                                     method, user_data, signature, help);
+    die_if_fault_occurred(&env);
+    xmlrpc_env_clean(&env);    
+}
+
+
+/*=========================================================================
+**  get_body
+**=========================================================================
+**  Slurp the body of the request into an xmlrpc_mem_block.
+*/
+
+static xmlrpc_mem_block *get_body (xmlrpc_env *env, size_t length)
+{
+    xmlrpc_mem_block *result;
+    char *contents;
+    size_t count;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+
+    /* Error-handling preconditions. */
+    result = NULL;
+
+    /* XXX - Puke if length is too big. */
+
+    /* Allocate our memory block. */
+    result = xmlrpc_mem_block_new(env, length);
+    XMLRPC_FAIL_IF_FAULT(env);
+    contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, result);
+
+    /* Get our data off the network.
+    ** XXX - Coercing 'size_t' to 'unsigned long' might be unsafe under
+    ** really weird circumstances. */
+    count = fread(contents, sizeof(char), length, stdin);
+    if (count < length)
+        XMLRPC_FAIL2(env, XMLRPC_INTERNAL_ERROR,
+                     "Expected %ld bytes, received %ld",
+                     (unsigned long) length, (unsigned long) count);
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (result)
+            xmlrpc_mem_block_free(result);
+        return NULL;
+    }
+    return result;
+}
+
+
+/*=========================================================================
+**  xmlrpc_cgi_process_call
+**=========================================================================
+**  Parse the incoming XML-RPC call, find the right method, call it, and
+**  serialize our response.
+*/
+
+void xmlrpc_cgi_process_call (void)
+{
+    xmlrpc_env env;
+    char *method, *type, *length_str;
+    int length;
+    xmlrpc_mem_block *input, *output;
+    char *input_data, *output_data;
+    size_t input_size, output_size;
+    int code;
+    char *message;
+
+    /* Error-handling preconditions. */
+    xmlrpc_env_init(&env);
+    input = output = NULL;
+
+    /* Set up a default error message. */
+    code = 500; message = "Internal Server Error";
+
+    /* Get our HTTP information from the environment. */
+    method = getenv("REQUEST_METHOD");
+    type = getenv("CONTENT_TYPE");
+    length_str = getenv("CONTENT_LENGTH");
+
+    /* Perform some sanity checks. */
+    if (!method || 0 != strcmp(method, "POST")) {
+        code = 405; message = "Method Not Allowed";
+        XMLRPC_FAIL(&env, XMLRPC_INTERNAL_ERROR, "Expected HTTP method POST");
+    }
+    if (!type || 0 != strcmp(type, "text/xml")) {
+        code = 400; message = "Bad Request";
+        XMLRPC_FAIL(&env, XMLRPC_INTERNAL_ERROR, "Expected text/xml content");
+    }
+    if (!length_str) {
+        code = 411; message = "Length Required";
+        XMLRPC_FAIL(&env, XMLRPC_INTERNAL_ERROR, "Content-length required");
+    }
+
+    /* Get our content length. */
+    length = atoi(length_str);
+    if (length <= 0) {
+        code = 400; message = "Bad Request";
+        XMLRPC_FAIL(&env, XMLRPC_INTERNAL_ERROR, "Content-length must be > 0");
+    }
+
+    /* SECURITY: Make sure our content length is legal.
+    ** XXX - We can cast 'input_len' because we know it's >= 0, yes? */
+    if ((size_t) length > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID)) {
+        code = 400; message = "Bad Request";
+        XMLRPC_FAIL(&env, XMLRPC_LIMIT_EXCEEDED_ERROR,
+                    "XML-RPC request too large");
+    }
+
+    /* Get our body. */
+    input = get_body(&env, length);
+    XMLRPC_FAIL_IF_FAULT(&env);
+    input_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, input);
+    input_size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, input);
+
+    /* Process our call. */
+    output = xmlrpc_registry_process_call(&env, registry, NULL,
+                                          input_data, input_size);
+    XMLRPC_FAIL_IF_FAULT(&env);
+    output_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output);
+    output_size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
+
+    /* Send our data. */
+    send_xml(output_data, output_size);
+    
+ cleanup:
+    if (input)
+        xmlrpc_mem_block_free(input);
+    if (output)
+        xmlrpc_mem_block_free(output);
+    
+    if (env.fault_occurred)
+        send_error(code, message, &env);
+
+    xmlrpc_env_clean(&env);
+}

+ 81 - 0
Utilities/cmxmlrpc/xmlrpc_cgi.h

@@ -0,0 +1,81 @@
+/* Copyright (C) 2001 by Eric Kidd. 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. */
+
+#ifndef  _XMLRPC_CGI_H_
+#define  _XMLRPC_CGI_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*=========================================================================
+**  XML-RPC CGI Server
+**=========================================================================
+**  A simple XML-RPC server based on the Common Gateway Interface.
+*/
+
+#define XMLRPC_CGI_NO_FLAGS (0)
+
+/* Initialize the CGI server library. */
+extern void
+xmlrpc_cgi_init (int flags);
+
+/* Fetch the internal registry, if you happen to need it. */
+extern xmlrpc_registry *
+xmlrpc_cgi_registry (void);
+
+/* Register a new method. */
+extern void
+xmlrpc_cgi_add_method (char *method_name,
+                       xmlrpc_method method,
+                       void *user_data);
+
+/* As above, but provide documentation (see xmlrpc_registry_add_method_w_doc
+** for more information). You should really use this one. */
+extern void
+xmlrpc_cgi_add_method_w_doc (char *method_name,
+                             xmlrpc_method method,
+                             void *user_data,
+                             char *signature,
+                             char *help);
+
+/* Parse the XML-RPC call, invoke the appropriate method, and send the
+** response over the network. In future releases, we reserve the right to
+** time out when reading data. For now, we rely on the webserver to blow us
+** away. */
+extern void
+xmlrpc_cgi_process_call (void);
+
+/* Clean up any internal data structures before exiting. */
+extern void
+xmlrpc_cgi_cleanup (void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _XMLRPC_CGI_H_ */

+ 981 - 0
Utilities/cmxmlrpc/xmlrpc_client.c

@@ -0,0 +1,981 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#undef PACKAGE
+#undef VERSION
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "bool.h"
+#include "mallocvar.h"
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_client.h"
+#include "xmlrpc_client_int.h"
+/* transport_config.h defines XMLRPC_DEFAULT_TRANSPORT,
+    MUST_BUILD_WININET_CLIENT, MUST_BUILD_CURL_CLIENT,
+    MUST_BUILD_LIBWWW_CLIENT 
+*/
+#include "transport_config.h"
+
+#if MUST_BUILD_WININET_CLIENT
+#include "xmlrpc_wininet_transport.h"
+#endif
+#if MUST_BUILD_CURL_CLIENT
+#include "xmlrpc_curl_transport.h"
+#endif
+#if MUST_BUILD_LIBWWW_CLIENT
+#include "xmlrpc_libwww_transport.h"
+#endif
+
+struct xmlrpc_client {
+/*----------------------------------------------------------------------------
+   This represents a client object.
+-----------------------------------------------------------------------------*/
+    struct clientTransport * transportP;
+};
+
+
+
+typedef struct call_info
+{
+    /* These fields are used when performing asynchronous calls.
+    ** The _asynch_data_holder contains server_url, method_name and
+    ** param_array, so it's the only thing we need to free. */
+    xmlrpc_value *_asynch_data_holder;
+    char *server_url;
+    char *method_name;
+    xmlrpc_value *param_array;
+    xmlrpc_response_handler callback;
+    void *user_data;
+
+    /* The serialized XML data passed to this call. We keep this around
+    ** for use by our source_anchor field. */
+    xmlrpc_mem_block *serialized_xml;
+} call_info;
+
+static bool clientInitialized = FALSE;
+
+/*=========================================================================
+**  Initialization and Shutdown
+**=========================================================================
+*/
+
+static struct clientTransportOps clientTransportOps;
+
+static struct xmlrpc_client client;
+    /* Some day, we need to make this dynamically allocated, so there can
+       be more than one client per program and just generally to provide
+       a cleaner interface.
+    */
+
+extern void
+xmlrpc_client_init(int          const flags,
+                   const char * const appname,
+                   const char * const appversion) {
+
+    struct xmlrpc_clientparms clientparms;
+
+    /* As our interface does not allow for failure, we just fail silently ! */
+    
+    xmlrpc_env env;
+    xmlrpc_env_init(&env);
+
+    clientparms.transport = XMLRPC_DEFAULT_TRANSPORT;
+
+    xmlrpc_client_init2(&env, flags,
+                        appname, appversion,
+                        &clientparms, XMLRPC_CPSIZE(transport));
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+const char * 
+xmlrpc_client_get_default_transport(xmlrpc_env * const env ATTR_UNUSED) {
+
+    return XMLRPC_DEFAULT_TRANSPORT;
+}
+
+
+
+static void
+setupTransport(xmlrpc_env * const envP,
+               const char * const transportName) {
+
+    if (FALSE) {
+    }
+#if MUST_BUILD_WININET_CLIENT
+    else if (strcmp(transportName, "wininet") == 0)
+        clientTransportOps = xmlrpc_wininet_transport_ops;
+#endif
+#if MUST_BUILD_CURL_CLIENT
+    else if (strcmp(transportName, "curl") == 0)
+        clientTransportOps = xmlrpc_curl_transport_ops;
+#endif
+#if MUST_BUILD_LIBWWW_CLIENT
+    else if (strcmp(transportName, "libwww") == 0)
+        clientTransportOps = xmlrpc_libwww_transport_ops;
+#endif
+    else
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "Unrecognized XML transport name '%s'", transportName);
+}
+
+
+
+void 
+xmlrpc_client_init2(xmlrpc_env *                const envP,
+                    int                         const flags,
+                    const char *                const appname,
+                    const char *                const appversion,
+                    struct xmlrpc_clientparms * const clientparmsP,
+                    unsigned int                const parm_size) {
+
+    if (clientInitialized)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "Xmlrpc-c client instance has already been initialized "
+            "(need to call xmlrpc_client_cleanup() before you can "
+            "reinitialize).");
+    else {
+        const char * transportName;
+
+        if (parm_size < XMLRPC_CPSIZE(transport) ||
+            clientparmsP->transport == NULL) {
+            /* He didn't specify a transport.  Use the default */
+            transportName = xmlrpc_client_get_default_transport(envP);
+        } else
+            transportName = clientparmsP->transport;
+
+        if (!envP->fault_occurred) {
+            setupTransport(envP, transportName);
+            if (!envP->fault_occurred) {
+                clientTransportOps.create(envP, flags, appname, appversion,
+                                          &client.transportP);
+                if (!envP->fault_occurred)
+                    clientInitialized = TRUE;
+            }
+        }
+    }
+}
+
+
+
+void 
+xmlrpc_client_cleanup() {
+
+    XMLRPC_ASSERT(clientInitialized);
+
+    clientTransportOps.destroy(client.transportP);
+    
+    clientInitialized = FALSE;
+}
+
+
+
+static void 
+call_info_free(call_info * const callInfoP) {
+
+    /* Assume the worst.. That only parts of the call_info are valid. */
+
+    XMLRPC_ASSERT_PTR_OK(callInfoP);
+
+    /* If this has been allocated, we're responsible for destroying it. */
+    if (callInfoP->_asynch_data_holder)
+        xmlrpc_DECREF(callInfoP->_asynch_data_holder);
+
+    /* Now we can blow away the XML data. */
+    if (callInfoP->serialized_xml)
+         xmlrpc_mem_block_free(callInfoP->serialized_xml);
+
+    free(callInfoP);
+}
+
+
+
+static void
+call_info_new(xmlrpc_env *         const envP,
+              xmlrpc_server_info * const server,
+              const char *         const method_name,
+              xmlrpc_value *       const argP,
+              call_info **         const callInfoPP) {
+/*----------------------------------------------------------------------------
+   Create a call_info object.  A call_info object represents an XML-RPC
+   call.
+-----------------------------------------------------------------------------*/
+    call_info * callInfoP;
+
+    XMLRPC_ASSERT_PTR_OK(argP);
+    XMLRPC_ASSERT_PTR_OK(callInfoPP);
+
+    if (method_name == NULL)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "method name argument is NULL pointer");
+    else if (server == NULL)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "server info argument is NULL pointer");
+    else {
+        MALLOCVAR(callInfoP);
+        if (callInfoP == NULL)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR,
+                "Couldn't allocate memory for xmlrpc_call_info");
+        else {
+            xmlrpc_mem_block * callXmlP;
+
+        /* Clear contents. */
+            memset(callInfoP, 0, sizeof(*callInfoP));
+        
+            /* Make the XML for our call */
+            callXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
+            if (!envP->fault_occurred) {
+                xmlrpc_serialize_call(envP, callXmlP, method_name, argP);
+                if (!envP->fault_occurred) {
+                    xmlrpc_traceXml("XML-RPC CALL", 
+                                    XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP),
+                                    XMLRPC_MEMBLOCK_SIZE(char, callXmlP));
+        
+                    callInfoP->serialized_xml = callXmlP;
+
+                    *callInfoPP = callInfoP;
+                }
+                if (envP->fault_occurred)
+                    XMLRPC_MEMBLOCK_FREE(char, callXmlP);
+            }
+            if (envP->fault_occurred)
+                free(callInfoP);
+        }
+    }
+}
+
+
+
+static void
+clientCallServerParams(xmlrpc_env *             const envP,
+                       struct clientTransport * const transportP,
+                       xmlrpc_server_info *     const serverP,
+                       const char *             const methodName,
+                       xmlrpc_value *           const paramArrayP,
+                       xmlrpc_value **          const resultPP) {
+
+    call_info * callInfoP;
+    
+    if (!clientInitialized)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "Xmlrpc-c client instance has not been initialized "
+            "(need to call xmlrpc_client_init2()).");
+    else {
+        call_info_new(envP, serverP, methodName, paramArrayP, &callInfoP);
+        if (!envP->fault_occurred) {
+            xmlrpc_mem_block * respXmlP;
+        
+            clientTransportOps.call(envP, transportP, serverP, 
+                                    callInfoP->serialized_xml, callInfoP, 
+                                    &respXmlP);
+            if (!envP->fault_occurred) {
+                xmlrpc_traceXml("XML-RPC RESPONSE", 
+                                XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
+                                XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
+            
+                *resultPP = xmlrpc_parse_response(
+                    envP,
+                    XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
+                    XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
+                XMLRPC_MEMBLOCK_FREE(char, respXmlP);
+            }                    
+            call_info_free(callInfoP);
+        }
+    }
+}
+
+
+
+xmlrpc_value * 
+xmlrpc_client_call_params(xmlrpc_env *   const envP,
+                          const char *   const serverUrl,
+                          const char *   const methodName,
+                          xmlrpc_value * const paramArrayP) {
+
+    xmlrpc_value *retval;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_PTR_OK(serverUrl);
+
+    if (!clientInitialized)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "Xmlrpc-c client instance has not been initialized "
+            "(need to call xmlrpc_client_init2()).");
+    else {
+        xmlrpc_server_info * serverP;
+        
+        /* Build a server info object and make our call. */
+        serverP = xmlrpc_server_info_new(envP, serverUrl);
+        if (!envP->fault_occurred) {
+            clientCallServerParams(envP, client.transportP, serverP, 
+                                   methodName, paramArrayP,
+                                   &retval);
+
+            xmlrpc_server_info_free(serverP);
+        }
+    }
+        
+    if (!envP->fault_occurred)
+        XMLRPC_ASSERT_VALUE_OK(retval);
+
+    return retval;
+}
+
+
+
+static xmlrpc_value * 
+xmlrpc_client_call_va(xmlrpc_env * const envP,
+                      const char * const server_url,
+                      const char * const method_name,
+                      const char * const format,
+                      va_list            args) {
+
+    xmlrpc_value * argP;
+    xmlrpc_value * retval;
+    xmlrpc_env argenv;
+    const char * suffix;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_PTR_OK(format);
+
+    /* Build our argument value. */
+    xmlrpc_env_init(&argenv);
+    xmlrpc_build_value_va(&argenv, format, args, &argP, &suffix);
+    if (argenv.fault_occurred) {
+        xmlrpc_env_set_fault_formatted(
+            envP, argenv.fault_code, "Invalid RPC arguments.  "
+            "The format argument must indicate a single array, and the "
+            "following arguments must correspond to that format argument.  "
+            "The failure is: %s",
+            argenv.fault_string);
+        xmlrpc_env_clean(&argenv);
+    } else {
+        XMLRPC_ASSERT_VALUE_OK(argP);
+        
+        if (*suffix != '\0')
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+                "specifier: '%s'.  There must be exactly one arument.",
+                suffix);
+        else {
+            /* Perform the actual XML-RPC call. */
+            retval = xmlrpc_client_call_params(
+                envP, server_url, method_name, argP);
+            if (!envP->fault_occurred)
+                XMLRPC_ASSERT_VALUE_OK(retval);
+        }
+        xmlrpc_DECREF(argP);
+    }
+    return retval;
+}
+
+
+
+xmlrpc_value * 
+xmlrpc_client_call(xmlrpc_env * const envP,
+                   const char *       const server_url,
+                   const char *       const method_name,
+                   const char *       const format,
+                   ...) {
+
+    xmlrpc_value * result;
+    va_list args;
+
+    va_start(args, format);
+    result = xmlrpc_client_call_va(envP, server_url,
+                                   method_name, format, args);
+    va_end(args);
+
+    return result;
+}
+
+
+
+xmlrpc_value * 
+xmlrpc_client_call_server(xmlrpc_env *         const envP,
+                          xmlrpc_server_info * const serverP,
+                          const char *         const methodName,
+                          const char *         const format, 
+                          ...) {
+
+    va_list args;
+    xmlrpc_value * paramArrayP;
+    xmlrpc_value * retval;
+    const char * suffix;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_PTR_OK(format);
+
+    /* Build our argument */
+    va_start(args, format);
+    xmlrpc_build_value_va(envP, format, args, &paramArrayP, &suffix);
+    va_end(args);
+
+    if (!envP->fault_occurred) {
+        if (*suffix != '\0')
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+                "specifier: '%s'.  There must be exactly one arument.",
+                suffix);
+        else
+            clientCallServerParams(envP, client.transportP, serverP, 
+                                   methodName, paramArrayP, 
+                                   &retval);
+
+        xmlrpc_DECREF(paramArrayP);
+    }
+    return retval;
+}
+
+
+void 
+xmlrpc_client_event_loop_finish_asynch(void) {
+    XMLRPC_ASSERT(clientInitialized);
+    clientTransportOps.finish_asynch(client.transportP, timeout_no, 0);
+}
+
+
+
+void 
+xmlrpc_client_event_loop_finish_asynch_timeout(timeout_t const timeout) {
+    XMLRPC_ASSERT(clientInitialized);
+    clientTransportOps.finish_asynch(client.transportP, timeout_yes, timeout);
+}
+
+
+
+static void 
+call_info_set_asynch_data(xmlrpc_env *   const env,
+                          call_info *    const info,
+                          const char *   const server_url,
+                          const char *   const method_name,
+                          xmlrpc_value * const argP,
+                          xmlrpc_response_handler callback,
+                          void *         const user_data) {
+
+    xmlrpc_value *holder;
+
+    /* Error-handling preconditions. */
+    holder = NULL;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(info);
+    XMLRPC_ASSERT(info->_asynch_data_holder == NULL);
+    XMLRPC_ASSERT_PTR_OK(server_url);
+    XMLRPC_ASSERT_PTR_OK(method_name);
+    XMLRPC_ASSERT_VALUE_OK(argP);
+
+    /* Install our callback and user_data.
+    ** (We're not responsible for destroying the user_data.) */
+    info->callback  = callback;
+    info->user_data = user_data;
+
+    /* Build an XML-RPC data structure to hold our other data. This makes
+    ** copies of server_url and method_name, and increments the reference
+    ** to the argument *argP. */
+    holder = xmlrpc_build_value(env, "(ssV)",
+                                server_url, method_name, argP);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Parse the newly-allocated structure into our public member variables.
+    ** This doesn't make any new references, so we can dispose of the whole
+    ** thing by DECREF'ing the one master reference. Nifty, huh? */
+    xmlrpc_parse_value(env, holder, "(ssV)",
+                       &info->server_url,
+                       &info->method_name,
+                       &info->param_array);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Hand over ownership of the holder to the call_info struct. */
+    info->_asynch_data_holder = holder;
+    holder = NULL;
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (holder)
+            xmlrpc_DECREF(holder);
+    }
+}
+
+/*=========================================================================
+**  xmlrpc_server_info
+**=========================================================================
+*/
+
+xmlrpc_server_info *
+xmlrpc_server_info_new (xmlrpc_env * const env,
+                        const char * const server_url) {
+
+    xmlrpc_server_info *server;
+    char *url_copy;
+
+    /* Error-handling preconditions. */
+    server = NULL;
+    url_copy = NULL;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(server_url);
+
+    /* Allocate our memory blocks. */
+    server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
+    XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for xmlrpc_server_info");
+    memset(server, 0, sizeof(xmlrpc_server_info));
+    url_copy = (char*) malloc(strlen(server_url) + 1);
+    XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for server URL");
+
+    /* Build our object. */
+    strcpy(url_copy, server_url);
+    server->_server_url = url_copy;
+    server->_http_basic_auth = NULL;
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (url_copy)
+            free(url_copy);
+        if (server)
+            free(server);
+        return NULL;
+    }
+    return server;
+}
+
+xmlrpc_server_info * xmlrpc_server_info_copy(xmlrpc_env *env,
+                                             xmlrpc_server_info *aserver)
+{
+    xmlrpc_server_info *server;
+    char *url_copy, *auth_copy;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(aserver);
+
+    /* Error-handling preconditions. */
+    server = NULL;
+    url_copy = NULL;
+    auth_copy = NULL;
+
+    /* Allocate our memory blocks. */
+    server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
+    XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for xmlrpc_server_info");
+    url_copy = (char*) malloc(strlen(aserver->_server_url) + 1);
+    XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for server URL");
+    auth_copy = (char*) malloc(strlen(aserver->_http_basic_auth) + 1);
+    XMLRPC_FAIL_IF_NULL(auth_copy, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for authentication info");
+
+    /* Build our object. */
+    strcpy(url_copy, aserver->_server_url);
+    server->_server_url = url_copy;
+    strcpy(auth_copy, aserver->_http_basic_auth);
+    server->_http_basic_auth = auth_copy;
+
+    cleanup:
+    if (env->fault_occurred) {
+        if (url_copy)
+            free(url_copy);
+        if (auth_copy)
+            free(auth_copy);
+        if (server)
+            free(server);
+        return NULL;
+    }
+    return server;
+
+}
+
+void xmlrpc_server_info_free (xmlrpc_server_info *server)
+{
+    XMLRPC_ASSERT_PTR_OK(server);
+    XMLRPC_ASSERT(server->_server_url != XMLRPC_BAD_POINTER);
+
+    if (server->_http_basic_auth)
+        free(server->_http_basic_auth);
+    free(server->_server_url);
+    server->_server_url = XMLRPC_BAD_POINTER;
+    free(server);
+}
+
+/*=========================================================================
+**  xmlrpc_client_call_asynch
+**=========================================================================
+*/
+
+void 
+xmlrpc_client_call_asynch(const char * const serverUrl,
+                          const char * const methodName,
+                          xmlrpc_response_handler callback,
+                          void *       const userData,
+                          const char * const format,
+                          ...) {
+
+    xmlrpc_env env;
+    va_list args;
+    xmlrpc_value * paramArrayP;
+    const char * suffix;
+
+    xmlrpc_env_init(&env);
+
+    XMLRPC_ASSERT_PTR_OK(serverUrl);
+    XMLRPC_ASSERT_PTR_OK(format);
+
+    /* Build our argument array. */
+    va_start(args, format);
+    xmlrpc_build_value_va(&env, format, args, &paramArrayP, &suffix);
+    va_end(args);
+    if (env.fault_occurred) {
+        /* Unfortunately, we have no way to return an error and the
+           regular callback for a failed RPC is designed to have the
+           parameter array passed to it.  This was probably an oversight
+           of the original asynch design, but now we have to be as
+           backward compatible as possible, so we do this:
+        */
+        (*callback)(serverUrl, methodName, NULL, userData, &env, NULL);
+    } else {
+        if (*suffix != '\0')
+            xmlrpc_env_set_fault_formatted(
+                &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+                "specifier: '%s'.  There must be exactly one arument.",
+                suffix);
+        else {
+            xmlrpc_server_info * serverP;
+            serverP = xmlrpc_server_info_new(&env, serverUrl);
+            if (!env.fault_occurred) {
+                xmlrpc_client_call_server_asynch_params(
+                    serverP, methodName, callback, userData, 
+                    paramArrayP);
+            }
+            xmlrpc_server_info_free(serverP);
+        }
+        if (env.fault_occurred)
+            (*callback)(serverUrl, methodName, paramArrayP, userData,
+                        &env, NULL);
+        xmlrpc_DECREF(paramArrayP);
+    }
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+void
+xmlrpc_client_call_asynch_params(const char *   const serverUrl,
+                                 const char *   const methodName,
+                                 xmlrpc_response_handler callback,
+                                 void *         const userData,
+                                 xmlrpc_value * const paramArrayP) {
+
+    xmlrpc_env env;
+    xmlrpc_server_info *serverP;
+
+    xmlrpc_env_init(&env);
+
+    XMLRPC_ASSERT_PTR_OK(serverUrl);
+
+    serverP = xmlrpc_server_info_new(&env, serverUrl);
+    if (!env.fault_occurred) {
+        xmlrpc_client_call_server_asynch_params(
+            serverP, methodName, callback, userData, paramArrayP);
+
+        xmlrpc_server_info_free(serverP);
+    }
+
+    if (env.fault_occurred)
+        /* We have no way to return failure; we report the failure
+           as it happened after we successfully started the RPC.
+        */
+        (*callback)(serverUrl, methodName, paramArrayP, userData,
+                    &env, NULL);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+void 
+xmlrpc_client_call_server_asynch(xmlrpc_server_info * const serverP,
+                                 const char *         const methodName,
+                                 xmlrpc_response_handler callback,
+                                 void *               const userData,
+                                 const char *         const format,
+                                 ...) {
+
+    xmlrpc_env env;
+    va_list args;
+    xmlrpc_value * paramArrayP;
+    const char * suffix;
+
+    xmlrpc_env_init(&env);
+
+    XMLRPC_ASSERT_PTR_OK(format);
+
+    /* Build our parameter array. */
+    va_start(args, format);
+    xmlrpc_build_value_va(&env, format, args, &paramArrayP, &suffix);
+    va_end(args);
+    if (env.fault_occurred) {
+        /* Unfortunately, we have no way to return an error and the
+           regular callback for a failed RPC is designed to have the
+           parameter array passed to it.  This was probably an oversight
+           of the original asynch design, but now we have to be as
+           backward compatible as possible, so we do this:
+        */
+        (*callback)(serverP->_server_url, methodName, NULL, userData, 
+                    &env, NULL);
+    } else {
+        if (*suffix != '\0')
+            xmlrpc_env_set_fault_formatted(
+                &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+                "specifier: '%s'.  There must be exactly one arument.",
+                suffix);
+        else {
+            xmlrpc_client_call_server_asynch_params(
+                serverP, methodName, callback, userData, paramArrayP);
+        }
+        xmlrpc_DECREF(paramArrayP);
+    }
+
+    if (env.fault_occurred)
+        (*callback)(serverP->_server_url, methodName, paramArrayP, userData,
+                    &env, NULL);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+static void
+asynchComplete(call_info *        const callInfoP,
+               xmlrpc_mem_block * const responseXmlP,
+               xmlrpc_env         const transportEnv) {
+/*----------------------------------------------------------------------------
+   Complete an asynchronous XML-RPC call request.
+
+   This includes calling the user's RPC completion routine.
+
+   'transportEnv' describes the an error that the transport
+   encountered in processing the call.  If the transport successfully
+   sent the call to the server and processed the response but the
+   server failed the call, 'transportEnv' indicates no error, and the
+   response in *callInfoP might very well indicate that the server
+   failed the request.
+-----------------------------------------------------------------------------*/
+    xmlrpc_env env;
+    xmlrpc_value * responseP;
+
+    xmlrpc_env_init(&env);
+
+    if (transportEnv.fault_occurred)
+        xmlrpc_env_set_fault_formatted(
+            &env, transportEnv.fault_code,
+            "Client transport failed to execute the RPC.  %s",
+            transportEnv.fault_string);
+
+    if (!env.fault_occurred)
+        responseP = xmlrpc_parse_response(
+            &env,
+            XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlP),
+            XMLRPC_MEMBLOCK_SIZE(char, responseXmlP));
+
+    /* Call the user's callback function with the result */
+    (*callInfoP->callback)(callInfoP->server_url, 
+                           callInfoP->method_name, 
+                           callInfoP->param_array,
+                           callInfoP->user_data, &env, responseP);
+
+    if (!env.fault_occurred)
+        xmlrpc_DECREF(responseP);
+
+    call_info_free(callInfoP);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+static void
+sendRequest(xmlrpc_env *             const envP,
+            struct clientTransport * const transportP,
+            xmlrpc_server_info *     const serverP,
+            const char *             const methodName,
+            xmlrpc_response_handler        responseHandler,
+            void *                   const userData,
+            xmlrpc_value *           const argP) {
+
+    call_info * callInfoP;
+
+    call_info_new(envP, serverP, methodName, argP, &callInfoP);
+    if (!envP->fault_occurred) {
+        call_info_set_asynch_data(envP, callInfoP, 
+                                  serverP->_server_url, methodName,
+                                  argP, responseHandler, userData);
+        if (!envP->fault_occurred)
+            clientTransportOps.send_request(
+                envP, transportP, serverP, callInfoP->serialized_xml,
+                &asynchComplete, callInfoP);
+
+        if (envP->fault_occurred)
+            call_info_free(callInfoP);
+        else {
+            /* asynchComplete() will free *callInfoP */
+        }
+    }
+    if (envP->fault_occurred) {
+        /* Transport did not start the call.  Report the call complete
+           (with error) now.
+        */
+        (*responseHandler)(serverP->_server_url, methodName, argP, userData,
+                           envP, NULL);
+    } else {
+        /* The transport will call *responseHandler() when it has completed
+           the call
+        */
+    }
+}
+
+
+
+void 
+xmlrpc_client_call_server_asynch_params(
+    xmlrpc_server_info * const serverP,
+    const char *         const methodName,
+    xmlrpc_response_handler    responseHandler,
+    void *               const userData,
+    xmlrpc_value *       const argP) {
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+
+    XMLRPC_ASSERT_PTR_OK(serverP);
+    XMLRPC_ASSERT_PTR_OK(methodName);
+    XMLRPC_ASSERT_PTR_OK(responseHandler);
+    XMLRPC_ASSERT_VALUE_OK(argP);
+
+    if (!clientInitialized)
+        xmlrpc_env_set_fault_formatted(
+            &env, XMLRPC_INTERNAL_ERROR, 
+            "Xmlrpc-c client instance has not been initialized "
+            "(need to call xmlrpc_client_init2()).");
+    else
+        sendRequest(&env, client.transportP, serverP, 
+                    methodName, responseHandler, userData, 
+                    argP);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+void 
+xmlrpc_server_info_set_basic_auth(xmlrpc_env *         const envP,
+                                  xmlrpc_server_info * const serverP,
+                                  const char *         const username,
+                                  const char *         const password) {
+
+    size_t username_len, password_len, raw_token_len;
+    char *raw_token;
+    xmlrpc_mem_block *token;
+    char *token_data, *auth_type, *auth_header;
+    size_t token_len, auth_type_len, auth_header_len;
+
+    /* Error-handling preconditions. */
+    raw_token = NULL;
+    token = NULL;
+    token_data = auth_type = auth_header = NULL;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_PTR_OK(serverP);
+    XMLRPC_ASSERT_PTR_OK(username);
+    XMLRPC_ASSERT_PTR_OK(password);
+
+    /* Calculate some lengths. */
+    username_len = strlen(username);
+    password_len = strlen(password);
+    raw_token_len = username_len + password_len + 1;
+
+    /* Build a raw token of the form 'username:password'. */
+    raw_token = (char*) malloc(raw_token_len + 1);
+    XMLRPC_FAIL_IF_NULL(raw_token, envP, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for auth token");
+    strcpy(raw_token, username);
+    raw_token[username_len] = ':';
+    strcpy(&raw_token[username_len + 1], password);
+
+    /* Encode our raw token using Base64. */
+    token = xmlrpc_base64_encode_without_newlines(envP, 
+                                                  (unsigned char*) raw_token,
+                                                  raw_token_len);
+    XMLRPC_FAIL_IF_FAULT(envP);
+    token_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, token);
+    token_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, token);
+
+    /* Build our actual header value. (I hate string processing in C.) */
+    auth_type = "Basic ";
+    auth_type_len = strlen(auth_type);
+    auth_header_len = auth_type_len + token_len;
+    auth_header = (char*) malloc(auth_header_len + 1);
+    XMLRPC_FAIL_IF_NULL(auth_header, envP, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for auth header");
+    memcpy(auth_header, auth_type, auth_type_len);
+    memcpy(&auth_header[auth_type_len], token_data, token_len);
+    auth_header[auth_header_len] = '\0';
+
+    /* Clean up any pre-existing authentication information, and install
+    ** the new value. */
+    if (serverP->_http_basic_auth)
+        free(serverP->_http_basic_auth);
+    serverP->_http_basic_auth = auth_header;
+
+ cleanup:
+    if (raw_token)
+        free(raw_token);
+    if (token)
+        xmlrpc_mem_block_free(token);
+    if (envP->fault_occurred) {
+        if (auth_header)
+            free(auth_header);
+    }
+}
+

+ 261 - 0
Utilities/cmxmlrpc/xmlrpc_client.h

@@ -0,0 +1,261 @@
+/*============================================================================
+                         xmlrpc_client.h
+==============================================================================
+  This header file defines the interface between xmlrpc.c and its users,
+  related to clients.
+
+  Copyright information is at the end of the file.
+============================================================================*/
+
+#ifndef  _XMLRPC_CLIENT_H_
+#define  _XMLRPC_CLIENT_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*=========================================================================
+**  Initialization and Shutdown
+**=========================================================================
+**  These routines initialize and terminate the XML-RPC client. If you're
+**  already using libwww on your own, you can pass
+**  XMLRPC_CLIENT_SKIP_LIBWWW_INIT to avoid initializing it twice.
+*/
+
+#define XMLRPC_CLIENT_NO_FLAGS         (0)
+#define XMLRPC_CLIENT_SKIP_LIBWWW_INIT (1)
+
+extern void
+xmlrpc_client_init(int          const flags,
+                   const char * const appname,
+                   const char * const appversion);
+
+struct xmlrpc_clientparms {
+    const char * transport;
+};
+
+#define XMLRPC_CP_MEMBER_OFFSET(mbrname) \
+  ((unsigned int)(char*)&((struct xmlrpc_clientparms *)0)->mbrname)
+#define XMLRPC_CP_MEMBER_SIZE(mbrname) \
+  sizeof(((struct xmlrpc_clientparms *)0)->mbrname)
+#define XMLRPC_CPSIZE(mbrname) \
+  (XMLRPC_CP_MEMBER_OFFSET(mbrname) + XMLRPC_CP_MEMBER_SIZE(mbrname))
+
+/* XMLRPC_CPSIZE(xyz) is the minimum size a struct xmlrpc_clientparms
+   must be to include the 'xyz' member.  This is essential to forward and
+   backward compatbility, as new members will be added to the end of the
+   struct in future releases.  This is how the callee knows whether or
+   not the caller is new enough to have supplied a certain parameter.
+*/
+
+void 
+xmlrpc_client_init2(xmlrpc_env *                const env,
+                    int                         const flags,
+                    const char *                const appname,
+                    const char *                const appversion,
+                    struct xmlrpc_clientparms * const clientparms,
+                    unsigned int                const parm_size);
+
+extern void
+xmlrpc_client_cleanup(void);
+
+const char * 
+xmlrpc_client_get_default_transport(xmlrpc_env * const env);
+
+/*=========================================================================
+** Required for both internal and external development.
+**=========================================================================
+*/
+/* A callback function to handle the response to an asynchronous call.
+** If 'fault->fault_occurred' is true, then response will be NULL. All
+** arguments except 'user_data' will be deallocated internally; please do
+** not free any of them yourself.
+** WARNING: param_array may (or may not) be NULL if fault->fault_occurred
+** is true, and you set up the call using xmlrpc_client_call_asynch.
+** WARNING: If asynchronous calls are still pending when the library is
+** shut down, your handler may (or may not) be called with a fault. */
+typedef void (*xmlrpc_response_handler) (const char *server_url,
+                                         const char *method_name,
+                                         xmlrpc_value *param_array,
+                                         void *user_data,
+                                         xmlrpc_env *fault,
+                                         xmlrpc_value *result);
+
+
+/*=========================================================================
+**  xmlrpc_server_info
+**=========================================================================
+**  We normally refer to servers by URL. But sometimes we need to do extra
+**  setup for particular servers. In that case, we can create an
+**  xmlrpc_server_info object, configure it in various ways, and call the
+**  remote server.
+**
+**  (This interface is also designed to discourage further multiplication
+**  of xmlrpc_client_call APIs. We have enough of those already. Please
+**  add future options and flags using xmlrpc_server_info.)
+*/
+
+typedef struct _xmlrpc_server_info xmlrpc_server_info;
+
+/* Create a new server info record, pointing to the specified server. */
+xmlrpc_server_info *
+xmlrpc_server_info_new(xmlrpc_env * const env,
+                       const char * const server_url);
+
+/* Create a new server info record, with a copy of the old server. */
+extern xmlrpc_server_info * 
+xmlrpc_server_info_copy(xmlrpc_env *env, xmlrpc_server_info *src_server);
+
+/* Delete a server info record. */
+extern void
+xmlrpc_server_info_free (xmlrpc_server_info *server);
+
+/* We support rudimentary basic authentication. This lets us talk to Zope
+** servers and similar critters. When called, this routine makes a copy
+** of all the authentication information and passes it to future requests.
+** Only the most-recently-set authentication information is used.
+** (In general, you shouldn't write XML-RPC servers which require this
+** kind of authentication--it confuses many client implementations.)
+** If we fail, leave the xmlrpc_server_info record unchanged. */
+void 
+xmlrpc_server_info_set_basic_auth(xmlrpc_env *         const envP,
+                                  xmlrpc_server_info * const serverP,
+                                  const char *         const username,
+                                  const char *         const password);
+
+
+/*=========================================================================
+**  xmlrpc_client_call
+**=========================================================================
+**  A synchronous XML-RPC client. Do not attempt to call any of these
+**  functions from inside an asynchronous callback!
+*/
+
+xmlrpc_value * 
+xmlrpc_client_call(xmlrpc_env * const envP,
+                   const char * const server_url,
+                   const char * const method_name,
+                   const char * const format,
+                   ...);
+
+xmlrpc_value * 
+xmlrpc_client_call_params (xmlrpc_env *   const env,
+                           const char *   const server_url,
+                           const char *   const method_name,
+                           xmlrpc_value * const param_array);
+
+xmlrpc_value * 
+xmlrpc_client_call_server(xmlrpc_env *         const envP,
+                          xmlrpc_server_info * const server,
+                          const char *         const method_name,
+                          const char *         const format, 
+                          ...);
+
+extern xmlrpc_value *
+xmlrpc_client_call_server_params (xmlrpc_env *env,
+                                  xmlrpc_server_info *server,
+                                  char *method_name,
+                                  xmlrpc_value *param_array);
+
+
+/*=========================================================================
+**  xmlrpc_client_call_asynch
+**=========================================================================
+**  An asynchronous XML-RPC client.
+*/
+
+/* Make an asynchronous XML-RPC call. We make internal copies of all
+** arguments except user_data, so you can deallocate them safely as soon
+** as you return. Errors will be passed to the callback. You will need
+** to run the event loop somehow; see below.
+** WARNING: If an error occurs while building the argument, the
+** response handler will be called with a NULL param_array. */
+void 
+xmlrpc_client_call_asynch(const char * const server_url,
+                          const char * const method_name,
+                          xmlrpc_response_handler callback,
+                          void *       const user_data,
+                          const char * const format,
+                          ...);
+
+/* As above, but use an xmlrpc_server_info object. The server object can be
+** safely destroyed as soon as this function returns. */
+void 
+xmlrpc_client_call_server_asynch(xmlrpc_server_info * const server,
+                                 const char *         const method_name,
+                                 xmlrpc_response_handler callback,
+                                 void *               const user_data,
+                                 const char *         const format,
+                                 ...);
+
+/* As above, but the parameter list is supplied as an xmlrpc_value
+** containing an array.
+*/
+void
+xmlrpc_client_call_asynch_params(const char *   const server_url,
+                                 const char *   const method_name,
+                                 xmlrpc_response_handler callback,
+                                 void *         const user_data,
+                                 xmlrpc_value * const paramArrayP);
+    
+/* As above, but use an xmlrpc_server_info object. The server object can be
+** safely destroyed as soon as this function returns. */
+void 
+xmlrpc_client_call_server_asynch_params(
+    xmlrpc_server_info * const server,
+    const char *         const method_name,
+    xmlrpc_response_handler callback,
+    void *               const user_data,
+    xmlrpc_value *       const paramArrayP);
+    
+/*=========================================================================
+**  Event Loop Interface
+**=========================================================================
+**  These functions can be used to run the XML-RPC event loop. If you
+**  don't like these, you can also run the libwww event loop directly.
+*/
+
+/* Finish all outstanding asynchronous calls. Alternatively, the loop
+** will exit if someone calls xmlrpc_client_event_loop_end. */
+extern void
+xmlrpc_client_event_loop_finish_asynch(void);
+
+
+/* Finish all outstanding asynchronous calls. */
+extern void
+xmlrpc_client_event_loop_finish_asynch_timeout(unsigned long milliseconds);
+
+
+
+/* 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. */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _XMLRPC_CLIENT_H_ */

+ 105 - 0
Utilities/cmxmlrpc/xmlrpc_client_int.h

@@ -0,0 +1,105 @@
+/*============================================================================
+                         xmlrpc_client_int.h
+==============================================================================
+  This header file defines the interface between client modules inside
+  xmlrpc-c.
+
+  Use this in addition to xmlrpc_client.h, which defines the external
+  interface.
+
+  Copyright information is at the end of the file.
+============================================================================*/
+
+
+#ifndef  _XMLRPC_CLIENT_INT_H_
+#define  _XMLRPC_CLIENT_INT_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct _xmlrpc_server_info {
+    char *_server_url;
+    char *_http_basic_auth;
+};
+
+/* Create a new server info record, with a copy of the old server. */
+extern xmlrpc_server_info * 
+xmlrpc_server_info_copy(xmlrpc_env *env, xmlrpc_server_info *aserver);
+
+
+/* A timeout in milliseconds. */
+typedef unsigned long timeout_t;
+
+/*=========================================================================
+** Transport Implementation functions.
+**=========================================================================
+*/
+#include "xmlrpc_transport.h"
+
+/* The generalized event loop. This uses the above flags. For more details,
+** see the wrapper functions below. If you're not using the timeout, the
+** 'milliseconds' parameter will be ignored.
+** Note that ANY event loop call will return immediately if there are
+** no outstanding XML-RPC calls. */
+extern void
+xmlrpc_client_event_loop_run_general (int flags, timeout_t milliseconds);
+
+/* Run the event loop forever. The loop will exit if someone calls
+** xmlrpc_client_event_loop_end. */
+extern void
+xmlrpc_client_event_loop_run (void);
+
+/* Run the event loop forever. The loop will exit if someone calls
+** xmlrpc_client_event_loop_end or the timeout expires.
+** (Note that ANY event loop call will return immediately if there are
+** no outstanding XML-RPC calls.) */
+extern void
+xmlrpc_client_event_loop_run_timeout (timeout_t milliseconds);
+
+/* End the running event loop immediately. This can also be accomplished
+** by calling the corresponding function in libwww.
+** (Note that ANY event loop call will return immediately if there are
+** no outstanding XML-RPC calls.) */
+extern void
+xmlrpc_client_event_loop_end (void);
+
+
+/* Return true if there are uncompleted asynchronous calls.
+** The exact value of this during a response callback is undefined. */
+extern int
+xmlrpc_client_asynch_calls_are_unfinished (void);
+
+/* 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. */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
+

+ 46 - 0
Utilities/cmxmlrpc/xmlrpc_config.h.in

@@ -0,0 +1,46 @@
+/* xmlrpc_config.h is generated from xmlrpc_config.h.in by 'configure'.
+
+   This file just uses plain AC_SUBST substitution, the same as
+   Makefile.config.  Wherever you see @XXX@, that gets replaced by the
+   value of 'configure' variable XXX.
+
+   Logical macros are 0 or 1 instead of the more traditional defined and
+   undefined.  That's so we can distinguish when compiling code between
+   "false" and some problem with the code.
+*/
+
+
+/* We hope to replace xmlrpc_amconfig.h some day with something that 
+   doesn't require a whole special set of software to build, to make
+   xmlrpc-c approachable by dumber developers.
+*/
+#include "xmlrpc_amconfig.h"
+
+
+#define VA_LIST_IS_ARRAY @VA_LIST_IS_ARRAY_DEFINE@
+
+#define HAVE_LIBWWW_SSL @HAVE_LIBWWW_SSL_DEFINE@
+
+#define ATTR_UNUSED @ATTR_UNUSED@
+
+#define HAVE_UNICODE_WCHAR @HAVE_UNICODE_WCHAR_DEFINE@
+
+#define DIRECTORY_SEPARATOR "@DIRECTORY_SEPARATOR@"
+
+/*  Xmlrpc-c code uses __inline__ to declare functions that should
+    be compiled as inline code.  GNU C recognizes the __inline__ keyword.
+    Others recognize 'inline' or '__inline' or nothing at all to say
+    a function should be inlined.
+
+    We could make 'configure' simply do a trial compile to figure out
+    which one, but for now, this approximation is easier:
+*/
+#ifndef __GNUC__
+#ifndef __inline__
+#ifdef __sgi
+#define __inline__ __inline
+#else
+#define __inline__
+#endif
+#endif
+#endif

+ 651 - 0
Utilities/cmxmlrpc/xmlrpc_curl_transport.c

@@ -0,0 +1,651 @@
+/*=============================================================================
+                           xmlrpc_curl_transport
+===============================================================================
+   Curl-based client transport for Xmlrpc-c
+
+   By Bryan Henderson 04.12.10.
+
+   Contributed to the public domain by its author.
+=============================================================================*/
+
+#include "xmlrpc_config.h"
+
+#include "bool.h"
+#include "mallocvar.h"
+#include "linklist.h"
+#include "casprintf.h"
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_client.h"
+#include "xmlrpc_client_int.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#if defined (WIN32) && defined(_DEBUG)
+#  include <crtdbg.h>
+#  define new DEBUG_NEW
+#  define malloc(size) _malloc_dbg( size, _NORMAL_BLOCK, __FILE__, __LINE__)
+#  undef THIS_FILE
+   static char THIS_FILE[] = __FILE__;
+#endif /*WIN32 && _DEBUG*/
+
+
+
+struct clientTransport {
+    pthread_mutex_t listLock;
+    struct list_head rpcList;
+        /* List of all RPCs that exist for this transport.  An RPC exists
+           from the time the user requests it until the time the user 
+           acknowledges it is done.
+        */
+};
+
+typedef struct {
+    /* This is all stuff that really ought to be in the CURL object,
+       but the Curl library is a little too simple for that.  So we
+       build a layer on top of it, and call it a "transaction," as
+       distinct from the Curl "session" represented by the CURL object.
+    */
+    CURL * curlSessionP;
+        /* Handle for Curl library session object */
+    char curlError[CURL_ERROR_SIZE];
+        /* Error message from Curl */
+    struct curl_slist * headerList;
+        /* The HTTP headers for the transaction */
+    const char * serverUrl;  /* malloc'ed - belongs to this object */
+} curlTransaction;
+
+
+
+typedef struct {
+    struct list_head link;  /* link in transport's list of RPCs */
+    curlTransaction * curlTransactionP;
+        /* The object which does the HTTP transaction, with no knowledge
+           of XML-RPC or Xmlrpc-c.
+        */
+    xmlrpc_mem_block * responseXmlP;
+    xmlrpc_bool threadExists;
+    pthread_t thread;
+    transport_asynch_complete complete;
+        /* Routine to call to complete the RPC after it is complete HTTP-wise.
+           NULL if none.
+        */
+    struct call_info * callInfoP;
+        /* User's identifier for this RPC */
+} rpc;
+
+
+
+static size_t 
+collect(void *  const ptr, 
+        size_t  const size, 
+        size_t  const nmemb,  
+        FILE  * const stream) {
+/*----------------------------------------------------------------------------
+   This is a Curl output function.  Curl calls this to deliver the
+   HTTP response body.  Curl thinks it's writing to a POSIX stream.
+-----------------------------------------------------------------------------*/
+    xmlrpc_mem_block * const responseXmlP = (xmlrpc_mem_block *) stream;
+    char * const buffer = ptr;
+    size_t const length = nmemb * size;
+
+    size_t retval;
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+    xmlrpc_mem_block_append(&env, responseXmlP, buffer, length);
+    if (env.fault_occurred)
+        retval = (size_t)-1;
+    else
+        /* Really?  Shouldn't it be like fread() and return 'nmemb'? */
+        retval = length;
+    
+    return retval;
+}
+
+
+
+static void
+initWindowsStuff(xmlrpc_env * const envP) {
+
+#if defined (WIN32)
+    /* This is CRITICAL so that cURL-Win32 works properly! */
+    WORD wVersionRequested;
+    WSADATA wsaData;
+    int err;
+    wVersionRequested = MAKEWORD(1, 1);
+    
+    err = WSAStartup(wVersionRequested, &wsaData);
+    if (LOBYTE(wsaData.wVersion) != 1 || 
+        HIBYTE( wsaData.wVersion) != 1) {
+        /* Tell the user that we couldn't find a useable */ 
+        /* winsock.dll. */ 
+        WSACleanup();
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, "Winsock reported that "
+            "it does not implement the requested version 1.1.");
+    }
+#else
+    if (0)
+        envP->fault_occurred = TRUE;  /* Avoid unused parm warning */
+#endif
+}
+
+
+
+static void 
+create(xmlrpc_env *              const envP,
+       int                       const flags ATTR_UNUSED,
+       const char *              const appname ATTR_UNUSED,
+       const char *              const appversion ATTR_UNUSED,
+       struct clientTransport ** const handlePP) {
+/*----------------------------------------------------------------------------
+   This does the 'create' operation for a Curl client transport.
+-----------------------------------------------------------------------------*/
+    struct clientTransport * transportP;
+
+    initWindowsStuff(envP);
+
+    MALLOCVAR(transportP);
+    if (transportP == NULL)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "Unable to allocate transport descriptor.");
+    else {
+        pthread_mutex_init(&transportP->listLock, NULL);
+        
+        list_make_empty(&transportP->rpcList);
+
+        /*
+         * This is the main global constructor for the app. Call this before
+         * _any_ libcurl usage. If this fails, *NO* libcurl functions may be
+         * used, or havoc may be the result.
+         */
+        curl_global_init(CURL_GLOBAL_ALL);
+
+        /* The above makes it look like Curl is not re-entrant.  We should
+           check into that.
+        */
+
+        *handlePP = transportP;
+    }
+}
+
+
+
+static void
+termWindowStuff(void) {
+
+#if defined (WIN32)
+    WSACleanup();
+#endif
+}
+
+
+
+static void 
+destroy(struct clientTransport * const clientTransportP) {
+/*----------------------------------------------------------------------------
+   This does the 'destroy' operation for a Libwww client transport.
+-----------------------------------------------------------------------------*/
+    XMLRPC_ASSERT(clientTransportP != NULL);
+
+    XMLRPC_ASSERT(list_is_empty(&clientTransportP->rpcList));
+
+    pthread_mutex_destroy(&clientTransportP->listLock);
+
+    curl_global_cleanup();
+
+    termWindowStuff();
+
+    free(clientTransportP);
+}
+
+
+
+static void
+createCurlHeaderList(xmlrpc_env *         const envP,
+                     xmlrpc_server_info * const serverP,
+                     struct curl_slist ** const headerListP) {
+
+    struct curl_slist * headerList;
+
+    headerList = NULL;  /* initial value */
+
+    headerList = curl_slist_append(headerList, "Content-Type: text/xml");
+
+    if (headerList == NULL)
+        xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR, 
+                "Could not add header.  curl_slist_append() failed.");
+    else {
+        /* Send an authorization header if we need one. */
+        if (serverP->_http_basic_auth) {
+            /* Make the authentication header "Authorization: " */
+            /* we need 15 + length of _http_basic_auth + 1 for null */
+
+            char * const authHeader = 
+                malloc(strlen(serverP->_http_basic_auth) + 15 + 1);
+            
+            if (authHeader == NULL)
+                xmlrpc_env_set_fault_formatted(
+                    envP, XMLRPC_INTERNAL_ERROR,
+                    "Couldn't allocate memory for authentication header");
+            else {
+                memcpy(authHeader,"Authorization: ", 15);
+                memcpy(authHeader + 15, serverP->_http_basic_auth,
+                       strlen(serverP->_http_basic_auth) + 1);
+
+                headerList = curl_slist_append(headerList, authHeader);
+                if (headerList == NULL)
+                    xmlrpc_env_set_fault_formatted(
+                        envP, XMLRPC_INTERNAL_ERROR,
+                        "Could not add authentication header.  "
+                        "curl_slist_append() failed.");
+                free(authHeader);
+            }
+        }
+        if (envP->fault_occurred)
+            free(headerList);
+    }
+    *headerListP = headerList;
+}
+
+
+
+static void
+setupCurlSession(xmlrpc_env *       const envP,
+                 CURL *             const curlSessionP,
+                 curlTransaction *  const curlTransactionP,
+                 xmlrpc_mem_block * const callXmlP,
+                 xmlrpc_mem_block * const responseXmlP) {
+
+    curl_easy_setopt(curlSessionP, CURLOPT_POST, 1 );
+    curl_easy_setopt(curlSessionP, CURLOPT_URL, curlTransactionP->serverUrl);
+    XMLRPC_MEMBLOCK_APPEND(char, envP, callXmlP, "\0", 1);
+    if (!envP->fault_occurred) {
+        curl_easy_setopt(curlSessionP, CURLOPT_POSTFIELDS, 
+                         XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP));
+        
+        curl_easy_setopt(curlSessionP, CURLOPT_FILE, responseXmlP);
+        curl_easy_setopt(curlSessionP, CURLOPT_HEADER, 0 );
+        curl_easy_setopt(curlSessionP, CURLOPT_WRITEFUNCTION, collect);
+        curl_easy_setopt(curlSessionP, CURLOPT_ERRORBUFFER, 
+                         curlTransactionP->curlError);
+        curl_easy_setopt(curlSessionP, CURLOPT_NOPROGRESS, 1);
+        
+        curl_easy_setopt(curlSessionP, CURLOPT_HTTPHEADER, 
+                         curlTransactionP->headerList);
+    }
+}
+
+
+
+static void
+createCurlTransaction(xmlrpc_env *         const envP,
+                      xmlrpc_server_info * const serverP,
+                      xmlrpc_mem_block *   const callXmlP,
+                      xmlrpc_mem_block *   const responseXmlP,
+                      curlTransaction **   const curlTransactionPP) {
+
+    curlTransaction * curlTransactionP;
+
+    MALLOCVAR(curlTransactionP);
+    if (curlTransactionP == NULL)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "No memory to create Curl transaction.");
+    else {
+        CURL * const curlSessionP = curl_easy_init();
+    
+        if (curlSessionP == NULL)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR,
+                "Could not create Curl session.  curl_easy_init() failed.");
+        else {
+            curlTransactionP->curlSessionP = curlSessionP;
+
+            curlTransactionP->serverUrl = strdup(serverP->_server_url);
+            if (curlTransactionP->serverUrl == NULL)
+                xmlrpc_env_set_fault_formatted(
+                    envP, XMLRPC_INTERNAL_ERROR,
+                    "Out of memory to store server URL.");
+            else {
+                createCurlHeaderList(envP, serverP, 
+                                     &curlTransactionP->headerList);
+
+                if (!envP->fault_occurred)
+                    setupCurlSession(envP, curlSessionP, curlTransactionP,
+                                     callXmlP, responseXmlP);
+
+                if (envP->fault_occurred)
+                    strfree(curlTransactionP->serverUrl);
+            }
+            if (envP->fault_occurred)
+                curl_easy_cleanup(curlSessionP);
+        }
+        if (envP->fault_occurred)
+            free(curlTransactionP);
+    }
+    *curlTransactionPP = curlTransactionP;
+}
+
+
+
+static void
+destroyCurlTransaction(curlTransaction * const curlTransactionP) {
+
+    curl_slist_free_all(curlTransactionP->headerList);
+    strfree(curlTransactionP->serverUrl);
+    curl_easy_cleanup(curlTransactionP->curlSessionP);
+}
+
+
+#include <unistd.h>
+static void
+performCurlTransaction(xmlrpc_env *      const envP,
+                       curlTransaction * const curlTransactionP) {
+
+    CURL * const curlSessionP = curlTransactionP->curlSessionP;
+
+    CURLcode res;
+
+    res = curl_easy_perform(curlSessionP);
+    
+    if (res != CURLE_OK)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_NETWORK_ERROR, "Curl failed to perform "
+            "HTTP POST request.  curl_easy_perform() says: %s", 
+            curlTransactionP->curlError);
+    else {
+        CURLcode res;
+        long http_result;
+        res = curl_easy_getinfo(curlSessionP, CURLINFO_HTTP_CODE, 
+                                &http_result);
+
+        if (res != CURLE_OK)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR, 
+                "Curl performed the HTTP POST request, but was "
+                "unable to say what the HTTP result code was.  "
+                "curl_easy_getinfo(CURLINFO_HTTP_CODE) says: %s", 
+                curlTransactionP->curlError);
+        else {
+            if (http_result != 200)
+                xmlrpc_env_set_fault_formatted(
+                    envP, XMLRPC_NETWORK_ERROR, "HTTP response: %ld",
+                    http_result);
+        }
+    }
+}
+
+
+
+static void
+doAsyncRpc2(void * const arg) {
+
+    rpc * const rpcP = arg;
+
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+
+    performCurlTransaction(&env, rpcP->curlTransactionP);
+
+    rpcP->complete(rpcP->callInfoP, rpcP->responseXmlP, env);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+#ifdef WIN32
+
+static unsigned __stdcall 
+doAsyncRpc(void * arg) {
+    doAsyncRpc2(arg);
+    return 0;
+}
+
+#else
+
+static void *
+doAsyncRpc(void * arg) {
+    doAsyncRpc2(arg);
+    return NULL;
+}
+
+#endif
+
+
+
+static void
+createRpcThread(xmlrpc_env *              const envP,
+                rpc *                     const rpcP,
+                pthread_t *               const threadP) {
+
+    int rc;
+
+    rc = pthread_create(threadP, NULL, doAsyncRpc, rpcP);
+    switch (rc) {
+    case 0: 
+        break;
+    case EAGAIN:
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "pthread_create() failed:  System Resources exceeded.");
+        break;
+    case EINVAL:
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "pthread_create() failed:  Param Error for attr.");
+        break;
+    case ENOMEM:
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "pthread_create() failed:  No memory for new thread.");
+        break;
+    default:
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, 
+            "pthread_create() failed: Unrecognized error code %d.", rc);
+        break;
+    }
+}
+
+
+
+static void
+rpcCreate(xmlrpc_env *             const envP,
+          struct clientTransport * const clientTransportP,
+          xmlrpc_server_info *     const serverP,
+          xmlrpc_mem_block *       const callXmlP,
+          xmlrpc_mem_block *       const responseXmlP,
+          transport_asynch_complete      complete, 
+          struct call_info *       const callInfoP,
+          rpc **                   const rpcPP) {
+
+    rpc * rpcP;
+
+    MALLOCVAR(rpcP);
+    if (rpcP == NULL)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "Couldn't allocate memory for rpc object");
+    else {
+        rpcP->callInfoP = callInfoP;
+        rpcP->complete  = complete;
+        rpcP->responseXmlP = responseXmlP;
+        rpcP->threadExists = FALSE;
+
+        createCurlTransaction(envP, serverP,
+                              callXmlP, responseXmlP, 
+                              &rpcP->curlTransactionP);
+        if (!envP->fault_occurred) {
+            if (complete) {
+                createRpcThread(envP, rpcP, &rpcP->thread);
+                if (!envP->fault_occurred)
+                    rpcP->threadExists = TRUE;
+            }
+            if (!envP->fault_occurred) {
+                list_init_header(&rpcP->link, rpcP);
+                pthread_mutex_lock(&clientTransportP->listLock);
+                list_add_head(&clientTransportP->rpcList, &rpcP->link);
+                pthread_mutex_unlock(&clientTransportP->listLock);
+            }
+            if (envP->fault_occurred)
+                    destroyCurlTransaction(rpcP->curlTransactionP);
+        }
+        if (envP->fault_occurred)
+            free(rpcP);
+    }
+    *rpcPP = rpcP;
+}
+
+
+
+static void 
+rpcDestroy(rpc * const rpcP) {
+
+    XMLRPC_ASSERT_PTR_OK(rpcP);
+    XMLRPC_ASSERT(!rpcP->threadExists);
+
+    destroyCurlTransaction(rpcP->curlTransactionP);
+
+    list_remove(&rpcP->link);
+
+    free(rpcP);
+}
+
+
+static void 
+sendRequest(xmlrpc_env *             const envP, 
+            struct clientTransport * const clientTransportP,
+            xmlrpc_server_info *     const serverP,
+            xmlrpc_mem_block *       const callXmlP,
+            transport_asynch_complete      complete,
+            struct call_info *       const callInfoP) {
+/*----------------------------------------------------------------------------
+   Initiate an XML-RPC rpc asynchronously.  Don't wait for it to go to
+   the server.
+
+   Unless we return failure, we arrange to have complete() called when
+   the rpc completes.
+
+   This does the 'send_request' operation for a Curl client transport.
+-----------------------------------------------------------------------------*/
+    rpc * rpcP;
+    xmlrpc_mem_block * responseXmlP;
+
+    responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
+    if (!envP->fault_occurred) {
+        rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
+                  complete, callInfoP,
+                  &rpcP);
+
+        if (envP->fault_occurred)
+            XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
+    }
+    /* The user's eventual finish_asynch call will destroy this RPC
+       and response buffer
+    */
+}
+
+
+
+static void * 
+finishRpc(struct list_head * const headerP, 
+          void *             const context ATTR_UNUSED) {
+    
+    rpc * const rpcP = headerP->itemP;
+
+    if (rpcP->threadExists) {
+        void *status;
+        int result;
+
+        result = pthread_join(rpcP->thread, &status);
+        
+        rpcP->threadExists = FALSE;
+    }
+
+    XMLRPC_MEMBLOCK_FREE(char, rpcP->responseXmlP);
+
+    rpcDestroy(rpcP);
+
+    return NULL;
+}
+
+
+
+static void 
+finishAsynch(struct clientTransport * const clientTransportP ATTR_UNUSED,
+             enum timeoutType         const timeoutType ATTR_UNUSED,
+             timeout_t                const timeout ATTR_UNUSED) {
+/*----------------------------------------------------------------------------
+   Wait for the threads of all outstanding RPCs to exit and destroy those
+   RPCs.
+
+   This does the 'finish_asynch' operation for a Curl client transport.
+-----------------------------------------------------------------------------*/
+    /* We ignore any timeout request.  Some day, we should figure out how
+       to set an alarm and interrupt running threads.
+    */
+
+    pthread_mutex_lock(&clientTransportP->listLock);
+
+    list_foreach(&clientTransportP->rpcList, finishRpc, NULL);
+
+    pthread_mutex_unlock(&clientTransportP->listLock);
+}
+
+
+
+static void
+call(xmlrpc_env *             const envP,
+     struct clientTransport * const clientTransportP,
+     xmlrpc_server_info *     const serverP,
+     xmlrpc_mem_block *       const callXmlP,
+     struct call_info *       const callInfoP,
+     xmlrpc_mem_block **      const responsePP) {
+
+    xmlrpc_mem_block * responseXmlP;
+    rpc * rpcP;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_PTR_OK(serverP);
+    XMLRPC_ASSERT_PTR_OK(callXmlP);
+    XMLRPC_ASSERT_PTR_OK(callInfoP);
+    XMLRPC_ASSERT_PTR_OK(responsePP);
+
+    responseXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
+    if (!envP->fault_occurred) {
+        rpcCreate(envP, clientTransportP, serverP, callXmlP, responseXmlP,
+                  NULL, NULL, &rpcP);
+        if (!envP->fault_occurred) {
+            performCurlTransaction(envP, rpcP->curlTransactionP);
+            
+            *responsePP = responseXmlP;
+            
+            rpcDestroy(rpcP);
+        }
+        if (envP->fault_occurred)
+            XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
+    }
+}
+
+
+
+struct clientTransportOps xmlrpc_curl_transport_ops = {
+    &create,
+    &destroy,
+    &sendRequest,
+    &call,
+    &finishAsynch,
+};

+ 8 - 0
Utilities/cmxmlrpc/xmlrpc_curl_transport.h

@@ -0,0 +1,8 @@
+#ifndef XMLRPC_CURL_TRANSPORT_H
+#define XMLRPC_CURL_TRANSPORT_H
+
+#include "xmlrpc_transport.h"
+
+extern struct clientTransportOps xmlrpc_curl_transport_ops;
+
+#endif

+ 1331 - 0
Utilities/cmxmlrpc/xmlrpc_data.c

@@ -0,0 +1,1331 @@
+/* Copyright information is at end of file */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "bool.h"
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+
+/* Borrowed from Python 1.5.2.
+** MPW pushes 'extended' for float and double types with varargs */
+#ifdef MPW
+typedef extended va_double;
+#else
+typedef double va_double;
+#endif
+
+/* Borrowed from Python 1.5.2.
+** Python copies its va_list objects before using them in certain
+** tricky fashions. We don't why Python does this, but since we're
+** abusing our va_list objects in a similar fashion, we'll copy them
+** too. */
+#if VA_LIST_IS_ARRAY
+#define VA_LIST_COPY(dest,src) memcpy((dest), (src), sizeof(va_list))
+#else
+#define VA_LIST_COPY(dest,src) ((dest) = (src))
+#endif
+
+
+static void
+destroyValue(xmlrpc_value * const valueP) {
+
+    /* First, we need to destroy this value's contents, if any. */
+    switch (valueP->_type) {
+    case XMLRPC_TYPE_INT:
+    case XMLRPC_TYPE_BOOL:
+    case XMLRPC_TYPE_DOUBLE:
+        break;
+        
+    case XMLRPC_TYPE_ARRAY:
+        xmlrpc_destroyArrayContents(valueP);
+        break;
+        
+    case XMLRPC_TYPE_STRING:
+#ifdef HAVE_UNICODE_WCHAR
+        if (valueP->_wcs_block)
+            xmlrpc_mem_block_free(valueP->_wcs_block);
+#endif /* HAVE_UNICODE_WCHAR */
+        /* Fall through. */
+
+    case XMLRPC_TYPE_DATETIME:
+    case XMLRPC_TYPE_BASE64:
+        xmlrpc_mem_block_clean(&valueP->_block);
+        break;
+
+    case XMLRPC_TYPE_STRUCT:
+        xmlrpc_destroyStruct(valueP);
+        break;
+
+    case XMLRPC_TYPE_C_PTR:
+        break;
+
+    case XMLRPC_TYPE_DEAD:
+        XMLRPC_ASSERT(FALSE); /* Can't happen, per entry conditions */
+
+    default:
+        XMLRPC_ASSERT(FALSE); /* There are no other possible values */
+    }
+
+    /* Next, we mark this value as invalid, to help catch refcount
+        ** errors. */
+    valueP->_type = XMLRPC_TYPE_DEAD;
+
+    /* Finally, we destroy the value itself. */
+    free(valueP);
+}
+
+
+
+/*=========================================================================
+**  Reference Counting
+**=========================================================================
+**  Some simple reference-counting code. The xmlrpc_DECREF routine is in
+**  charge of destroying values when their reference count equals zero.
+*/
+
+void 
+xmlrpc_INCREF (xmlrpc_value * const valueP) {
+
+    XMLRPC_ASSERT_VALUE_OK(valueP);
+    XMLRPC_ASSERT(valueP->_refcount > 0);
+
+    valueP->_refcount++;
+}
+
+
+
+void 
+xmlrpc_DECREF (xmlrpc_value * const valueP) {
+
+    XMLRPC_ASSERT_VALUE_OK(valueP);
+    XMLRPC_ASSERT(valueP->_refcount > 0);
+    XMLRPC_ASSERT(valueP->_type != XMLRPC_TYPE_DEAD);
+
+    valueP->_refcount--;
+
+    /* If we have no more refs, we need to deallocate this value. */
+    if (valueP->_refcount == 0)
+        destroyValue(valueP);
+}
+
+
+
+/*=========================================================================
+    Utiltiies
+=========================================================================*/
+
+static const char *
+typeName(xmlrpc_type const type) {
+
+    switch(type) {
+
+    case XMLRPC_TYPE_INT: return "INT";
+    case XMLRPC_TYPE_BOOL: return "BOOL";
+    case XMLRPC_TYPE_DOUBLE: return "DOUBLE";
+    case XMLRPC_TYPE_DATETIME: return "DATETIME";
+    case XMLRPC_TYPE_STRING: return "STRING";
+    case XMLRPC_TYPE_BASE64: return "BASE64";
+    case XMLRPC_TYPE_ARRAY: return "ARRAY";
+    case XMLRPC_TYPE_STRUCT: return "STRUCT";
+    case XMLRPC_TYPE_C_PTR: return "C_PTR";
+    case XMLRPC_TYPE_DEAD: return "DEAD";
+    default: return "???";
+    }
+}
+
+
+
+static void
+verifyNoNulls(xmlrpc_env * const envP,
+              const char * const contents,
+              unsigned int const len) {
+/*----------------------------------------------------------------------------
+   Verify that the character array 'contents', which is 'len' bytes long,
+   does not contain any NUL characters, which means it can be made into
+   a passable ASCIIZ string just by adding a terminating NUL.
+
+   Fail if the array contains a NUL.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < len && !envP->fault_occurred; i++)
+        if (contents[i] == '\0')
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_TYPE_ERROR, 
+                "String must not contain NUL characters");
+}
+
+
+
+static void
+verifyNoNullsW(xmlrpc_env *    const envP,
+               const wchar_t * const contents,
+               unsigned int    const len) {
+/*----------------------------------------------------------------------------
+   Same as verifyNoNulls(), but for wide characters.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < len && !envP->fault_occurred; i++)
+        if (contents[i] == '\0')
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_TYPE_ERROR, 
+                "String must not contain NUL characters");
+}
+
+
+
+static void
+validateType(xmlrpc_env *         const envP,
+             const xmlrpc_value * const valueP,
+             xmlrpc_type          const expectedType) {
+
+    if (valueP->_type != expectedType) {
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
+            "type %s was expected.", 
+            typeName(valueP->_type), typeName(expectedType));
+    }
+}
+
+
+
+/*=========================================================================
+    Extracting XML-RPC value
+===========================================================================
+  These routines extract XML-RPC values into ordinary C data types.
+
+  For array and struct values, see the separates files xmlrpc_array.c
+  and xmlrpc_struct.c.
+=========================================================================*/
+
+void 
+xmlrpc_read_int(xmlrpc_env *         const envP,
+                const xmlrpc_value * const valueP,
+                xmlrpc_int32 *       const intValueP) {
+
+    validateType(envP, valueP, XMLRPC_TYPE_INT);
+    if (!envP->fault_occurred)
+        *intValueP = valueP->_value.i;
+}
+
+
+
+void
+xmlrpc_read_double(xmlrpc_env *         const envP,
+                   const xmlrpc_value * const valueP,
+                   xmlrpc_double *      const doubleValueP) {
+    
+    validateType(envP, valueP, XMLRPC_TYPE_DOUBLE);
+    if (!envP->fault_occurred)
+        *doubleValueP = valueP->_value.d;
+
+}
+
+
+
+void
+xmlrpc_read_bool(xmlrpc_env *         const envP,
+                 const xmlrpc_value * const valueP,
+                 xmlrpc_bool *        const boolValueP) {
+
+    validateType(envP, valueP, XMLRPC_TYPE_BOOL);
+    if (!envP->fault_occurred)
+        *boolValueP = valueP->_value.b;
+}
+
+
+
+void
+xmlrpc_read_string(xmlrpc_env *         const envP,
+                   const xmlrpc_value * const valueP,
+                   const char **        const stringValueP) {
+/*----------------------------------------------------------------------------
+   Read the value of an XML-RPC string an ASCIIZ string.
+
+   Fail if the string contains null characters (which means it wasn't
+   really a string, but XML-RPC doesn't seem to understand what a string
+   is, and such values are possible).
+-----------------------------------------------------------------------------*/
+    validateType(envP, valueP, XMLRPC_TYPE_STRING);
+    if (!envP->fault_occurred) {
+        unsigned int const size = 
+            XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
+        const char * const contents = 
+            XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
+
+        verifyNoNulls(envP, contents, size);
+        if (!envP->fault_occurred) {
+            char * stringValue;
+            
+            stringValue = malloc(size+1);
+            if (stringValue == NULL)
+                xmlrpc_env_set_fault_formatted(
+                    envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate space "
+                    "for %u-character string", size);
+            else {
+                memcpy(stringValue,
+                       XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block), size);
+                stringValue[size] = '\0';
+
+                *stringValueP = stringValue;
+            }
+        }
+    }
+}
+
+
+
+void
+xmlrpc_read_string_lp(xmlrpc_env *         const envP,
+                      const xmlrpc_value * const valueP,
+                      unsigned int *       const lengthP,
+                      const char **        const stringValueP) {
+
+    validateType(envP, valueP, XMLRPC_TYPE_STRING);
+    if (!envP->fault_occurred) {
+        unsigned int const size = 
+            XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
+        const char * const contents = 
+            XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
+
+        char * stringValue;
+
+        stringValue = malloc(size);
+        if (stringValue == NULL)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate %u bytes "
+                "for string.", size);
+        else {
+            memcpy(stringValue, contents, size);
+            *stringValueP = stringValue;
+            *lengthP = size;
+        }
+    }
+}
+
+
+
+/*=========================================================================
+**  Building XML-RPC values.
+**=========================================================================
+**  Build new XML-RPC values from a format string. This code is heavily
+**  inspired by Py_BuildValue from Python 1.5.2. In particular, our
+**  particular abuse of the va_list data type is copied from the equivalent
+**  Python code in modsupport.c. Since Python is portable, our code should
+**  (in theory) also be portable.
+*/
+
+
+xmlrpc_type xmlrpc_value_type (xmlrpc_value* value)
+{
+    XMLRPC_ASSERT_VALUE_OK(value);
+    return value->_type;
+}
+
+
+
+static void
+createXmlrpcValue(xmlrpc_env *    const envP,
+                  xmlrpc_value ** const valPP) {
+/*----------------------------------------------------------------------------
+   Create a blank xmlrpc_value to be filled in.
+
+   Set the reference count to 1.
+-----------------------------------------------------------------------------*/
+    xmlrpc_value * valP;
+
+    valP = (xmlrpc_value*) malloc(sizeof(xmlrpc_value));
+    if (!valP)
+        xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
+                             "Could not allocate memory for xmlrpc_value");
+    else
+        valP->_refcount = 1;
+
+    *valPP = valP;
+}
+
+
+
+static void
+mkInt(xmlrpc_env *    const envP, 
+      xmlrpc_int32    const value,
+      xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type    = XMLRPC_TYPE_INT;
+        valP->_value.i = value;
+    }
+    *valPP = valP;
+}
+
+
+
+static void
+mkBool(xmlrpc_env *    const envP, 
+       xmlrpc_bool     const value,
+       xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type = XMLRPC_TYPE_BOOL;
+        valP->_value.b = value;
+    }
+    *valPP = valP;
+}
+
+
+
+static void
+mkDouble(xmlrpc_env *    const envP, 
+         double          const value,
+         xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type = XMLRPC_TYPE_DOUBLE;
+        valP->_value.d = value;
+    }
+    *valPP = valP;
+}
+
+
+
+#ifdef HAVE_UNICODE_WCHAR
+#define MAKE_WCS_BLOCK_NULL(val) ((val)->_wcs_block = NULL)
+#else
+#define MAKE_WCS_BLOCK_NULL(val) while (0) do {};
+#endif
+
+
+
+static void
+mkString(xmlrpc_env *    const envP, 
+         const char *    const value,
+         unsigned int    const length,
+         xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type = XMLRPC_TYPE_STRING;
+        MAKE_WCS_BLOCK_NULL(valP);
+        XMLRPC_MEMBLOCK_INIT(char, envP, &valP->_block, length + 1);
+        if (!envP->fault_occurred) {
+            char * const contents =
+                XMLRPC_MEMBLOCK_CONTENTS(char, &valP->_block);
+            memcpy(contents, value, length);
+            contents[length] = '\0';
+        }
+        if (envP->fault_occurred)
+            free(valP);
+    }
+    *valPP = valP;
+}
+
+
+
+static void
+getString(xmlrpc_env *    const envP,
+          const char **   const formatP,
+          va_list *       const args,
+          xmlrpc_value ** const valPP) {
+
+    const char * str;
+    unsigned int len;
+    
+    str = (const char*) va_arg(*args, char*);
+    if (**formatP == '#') {
+        (*formatP)++;
+        len = (size_t) va_arg(*args, size_t);
+    } else
+        len = strlen(str);
+
+    mkString(envP, str, len, valPP);
+}
+
+
+
+#ifdef HAVE_UNICODE_WCHAR
+static void
+mkWideString(xmlrpc_env *    const envP,
+             wchar_t *       const wcs,
+             size_t          const wcs_len,
+             xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+    char *contents;
+    wchar_t *wcs_contents;
+    int block_is_inited;
+    xmlrpc_mem_block *utf8_block;
+    char *utf8_contents;
+    size_t utf8_len;
+
+    /* Error-handling preconditions. */
+    valP = NULL;
+    utf8_block = NULL;
+    block_is_inited = 0;
+
+    /* Initialize our XML-RPC value. */
+    valP = (xmlrpc_value*) malloc(sizeof(xmlrpc_value));
+    XMLRPC_FAIL_IF_NULL(valP, envP, XMLRPC_INTERNAL_ERROR,
+                        "Could not allocate memory for wide string");
+    valP->_refcount = 1;
+    valP->_type = XMLRPC_TYPE_STRING;
+
+    /* More error-handling preconditions. */
+    valP->_wcs_block = NULL;
+
+    /* Build our wchar_t block first. */
+    valP->_wcs_block =
+        XMLRPC_TYPED_MEM_BLOCK_NEW(wchar_t, envP, wcs_len + 1);
+    XMLRPC_FAIL_IF_FAULT(envP);
+    wcs_contents =
+        XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, valP->_wcs_block);
+    memcpy(wcs_contents, wcs, wcs_len * sizeof(wchar_t));
+    wcs_contents[wcs_len] = '\0';
+    
+    /* Convert the wcs block to UTF-8. */
+    utf8_block = xmlrpc_wcs_to_utf8(envP, wcs_contents, wcs_len + 1);
+    XMLRPC_FAIL_IF_FAULT(envP);
+    utf8_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, utf8_block);
+    utf8_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, utf8_block);
+
+    /* XXX - We need an extra memcopy to initialize _block. */
+    XMLRPC_TYPED_MEM_BLOCK_INIT(char, envP, &valP->_block, utf8_len);
+    XMLRPC_FAIL_IF_FAULT(envP);
+    block_is_inited = 1;
+    contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
+    memcpy(contents, utf8_contents, utf8_len);
+
+ cleanup:
+    if (utf8_block)
+        xmlrpc_mem_block_free(utf8_block);
+    if (envP->fault_occurred) {
+        if (valP) {
+            if (valP->_wcs_block)
+                xmlrpc_mem_block_free(valP->_wcs_block);
+            if (block_is_inited)
+                xmlrpc_mem_block_clean(&valP->_block);
+            free(valP);
+        }
+    }
+    *valPP = valP;
+}
+#endif /* HAVE_UNICODE_WCHAR */
+
+
+
+static void
+getWideString(xmlrpc_env *    const envP,
+              const char **   const formatP,
+              va_list *       const args,
+              xmlrpc_value ** const valPP) {
+#ifdef HAVE_UNICODE_WCHAR
+
+    wchar_t *wcs;
+    size_t len;
+    
+    wcs = (wchar_t*) va_arg(*args, wchar_t*);
+    if (**formatP == '#') {
+        (*formatP)++;
+        len = (size_t) va_arg(*args, size_t);
+    } else
+        len = wcslen(wcs);
+
+    mkWideString(envP, wcs, len, valPP);
+
+#endif /* HAVE_UNICODE_WCHAR */
+}
+
+
+
+static void
+mkDatetime(xmlrpc_env *    const envP, 
+           const char *    const value,
+           xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type = XMLRPC_TYPE_DATETIME;
+
+        XMLRPC_TYPED_MEM_BLOCK_INIT(
+            char, envP, &valP->_block, strlen(value) + 1);
+        if (!envP->fault_occurred) {
+            char * const contents =
+                XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
+            strcpy(contents, value);
+        }
+        if (envP->fault_occurred)
+            free(valP);
+    }
+    *valPP = valP;
+}
+
+
+
+static void
+mkBase64(xmlrpc_env *          const envP, 
+         const unsigned char * const value,
+         size_t                const length,
+         xmlrpc_value **       const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type = XMLRPC_TYPE_BASE64;
+
+        xmlrpc_mem_block_init(envP, &valP->_block, length);
+        if (!envP->fault_occurred) {
+            char * const contents = 
+                xmlrpc_mem_block_contents(&valP->_block);
+            memcpy(contents, value, length);
+        }
+        if (envP->fault_occurred)
+            free(valP);
+    }
+    *valPP = valP;
+}
+
+
+
+static void
+getBase64(xmlrpc_env *    const envP,
+          va_list *       const args,
+          xmlrpc_value ** const valPP) {
+
+    unsigned char * value;
+    size_t          length;
+    
+    value  = (unsigned char*) va_arg(*args, unsigned char*);
+    length = (size_t)         va_arg(*args, size_t);
+
+    mkBase64(envP, value, length, valPP);
+}
+
+
+
+static void
+mkCPtr(xmlrpc_env *    const envP, 
+       void *          const value,
+       xmlrpc_value ** const valPP) {
+
+    xmlrpc_value * valP;
+
+    createXmlrpcValue(envP, &valP);
+
+    if (!envP->fault_occurred) {
+        valP->_type = XMLRPC_TYPE_C_PTR;
+        valP->_value.c_ptr = value;
+    }
+    *valPP = valP;
+}
+
+
+
+static void
+mkArrayFromVal(xmlrpc_env *    const envP, 
+               xmlrpc_value *  const value,
+               xmlrpc_value ** const valPP) {
+
+    if (xmlrpc_value_type(value) != XMLRPC_TYPE_ARRAY)
+        xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
+                             "Array format ('A'), non-array xmlrpc_value");
+    else
+        xmlrpc_INCREF(value);
+
+    *valPP = value;
+}
+
+
+
+static void
+mkStructFromVal(xmlrpc_env *    const envP, 
+                xmlrpc_value *  const value,
+                xmlrpc_value ** const valPP) {
+
+    if (xmlrpc_value_type(value) != XMLRPC_TYPE_STRUCT)
+        xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
+                             "Struct format ('S'), non-struct xmlrpc_value");
+    else
+        xmlrpc_INCREF(value);
+
+    *valPP = value;
+}
+
+
+
+static void
+getValue(xmlrpc_env *    const envP, 
+         const char**    const format, 
+         va_list *             args,
+         xmlrpc_value ** const valPP);
+
+
+
+static void
+createXmlrpcArray(xmlrpc_env *    const envP,
+                  xmlrpc_value ** const arrayPP) {
+/*----------------------------------------------------------------------------
+   Create an empty array xmlrpc_value.
+-----------------------------------------------------------------------------*/
+    xmlrpc_value * arrayP;
+
+    createXmlrpcValue(envP, &arrayP);
+    if (!envP->fault_occurred) {
+        arrayP->_type = XMLRPC_TYPE_ARRAY;
+        XMLRPC_TYPED_MEM_BLOCK_INIT(xmlrpc_value*, envP, &arrayP->_block, 0);
+        if (envP->fault_occurred)
+            free(arrayP);
+    }
+    *arrayPP = arrayP;
+}
+
+
+
+static void
+getArray(xmlrpc_env *    const envP,
+         const char **   const formatP,
+         char            const delimiter,
+         va_list *       const args,
+         xmlrpc_value ** const arrayPP) {
+
+    xmlrpc_value * arrayP;
+
+    createXmlrpcArray(envP, &arrayP);
+
+    /* Add items to the array until we hit our delimiter. */
+    
+    while (**formatP != delimiter && !envP->fault_occurred) {
+        
+        xmlrpc_value * itemP;
+        
+        if (**formatP == '\0')
+            xmlrpc_env_set_fault(
+                envP, XMLRPC_INTERNAL_ERROR,
+                "format string ended before closing ')'.");
+        else {
+            getValue(envP, formatP, args, &itemP);
+            if (!envP->fault_occurred) {
+                xmlrpc_array_append_item(envP, arrayP, itemP);
+                xmlrpc_DECREF(itemP);
+            }
+        }
+    }
+    if (envP->fault_occurred)
+        xmlrpc_DECREF(arrayP);
+
+    *arrayPP = arrayP;
+}
+
+
+
+static void
+getStructMember(xmlrpc_env *    const envP,
+                const char **   const formatP,
+                va_list *       const args,
+                xmlrpc_value ** const keyPP,
+                xmlrpc_value ** const valuePP) {
+
+
+    /* Get the key */
+    getValue(envP, formatP, args, keyPP);
+    if (!envP->fault_occurred) {
+        if (**formatP != ':')
+            xmlrpc_env_set_fault(
+                envP, XMLRPC_INTERNAL_ERROR,
+                "format string does not have ':' after a "
+                "structure member key.");
+        else {
+            /* Skip over colon that separates key from value */
+            (*formatP)++;
+            
+            /* Get the value */
+            getValue(envP, formatP, args, valuePP);
+        }
+        if (envP->fault_occurred)
+            xmlrpc_DECREF(*keyPP);
+    }
+}
+            
+            
+
+static void
+getStruct(xmlrpc_env *    const envP,
+          const char **   const formatP,
+          char            const delimiter,
+          va_list *       const args,
+          xmlrpc_value ** const structPP) {
+
+    xmlrpc_value * structP;
+
+    structP = xmlrpc_struct_new(envP);
+    if (!envP->fault_occurred) {
+        while (**formatP != delimiter && !envP->fault_occurred) {
+            xmlrpc_value * keyP;
+            xmlrpc_value * valueP;
+            
+            getStructMember(envP, formatP, args, &keyP, &valueP);
+            
+            if (!envP->fault_occurred) {
+                if (**formatP == ',')
+                    (*formatP)++;  /* Skip over the comma */
+                else if (**formatP == delimiter) {
+                    /* End of the line */
+                } else 
+                    xmlrpc_env_set_fault(
+                        envP, XMLRPC_INTERNAL_ERROR,
+                        "format string does not have ',' or ')' after "
+                        "a structure member");
+                
+                if (!envP->fault_occurred)
+                    /* Add the new member to the struct. */
+                    xmlrpc_struct_set_value_v(envP, structP, keyP, valueP);
+                
+                xmlrpc_DECREF(valueP);
+                xmlrpc_DECREF(keyP);
+            }
+        }
+        if (envP->fault_occurred)
+            xmlrpc_DECREF(structP);
+    }
+    *structPP = structP;
+}
+
+
+
+static void
+getValue(xmlrpc_env *    const envP, 
+         const char**    const formatP,
+         va_list *       const args,
+         xmlrpc_value ** const valPP) {
+/*----------------------------------------------------------------------------
+   Get the next value from the list.  *formatP points to the specifier
+   for the next value in the format string (i.e. to the type code
+   character) and we move *formatP past the whole specifier for the
+   next value.  We read the required arguments from 'args'.  We return
+   the value as *valPP with a reference to it.
+
+   For example, if *formatP points to the "i" in the string "sis",
+   we read one argument from 'args' and return as *valP an integer whose
+   value is the argument we read.  We advance *formatP to point to the
+   last 's' and advance 'args' to point to the argument that belongs to
+   that 's'.
+-----------------------------------------------------------------------------*/
+    char const formatChar = *(*formatP)++;
+
+    switch (formatChar) {
+    case 'i':
+        mkInt(envP, (xmlrpc_int32) va_arg(*args, xmlrpc_int32), valPP);
+        break;
+
+    case 'b':
+        mkBool(envP, (xmlrpc_bool) va_arg(*args, xmlrpc_bool), valPP);
+        break;
+
+    case 'd':
+        mkDouble(envP, (double) va_arg(*args, va_double), valPP);
+        break;
+
+    case 's':
+        getString(envP, formatP, args, valPP);
+        break;
+
+    case 'w':
+        getWideString(envP, formatP, args, valPP);
+        break;
+
+    /* The code 't' is reserved for a better, time_t based
+       implementation of dateTime conversion.  
+    */
+    case '8':
+        mkDatetime(envP, (char*) va_arg(*args, char*), valPP);
+        break;
+
+    case '6':
+        getBase64(envP, args, valPP);
+        break;
+
+    case 'p':
+        /* We might someday want to use the code 'p!' to read in a
+           cleanup function for this pointer. 
+        */
+        mkCPtr(envP, (void*) va_arg(*args, void*), valPP);
+        break;      
+
+    case 'A':
+        mkArrayFromVal(envP, (xmlrpc_value*) va_arg(*args, xmlrpc_value*),
+                       valPP);
+        break;
+
+    case 'S':
+        mkStructFromVal(envP, (xmlrpc_value*) va_arg(*args, xmlrpc_value*),
+                        valPP);
+        break;
+
+    case 'V':
+        *valPP = (xmlrpc_value*) va_arg(*args, xmlrpc_value*);
+        xmlrpc_INCREF(*valPP);
+        break;
+
+    case '(':
+        getArray(envP, formatP, ')', args, valPP);
+        if (!envP->fault_occurred) {
+            XMLRPC_ASSERT(**formatP == ')');
+            (*formatP)++;  /* Skip over closing parenthesis */
+        }
+        break;
+
+    case '{': 
+        getStruct(envP, formatP, '}', args, valPP);
+        if (!envP->fault_occurred) {
+            XMLRPC_ASSERT(**formatP == '}');
+            (*formatP)++;  /* Skip over closing brace */
+        }
+        break;
+
+    default: {
+        const char * const badCharacter = xmlrpc_makePrintableChar(formatChar);
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "Unexpected character '%s' in format string", badCharacter);
+        xmlrpc_strfree(badCharacter);
+        }
+    }
+}
+
+
+
+void
+xmlrpc_build_value_va(xmlrpc_env *    const envP,
+                      const char *    const format,
+                      va_list               args,
+                      xmlrpc_value ** const valPP,
+                      const char **   const tailP) {
+
+    const char * formatCursor;
+    va_list args_copy;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT(format != NULL);
+
+    if (strlen(format) == 0)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, "Format string is empty.");
+    else {
+        formatCursor = &format[0];
+        VA_LIST_COPY(args_copy, args);
+        getValue(envP, &formatCursor, &args_copy, valPP);
+        
+        if (!envP->fault_occurred)
+            XMLRPC_ASSERT_VALUE_OK(*valPP);
+        
+        *tailP = formatCursor;
+    }
+}
+
+
+
+xmlrpc_value * 
+xmlrpc_build_value(xmlrpc_env * const envP,
+                   const char * const format, 
+                   ...) {
+
+    va_list args;
+    xmlrpc_value* retval;
+    const char * suffix;
+
+    va_start(args, format);
+    xmlrpc_build_value_va(envP, format, args, &retval, &suffix);
+    va_end(args);
+
+    if (!envP->fault_occurred) {
+        if (*suffix != '\0')
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+                "specifier: '%s'.  There must be exactly one arument.",
+                suffix);
+    
+        if (envP->fault_occurred)
+            xmlrpc_DECREF(retval);
+    }
+    return retval;
+}
+
+
+/*=========================================================================
+**  Parsing XML-RPC values.
+**=========================================================================
+**  Parse an XML-RPC value based on a format string. This code is heavily
+**  inspired by Py_BuildValue from Python 1.5.2.
+*/
+
+/* Prototype for recursive invocation: */
+
+static void 
+parsevalue(xmlrpc_env *   const env,
+           xmlrpc_value * const val,
+           const char **  const format,
+           va_list *            args);
+
+static void 
+parsearray(xmlrpc_env *         const env,
+           const xmlrpc_value * const array,
+           const char **        const format,
+           char                 const delimiter,
+           va_list *                  args) {
+
+    int size, i;
+    xmlrpc_value *item;
+
+    /* Fetch the array size. */
+    size = xmlrpc_array_size(env, array);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Loop over the items in the array. */
+    for (i = 0; i < size; i++) {
+        /* Bail out if the caller didn't care about the rest of the items. */
+        if (**format == '*')
+            break;
+
+        item = xmlrpc_array_get_item(env, array, i);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        XMLRPC_ASSERT(**format != '\0');
+        if (**format == delimiter)
+            XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Too many items in array");
+        parsevalue(env, item, format, args);
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+    if (**format == '*')
+        (*format)++;
+    if (**format != delimiter)
+        XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Not enough items in array");
+
+           cleanup:
+    return;
+}
+
+
+
+static void 
+parsestruct(xmlrpc_env *   const env,
+            xmlrpc_value * const strct,
+            const char **  const format,
+            char           const delimiter,
+            va_list *            args) {
+
+    xmlrpc_value *key, *value;
+    char *keystr;
+    size_t keylen;
+
+    /* Set up error handling preconditions. */
+    key = NULL;
+
+    /* Build the members of our struct. */
+    while (**format != '*' && **format != delimiter && **format != '\0') {
+
+        /* Get our key, and skip over the ':' character. Notice the
+        ** sudden call to getValue--we're going in the opposite direction. */
+        getValue(env, format, args, &key);
+        XMLRPC_FAIL_IF_FAULT(env);
+        XMLRPC_ASSERT(**format == ':');
+        (*format)++;
+
+        /* Look up the value for our key. */
+        xmlrpc_parse_value(env, key, "s#", &keystr, &keylen);
+        XMLRPC_FAIL_IF_FAULT(env);
+        value = xmlrpc_struct_get_value_n(env, strct, keystr, keylen);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        /* Get our value, and skip over the ',' character (if present). */
+        parsevalue(env, value, format, args);
+        XMLRPC_FAIL_IF_FAULT(env);
+        XMLRPC_ASSERT(**format == ',' || **format == delimiter);
+        if (**format == ',')
+            (*format)++;
+
+        /* Release our reference, and restore our invariant. */
+        xmlrpc_DECREF(key);
+        key = NULL;
+    }
+    if (**format == '*') {
+        (*format)++;
+        if (**format != delimiter && **format != '\0')
+            XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR, 
+                        "* can appear only at the end "
+                        "of a structure format specifier");
+    } else {
+        /* Here we're supposed to fail if he didn't extract all the
+           members.  But we don't know how to determine whether he
+           specified all the members, so we always fail.
+        */
+        XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR, "You must specify '*' as the "
+                    "last member of a structure in a format specifier "
+                    "used for parsing an xmlrpc_value"); 
+    }
+    XMLRPC_ASSERT(**format == delimiter || **format == '\0');
+
+cleanup:
+    if (key)
+        xmlrpc_DECREF(key);
+}
+
+
+static void 
+parsevalue(xmlrpc_env *   const envP,
+           xmlrpc_value * const valueP,
+           const char **  const format,
+           va_list *            args) {
+
+    char formatSpecChar;
+
+    formatSpecChar = *(*format)++;
+
+    switch (formatSpecChar) {
+    case 'i':
+        validateType(envP, valueP, XMLRPC_TYPE_INT);
+        if (!envP->fault_occurred) {
+            xmlrpc_int32 * const int32ptr = 
+                (xmlrpc_int32*) va_arg(*args, xmlrpc_int32*);
+            *int32ptr = valueP->_value.i;
+        }
+        break;
+
+    case 'b':
+        validateType(envP, valueP, XMLRPC_TYPE_BOOL);
+        if (!envP->fault_occurred) {
+            xmlrpc_bool * const boolptr =
+                (xmlrpc_bool*) va_arg(*args, xmlrpc_bool*);
+            *boolptr = valueP->_value.b;       
+        }
+        break;
+
+    case 'd':
+        validateType(envP, valueP, XMLRPC_TYPE_DOUBLE);
+        if (!envP->fault_occurred) {
+            double * const doubleptr = (double*) va_arg(*args, double*);
+            *doubleptr = valueP->_value.d;     
+        }
+        break;
+
+    case 's':
+        validateType(envP, valueP, XMLRPC_TYPE_STRING);
+        if (!envP->fault_occurred) {
+            char * const contents =
+                XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
+            size_t const len = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block) - 1;
+            
+            char ** const strptr = (char**) va_arg(*args, char**);
+            if (**format == '#') {
+                size_t * const sizeptr = (size_t*) va_arg(*args, size_t**);
+                (*format)++;
+                *sizeptr = len;
+            } else
+                verifyNoNulls(envP, contents, len);
+            *strptr = contents;
+        }
+        break;
+
+#ifdef HAVE_UNICODE_WCHAR
+    case 'w':
+        validateType(envP, valueP, XMLRPC_TYPE_STRING);
+        if (!envP->fault_occurred) {
+            if (!valueP->_wcs_block) {
+                /* Allocate a wchar_t string if we don't have one. */
+                char * const contents = 
+                    XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
+                size_t const len = 
+                    XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block) - 1;
+                valueP->_wcs_block = 
+                    xmlrpc_utf8_to_wcs(envP, contents, len + 1);
+            }
+            if (!envP->fault_occurred) {
+                wchar_t * const wcontents = 
+                    XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);
+                size_t const len = 
+                    XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block) - 1;
+
+                wchar_t ** const wcsptr = (wchar_t**) va_arg(*args, wchar_t**);
+                if (**format == '#') {
+                    size_t * const sizeptr = (size_t*) va_arg(*args, size_t**);
+                    (*format)++;
+                    *sizeptr = len;
+                } else
+                    verifyNoNullsW(envP, wcontents, len);
+                *wcsptr = wcontents;
+            }
+        }
+        break;
+#endif /* HAVE_UNICODE_WCHAR */
+        
+    case '8':
+        /* The code 't' is reserved for a better, time_t based
+        ** implementation of dateTime conversion. */
+        validateType(envP, valueP, XMLRPC_TYPE_DATETIME);
+        if (!envP->fault_occurred) {
+            char * const contents = 
+                XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
+            char ** const strptr = (char**) va_arg(*args, char**);
+            *strptr = contents;
+        }
+        break;
+
+    case '6':
+        validateType(envP, valueP, XMLRPC_TYPE_BASE64);
+        if (!envP->fault_occurred) {
+            unsigned char * const bin_data =
+                XMLRPC_MEMBLOCK_CONTENTS(unsigned char,
+                                         &valueP->_block);
+            size_t const len = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
+            unsigned char ** const binptr =
+                (unsigned char**) va_arg(*args, unsigned char**);
+            size_t * const sizeptr = (size_t*) va_arg(*args, size_t**);        
+            *binptr = bin_data;
+            *sizeptr = len;
+        }
+        break;
+
+    case 'p':
+        validateType(envP, valueP, XMLRPC_TYPE_C_PTR);
+        if (!envP->fault_occurred) {
+            void ** const voidptrptr = (void**) va_arg(*args, void**);
+            *voidptrptr = valueP->_value.c_ptr;
+        }
+        break;
+
+    case 'V': {
+        xmlrpc_value ** const valptr =
+            (xmlrpc_value**) va_arg(*args, xmlrpc_value**);
+        *valptr = valueP;
+    }
+        break;
+
+    case 'A':
+        validateType(envP, valueP, XMLRPC_TYPE_ARRAY);
+        if (!envP->fault_occurred) {
+            xmlrpc_value ** const valptr =
+                (xmlrpc_value**) va_arg(*args, xmlrpc_value**);
+            *valptr = valueP;
+        }
+        break;
+
+    case 'S':
+        validateType(envP, valueP, XMLRPC_TYPE_STRUCT);
+        if (!envP->fault_occurred) {
+            xmlrpc_value ** const valptr =
+                (xmlrpc_value**) va_arg(*args, xmlrpc_value**);
+            *valptr = valueP;
+        }
+        break;
+
+    case '(':
+        validateType(envP, valueP, XMLRPC_TYPE_ARRAY);
+        if (!envP->fault_occurred) {
+            parsearray(envP, valueP, format, ')', args);
+            (*format)++;
+        }
+        break;
+
+    case '{':
+        validateType(envP, valueP, XMLRPC_TYPE_STRUCT);
+        if (!envP->fault_occurred) {
+            parsestruct(envP, valueP, format, '}', args);
+            (*format)++;
+        }
+        break;
+
+    default:
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR, "Invalid format character '%c'",
+            formatSpecChar);
+    }
+}
+
+
+
+void 
+xmlrpc_parse_value_va(xmlrpc_env *   const envP,
+                      xmlrpc_value * const value,
+                      const char *   const format,
+                      va_list              args) {
+
+    const char *format_copy;
+    va_list args_copy;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(value);
+    XMLRPC_ASSERT(format != NULL);
+
+    format_copy = format;
+    VA_LIST_COPY(args_copy, args);
+    parsevalue(envP, value, &format_copy, &args_copy);
+    if (!envP->fault_occurred) {
+        XMLRPC_ASSERT(*format_copy == '\0');
+    }
+}
+
+
+
+void 
+xmlrpc_parse_value(xmlrpc_env *   const envP,
+                   xmlrpc_value * const value,
+                   const char *   const format, 
+                   ...) {
+
+    va_list args;
+
+    va_start(args, format);
+    xmlrpc_parse_value_va(envP, value, format, args);
+    va_end(args);
+}
+
+
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+** Copyright (C) 2001 by Eric Kidd. 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. */

+ 395 - 0
Utilities/cmxmlrpc/xmlrpc_expat.c

@@ -0,0 +1,395 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <expat.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_xmlparser.h"
+
+/* Define the contents of our internal structure. */
+struct _xml_element {
+    struct _xml_element *_parent;
+    char *_name;
+    xmlrpc_mem_block _cdata;    /* char */
+    xmlrpc_mem_block _children; /* xml_element* */
+};
+
+/* Check that we're using expat in UTF-8 mode, not wchar_t mode.
+** If you need to use expat in wchar_t mode, write a subroutine to
+** copy a wchar_t string to a char string & return an error for
+** any non-ASCII characters. Then call this subroutine on all
+** XML_Char strings passed to our event handlers before using the
+** data. */
+/* #if sizeof(char) != sizeof(XML_Char)
+** #error expat must define XML_Char to be a regular char. 
+** #endif
+*/
+
+#define XMLRPC_ASSERT_ELEM_OK(elem) \
+    XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
+
+
+/*=========================================================================
+**  xml_element_new
+**=========================================================================
+**  Create a new xml_element. This routine isn't exported, because the
+**  arguments are implementation-dependent.
+*/
+
+static xml_element *xml_element_new (xmlrpc_env *env, char *name)
+{
+    xml_element *retval;
+    int name_valid, cdata_valid, children_valid;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(name != NULL);
+
+    /* Set up our error-handling preconditions. */
+    retval = NULL;
+    name_valid = cdata_valid = children_valid = 0;
+
+    /* Allocate our xml_element structure. */
+    retval = (xml_element*) malloc(sizeof(xml_element));
+    XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for XML element");
+
+    /* Set our parent field to NULL. */
+    retval->_parent = NULL;
+    
+    /* Copy over the element name. */
+    retval->_name = (char*) malloc(strlen(name) + 1);
+    XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
+                        "Couldn't allocate memory for XML element");
+    name_valid = 1;
+    strcpy(retval->_name, name);
+
+    /* Initialize a block to hold our CDATA. */
+    XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
+    XMLRPC_FAIL_IF_FAULT(env);
+    cdata_valid = 1;
+
+    /* Initialize a block to hold our child elements. */
+    XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
+    XMLRPC_FAIL_IF_FAULT(env);
+    children_valid = 1;
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (retval) {
+            if (name_valid)
+                free(retval->_name);
+            if (cdata_valid)
+                xmlrpc_mem_block_clean(&retval->_cdata);
+            if (children_valid)
+                xmlrpc_mem_block_clean(&retval->_children);
+            free(retval);
+        }
+        return NULL;
+    } else {
+        return retval;
+    }
+}
+
+
+/*=========================================================================
+**  xml_element_free
+**=========================================================================
+**  Blow away an existing element & all of its child elements.
+*/
+
+void xml_element_free (xml_element *elem)
+{
+    xmlrpc_mem_block *children;
+    int size, i;
+    xml_element **contents;
+
+    XMLRPC_ASSERT_ELEM_OK(elem);
+
+    free(elem->_name);
+    elem->_name = XMLRPC_BAD_POINTER;
+    xmlrpc_mem_block_clean(&elem->_cdata);
+
+    /* Deallocate all of our children recursively. */
+    children = &elem->_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(&elem->_children);
+    free(elem);
+}
+
+
+/*=========================================================================
+**  Miscellaneous Accessors
+**=========================================================================
+**  Return the fields of the xml_element. See the header for more
+**  documentation on each function works.
+*/
+
+char *xml_element_name (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return elem->_name;
+}
+
+/* The result of this function is NOT VALID until the end_element handler
+** has been called! */
+size_t xml_element_cdata_size (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
+}
+
+char *xml_element_cdata (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
+}
+
+size_t xml_element_children_size (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
+}
+
+xml_element **xml_element_children (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
+}
+
+
+/*=========================================================================
+**  Internal xml_element Utility Functions
+**=========================================================================
+*/
+
+static void xml_element_append_cdata (xmlrpc_env *env,
+                                      xml_element *elem,
+                                      char *cdata,
+                                      size_t size)
+{
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_ELEM_OK(elem);    
+
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
+}
+
+/* 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. */
+static void xml_element_append_child (xmlrpc_env *env,
+                                      xml_element *elem,
+                                      xml_element *child)
+{
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    XMLRPC_ASSERT_ELEM_OK(child);
+    XMLRPC_ASSERT(child->_parent == NULL);
+
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
+                                  &child, 1);
+    if (!env->fault_occurred)
+        child->_parent = elem;
+    else
+        xml_element_free(child);
+}
+
+
+/*=========================================================================
+**  Our parse context. We pass this around as expat user data.
+**=========================================================================
+*/
+
+typedef struct {
+    xmlrpc_env *env;
+    xml_element *root;
+    xml_element *current;
+} parse_context;
+
+
+/*=========================================================================
+**  Expat Event Handler Functions
+**=========================================================================
+*/
+
+static void
+start_element (void *user_data, XML_Char *name, XML_Char **atts ATTR_UNUSED)
+{
+    parse_context *context;
+    xml_element *elem, *new_current;
+
+    XMLRPC_ASSERT(user_data != NULL && name != NULL);
+
+    /* Get our context and see if an error has already occured. */
+    context = (parse_context*) user_data;
+    if (!context->env->fault_occurred) {
+
+        /* Set up our error-handling preconditions. */
+        elem = NULL;
+
+        /* Build a new element. */
+        elem = xml_element_new(context->env, name);
+        XMLRPC_FAIL_IF_FAULT(context->env);
+
+        /* Insert it in the appropriate place. */
+        if (!context->root) {
+            context->root = elem;
+            context->current = elem;
+            elem = NULL;
+        } else {
+            XMLRPC_ASSERT(context->current != NULL);
+
+            /* (We need to watch our error handling invariants very carefully
+            ** here. Read the docs for xml_element_append_child. */
+            new_current = elem;
+            xml_element_append_child(context->env, context->current, elem);
+            elem = NULL;
+            XMLRPC_FAIL_IF_FAULT(context->env);
+            context->current = new_current;
+        }
+
+ cleanup:
+        if (elem)
+            xml_element_free(elem);
+    }
+}
+
+static void end_element (void *user_data, XML_Char *name)
+{
+    parse_context *context;
+
+    XMLRPC_ASSERT(user_data != NULL && name != NULL);
+
+    /* Get our context and see if an error has already occured. */
+    context = (parse_context*) user_data;
+    if (!context->env->fault_occurred) {
+
+        /* XXX - I think expat enforces these facts, but I want to be sure.
+        ** If one of these assertion ever fails, it should be replaced by a
+        ** non-assertion runtime error check. */
+        XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
+        XMLRPC_ASSERT(context->current->_parent != NULL ||
+                      context->current == context->root);
+
+        /* Add a trailing '\0' to our cdata. */
+        xml_element_append_cdata(context->env, context->current, "\0", 1);
+        XMLRPC_FAIL_IF_FAULT(context->env);     
+
+        /* Pop our "stack" of elements. */
+        context->current = context->current->_parent;
+
+ cleanup:
+        return;
+    }
+}
+
+static void character_data (void *user_data, XML_Char *s, int len)
+{
+    parse_context *context;
+
+    XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
+
+    /* Get our context and see if an error has already occured. */
+    context = (parse_context*) user_data;
+    if (!context->env->fault_occurred) {
+
+        XMLRPC_ASSERT(context->current != NULL);
+        
+        xml_element_append_cdata(context->env, context->current, s, len);
+        XMLRPC_FAIL_IF_FAULT(context->env);
+
+ cleanup:
+        return;
+    }
+}
+
+
+/*=========================================================================
+**  Expat Driver
+**=========================================================================
+**  XXX - We should allow the user to specify the encoding of our xml_data.
+*/
+
+xml_element *xml_parse (xmlrpc_env *env, const char *xml_data, int xml_len)
+{
+    parse_context context;
+    XML_Parser parser;
+    int ok;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(xml_data != NULL && xml_len >= 0);
+
+    /* Set up our error-handling preconditions. */
+    parser = NULL;
+    context.root = NULL;
+    
+    /* Set up the rest of our parse context. */
+    context.env     = env;
+    context.current = NULL;
+
+    /* Set up our XML parser. */
+    parser = XML_ParserCreate(NULL);
+    XMLRPC_FAIL_IF_NULL(parser, env, XMLRPC_INTERNAL_ERROR,
+                        "Could not create expat parser");
+    XML_SetUserData(parser, &context);
+    XML_SetElementHandler(parser,
+                          (XML_StartElementHandler) start_element,
+                          (XML_EndElementHandler) end_element);
+    XML_SetCharacterDataHandler(parser,
+                                (XML_CharacterDataHandler) character_data);
+
+    /* Parse our data. */
+    ok = XML_Parse(parser, xml_data, xml_len, 1);
+    if (!ok)
+        XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
+                    (char*) XML_ErrorString(XML_GetErrorCode(parser)));
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Perform some sanity checks. */
+    XMLRPC_ASSERT(context.root != NULL);
+    XMLRPC_ASSERT(context.current == NULL);
+
+ cleanup:
+    if (parser)
+        XML_ParserFree(parser);
+
+    if (env->fault_occurred) {
+        if (context.root)
+            xml_element_free(context.root);
+        return NULL;
+    } else {
+        return context.root;
+    }
+}

+ 112 - 0
Utilities/cmxmlrpc/xmlrpc_int.h

@@ -0,0 +1,112 @@
+/*============================================================================
+                         xmlrpc_client_int.h
+==============================================================================
+  This header file defines the interface between modules inside
+  xmlrpc-c.
+
+  Use this in addition to xmlrpc.h, which defines the external
+  interface.
+
+  Copyright information is at the end of the file.
+============================================================================*/
+
+
+#ifndef  _XMLRPC_INT_H_
+#define  _XMLRPC_INT_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+struct _xmlrpc_value {
+    xmlrpc_type _type;
+    int _refcount;
+
+    /* Certain data types store their data directly in the xmlrpc_value. */
+    union {
+        xmlrpc_int32 i;
+        xmlrpc_bool b;
+        double d;
+        /* time_t t */
+        void *c_ptr;
+    } _value;
+    
+    /* Other data types use a memory block. */
+    xmlrpc_mem_block _block;
+
+#ifdef HAVE_UNICODE_WCHAR
+    /* We may need to convert our string data to a wchar_t string. */
+    xmlrpc_mem_block *_wcs_block;
+#endif
+};
+
+typedef struct {
+    unsigned char key_hash;
+    xmlrpc_value *key;
+    xmlrpc_value *value;
+} _struct_member;
+
+
+struct _xmlrpc_registry {
+    int _introspection_enabled;
+    xmlrpc_value *_methods;
+    xmlrpc_value *_default_method;
+    xmlrpc_value *_preinvoke_method;
+};
+
+
+/* When we deallocate a pointer in a struct, we often replace it with
+** this and throw in a few assertions here and there. */
+#define XMLRPC_BAD_POINTER ((void*) 0xDEADBEEF)
+
+
+void
+xmlrpc_traceXml(const char * const label, 
+                const char * const xml,
+                unsigned int const xmlLength);
+
+void
+xmlrpc_destroyStruct(xmlrpc_value * const structP);
+
+void
+xmlrpc_destroyArrayContents(xmlrpc_value * const arrayP);
+
+const char * 
+xmlrpc_makePrintable(const char * const input);
+
+const char *
+xmlrpc_makePrintableChar(char const input);
+
+
+
+/* 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. */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif

+ 406 - 0
Utilities/cmxmlrpc/xmlrpc_libxml2.c

@@ -0,0 +1,406 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+** Copyright (C) 2002 Ximian, Inc.
+**
+** 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/parser.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_xmlparser.h"
+
+/* Define the contents of our internal structure. */
+struct _xml_element {
+    struct _xml_element *_parent;
+    char *_name;
+    xmlrpc_mem_block _cdata;    /* char */
+    xmlrpc_mem_block _children; /* xml_element* */
+};
+
+#define XMLRPC_ASSERT_ELEM_OK(elem) \
+    XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
+
+
+/*=========================================================================
+**  xml_element_new
+**=========================================================================
+**  Create a new xml_element. This routine isn't exported, because the
+**  arguments are implementation-dependent.
+*/
+
+static xml_element *xml_element_new (xmlrpc_env *env, char *name)
+{
+    xml_element *retval;
+    int name_valid, cdata_valid, children_valid;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(name != NULL);
+
+    /* Set up our error-handling preconditions. */
+    retval = NULL;
+    name_valid = cdata_valid = children_valid = 0;
+
+    /* Allocate our xml_element structure. */
+    retval = (xml_element*) malloc(sizeof(xml_element));
+    XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
+            "Couldn't allocate memory for XML element");
+
+    /* Set our parent field to NULL. */
+    retval->_parent = NULL;
+    
+    /* Copy over the element name. */
+    retval->_name = (char*) malloc(strlen(name) + 1);
+    XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
+            "Couldn't allocate memory for XML element");
+    name_valid = 1;
+    strcpy(retval->_name, name);
+
+    /* Initialize a block to hold our CDATA. */
+    XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
+    XMLRPC_FAIL_IF_FAULT(env);
+    cdata_valid = 1;
+
+    /* Initialize a block to hold our child elements. */
+    XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
+    XMLRPC_FAIL_IF_FAULT(env);
+    children_valid = 1;
+
+ cleanup:
+    if (env->fault_occurred) {
+    if (retval) {
+        if (name_valid)
+        free(retval->_name);
+        if (cdata_valid)
+        xmlrpc_mem_block_clean(&retval->_cdata);
+        if (children_valid)
+        xmlrpc_mem_block_clean(&retval->_children);
+        free(retval);
+    }
+    return NULL;
+    } else {
+    return retval;
+    }
+}
+
+
+/*=========================================================================
+**  xml_element_free
+**=========================================================================
+**  Blow away an existing element & all of its child elements.
+*/
+
+void xml_element_free (xml_element *elem)
+{
+    xmlrpc_mem_block *children;
+    int size, i;
+    xml_element **contents;
+
+    XMLRPC_ASSERT_ELEM_OK(elem);
+
+    free(elem->_name);
+    elem->_name = XMLRPC_BAD_POINTER;
+    xmlrpc_mem_block_clean(&elem->_cdata);
+
+    /* Deallocate all of our children recursively. */
+    children = &elem->_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(&elem->_children);
+    free(elem);
+}
+
+
+/*=========================================================================
+**  Miscellaneous Accessors
+**=========================================================================
+**  Return the fields of the xml_element. See the header for more
+**  documentation on each function works.
+*/
+
+char *xml_element_name (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return elem->_name;
+}
+
+/* The result of this function is NOT VALID until the end_element handler
+** has been called! */
+size_t xml_element_cdata_size (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
+}
+
+char *xml_element_cdata (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
+}
+
+size_t xml_element_children_size (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
+}
+
+xml_element **xml_element_children (xml_element *elem)
+{
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
+}
+
+
+/*=========================================================================
+**  Internal xml_element Utility Functions
+**=========================================================================
+*/
+
+static void xml_element_append_cdata (xmlrpc_env *env,
+                      xml_element *elem,
+                      char *cdata,
+                      size_t size)
+{
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_ELEM_OK(elem);    
+
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
+}
+
+/* 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. */
+static void xml_element_append_child (xmlrpc_env *env,
+                      xml_element *elem,
+                      xml_element *child)
+{
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_ELEM_OK(elem);
+    XMLRPC_ASSERT_ELEM_OK(child);
+    XMLRPC_ASSERT(child->_parent == NULL);
+
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
+                                  &child, 1);
+    if (!env->fault_occurred)
+    child->_parent = elem;
+    else
+        xml_element_free(child);
+}
+
+
+/*=========================================================================
+**  Our parse context. We pass this around as libxml user data.
+**=========================================================================
+*/
+
+typedef struct {
+    xmlrpc_env *env;
+    xml_element *root;
+    xml_element *current;
+} parse_context;
+
+
+/*=========================================================================
+**  LibXML Event Handler Functions
+**=========================================================================
+*/
+
+static void
+start_element (void *user_data, const xmlChar *name, const xmlChar **attrs)
+{
+    parse_context *context;
+    xml_element *elem, *new_current;
+
+    XMLRPC_ASSERT(user_data != NULL && name != NULL);
+
+    /* Get our context and see if an error has already occured. */
+    context = (parse_context*) user_data;
+    if (!context->env->fault_occurred) {
+
+    /* Set up our error-handling preconditions. */
+    elem = NULL;
+
+    /* Build a new element. */
+    elem = xml_element_new(context->env, (char *) name);
+    XMLRPC_FAIL_IF_FAULT(context->env);
+
+    /* Insert it in the appropriate place. */
+    if (!context->root) {
+        context->root = elem;
+        context->current = elem;
+        elem = NULL;
+    } else {
+        XMLRPC_ASSERT(context->current != NULL);
+
+        /* (We need to watch our error handling invariants very carefully
+        ** here. Read the docs for xml_element_append_child. */
+        new_current = elem;
+        xml_element_append_child(context->env, context->current, elem);
+        elem = NULL;
+        XMLRPC_FAIL_IF_FAULT(context->env);
+        context->current = new_current;
+    }
+
+ cleanup:
+    if (elem)
+        xml_element_free(elem);
+    }
+}
+
+static void
+end_element (void *user_data, const xmlChar *name)
+{
+    parse_context *context;
+
+    XMLRPC_ASSERT(user_data != NULL && name != NULL);
+
+    /* Get our context and see if an error has already occured. */
+    context = (parse_context*) user_data;
+    if (!context->env->fault_occurred) {
+
+    /* XXX - I think expat enforces these facts, but I want to be sure.
+    ** If one of these assertion ever fails, it should be replaced by a
+    ** non-assertion runtime error check. */
+    XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
+    XMLRPC_ASSERT(context->current->_parent != NULL ||
+              context->current == context->root);
+
+    /* Add a trailing '\0' to our cdata. */
+    xml_element_append_cdata(context->env, context->current, "\0", 1);
+    XMLRPC_FAIL_IF_FAULT(context->env); 
+
+    /* Pop our "stack" of elements. */
+    context->current = context->current->_parent;
+
+ cleanup:
+    return;
+    }
+}
+
+static void character_data (void *user_data, const xmlChar *s, int len)
+{
+    parse_context *context;
+
+    XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
+
+    /* Get our context and see if an error has already occured. */
+    context = (parse_context*) user_data;
+    if (!context->env->fault_occurred) {
+
+    XMLRPC_ASSERT(context->current != NULL);
+    
+    xml_element_append_cdata(context->env, context->current, (char *) s, len);
+    XMLRPC_FAIL_IF_FAULT(context->env);
+
+ cleanup:
+    return;
+    }
+}
+
+
+/*=========================================================================
+**  LibXML Driver
+**=========================================================================
+**  XXX - We should allow the user to specify the encoding of our xml_data.
+*/
+
+static xmlSAXHandler sax_handler = {
+    NULL,      /* internalSubset */
+    NULL,      /* isStandalone */
+    NULL,      /* hasInternalSubset */
+    NULL,      /* hasExternalSubset */
+    NULL,      /* resolveEntity */
+    NULL,      /* getEntity */
+    NULL,      /* entityDecl */
+    NULL,      /* notationDecl */
+    NULL,      /* attributeDecl */
+    NULL,      /* elementDecl */
+    NULL,      /* unparsedEntityDecl */
+    NULL,      /* setDocumentLocator */
+    NULL,      /* startDocument */
+    NULL,      /* endDocument */
+    start_element,       /* startElement */
+    end_element,         /* endElement */
+    NULL,      /* reference */
+    character_data,      /* characters */
+    NULL,      /* ignorableWhitespace */
+    NULL,      /* processingInstruction */
+    NULL,      /* comment */
+    NULL,      /* warning */
+    NULL,      /* error */
+    NULL,      /* fatalError */
+};
+
+xml_element *xml_parse (xmlrpc_env *env, char *xml_data, int xml_len)
+{
+    parse_context context;
+    xmlParserCtxt *parser;
+    int err;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(xml_data != NULL && xml_len >= 0);
+
+    /* Set up our error-handling preconditions. */
+    parser = NULL;
+    context.root = NULL;
+    
+    /* Set up the rest of our parse context. */
+    context.env     = env;
+    context.current = NULL;
+
+    /* Set up our XML parser. */
+    parser = xmlCreatePushParserCtxt(&sax_handler, &context, NULL, 0, NULL);
+    XMLRPC_FAIL_IF_NULL(parser, env, XMLRPC_INTERNAL_ERROR,
+            "Could not create expat parser");
+
+    /* Parse our data. */
+    err = xmlParseChunk(parser, xml_data, xml_len, 1);
+    if (err)
+        XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR, "XML parsing failed");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Perform some sanity checks. */
+    XMLRPC_ASSERT(context.root != NULL);
+    XMLRPC_ASSERT(context.current == NULL);
+
+ cleanup:
+    if (parser)
+        xmlFreeParserCtxt(parser);
+
+    if (env->fault_occurred) {
+        if (context.root)
+            xml_element_free(context.root);
+        return NULL;
+    } else {
+        return context.root;
+    }
+}

+ 767 - 0
Utilities/cmxmlrpc/xmlrpc_parse.c

@@ -0,0 +1,767 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_xmlparser.h"
+
+
+/*=========================================================================
+**  Data Format
+**=========================================================================
+**  All XML-RPC documents contain a single methodCall or methodResponse
+**  element.
+**
+**  methodCall     methodName, params
+**  methodResponse (params|fault)
+**  params         param*
+**  param          value
+**  fault          value
+**  value          (i4|int|boolean|string|double|dateTime.iso8601|base64|
+**                  struct|array)
+**  array          data
+**  data           value*
+**  struct         member*
+**  member         name, value
+**
+**  Contain CDATA: methodName, i4, int, boolean, string, double,
+**                 dateTime.iso8601, base64, name
+**
+**  We attempt to validate the structure of the XML document carefully.
+**  We also try *very* hard to handle malicious data gracefully, and without
+**  leaking memory.
+**
+**  The CHECK_NAME and CHECK_CHILD_COUNT macros examine an XML element, and
+**  invoke XMLRPC_FAIL if something looks wrong.
+*/
+
+#define CHECK_NAME(env,elem,name) \
+    do \
+        if (strcmp((name), xml_element_name(elem)) != 0) \
+            XMLRPC_FAIL2(env, XMLRPC_PARSE_ERROR, \
+             "Expected element of type <%s>, found <%s>", \
+                         (name), xml_element_name(elem)); \
+    while (0)
+
+#define CHECK_CHILD_COUNT(env,elem,count) \
+    do \
+        if (xml_element_children_size(elem) != (count)) \
+            XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR, \
+             "Expected <%s> to have %d children, found %d", \
+                         xml_element_name(elem), (count), \
+                         xml_element_children_size(elem)); \
+    while (0)
+
+static xml_element *
+get_child_by_name (xmlrpc_env *env, xml_element *parent, char *name)
+{
+    size_t child_count, i;
+    xml_element **children;
+
+    children = xml_element_children(parent);
+    child_count = xml_element_children_size(parent);
+    for (i = 0; i < child_count; i++) {
+        if (0 == strcmp(xml_element_name(children[i]), name))
+            return children[i];
+    }
+    
+    xmlrpc_env_set_fault_formatted(env, XMLRPC_PARSE_ERROR,
+                                   "Expected <%s> to have child <%s>",
+                                   xml_element_name(parent), name);
+    return NULL;
+}
+
+
+/*=========================================================================
+**  Number-Parsing Functions
+**=========================================================================
+**  These functions mirror atoi, atof, etc., but provide better
+**  error-handling.  These routines may reset errno to zero.
+*/
+
+static xmlrpc_int32
+xmlrpc_atoi(xmlrpc_env *env, char *str, size_t strlen,
+            xmlrpc_int32 min, xmlrpc_int32 max)
+{
+    long i;
+    char *end;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(str);
+
+    /* Suppress compiler warnings. */
+    i = 0;
+
+    /* Check for leading white space. */
+    if (isspace(str[0]))
+    XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+                 "\"%s\" must not contain whitespace", str);
+
+    /* Convert the value. */
+    end = str + strlen;
+    errno = 0;
+    i = strtol(str, &end, 10);
+
+    /* Look for ERANGE. */
+    if (errno != 0)
+        /* XXX - Do all operating systems have thread-safe strerror? */
+        XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
+                     "error parsing \"%s\": %s (%d)",
+                     str, strerror(errno), errno);
+    
+    /* Look for out-of-range errors which didn't produce ERANGE. */
+    if (i < min || i > max)
+        XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
+                     "\"%s\" must be in range %d to %d", str, min, max);
+
+    /* Check for unused characters. */
+    if (end != str + strlen)
+        XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+                     "\"%s\" contained trailing data", str);
+    
+ cleanup:
+    errno = 0;
+    if (env->fault_occurred)
+        return 0;
+    return (xmlrpc_int32) i;
+}
+
+
+
+static double
+xmlrpc_atod(xmlrpc_env *env, char *str, size_t strlen)
+{
+    double d;
+    char *end;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(str);
+
+    /* Suppress compiler warnings. */
+    d = 0.0;
+
+    /* Check for leading white space. */
+    if (isspace(str[0]))
+        XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+                     "\"%s\" must not contain whitespace", str);
+    
+    /* Convert the value. */
+    end = str + strlen;
+    errno = 0;
+    d = strtod(str, &end);
+    
+    /* Look for ERANGE. */
+    if (errno != 0)
+        /* XXX - Do all operating systems have thread-safe strerror? */
+        XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
+                     "error parsing \"%s\": %s (%d)",
+                     str, strerror(errno), errno);
+    
+    /* Check for unused characters. */
+    if (end != str + strlen)
+        XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+                     "\"%s\" contained trailing data", str);
+
+ cleanup:
+    errno = 0;
+    if (env->fault_occurred)
+        return 0.0;
+    return d;
+}
+
+
+/*=========================================================================
+**  make_string
+**=========================================================================
+**  Make an XML-RPC string.
+**
+** SECURITY: We validate our UTF-8 first.  This incurs a performance
+** penalty, but ensures that we will never pass maliciously malformed
+** UTF-8 data back up to the user layer, where it could wreak untold
+** damange. Don't comment out this check unless you know *exactly* what
+** you're doing.  (Win32 developers who remove this check are *begging*
+** to wind up on BugTraq, because many of the Win32 filesystem routines
+** rely on an insecure UTF-8 decoder.)
+**
+** XXX - This validation is redundant if the user chooses to convert
+** UTF-8 data into a wchar_t string.
+*/
+
+static xmlrpc_value *
+make_string(xmlrpc_env *env, char *cdata, size_t cdata_size)
+{
+#ifdef HAVE_UNICODE_WCHAR
+    xmlrpc_validate_utf8(env, cdata, cdata_size);
+#endif
+
+    if (env->fault_occurred)
+        return NULL;
+    return xmlrpc_build_value(env, "s#", cdata, cdata_size);
+}
+
+
+
+/*=========================================================================
+**  convert_value
+**=========================================================================
+**  Convert an XML element representing a value into an xmlrpc_value.
+*/
+
+static xmlrpc_value *
+convert_array (xmlrpc_env *env, unsigned *depth, xml_element *elem);
+static xmlrpc_value *
+convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem);
+
+
+
+static xmlrpc_value *
+convert_value(xmlrpc_env *env, unsigned *depth, xml_element *elem)
+{
+    xml_element *child;
+    int child_count;
+    char *cdata, *child_name;
+    size_t cdata_size, ascii_len;
+    xmlrpc_mem_block *decoded;
+    unsigned char *ascii_data;
+    xmlrpc_value *retval;
+    xmlrpc_int32 i;
+    double d;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(elem != NULL);
+
+    /* Error-handling precoditions.
+    ** If we haven't changed any of these from their default state, we're
+    ** allowed to tail-call xmlrpc_build_value. */
+    retval = NULL;
+    decoded = NULL;
+
+    /* Make sure we haven't recursed too deeply. */
+    if (*depth > xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID))
+    XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
+                "Nested data structure too deep.");
+
+    /* Validate our structure, and see whether we have a child element. */
+    CHECK_NAME(env, elem, "value");
+    child_count = xml_element_children_size(elem);
+
+    if (child_count == 0) {
+        /* We have no type element, so treat the value as a string. */
+        cdata = xml_element_cdata(elem);
+        cdata_size = xml_element_cdata_size(elem);
+        return make_string(env, cdata, cdata_size);
+    } else {
+        /* We should have a type tag inside our value tag. */
+        CHECK_CHILD_COUNT(env, elem, 1);
+        child = xml_element_children(elem)[0];
+        
+        /* Parse our value-containing element. */
+    child_name = xml_element_name(child);
+    if (strcmp(child_name, "struct") == 0) {
+        return convert_struct(env, depth, child);
+    } else if (strcmp(child_name, "array") == 0) {
+        CHECK_CHILD_COUNT(env, child, 1);
+        return convert_array(env, depth, child);
+    } else {
+        CHECK_CHILD_COUNT(env, child, 0);
+        cdata = xml_element_cdata(child);
+        cdata_size = xml_element_cdata_size(child);
+        if (strcmp(child_name, "i4") == 0 ||
+            strcmp(child_name, "int") == 0)
+        {
+            i = xmlrpc_atoi(env, cdata, strlen(cdata),
+                            XMLRPC_INT32_MIN, XMLRPC_INT32_MAX);
+            XMLRPC_FAIL_IF_FAULT(env);
+            return xmlrpc_build_value(env, "i", i);
+        } else if (strcmp(child_name, "string") == 0) {
+            return make_string(env, cdata, cdata_size);
+        } else if (strcmp(child_name, "boolean") == 0) {
+            i = xmlrpc_atoi(env, cdata, strlen(cdata), 0, 1);
+            XMLRPC_FAIL_IF_FAULT(env);
+            return xmlrpc_build_value(env, "b", (xmlrpc_bool) i);
+        } else if (strcmp(child_name, "double") == 0) {
+            d = xmlrpc_atod(env, cdata, strlen(cdata));
+            XMLRPC_FAIL_IF_FAULT(env);
+            return xmlrpc_build_value(env, "d", d);
+        } else if (strcmp(child_name, "dateTime.iso8601") == 0) {
+            return xmlrpc_build_value(env, "8", cdata);
+        } else if (strcmp(child_name, "base64") == 0) {
+            /* No more tail calls once we do this! */
+            decoded = xmlrpc_base64_decode(env, cdata, cdata_size);
+            XMLRPC_FAIL_IF_FAULT(env);
+            ascii_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
+                                                         decoded);
+            ascii_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char,
+                                                    decoded);
+            retval = xmlrpc_build_value(env, "6", ascii_data, ascii_len);
+            XMLRPC_FAIL_IF_FAULT(env);      
+        } else {
+            XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
+                         "Unknown value type <%s>", child_name);
+        }
+    }
+    }
+
+ cleanup:
+    if (decoded)
+    xmlrpc_mem_block_free(decoded);
+    if (env->fault_occurred) {
+        if (retval)
+            xmlrpc_DECREF(retval);
+        return NULL;
+    }
+    return retval;
+}
+
+
+
+/*=========================================================================
+**  convert_array
+**=========================================================================
+**  Convert an XML element representing an array into an xmlrpc_value.
+*/
+
+static xmlrpc_value *
+convert_array(xmlrpc_env *env, unsigned *depth, xml_element *elem)
+{
+    xml_element *data, **values, *value;
+    xmlrpc_value *array, *item;
+    int size, i;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(elem != NULL);
+
+    /* Set up our error-handling preconditions. */
+    array = item = NULL;
+    (*depth)++;
+
+    /* Allocate an array to hold our values. */
+    array = xmlrpc_build_value(env, "()");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* We don't need to check our element name--our callers do that. */
+    CHECK_CHILD_COUNT(env, elem, 1);
+    data = xml_element_children(elem)[0];
+    CHECK_NAME(env, data, "data");
+    
+    /* Iterate over our children. */
+    values = xml_element_children(data);
+    size = xml_element_children_size(data);
+    for (i = 0; i < size; i++) {
+        value = values[i];
+        item = convert_value(env, depth, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        xmlrpc_array_append_item(env, array, item);
+        xmlrpc_DECREF(item);
+        item = NULL;
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+ cleanup:
+    (*depth)--;
+    if (item)
+        xmlrpc_DECREF(item);
+    if (env->fault_occurred) {
+        if (array)
+            xmlrpc_DECREF(array);
+        return NULL;
+    }
+    return array;
+}
+
+
+
+/*=========================================================================
+**  convert_struct
+**=========================================================================
+**  Convert an XML element representing a struct into an xmlrpc_value.
+*/
+
+static xmlrpc_value *
+convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem)
+{
+    xmlrpc_value *strct, *key, *value;
+    xml_element **members, *member, *name_elem, *value_elem;
+    int size, i;
+    char *cdata;
+    size_t cdata_size;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(elem != NULL);
+
+    /* Set up our error-handling preconditions. */
+    strct = key = value = NULL;
+    (*depth)++;
+
+    /* Allocate an array to hold our members. */
+    strct = xmlrpc_struct_new(env);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Iterate over our children, extracting key/value pairs. */
+    /* We don't need to check our element name--our callers do that. */
+    members = xml_element_children(elem);
+    size = xml_element_children_size(elem);
+    for (i = 0; i < size; i++) {
+        member = members[i];
+        CHECK_NAME(env, member, "member");
+        CHECK_CHILD_COUNT(env, member, 2);
+
+        /* Get our key. */
+        name_elem = get_child_by_name(env, member, "name");
+        XMLRPC_FAIL_IF_FAULT(env);
+        CHECK_CHILD_COUNT(env, name_elem, 0);
+        cdata = xml_element_cdata(name_elem);
+        cdata_size = xml_element_cdata_size(name_elem);
+        key = make_string(env, cdata, cdata_size);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        /* Get our value. */
+        value_elem = get_child_by_name(env, member, "value");
+        XMLRPC_FAIL_IF_FAULT(env);
+        value = convert_value(env, depth, value_elem);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        /* Add the key/value pair to our struct. */
+        xmlrpc_struct_set_value_v(env, strct, key, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        /* Release our references & memory, and restore our invariants. */
+        xmlrpc_DECREF(key);
+        key = NULL;
+        xmlrpc_DECREF(value);
+        value = NULL;
+    }
+    
+ cleanup:
+    (*depth)--;
+    if (key)
+        xmlrpc_DECREF(key);
+    if (value)
+        xmlrpc_DECREF(value);
+    if (env->fault_occurred) {
+        if (strct)
+            xmlrpc_DECREF(strct);
+        return NULL;
+    }
+    return strct;
+}
+
+
+
+/*=========================================================================
+**  convert_params
+**=========================================================================
+**  Convert an XML element representing a list of params into an
+**  xmlrpc_value (of type array).
+*/
+
+static xmlrpc_value *
+convert_params(xmlrpc_env *env, unsigned *depth, xml_element *elem)
+{
+    xmlrpc_value *array, *item;
+    int size, i;
+    xml_element **params, *param, *value;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(elem != NULL);
+
+    /* Set up our error-handling preconditions. */
+    array = item = NULL;
+
+    /* Allocate an array to hold our parameters. */
+    array = xmlrpc_build_value(env, "()");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* We're responsible for checking our own element name. */
+    CHECK_NAME(env, elem, "params");    
+
+    /* Iterate over our children. */
+    size = xml_element_children_size(elem);
+    params = xml_element_children(elem);
+    for (i = 0; i < size; i++) {
+        param = params[i];
+        CHECK_NAME(env, param, "param");
+        CHECK_CHILD_COUNT(env, param, 1);
+
+        value = xml_element_children(param)[0];
+        item = convert_value(env, depth, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        xmlrpc_array_append_item(env, array, item);
+        xmlrpc_DECREF(item);
+        item = NULL;
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (array)
+            xmlrpc_DECREF(array);
+        if (item)
+            xmlrpc_DECREF(item);
+        return NULL;
+    }
+    return array;
+}
+
+
+
+static void
+parseCallXml(xmlrpc_env *   const envP,
+             const char *   const xmlData,
+             size_t         const xmlLen,
+             xml_element ** const callElemP) {
+
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+    *callElemP = xml_parse(&env, xmlData, xmlLen);
+    if (env.fault_occurred)
+        xmlrpc_env_set_fault_formatted(
+            envP, env.fault_code, "Call is not valid XML.  %s",
+            env.fault_string);
+    xmlrpc_env_clean(&env);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_parse_call
+**=========================================================================
+**  Given some XML text, attempt to parse it as an XML-RPC call. Return
+**  a newly allocated xmlrpc_call structure (or NULL, if an error occurs).
+**  The two output variables will contain either valid values (which
+**  must free() and xmlrpc_DECREF(), respectively) or NULLs (if an error
+**  occurs).
+*/
+
+void 
+xmlrpc_parse_call(xmlrpc_env *    const envP,
+                  const char *    const xml_data,
+                  size_t          const xml_len,
+                  const char **   const out_method_nameP,
+                  xmlrpc_value ** const out_param_arrayPP) {
+
+    xml_element *call_elem, *name_elem, *params_elem;
+    char *cdata;
+    unsigned depth;
+    size_t call_child_count;
+    char * outMethodName;
+    xmlrpc_value * outParamArrayP;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT(xml_data != NULL);
+    XMLRPC_ASSERT(out_method_nameP != NULL && out_param_arrayPP != NULL);
+
+    /* Set up our error-handling preconditions. */
+    outMethodName = NULL;
+    outParamArrayP = NULL;
+    call_elem = 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 in RAM, so you should try to enforce it
+    ** *before* reading any data off the network. */
+    if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
+        XMLRPC_FAIL(envP, XMLRPC_LIMIT_EXCEEDED_ERROR,
+                    "XML-RPC request too large");
+
+    parseCallXml(envP, xml_data, xml_len, &call_elem);
+    XMLRPC_FAIL_IF_FAULT(envP);
+
+    /* Pick apart and verify our structure. */
+    CHECK_NAME(envP, call_elem, "methodCall");
+    call_child_count = xml_element_children_size(call_elem);
+    if (call_child_count != 2 && call_child_count != 1)
+        XMLRPC_FAIL1(envP, XMLRPC_PARSE_ERROR,
+                     "Expected <methodCall> to have 1 or 2 children, found %d",
+                     call_child_count);
+
+    /* Extract the method name.
+    ** SECURITY: We make sure the method name is valid UTF-8. */
+    name_elem = get_child_by_name(envP, call_elem, "methodName");
+    XMLRPC_FAIL_IF_FAULT(envP);
+    CHECK_CHILD_COUNT(envP, name_elem, 0);
+    cdata = xml_element_cdata(name_elem);
+#ifdef HAVE_UNICODE_WCHAR
+    xmlrpc_validate_utf8(envP, cdata, strlen(cdata));
+    XMLRPC_FAIL_IF_FAULT(envP);
+#endif /* HAVE_UNICODE_WCHAR */
+    outMethodName = malloc(strlen(cdata) + 1);
+    XMLRPC_FAIL_IF_NULL(outMethodName, envP, XMLRPC_INTERNAL_ERROR,
+                        "Could not allocate memory for method name");
+    strcpy(outMethodName, cdata);
+    
+    /* Convert our parameters. */
+    if (call_child_count == 1) {
+        /* Workaround for Ruby XML-RPC and old versions of xmlrpc-epi. */
+        outParamArrayP = xmlrpc_build_value(envP, "()");
+        XMLRPC_FAIL_IF_FAULT(envP);
+    } else {
+        params_elem = get_child_by_name(envP, call_elem, "params");
+        XMLRPC_FAIL_IF_FAULT(envP);
+        depth = 0;
+        outParamArrayP = convert_params(envP, &depth, params_elem);
+        XMLRPC_ASSERT(depth == 0);
+        XMLRPC_FAIL_IF_FAULT(envP);
+    }
+
+cleanup:
+    if (call_elem)
+        xml_element_free(call_elem);
+    if (envP->fault_occurred) {
+        if (outMethodName)
+            free(outMethodName);
+        if (outParamArrayP)
+            xmlrpc_DECREF(outParamArrayP);
+        outMethodName  = NULL;
+        outParamArrayP = NULL;
+    }
+    *out_method_nameP  = outMethodName;
+    *out_param_arrayPP = outParamArrayP;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_parse_response
+**=========================================================================
+**  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. If the response is a fault, or an error
+**  occurs during processing, return NULL and set up env appropriately.
+*/
+
+xmlrpc_value *
+xmlrpc_parse_response(xmlrpc_env *env,
+                      const char *xml_data,
+                      size_t xml_len) {
+
+    xml_element *response, *child, *value;
+    unsigned depth;
+    xmlrpc_value *params, *retval, *fault;
+    int retval_incremented;
+
+    xmlrpc_value *fault_code_value, *fault_str_value;
+    xmlrpc_int32 fault_code;
+    char *fault_str;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(xml_data != NULL);
+
+    /* Set up our error-handling preconditions. */
+    response = NULL;
+    params = fault = NULL;
+    retval_incremented = 0;
+
+    /* 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 in RAM, so you should try to enforce it
+    ** *before* reading any data off the network. */
+    if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
+        XMLRPC_FAIL(env, XMLRPC_LIMIT_EXCEEDED_ERROR,
+                    "XML-RPC response too large");
+
+    /* SECURITY: Set up our recursion depth counter. */
+    depth = 0;
+
+    /* Parse our XML data. */
+    response = xml_parse(env, xml_data, xml_len);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Pick apart and verify our structure. */
+    CHECK_NAME(env, response, "methodResponse");
+    CHECK_CHILD_COUNT(env, response, 1);
+    child = xml_element_children(response)[0];
+
+    /* Parse the response itself. */
+    if (strcmp("params", xml_element_name(child)) == 0) {
+
+        /* Convert our parameter list. */
+        params = convert_params(env, &depth, child);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        /* Extract the return value, and jiggle our reference counts. */
+        xmlrpc_parse_value(env, params, "(V)", &retval);
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_INCREF(retval);
+        retval_incremented = 1;
+
+    } else if (strcmp("fault", xml_element_name(child)) == 0) {
+    
+        /* Convert our fault structure. */
+        CHECK_CHILD_COUNT(env, child, 1);
+        value = xml_element_children(child)[0];
+        fault = convert_value(env, &depth, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+        XMLRPC_TYPE_CHECK(env, fault, XMLRPC_TYPE_STRUCT);
+
+        /* Get our fault code. */
+        fault_code_value = xmlrpc_struct_get_value(env, fault, "faultCode");
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_parse_value(env, fault_code_value, "i", &fault_code);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Get our fault string. */
+        fault_str_value = xmlrpc_struct_get_value(env, fault, "faultString");
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_parse_value(env, fault_str_value, "s", &fault_str);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Return our fault. */
+        XMLRPC_FAIL(env, fault_code, fault_str);
+
+    } else {
+        XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
+                    "Expected <params> or <fault> in <methodResponse>");
+    }
+
+    /* Sanity-check our depth-counting code. */
+    XMLRPC_ASSERT(depth == 0);
+    
+cleanup:
+    if (response)
+        xml_element_free(response);
+    if (params)
+        xmlrpc_DECREF(params);
+    if (fault)
+        xmlrpc_DECREF(fault);
+
+    if (env->fault_occurred) {
+        if (retval_incremented)
+            xmlrpc_DECREF(retval);
+        return NULL;
+    }
+    return retval;
+}

+ 66 - 0
Utilities/cmxmlrpc/xmlrpc_pthreads.h

@@ -0,0 +1,66 @@
+/* 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. */
+
+#ifndef WIN32
+#       define _REENTRANT
+#       include <pthread.h>
+#elif defined (WIN32)
+
+typedef HANDLE pthread_t;
+typedef CRITICAL_SECTION pthread_mutex_t;
+
+#define PTHREAD_MUTEX_INITIALIZER NULL
+//usage: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+typedef
+struct
+{
+        int attrs; //currently unused. placeholder.
+} pthread_attr_t;
+
+typedef
+struct
+{
+        int attrs; //currently unused. placeholder.
+} pthread_mutexattr_t;
+
+//typedef void * (*pthread_func)(void *);
+typedef unsigned ( __stdcall *pthread_func )( void * );
+
+extern int pthread_create(pthread_t *new_thread_ID,
+          const pthread_attr_t *attr,
+          pthread_func start_func, void *arg);
+extern int pthread_cancel(pthread_t target_thread);
+extern int pthread_join(pthread_t target_thread, void **status);
+extern int pthread_detach(pthread_t target_thread);
+
+extern int pthread_mutex_init(pthread_mutex_t *mp,
+                                        const pthread_mutexattr_t *attr);
+extern int pthread_mutex_lock(pthread_mutex_t *mp);
+extern int pthread_mutex_unlock(pthread_mutex_t *mp);
+extern int pthread_mutex_destroy(pthread_mutex_t *mp);
+
+#endif
+

+ 829 - 0
Utilities/cmxmlrpc/xmlrpc_registry.c

@@ -0,0 +1,829 @@
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+** Copyright (C) 2001 by Eric Kidd. All rights reserved.
+** Copyright (C) 2001 by Luke Howard. 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_server.h"
+#include "xmlrpc_int.h"
+
+/*=========================================================================
+**  XML-RPC Server Method Registry
+**=========================================================================
+**  A method registry maintains a list of functions, and handles
+**  dispatching. To build an XML-RPC server, just add a communications
+**  protocol. :-)
+*/
+
+static void
+install_system_methods (xmlrpc_env *env, xmlrpc_registry *registry);
+
+xmlrpc_registry *
+xmlrpc_registry_new(xmlrpc_env *env) {
+
+    xmlrpc_value *methods;
+    xmlrpc_registry *registry;
+    int registry_valid;
+    
+    XMLRPC_ASSERT_ENV_OK(env);
+    
+    /* Error-handling preconditions. */
+    methods = NULL;
+    registry = NULL;
+    registry_valid = 0;
+
+    /* Allocate our memory. */
+    methods = xmlrpc_struct_new(env);
+    XMLRPC_FAIL_IF_FAULT(env);
+    registry = (xmlrpc_registry*) malloc(sizeof(xmlrpc_registry));
+    XMLRPC_FAIL_IF_NULL(registry, env, XMLRPC_INTERNAL_ERROR,
+                        "Could not allocate memory for registry");
+    
+    /* Set everything up. */
+    registry->_introspection_enabled = 1;
+    registry->_methods = methods;
+    registry->_default_method = NULL;
+    registry->_preinvoke_method = NULL;
+    registry_valid = 1;
+
+    /* Install our system methods. */
+    install_system_methods(env, registry);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (registry_valid) {
+            xmlrpc_registry_free(registry);
+        } else {
+            if (methods)
+                xmlrpc_DECREF(methods);
+            if (registry)
+                free(registry);
+        }
+        return NULL;
+    }
+    return registry;
+}
+
+
+
+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);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_registry_disable_introspection
+**=========================================================================
+**  See xmlrpc.h for more documentation.
+*/
+
+void 
+xmlrpc_registry_disable_introspection(xmlrpc_registry * registry) {
+    XMLRPC_ASSERT_PTR_OK(registry);
+    registry->_introspection_enabled = 0;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_registry_add_method
+**=========================================================================
+**  See xmlrpc.h for more documentation.
+*/
+
+void 
+xmlrpc_registry_add_method(xmlrpc_env *env,
+                           xmlrpc_registry *registry,
+                           const char *host,
+                           const char *method_name,
+                           xmlrpc_method method,
+                           void *user_data) {
+
+    xmlrpc_registry_add_method_w_doc (env, registry, host, method_name,
+                      method, user_data, "?",
+                      "No help is available for this method.");
+}
+
+
+
+void 
+xmlrpc_registry_add_method_w_doc(xmlrpc_env *env,
+                                 xmlrpc_registry *registry,
+                                 const char *host,
+                                 const char *method_name,
+                                 xmlrpc_method method,
+                                 void *user_data,
+                                 const char *signature,
+                                 const char *help) {
+    xmlrpc_value *method_info;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(registry);
+    XMLRPC_ASSERT(host == NULL);
+    XMLRPC_ASSERT_PTR_OK(method_name);
+    XMLRPC_ASSERT_PTR_OK(method);
+
+    /* Error-handling preconditions. */
+    method_info = NULL;
+
+    /* Store our method and user data into our hash table. */
+    method_info = xmlrpc_build_value(env, "(ppss)", (void*) method, user_data,
+                                     signature, help);
+    XMLRPC_FAIL_IF_FAULT(env);
+    xmlrpc_struct_set_value(env, registry->_methods, method_name, method_info);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (method_info)
+    xmlrpc_DECREF(method_info);
+
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_registry_set_default_method
+**=========================================================================
+**  See xmlrpc.h for more documentation.
+*/
+
+void 
+xmlrpc_registry_set_default_method(xmlrpc_env *env,
+                                   xmlrpc_registry *registry,
+                                   xmlrpc_default_method handler,
+                                   void *user_data) {
+    xmlrpc_value *method_info;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(registry);
+    XMLRPC_ASSERT_PTR_OK(handler);
+
+    /* Error-handling preconditions. */
+    method_info = NULL;
+    
+    /* Store our method and user data into our hash table. */
+    method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Dispose of any pre-existing default method and install ours. */
+    if (registry->_default_method)
+        xmlrpc_DECREF(registry->_default_method);
+    registry->_default_method = method_info;
+    
+cleanup:
+    if (env->fault_occurred) {
+        if (method_info)
+            xmlrpc_DECREF(method_info);
+    }
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_registry_set_preinvoke_method
+**=========================================================================
+**  See xmlrpc.h for more documentation.
+*/
+
+void 
+xmlrpc_registry_set_preinvoke_method(xmlrpc_env *env,
+                                     xmlrpc_registry *registry,
+                                     xmlrpc_preinvoke_method handler,
+                                     void *user_data) {
+    xmlrpc_value *method_info;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(registry);
+    XMLRPC_ASSERT_PTR_OK(handler);
+
+    /* Error-handling preconditions. */
+    method_info = NULL;
+
+    /* Store our method and user data into our hash table. */
+    method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Dispose of any pre-existing preinvoke method and install ours. */
+    if (registry->_preinvoke_method)
+        xmlrpc_DECREF(registry->_preinvoke_method);
+    registry->_preinvoke_method = method_info;
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (method_info)
+            xmlrpc_DECREF(method_info);
+    }
+}
+
+
+
+/*=========================================================================
+**  dispatch_call
+**=========================================================================
+**  An internal method which actually does the dispatch. This may get
+**  prettified and exported at some point in the future.
+*/
+
+static void
+callPreinvokeMethodIfAny(xmlrpc_env *      const envP,
+                         xmlrpc_registry * const registryP,
+                         const char *      const methodName,
+                         xmlrpc_value *    const paramArrayP) {
+
+    /* Get the preinvoke method, if it is set. */
+    if (registryP->_preinvoke_method) {
+        xmlrpc_preinvoke_method preinvoke_method;
+        void * user_data;
+
+        xmlrpc_parse_value(envP, registryP->_preinvoke_method, "(pp)",
+                           &preinvoke_method, &user_data);
+        if (!envP->fault_occurred)
+            (*preinvoke_method)(envP, methodName,
+                                paramArrayP, user_data);
+    }
+}
+
+
+
+static void
+callDefaultMethod(xmlrpc_env *    const envP,
+                  xmlrpc_value *  const defaultMethodInfo,
+                  const char *    const methodName,
+                  xmlrpc_value *  const paramArrayP,
+                  xmlrpc_value ** const resultPP) {
+
+    xmlrpc_default_method default_method;
+    void * user_data;
+
+    xmlrpc_parse_value(envP, defaultMethodInfo, "(pp)",
+                       &default_method, &user_data);
+
+    if (!envP->fault_occurred)
+        *resultPP = (*default_method)(envP, NULL, methodName,
+                                      paramArrayP, user_data);
+}
+    
+
+
+static void
+callNamedMethod(xmlrpc_env *    const envP,
+                xmlrpc_value *  const methodInfo,
+                xmlrpc_value *  const paramArrayP,
+                xmlrpc_value ** const resultPP) {
+
+    xmlrpc_method method;
+    void * user_data;
+    
+    xmlrpc_parse_value(envP, methodInfo, "(pp*)", &method, &user_data);
+    if (!envP->fault_occurred)
+        *resultPP = (*method)(envP, paramArrayP, user_data);
+}
+
+
+
+static void
+dispatch_call(xmlrpc_env *      const envP, 
+              xmlrpc_registry * const registryP,
+              const char *      const methodName, 
+              xmlrpc_value *    const paramArrayP,
+              xmlrpc_value **   const resultPP) {
+
+    callPreinvokeMethodIfAny(envP, registryP, methodName, paramArrayP);
+    if (!envP->fault_occurred) {
+        xmlrpc_value * method_info;
+
+        /* Look up the method info for the named method. */
+        xmlrpc_struct_find_value(envP, registryP->_methods,
+                                 methodName, &method_info);
+        if (!envP->fault_occurred) {
+            if (method_info)
+                callNamedMethod(envP, method_info, paramArrayP, resultPP);
+            else {
+                if (registryP->_default_method)
+                    callDefaultMethod(envP, registryP->_default_method, 
+                                      methodName, paramArrayP,
+                                      resultPP);
+                else {
+                    /* No matching method, and no default. */
+                    xmlrpc_env_set_fault_formatted(
+                        envP, XMLRPC_NO_SUCH_METHOD_ERROR,
+                        "Method '%s' not defined", methodName);
+                }
+            } 
+        }
+    }
+    /* For backward compatibility, for sloppy users: */
+    if (envP->fault_occurred)
+        *resultPP = NULL;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_registry_process_call
+**=========================================================================
+**
+*/
+
+xmlrpc_mem_block *
+xmlrpc_registry_process_call(xmlrpc_env *      const envP,
+                             xmlrpc_registry * const registryP,
+                             const char *      const host ATTR_UNUSED,
+                             const char *      const xml_data,
+                             size_t            const xml_len) {
+
+    xmlrpc_mem_block * output;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_PTR_OK(xml_data);
+    
+    xmlrpc_traceXml("XML-RPC CALL", xml_data, xml_len);
+
+    /* Allocate our output buffer.
+    ** If this fails, we need to die in a special fashion. */
+    output = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
+    if (!envP->fault_occurred) {
+        const char * methodName;
+        xmlrpc_value * paramArray;
+        xmlrpc_env fault;
+
+        xmlrpc_env_init(&fault);
+
+        xmlrpc_parse_call(&fault, xml_data, xml_len, 
+                          &methodName, &paramArray);
+
+        if (!fault.fault_occurred) {
+            xmlrpc_value * result;
+            
+            dispatch_call(&fault, registryP, methodName, paramArray, &result);
+
+            if (!fault.fault_occurred) {
+                xmlrpc_serialize_response(envP, output, result);
+
+                /* A comment here used to say that
+                   xmlrpc_serialize_response() could fail and "leave
+                   stuff in the buffer."  Don't know what that means,
+                   but it sounds like something that needs to be
+                   fixed.  The old code aborted the program here if
+                   xmlrpc_serialize_repsonse() failed.  04.11.17 
+                */
+                xmlrpc_DECREF(result);
+            } 
+            xmlrpc_strfree(methodName);
+            xmlrpc_DECREF(paramArray);
+        }
+        if (!envP->fault_occurred && fault.fault_occurred)
+            xmlrpc_serialize_fault(envP, output, &fault);
+
+        xmlrpc_env_clean(&fault);
+
+        if (envP->fault_occurred)
+            XMLRPC_MEMBLOCK_FREE(char, output);
+        else
+            xmlrpc_traceXml("XML-RPC RESPONSE", 
+                            XMLRPC_MEMBLOCK_CONTENTS(char, output),
+                            XMLRPC_MEMBLOCK_SIZE(char, output));
+    }
+    return output;
+}
+
+
+
+/*=========================================================================
+**  system.multicall
+**=========================================================================
+**  Low-tech support for transparent, boxed methods.
+*/
+
+static char *multicall_help =
+"Process an array of calls, and return an array of results. Calls should "
+"be structs of the form {'methodName': string, 'params': array}. Each "
+"result will either be a single-item array containg the result value, or "
+"a struct of the form {'faultCode': int, 'faultString': string}. This "
+"is useful when you need to make lots of small calls without lots of "
+"round trips.";
+
+static xmlrpc_value *
+call_one_method(xmlrpc_env *env, xmlrpc_registry *registry,
+                xmlrpc_value *method_info) {
+
+    xmlrpc_value *result_val, *result;
+    char *method_name;
+    xmlrpc_value *param_array;
+
+    /* Error-handling preconditions. */
+    result = result_val = NULL;
+    
+    /* Extract our method name and parameters. */
+    xmlrpc_parse_value(env, method_info, "{s:s,s:A,*}",
+                       "methodName", &method_name,
+                       "params", &param_array);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Watch out for a deep recursion attack. */
+    if (strcmp(method_name, "system.multicall") == 0)
+        XMLRPC_FAIL(env, XMLRPC_REQUEST_REFUSED_ERROR,
+                    "Recursive system.multicall strictly forbidden");
+    
+    /* Perform the call. */
+    dispatch_call(env, registry, method_name, param_array, &result_val);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+    /* Build our one-item result array. */
+    result = xmlrpc_build_value(env, "(V)", result_val);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+ cleanup:
+    if (result_val)
+        xmlrpc_DECREF(result_val);
+    if (env->fault_occurred) {
+        if (result)
+            xmlrpc_DECREF(result);
+        return NULL;
+    }
+    return result;
+}
+
+
+
+static xmlrpc_value *
+system_multicall(xmlrpc_env *env,
+                 xmlrpc_value *param_array,
+                 void *user_data) {
+
+    xmlrpc_registry *registry;
+    xmlrpc_value *methlist, *methinfo, *results, *result;
+    size_t size, i;
+    xmlrpc_env env2;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_VALUE_OK(param_array);
+    XMLRPC_ASSERT_PTR_OK(user_data);
+
+    /* Error-handling preconditions. */
+    results = result = NULL;
+    xmlrpc_env_init(&env2);
+    
+    /* Turn our arguments into something more useful. */
+    registry = (xmlrpc_registry*) user_data;
+    xmlrpc_parse_value(env, param_array, "(A)", &methlist);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Create an empty result list. */
+    results = xmlrpc_build_value(env, "()");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Loop over our input list, calling each method in turn. */
+    size = xmlrpc_array_size(env, methlist);
+    XMLRPC_ASSERT_ENV_OK(env);
+    for (i = 0; i < size; i++) {
+        methinfo = xmlrpc_array_get_item(env, methlist, i);
+        XMLRPC_ASSERT_ENV_OK(env);
+        
+        /* Call our method. */
+        xmlrpc_env_clean(&env2);
+        xmlrpc_env_init(&env2);
+        result = call_one_method(&env2, registry, methinfo);
+        
+        /* Turn any fault into a structure. */
+        if (env2.fault_occurred) {
+            XMLRPC_ASSERT(result == NULL);
+            result = 
+                xmlrpc_build_value(env, "{s:i,s:s}",
+                                   "faultCode", (xmlrpc_int32) env2.fault_code,
+                                   "faultString", env2.fault_string);
+            XMLRPC_FAIL_IF_FAULT(env);
+        }
+        
+        /* Append this method result to our master array. */
+        xmlrpc_array_append_item(env, results, result);
+        xmlrpc_DECREF(result);
+        result = NULL;
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+ cleanup:
+    xmlrpc_env_clean(&env2);
+    if (result)
+        xmlrpc_DECREF(result);
+    if (env->fault_occurred) {
+        if (results)
+            xmlrpc_DECREF(results);
+        return NULL;
+    }
+    return results;
+}
+
+
+
+/*=========================================================================
+**  system.listMethods
+**=========================================================================
+**  List all available methods by name.
+*/
+
+static char *listMethods_help =
+"Return an array of all available XML-RPC methods on this server.";
+
+static xmlrpc_value *
+system_listMethods(xmlrpc_env *env,
+                   xmlrpc_value *param_array,
+                   void *user_data) {
+
+    xmlrpc_registry *registry;
+    xmlrpc_value *method_names, *method_name, *method_info;
+    size_t size, i;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_VALUE_OK(param_array);
+    XMLRPC_ASSERT_PTR_OK(user_data);
+
+    /* Error-handling preconditions. */
+    method_names = NULL;
+
+    /* Turn our arguments into something more useful. */
+    registry = (xmlrpc_registry*) user_data;
+    xmlrpc_parse_value(env, param_array, "()");
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+    /* Make sure we're allowed to introspect. */
+    if (!registry->_introspection_enabled)
+        XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
+                    "Introspection disabled for security reasons");
+    
+    /* Iterate over all the methods in the registry, adding their names
+    ** to a list. */
+    method_names = xmlrpc_build_value(env, "()");
+    XMLRPC_FAIL_IF_FAULT(env);
+    size = xmlrpc_struct_size(env, registry->_methods);
+    XMLRPC_FAIL_IF_FAULT(env);
+    for (i = 0; i < size; i++) {
+        xmlrpc_struct_get_key_and_value(env, registry->_methods, i,
+                                        &method_name, &method_info);
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_array_append_item(env, method_names, method_name);
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (method_names)
+            xmlrpc_DECREF(method_names);
+        return NULL;
+    }
+    return method_names;
+}
+
+
+
+/*=========================================================================
+**  system.methodHelp
+**=========================================================================
+**  Get the help string for a particular method.
+*/
+
+static char *methodHelp_help =
+"Given the name of a method, return a help string.";
+
+static xmlrpc_value *
+system_methodHelp(xmlrpc_env *env,
+                  xmlrpc_value *param_array,
+                  void *user_data) {
+
+    xmlrpc_registry *registry;
+    char *method_name;
+    xmlrpc_value *ignored1, *ignored2, *ignored3, *help;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_VALUE_OK(param_array);
+    XMLRPC_ASSERT_PTR_OK(user_data);
+
+    /* Turn our arguments into something more useful. */
+    registry = (xmlrpc_registry*) user_data;
+    xmlrpc_parse_value(env, param_array, "(s)", &method_name);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+    /* Make sure we're allowed to introspect. */
+    if (!registry->_introspection_enabled)
+        XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
+                    "Introspection disabled for security reasons");
+    
+    /* Get our documentation string. */
+    xmlrpc_parse_value(env, registry->_methods, "{s:(VVVV*),*}",
+                       method_name, &ignored1, &ignored2, &ignored3, &help);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+ cleanup:
+    if (env->fault_occurred)
+        return NULL;
+    xmlrpc_INCREF(help);
+    return help;
+}
+
+
+
+/*=========================================================================
+**  system.methodSignature
+**=========================================================================
+**  Return an array of arrays describing possible signatures for this
+**  method.
+**
+**  XXX - This is the ugliest function in the entire library.
+*/
+
+static char *methodSignature_help =
+"Given the name of a method, return an array of legal signatures. "
+"Each signature is an array of strings. The first item of each signature "
+"is the return type, and any others items are parameter types.";
+
+static char *bad_sig_str =
+"Application has incorrect method signature information";
+
+#define BAD_SIG(env) \
+    XMLRPC_FAIL((env), XMLRPC_INTERNAL_ERROR, bad_sig_str);
+
+static xmlrpc_value *
+system_methodSignature(xmlrpc_env *env,
+                       xmlrpc_value *param_array,
+                       void *user_data) {
+
+    xmlrpc_registry *registry;
+    char *method_name;
+    xmlrpc_value *ignored1, *ignored2, *ignored3;
+    xmlrpc_value *item, *current, *result;
+    int at_sig_start;
+    char *sig, *code;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_VALUE_OK(param_array);
+    XMLRPC_ASSERT_PTR_OK(user_data);
+
+    /* Error-handling preconditions. */
+    item = current = result = NULL;
+
+    /* Turn our arguments into something more useful. */
+    registry = (xmlrpc_registry*) user_data;
+    xmlrpc_parse_value(env, param_array, "(s)", &method_name);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Make sure we're allowed to introspect. */
+    if (!registry->_introspection_enabled)
+        XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
+                    "Introspection disabled for security reasons");
+    
+    /* Get our signature string. */
+    xmlrpc_parse_value(env, registry->_methods, "{s:(VVsV*),*}",
+                       method_name, &ignored1, &ignored2, &sig, &ignored3);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+    if (sig[0] == '?' && sig[1] == '\0') {
+        /* No signature supplied. */
+        result = xmlrpc_build_value(env, "s", "undef");
+        XMLRPC_FAIL_IF_FAULT(env);
+    } else {
+        /* Build an array of arrays. */
+        current = xmlrpc_build_value(env, "()");
+        XMLRPC_FAIL_IF_FAULT(env);
+        result = xmlrpc_build_value(env, "(V)", current);
+        XMLRPC_FAIL_IF_FAULT(env);
+        at_sig_start = 1;
+        
+        do {
+        next_loop:
+            
+            /* Process the current code. */
+            switch (*(sig++)) {
+            case 'i': code = "int"; break;
+            case 'b': code = "boolean"; break;
+            case 'd': code = "double"; break;
+            case 's': code = "string"; break;
+            case '8': code = "dateTime.iso8601"; break;
+            case '6': code = "base64"; break;
+            case 'S': code = "struct"; break;
+            case 'A': code = "array"; break;
+                
+            case ',':
+                /* Start a new signature array. */
+                if (at_sig_start)
+                    BAD_SIG(env);
+                xmlrpc_DECREF(current);
+                current = xmlrpc_build_value(env, "()");
+                XMLRPC_FAIL_IF_FAULT(env);
+                xmlrpc_array_append_item(env, result, current);
+                XMLRPC_FAIL_IF_FAULT(env);
+                at_sig_start = 1;
+                goto next_loop;
+                
+            default:
+            BAD_SIG(env);
+            }
+            
+            /* Append the appropriate string to our current signature. */
+            item = xmlrpc_build_value(env, "s", code);
+            XMLRPC_FAIL_IF_FAULT(env);
+            xmlrpc_array_append_item(env, current, item);
+            xmlrpc_DECREF(item);
+            item = NULL;
+            XMLRPC_FAIL_IF_FAULT(env);
+            
+            /* Advance to the next code, and skip over ':' if necessary. */
+            if (at_sig_start) {
+                if (*sig != ':')
+                    BAD_SIG(env);
+                sig++;
+                at_sig_start = 0;
+            }
+            
+        } while (*sig != '\0');
+    }
+    
+ cleanup:
+    if (item)
+        xmlrpc_DECREF(item);
+    if (current)
+        xmlrpc_DECREF(current);
+    if (env->fault_occurred) {
+        if (result)
+            xmlrpc_DECREF(result);
+        return NULL;
+    }
+    return result;
+}
+
+
+
+/*=========================================================================
+**  install_system_methods
+**=========================================================================
+**  Install the standard methods under system.*.
+**  This particular function is highly experimental, and may disappear
+**  without warning.
+*/
+
+static void
+install_system_methods(xmlrpc_env *env, xmlrpc_registry *registry) {
+
+    xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+                                     "system.listMethods",
+                                     &system_listMethods, registry,
+                                     "A:", listMethods_help);
+    XMLRPC_FAIL_IF_FAULT(env);
+    xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+                                     "system.methodSignature",
+                                     &system_methodSignature, registry,
+                                     "A:s", methodSignature_help);
+    XMLRPC_FAIL_IF_FAULT(env);
+    xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+                                     "system.methodHelp",
+                                     &system_methodHelp, registry,
+                                     "s:s", methodHelp_help);
+    XMLRPC_FAIL_IF_FAULT(env);
+    xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+                                     "system.multicall",
+                                     &system_multicall, registry,
+                                     "A:A", multicall_help);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+ cleanup:
+    return;
+}

+ 626 - 0
Utilities/cmxmlrpc/xmlrpc_serialize.c

@@ -0,0 +1,626 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+
+#define CRLF "\015\012"
+#define SMALL_BUFFER_SZ (128)
+#define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF
+
+
+/*=========================================================================
+**  format_out
+**=========================================================================
+**  A lightweight print routine for use with various serialization
+**  functions. Only use this routine 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!
+*/
+
+static void 
+format_out(xmlrpc_env *env,
+           xmlrpc_mem_block *output,
+           char *format_string,
+           ...) {
+
+    va_list args;
+    char buffer[SMALL_BUFFER_SZ];
+    int count;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+
+    va_start(args, format_string);
+
+    /* We assume that this function is present and works correctly. Right. */
+    count = vsnprintf(buffer, SMALL_BUFFER_SZ, format_string, 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 NULL. */
+    if (count < 0 || count >= (SMALL_BUFFER_SZ - 1))
+    XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
+                "format_out overflowed internal buffer");
+
+    /* Append our new data to our output. */
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, buffer, count);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+cleanup:
+    va_end(args);
+}
+
+
+/*=========================================================================
+**  Warnings About Invalid UTF-8
+**=========================================================================
+**  We claim to send UTF-8 data to the network.  But we rely on application
+**  programs to pass us correctly-formed UTF-8 data, which is very naive
+**  and optimistic.
+**
+**  In debudding mode, we call this routine to issue dire-sounding
+**  warnings.  For the sake of safety, this routine never exits the
+**  program or does anything else drastic.
+**
+**  This routine almost certainly slows down our output.
+*/
+
+#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
+
+static void 
+sanity_check_utf8(const char * const str,
+                  size_t       const len) {
+
+    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",
+                "Application sending corrupted UTF-8 data to network",
+                env.fault_string);
+    xmlrpc_env_clean(&env);
+}
+#endif
+
+
+
+/*=========================================================================
+**  Escaping Strings
+**=========================================================================
+*/
+
+static xmlrpc_mem_block * 
+escape_string(xmlrpc_env * const env, 
+              const char * const str,
+              size_t       const len) {
+
+    xmlrpc_mem_block *retval;
+    size_t i, needed;
+    char *out;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(str != NULL);
+
+    /* Set up our error-handling preconditions. */
+    retval = NULL;
+
+    /* Sanity-check this string before we print it. */
+#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
+    sanity_check_utf8(str, len);
+#endif    
+
+    /* Calculate the amount of space we'll need. */
+    needed = 0;
+    for (i = 0; i < len; i++) {
+        if (str[i] == '<')
+            needed += 4; /* &lt; */
+        else if (str[i] == '>')
+            needed += 4; /* &gt; */
+        else if (str[i] == '&')
+            needed += 5; /* &amp; */
+        else
+            needed++;
+    }
+
+    /* Allocate our memory block. */
+    retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Copy over the newly-allocated data. */
+    out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
+    for (i = 0; i < len; i++) {
+        if (str[i] == '<') {
+            *out++ = '&';
+            *out++ = 'l';
+            *out++ = 't';
+            *out++ = ';';
+        } else if (str[i] == '>') {
+            *out++ = '&';
+            *out++ = 'g';
+            *out++ = 't';
+            *out++ = ';';
+        } else if (str[i] == '&') {
+            *out++ = '&';
+            *out++ = 'a';
+            *out++ = 'm';
+            *out++ = 'p';
+            *out++ = ';';
+        } else {
+            *out++ = str[i];
+        }
+    }
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (retval)
+            XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
+        retval = NULL;
+    }
+    return retval;
+}
+
+
+
+static xmlrpc_mem_block* 
+escape_block (xmlrpc_env *env,
+              xmlrpc_mem_block *block) {
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(block != NULL);
+
+    return escape_string(env,
+                         XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
+                         XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_string_data
+**=========================================================================
+**  Escape and print the contents of a string.
+*/                
+
+static void 
+xmlrpc_serialize_string_data(xmlrpc_env *env,
+                             xmlrpc_mem_block *output,
+                             xmlrpc_value *string) {
+
+    xmlrpc_mem_block *escaped;
+    char *contents;
+    size_t size;
+
+    /* Since this routine can only be called internally, we only need
+    ** an assertion here, not a runtime type check.
+    ** XXX - Temporarily disabled because we're using this code to
+    ** print <dateTime.iso8601> values as well. */
+    /* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
+
+    /* Escape any '&' and '<' characters in the string. */
+    escaped = escape_block(env, &string->_block);
+    XMLRPC_FAIL_IF_FAULT(env);
+    contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
+    size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
+    
+    /* Print the string. */
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+ cleanup:
+    if (escaped)
+        XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_base64_data
+**=========================================================================
+**  Print the contents of a memory block as well-formed Base64 data.
+*/                
+
+static void 
+xmlrpc_serialize_base64_data (xmlrpc_env *env,
+                              xmlrpc_mem_block *output,
+                              unsigned char* data, size_t len) {
+
+    xmlrpc_mem_block *encoded;
+    unsigned char *contents;
+    size_t size;
+
+    /* Encode the data. */
+    encoded = xmlrpc_base64_encode(env, data, len);
+    XMLRPC_FAIL_IF_FAULT(env);
+    contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, encoded);
+    size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, encoded);
+
+    /* Print the data. */
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (encoded)
+        XMLRPC_TYPED_MEM_BLOCK_FREE(char, encoded);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_struct
+**=========================================================================
+**  Dump the contents of a struct.
+*/                
+
+static void 
+xmlrpc_serialize_struct(xmlrpc_env *env,
+                        xmlrpc_mem_block *output,
+                        xmlrpc_value *strct) {
+
+    size_t size;
+    size_t i;
+    xmlrpc_value *key, *value;
+
+    format_out(env, output, "<struct>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    size = xmlrpc_struct_size(env, strct);
+    XMLRPC_FAIL_IF_FAULT(env);
+    for (i = 0; i < size; i++) {
+        xmlrpc_struct_get_key_and_value(env, strct, i, &key, &value);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "<member><name>");
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_serialize_string_data(env, output, key);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "</name>"CRLF);
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_serialize_value(env, output, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "</member>"CRLF);
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+    format_out(env, output, "</struct>");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+cleanup:
+    return;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_value
+**=========================================================================
+**  Dump a value in the appropriate fashion.
+*/                
+
+void 
+xmlrpc_serialize_value(xmlrpc_env *env,
+                       xmlrpc_mem_block *output,
+                       xmlrpc_value *value) {
+
+    xmlrpc_value *item;
+    size_t size;
+    unsigned char* contents;
+    size_t i;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(output != NULL);
+    XMLRPC_ASSERT_VALUE_OK(value);
+
+    /* Print our ubiquitous header. */
+    format_out(env, output, "<value>");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    switch (value->_type) {
+
+    case XMLRPC_TYPE_INT:
+        /* XXX - We assume that '%i' is the appropriate format specifier
+        ** for an xmlrpc_int32 value. We should add some test cases to
+        ** make sure this works. */
+        format_out(env, output, "<i4>%i</i4>", value->_value.i);
+        break;
+
+    case XMLRPC_TYPE_BOOL:
+        /* XXX - We assume that '%i' is the appropriate format specifier
+        ** for an xmlrpc_bool value. */
+        format_out(env, output, "<boolean>%i</boolean>",
+                   (value->_value.b) ? 1 : 0);
+        break;
+
+    case XMLRPC_TYPE_DOUBLE:
+        /* We must output a number of the form [+-]?\d*.\d*. */
+        format_out(env, output, "<double>%.17g</double>", value->_value.d);
+        break;
+
+    case XMLRPC_TYPE_STRING:
+        format_out(env, output, "<string>");
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_serialize_string_data(env, output, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "</string>");
+        break;
+
+    case XMLRPC_TYPE_ARRAY:
+        format_out(env, output, "<array><data>"CRLF);
+        XMLRPC_FAIL_IF_FAULT(env);
+
+        /* Serialize each item. */
+        size = xmlrpc_array_size(env, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+        for (i = 0; i < size; i++) {
+            item = xmlrpc_array_get_item(env, value, i);
+            XMLRPC_FAIL_IF_FAULT(env);
+            xmlrpc_serialize_value(env, output, item);
+            XMLRPC_FAIL_IF_FAULT(env);
+            format_out(env, output, CRLF);
+            XMLRPC_FAIL_IF_FAULT(env);
+        }
+
+        format_out(env, output, "</data></array>");
+        break;
+
+    case XMLRPC_TYPE_STRUCT:
+        xmlrpc_serialize_struct(env, output, value);
+        break;
+
+    case XMLRPC_TYPE_BASE64:
+        format_out(env, output, "<base64>"CRLF);
+        XMLRPC_FAIL_IF_FAULT(env);
+        contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
+                                                   &value->_block);
+        size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, &value->_block);
+        xmlrpc_serialize_base64_data(env, output, contents, size);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "</base64>");
+        break;      
+
+    case XMLRPC_TYPE_DATETIME:
+        format_out(env, output, "<dateTime.iso8601>");
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_serialize_string_data(env, output, value);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "</dateTime.iso8601>");
+        break;
+
+    case XMLRPC_TYPE_C_PTR:
+        xmlrpc_env_set_fault_formatted(
+            env, XMLRPC_INTERNAL_ERROR,
+            "Tried to serialize a C pointer value.");
+        break;
+
+    case XMLRPC_TYPE_DEAD:
+        xmlrpc_env_set_fault_formatted(
+            env, XMLRPC_INTERNAL_ERROR,
+            "Tried to serialize a deaad value.");
+        break;
+
+    default:
+        xmlrpc_env_set_fault_formatted(
+            env, XMLRPC_INTERNAL_ERROR,
+            "Invalid xmlrpc_value type: %d", value->_type);
+    }
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Print our ubiquitous footer. */
+    format_out(env, output, "</value>");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    return;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_params
+**=========================================================================
+**  Serialize a list as a set of parameters.
+*/                
+
+void 
+xmlrpc_serialize_params(xmlrpc_env *env,
+                        xmlrpc_mem_block *output,
+                        xmlrpc_value *param_array) {
+
+    size_t size, i;
+    xmlrpc_value *item;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(output != NULL);
+    XMLRPC_ASSERT_VALUE_OK(param_array);
+
+    format_out(env, output, "<params>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Dump each parameter. */
+    size = xmlrpc_array_size(env, param_array);
+    XMLRPC_FAIL_IF_FAULT(env);
+    for (i = 0; i < size; i++) {
+        format_out(env, output, "<param>");
+        XMLRPC_FAIL_IF_FAULT(env);
+        item = xmlrpc_array_get_item(env, param_array, i);
+        XMLRPC_FAIL_IF_FAULT(env);
+        xmlrpc_serialize_value(env, output, item);
+        XMLRPC_FAIL_IF_FAULT(env);
+        format_out(env, output, "</param>"CRLF);
+        XMLRPC_FAIL_IF_FAULT(env);
+    }
+
+    format_out(env, output, "</params>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:    
+    return;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_call
+**=========================================================================
+**  Serialize an XML-RPC call.
+*/                
+
+void 
+xmlrpc_serialize_call(xmlrpc_env *       const env,
+                      xmlrpc_mem_block * const output,
+                      const char *       const method_name,
+                      xmlrpc_value *     const param_array) {
+
+    xmlrpc_mem_block *escaped;
+    char *contents;
+    size_t size;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(output != NULL);
+    XMLRPC_ASSERT(method_name != NULL);
+    XMLRPC_ASSERT_VALUE_OK(param_array);
+    
+    /* Set up our error-handling preconditions. */
+    escaped = NULL;
+
+    /* Dump our header. */
+    format_out(env, output, XML_PROLOGUE);
+    XMLRPC_FAIL_IF_FAULT(env);
+    format_out(env, output, "<methodCall>"CRLF"<methodName>");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Dump the method name. */
+    escaped = escape_string(env, method_name, strlen(method_name));
+    XMLRPC_FAIL_IF_FAULT(env);
+    contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
+    size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped);
+    XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
+    XMLRPC_FAIL_IF_FAULT(env);    
+
+    /* Dump our parameters and footer. */
+    format_out(env, output, "</methodName>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+    xmlrpc_serialize_params(env, output, param_array);
+    XMLRPC_FAIL_IF_FAULT(env);
+    format_out(env, output, "</methodCall>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (escaped)
+        xmlrpc_mem_block_free(escaped);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_response
+**=========================================================================
+**  Serialize the (non-fault) response to an XML-RPC call.
+*/                
+
+void 
+xmlrpc_serialize_response (xmlrpc_env *env,
+                           xmlrpc_mem_block *output,
+                           xmlrpc_value *value) {
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(output != NULL);
+    XMLRPC_ASSERT_VALUE_OK(value);
+
+    format_out(env, output, XML_PROLOGUE);
+    XMLRPC_FAIL_IF_FAULT(env);
+    format_out(env, output, "<methodResponse>"CRLF"<params>"CRLF"<param>");
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    xmlrpc_serialize_value(env, output, value);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    format_out(env, output,
+               "</param>"CRLF"</params>"CRLF"</methodResponse>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+    
+ cleanup:
+    return;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_serialize_fault
+**=========================================================================
+**  Serialize an XML-RPC fault.
+**
+**  If this function fails, it will set up the first env argument. You'll
+**  need to take some other drastic action to produce a serialized fault
+**  of your own. (This function should only fail in an out-of-memory
+**  situation, AFAIK.)
+*/                
+
+void 
+xmlrpc_serialize_fault(xmlrpc_env *env,
+                       xmlrpc_mem_block *output,
+                       xmlrpc_env *fault) {
+
+    xmlrpc_value *strct;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(output != NULL);
+    XMLRPC_ASSERT(fault != NULL && fault->fault_occurred);
+
+    /* Set up our error-handling preconditions. */
+    strct = NULL;
+
+    /* Build a fault structure. */
+    strct = xmlrpc_build_value(env, "{s:i,s:s}",
+                   "faultCode", (xmlrpc_int32) fault->fault_code,
+                   "faultString", fault->fault_string);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Output our header. */
+    format_out(env, output, XML_PROLOGUE);
+    XMLRPC_FAIL_IF_FAULT(env);
+    format_out(env, output, "<methodResponse>"CRLF"<fault>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Serialize our fault structure. */
+    xmlrpc_serialize_value(env, output, strct);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Output our footer. */
+    format_out(env, output, CRLF"</fault>"CRLF"</methodResponse>"CRLF);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (strct)
+        xmlrpc_DECREF(strct);
+}

+ 179 - 0
Utilities/cmxmlrpc/xmlrpc_server.h

@@ -0,0 +1,179 @@
+/* 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. */
+
+
+#ifndef  _XMLRPC_SERVER_H_
+#define  _XMLRPC_SERVER_H_ 1
+
+#include <xmlrpc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*=========================================================================
+**  XML-RPC Server Method Registry
+**=========================================================================
+**  A method registry maintains a list of functions, and handles
+**  dispatching. To build an XML-RPC server, just add an XML-RPC protocol
+**  driver.
+**
+**  Methods are C functions which take some combination of the following
+**  parameters. All pointers except user_data belong to the library, and
+**  must not be freed by the callback or used after the callback returns.
+**
+**  env:          An XML-RPC error-handling environment. No faults will be
+**                set when the function is called. If an error occurs,
+**                set an appropriate fault and return NULL. (If a fault is
+**                set, the NULL return value will be enforced!)
+**  host:         The 'Host:' header passed by the XML-RPC client, or NULL,
+**                if no 'Host:' header has been provided.
+**  method_name:  The name used to call this method.
+**  user_data:    The user_data used to register this method.
+**  param_array:  The parameters passed to this function, stored in an
+**                XML-RPC array. You are *not* responsible for calling
+**                xmlrpc_DECREF on this array.
+**
+**  Return value: If no fault has been set, the function must return a
+**                valid xmlrpc_value. This will be serialized, returned
+**                to the caller, and xmlrpc_DECREF'd.
+*/
+
+/* A function to call before invoking a method for doing things like access
+** control or sanity checks.  If a fault is set from this function, the
+** method will not be called and the fault will be returned. */
+typedef void
+(*xmlrpc_preinvoke_method)(xmlrpc_env *   env,
+                           const char *   method_name,
+                           xmlrpc_value * param_array,
+                           void *         user_data);
+
+/* An ordinary method. */
+typedef xmlrpc_value *
+(*xmlrpc_method)(xmlrpc_env *   env,
+                 xmlrpc_value * param_array,
+                 void *         user_data);
+
+/* A default method to call if no method can be found. */
+typedef xmlrpc_value *
+(*xmlrpc_default_method)(xmlrpc_env *   env,
+                         const char *   host,
+                         const char *   method_name,
+                         xmlrpc_value * param_array,
+                         void *         user_data);
+
+/* Our registry structure. This has no public members. */
+typedef struct _xmlrpc_registry xmlrpc_registry;
+
+/* Create a new method registry. */
+xmlrpc_registry *
+xmlrpc_registry_new(xmlrpc_env * env);
+
+/* Delete a method registry. */
+void
+xmlrpc_registry_free(xmlrpc_registry * registry);
+
+/* Disable introspection.  The xmlrpc_registry has introspection
+** capability built-in.  If you want to make nosy people work harder,
+** you can turn this off. */
+void
+xmlrpc_registry_disable_introspection(xmlrpc_registry * registry);
+
+/* Register a method. The host parameter must be NULL (for now). You
+** are responsible for owning and managing user_data. The registry
+** will make internal copies of any other pointers it needs to
+** keep around. */
+void
+xmlrpc_registry_add_method(xmlrpc_env *      env,
+                           xmlrpc_registry * registry,
+                           const char *      host,
+                           const char *      method_name,
+                           xmlrpc_method     method,
+                           void *            user_data);
+
+/* As above, but allow the user to supply introspection information. 
+**
+** Signatures use their own little description language. It consists
+** of one-letter type code (similar to the ones used in xmlrpc_parse_value)
+** for the result, a colon, and zero or more one-letter type codes for
+** the parameters. For example:
+**   i:ibdsAS86
+** If a function has more than one possible prototype, separate them with
+** commas:
+**   i:,i:s,i:ii
+** If the function signature can't be represented using this language,
+** pass a single question mark:
+**   ?
+** Help strings are ASCII text, and may contain HTML markup. */
+void
+xmlrpc_registry_add_method_w_doc(xmlrpc_env *      env,
+                                 xmlrpc_registry * registry,
+                                 const char *      host,
+                                 const char *      method_name,
+                                 xmlrpc_method     method,
+                                 void *            user_data,
+                                 const char *      signature,
+                                 const char *      help);
+
+/* Given a registry, a host name, and XML data; parse the <methodCall>,
+** find the appropriate method, call it, serialize the response, and
+** return it as an xmlrpc_mem_block. Most errors will be serialized
+** as <fault> responses. If a *really* bad error occurs, set a fault and
+** return NULL. (Actually, we currently give up with a fatal error,
+** but that should change eventually.)
+** The caller is responsible for destroying the memory block. */
+xmlrpc_mem_block *
+xmlrpc_registry_process_call(xmlrpc_env *      const envP,
+                             xmlrpc_registry * const registryP,
+                             const char *      const host,
+                             const char *      const xml_data,
+                             size_t            const xml_len);
+
+/* Define a default method for the specified registry.  This will be invoked
+** if no other method matches.  The user_data pointer is property of the
+** application, and will not be freed or manipulated by the registry. */
+void
+xmlrpc_registry_set_default_method(xmlrpc_env *          env,
+                                   xmlrpc_registry *     registry,
+                                   xmlrpc_default_method handler,
+                                   void *                user_data);
+
+/* Define a preinvoke method for the specified registry.  This function will
+** be called before any method (either the default or a registered one) is
+** invoked.  Applications can use this to do things like access control or
+** sanity checks.  The user_data pointer is property of the application,
+** and will not be freed or manipulated by the registry. */
+void
+xmlrpc_registry_set_preinvoke_method(xmlrpc_env *            env,
+                                     xmlrpc_registry *       registry,
+                                     xmlrpc_preinvoke_method method,
+                                     void *                  user_data);
+
+                    
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 799 - 0
Utilities/cmxmlrpc/xmlrpc_server_abyss.c

@@ -0,0 +1,799 @@
+/* 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.
+**
+** There is more copyright information in the bottom half of this file. 
+** Please see it for more details. */
+
+#include "xmlrpc_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "abyss.h"
+
+#include "xmlrpc.h"
+#include "xmlrpc_server.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_server_abyss.h"
+#include "xmlrpc_server_abyss_int.h"
+
+
+/*=========================================================================
+**  die_if_fault_occurred
+**=========================================================================
+**  If certain kinds of out-of-memory errors occur during server setup,
+**  we want to quit and print an error.
+*/
+
+static void die_if_fault_occurred(xmlrpc_env *env) {
+    if (env->fault_occurred) {
+        fprintf(stderr, "Unexpected XML-RPC fault: %s (%d)\n",
+                env->fault_string, env->fault_code);
+        exit(1);
+    }
+}
+
+
+
+/*=========================================================================
+**  send_xml_data
+**=========================================================================
+**  Blast some XML data back to the client.
+*/
+
+static void 
+send_xml_data (TSession * const r, 
+               char *     const buffer, 
+               uint64     const len) {
+
+    const char * const http_cookie = NULL;
+        /* This used to set http_cookie to getenv("HTTP_COOKIE"), but
+           that doesn't make any sense -- environment variables are not
+           appropriate for this.  So for now, cookie code is disabled.
+           - Bryan 2004.10.03.
+        */
+
+    /* fwrite(buffer, sizeof(char), len, stderr); */
+
+    /* XXX - Is it safe to chunk our response? */
+    ResponseChunked(r);
+
+    ResponseStatus(r, 200);
+    
+    if (http_cookie) {
+        /* There's an auth cookie, so pass it back in the response. */
+
+        char *cookie_response;
+ 
+        cookie_response = malloc(10+strlen(http_cookie));
+        sprintf(cookie_response, "auth=%s", http_cookie);
+        
+        /* Return abyss response. */
+        ResponseAddField(r, "Set-Cookie", cookie_response);
+
+        free(cookie_response);
+    }   
+ 
+  
+    ResponseContentType(r, "text/xml; charset=\"utf-8\"");
+    ResponseContentLength(r, len);
+    
+    ResponseWrite(r);
+    
+    HTTPWrite(r, buffer, len);
+    HTTPWriteEnd(r);
+}
+
+
+
+/*=========================================================================
+**  send_error
+**=========================================================================
+**  Send an error back to the client.
+*/
+
+static void
+send_error(TSession *   const abyssSessionP, 
+           unsigned int const status) {
+
+    ResponseStatus(abyssSessionP, (uint16) status);
+    ResponseError(abyssSessionP);
+}
+
+
+
+/*=========================================================================
+**  get_buffer_data
+**=========================================================================
+**  Extract some data from the TConn's underlying input buffer. Do not
+**  extract more than 'max'.
+*/
+
+static void
+get_buffer_data(TSession * const r, 
+                int        const max, 
+                char **    const out_start, 
+                int *      const out_len) {
+
+    /* Point to the start of our data. */
+    *out_start = &r->conn->buffer[r->conn->bufferpos];
+
+    /* Decide how much data to retrieve. */
+    *out_len = r->conn->buffersize - r->conn->bufferpos;
+    if (*out_len > max)
+        *out_len = max;
+
+    /* Update our buffer position. */
+    r->conn->bufferpos += *out_len;
+}
+
+
+
+/*=========================================================================
+**  get_body
+**=========================================================================
+**  Slurp the body of the request into an xmlrpc_mem_block.
+*/
+
+static void
+getBody(xmlrpc_env *        const envP,
+        TSession *          const abyssSessionP,
+        unsigned int        const contentSize,
+        xmlrpc_mem_block ** const bodyP) {
+/*----------------------------------------------------------------------------
+   Get the entire body from the Abyss session and return it as the new
+   memblock *bodyP.
+
+   The first chunk of the body may already be in Abyss's buffer.  We
+   retrieve that before reading more.
+-----------------------------------------------------------------------------*/
+    xmlrpc_mem_block * body;
+
+    body = xmlrpc_mem_block_new(envP, 0);
+    if (!envP->fault_occurred) {
+        unsigned int bytesRead;
+        char * chunkPtr;
+        int chunkLen;
+
+        bytesRead = 0;
+
+        while (!envP->fault_occurred && bytesRead < contentSize) {
+            get_buffer_data(abyssSessionP, contentSize - bytesRead, 
+                            &chunkPtr, &chunkLen);
+            bytesRead += chunkLen;
+
+            XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body, 
+                                          chunkPtr, chunkLen);
+            
+            if (bytesRead < contentSize) {
+                /* Get the next chunk of data from the connection into the
+                   buffer 
+                */
+                abyss_bool succeeded;
+            
+                /* Reset our read buffer & flush data from previous reads. */
+                ConnReadInit(abyssSessionP->conn);
+            
+                /* Read more network data into our buffer. If we encounter
+                   a timeout, exit immediately. We're very forgiving about
+                   the timeout here. We allow a full timeout per network
+                   read, which would allow somebody to keep a connection
+                   alive nearly indefinitely. But it's hard to do anything
+                   intelligent here without very complicated code. 
+                */
+                succeeded = ConnRead(abyssSessionP->conn,
+                                     abyssSessionP->server->timeout);
+                if (!succeeded)
+                    xmlrpc_env_set_fault_formatted(
+                        envP, XMLRPC_TIMEOUT_ERROR, "Timed out waiting for "
+                        "client to send its POST data");
+            }
+        }
+        if (envP->fault_occurred)
+            xmlrpc_mem_block_free(body);
+        else
+            *bodyP = body;
+    }
+}
+
+
+
+static void
+storeCookies(TSession *     const httpRequestP,
+             unsigned int * const httpErrorP) {
+/*----------------------------------------------------------------------------
+   Get the cookie settings from the HTTP headers and remember them for
+   use in responses.
+-----------------------------------------------------------------------------*/
+    const char * const cookie = RequestHeaderValue(httpRequestP, "cookie");
+    if (cookie) {
+        /* 
+           Setting the value in an environment variable doesn't make
+           any sense.  So for now, cookie code is disabled.
+           -Bryan 04.10.03.
+
+        setenv("HTTP_COOKIE", cookie, 1);
+        */
+    }
+    /* TODO: parse HTTP_COOKIE to find auth pair, if there is one */
+
+    *httpErrorP = 0;
+}
+
+
+
+
+static void
+validateContentType(TSession *     const httpRequestP,
+                    unsigned int * const httpErrorP) {
+/*----------------------------------------------------------------------------
+   If the client didn't specify a content-type of "text/xml", return      
+   "400 Bad Request".  We can't allow the client to default this header,
+   because some firewall software may rely on all XML-RPC requests
+   using the POST method and a content-type of "text/xml". 
+-----------------------------------------------------------------------------*/
+    const char * const content_type =
+        RequestHeaderValue(httpRequestP, "content-type");
+    if (content_type == NULL || strcmp(content_type, "text/xml") != 0)
+        *httpErrorP = 400;
+    else
+        *httpErrorP = 0;
+}
+
+
+
+static void
+processContentLength(TSession *     const httpRequestP,
+                     unsigned int * const inputLenP,
+                     unsigned int * const httpErrorP) {
+/*----------------------------------------------------------------------------
+  Make sure the content length is present and non-zero.  This is
+  technically required by XML-RPC, but we only enforce it because we
+  don't want to figure out how to safely handle HTTP < 1.1 requests
+  without it.  If the length is missing, return "411 Length Required". 
+-----------------------------------------------------------------------------*/
+    const char * const content_length = 
+        RequestHeaderValue(httpRequestP, "content-length");
+    if (content_length == NULL)
+        *httpErrorP = 411;
+    else {
+        int const contentLengthValue = atoi(content_length);
+        if (contentLengthValue <= 0)
+            *httpErrorP = 400;
+        else {
+            *httpErrorP = 0;
+            *inputLenP = (unsigned int)contentLengthValue;
+        }
+    }
+}
+
+
+/****************************************************************************
+    Abyss handlers (to be registered with and called by Abyss)
+****************************************************************************/
+
+/* XXX - This variable is *not* currently threadsafe. Once the server has
+** been started, it must be treated as read-only. */
+static xmlrpc_registry *global_registryP;
+
+static const char * trace_abyss;
+
+static void
+processCall(TSession * const abyssSessionP,
+            int        const inputLen) {
+/*----------------------------------------------------------------------------
+   Handle an RPC request.  This is an HTTP request that has the proper form
+   to be one of our RPCs.
+-----------------------------------------------------------------------------*/
+    xmlrpc_env env;
+
+    if (trace_abyss)
+        fprintf(stderr, "xmlrpc_server_abyss RPC2 handler processing RPC.\n");
+
+    xmlrpc_env_init(&env);
+
+    /* SECURITY: Make sure our content length is legal.
+       XXX - We can cast 'inputLen' because we know it's >= 0, yes? 
+    */
+    if ((size_t) inputLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
+        xmlrpc_env_set_fault_formatted(
+            &env, XMLRPC_LIMIT_EXCEEDED_ERROR,
+            "XML-RPC request too large (%d bytes)", inputLen);
+    else {
+        xmlrpc_mem_block *body;
+        /* Read XML data off the wire. */
+        getBody(&env, abyssSessionP, inputLen, &body);
+        if (!env.fault_occurred) {
+            xmlrpc_mem_block * output;
+            /* Process the RPC. */
+            output = xmlrpc_registry_process_call(
+                &env, global_registryP, NULL, 
+                XMLRPC_MEMBLOCK_CONTENTS(char, body),
+                XMLRPC_MEMBLOCK_SIZE(char, body));
+            if (!env.fault_occurred) {
+            /* Send our the result. */
+                send_xml_data(abyssSessionP, 
+                              XMLRPC_MEMBLOCK_CONTENTS(char, output),
+                              XMLRPC_MEMBLOCK_SIZE(char, output));
+                
+                XMLRPC_MEMBLOCK_FREE(char, output);
+            }
+            XMLRPC_MEMBLOCK_FREE(char, body);
+        }
+    }
+    if (env.fault_occurred) {
+        if (env.fault_code == XMLRPC_TIMEOUT_ERROR)
+            send_error(abyssSessionP, 408); /* 408 Request Timeout */
+        else
+            send_error(abyssSessionP, 500); /* 500 Internal Server Error */
+    }
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_server_abyss_rpc2_handler
+**=========================================================================
+**  This handler processes all requests to '/RPC2'. See the header for
+**  more documentation.
+*/
+
+xmlrpc_bool 
+xmlrpc_server_abyss_rpc2_handler (TSession * const r) {
+
+    xmlrpc_bool retval;
+
+    if (trace_abyss)
+        fprintf(stderr, "xmlrpc_server_abyss RPC2 handler called.\n");
+
+    /* We handle only requests to /RPC2, the default XML-RPC URL.
+       Everything else we pass through to other handlers. 
+    */
+    if (strcmp(r->uri, "/RPC2") != 0)
+        retval = FALSE;
+    else {
+        retval = TRUE;
+
+        /* We understand only the POST HTTP method.  For anything else, return
+           "405 Method Not Allowed". 
+        */
+        if (r->method != m_post)
+            send_error(r, 405);
+        else {
+            unsigned int httpError;
+            storeCookies(r, &httpError);
+            if (httpError)
+                send_error(r, httpError);
+            else {
+                unsigned int httpError;
+                validateContentType(r, &httpError);
+                if (httpError)
+                    send_error(r, httpError);
+                else {
+                    unsigned int httpError;
+                    int inputLen;
+
+                    processContentLength(r, &inputLen, &httpError);
+                    if (httpError)
+                        send_error(r, httpError);
+
+                    processCall(r, inputLen);
+                }
+            }
+        }
+    }
+    if (trace_abyss)
+        fprintf(stderr, "xmlrpc_server_abyss RPC2 handler returning.\n");
+    return retval;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_server_abyss_default_handler
+**=========================================================================
+**  This handler returns a 404 Not Found for all requests. See the header
+**  for more documentation.
+*/
+
+xmlrpc_bool 
+xmlrpc_server_abyss_default_handler (TSession * const r) {
+    send_error(r, 404);
+
+    return TRUE;
+}
+
+
+
+/**************************************************************************
+**
+** The code below was adapted from the main.c file of the Abyss webserver
+** project. In addition to the other copyrights on this file, the following
+** code is also under this copyright:
+**
+** Copyright (C) 2000 by Moez Mahfoudh <[email protected]>.
+** 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.
+**
+**************************************************************************/
+
+#include <time.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <io.h>
+#else
+/* Must check this
+#include <sys/io.h>
+*/
+#endif  /* _WIN32 */
+
+#ifdef _UNIX
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include <grp.h>
+#endif
+
+
+#ifdef _UNIX
+static void 
+sigterm(int const sig) {
+    TraceExit("Signal %d received. Exiting...\n",sig);
+}
+#endif
+
+
+#ifdef _UNIX
+static void 
+sigchld(int const sig ATTR_UNUSED) {
+/*----------------------------------------------------------------------------
+   This is a signal handler for a SIGCHLD signal (which informs us that
+   one of our child processes has terminated).
+
+   We respond by reaping the zombie process.
+
+   Implementation note: In some systems, just setting the signal handler
+   to SIG_IGN (ignore signal) does this.  In others, it doesn't.
+-----------------------------------------------------------------------------*/
+    pid_t pid;
+    int status;
+    
+    /* Reap defunct children until there aren't any more. */
+    for (;;) {
+        pid = waitpid( (pid_t) -1, &status, WNOHANG );
+    
+        /* none left */
+        if (pid==0)
+            break;
+    
+        if (pid<0) {
+            /* because of ptrace */
+            if (errno==EINTR)   
+                continue;
+        
+            break;
+        }
+    }
+}
+#endif /* _UNIX */
+
+static TServer globalSrv;
+    /* When you use the old interface (xmlrpc_server_abyss_init(), etc.),
+       this is the Abyss server to which they refer.  Obviously, there can be
+       only one Abyss server per program using this interface.
+    */
+
+
+void 
+xmlrpc_server_abyss_init(int          const flags ATTR_UNUSED, 
+                         const char * const config_file) {
+
+    DateInit();
+    MIMETypeInit();
+
+    ServerCreate(&globalSrv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
+    
+    ConfReadServerFile(config_file, &globalSrv);
+
+    xmlrpc_server_abyss_init_registry();
+        /* Installs /RPC2 handler and default handler that use the
+           built-in registry.
+        */
+
+    ServerInit(&globalSrv);
+}
+
+
+
+static void
+setupSignalHandlers(void) {
+#ifdef _UNIX
+    struct sigaction mysigaction;
+    
+    sigemptyset(&mysigaction.sa_mask);
+    mysigaction.sa_flags = 0;
+
+    /* These signals abort the program, with tracing */
+    mysigaction.sa_handler = sigterm;
+    sigaction(SIGTERM, &mysigaction, NULL);
+    sigaction(SIGINT,  &mysigaction, NULL);
+    sigaction(SIGHUP,  &mysigaction, NULL);
+    sigaction(SIGUSR1, &mysigaction, NULL);
+
+    /* This signal indicates connection closed in the middle */
+    mysigaction.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &mysigaction, NULL);
+    
+    /* This signal indicates a child process (request handler) has died */
+    mysigaction.sa_handler = sigchld;
+    sigaction(SIGCHLD, &mysigaction, NULL);
+#endif
+}    
+
+
+
+static void
+runServer(TServer *  const srvP,
+          runfirstFn const runfirst,
+          void *     const runfirstArg) {
+
+    setupSignalHandlers();
+
+#ifdef _UNIX
+    /* Become a daemon */
+    switch (fork()) {
+    case 0:
+        break;
+    case -1:
+        TraceExit("Unable to become a daemon");
+    default:
+        exit(0);
+    };
+    
+    setsid();
+
+    /* Change the current user if we are root */
+    if (getuid()==0) {
+        if (srvP->uid == (uid_t)-1)
+            TraceExit("Can't run under root privileges.  "
+                      "Please add a User option in your "
+                      "Abyss configuration file.");
+
+#ifdef HAVE_SETGROUPS   
+        if (setgroups(0,NULL)==(-1))
+            TraceExit("Failed to setup the group.");
+        if (srvP->gid != (gid_t)-1)
+            if (setgid(srvP->gid)==(-1))
+                TraceExit("Failed to change the group.");
+#endif
+        
+        if (setuid(srvP->uid) == -1)
+            TraceExit("Failed to change the user.");
+    };
+    
+    if (srvP->pidfile!=(-1)) {
+        char z[16];
+    
+        sprintf(z,"%d",getpid());
+        FileWrite(&srvP->pidfile,z,strlen(z));
+        FileClose(&srvP->pidfile);
+    };
+#endif
+    
+    /* We run the user supplied runfirst after forking, but before accepting
+       connections (helpful when running with threads)
+    */
+    if (runfirst)
+        runfirst(runfirstArg);
+
+    ServerRun(srvP);
+
+    /* We can't exist here because ServerRun doesn't return */
+    XMLRPC_ASSERT(FALSE);
+}
+
+
+
+void 
+xmlrpc_server_abyss_run_first(runfirstFn const runfirst,
+                              void *     const runfirstArg) {
+    
+    runServer(&globalSrv, runfirst, runfirstArg);
+}
+
+
+
+void 
+xmlrpc_server_abyss_run(void) {
+    runServer(&globalSrv, NULL, NULL);
+}
+
+
+
+void
+xmlrpc_server_abyss_set_handlers(TServer *         const srvP,
+                                 xmlrpc_registry * const registryP) {
+
+    /* Abyss ought to have a way to register with a handler an argument
+       that gets passed to the handler every time it is called.  That's
+       where we should put the registry handle.  But we don't find such
+       a thing in Abyss, so we use the global variable 'global_registryP'.
+    */
+    global_registryP = registryP;
+
+    trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
+                                 
+    ServerAddHandler(srvP, xmlrpc_server_abyss_rpc2_handler);
+    ServerDefaultHandler(srvP, xmlrpc_server_abyss_default_handler);
+}
+
+
+
+void
+xmlrpc_server_abyss(xmlrpc_env *                      const envP,
+                    const xmlrpc_server_abyss_parms * const parmsP,
+                    unsigned int                      const parm_size) {
+ 
+    XMLRPC_ASSERT_ENV_OK(envP);
+
+    if (parm_size < XMLRPC_APSIZE(registryP))
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INTERNAL_ERROR,
+            "You must specify members at least up through "
+            "'registryP' in the server parameters argument.  "
+            "That would mean the parameter size would be >= %u "
+            "but you specified a size of %u",
+            XMLRPC_APSIZE(registryP), parm_size);
+    else {
+        TServer srv;
+        runfirstFn runfirst;
+        void * runfirstArg;
+
+        DateInit();
+        MIMETypeInit();
+        
+        ServerCreate(&srv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
+        
+        ConfReadServerFile(parmsP->config_file_name, &srv);
+        
+        xmlrpc_server_abyss_set_handlers(&srv, parmsP->registryP);
+        
+        ServerInit(&srv);
+
+        if (parm_size >= XMLRPC_APSIZE(runfirst_arg)) {
+            runfirst    = parmsP->runfirst;
+            runfirstArg = parmsP->runfirst_arg;
+        } else {
+            runfirst    = NULL;
+            runfirstArg = NULL;
+        }
+        runServer(&srv, runfirst, runfirstArg);
+    }
+}
+
+
+
+/*=========================================================================
+**  XML-RPC Server Method Registry
+**=========================================================================
+**  A simple front-end to our method registry.
+*/
+
+/* XXX - This variable is *not* currently threadsafe. Once the server has
+** been started, it must be treated as read-only. */
+static xmlrpc_registry *builtin_registryP;
+
+void 
+xmlrpc_server_abyss_init_registry(void) {
+
+    /* This used to just create the registry and Caller would be
+       responsible for adding the handlers that use it.
+
+       But that isn't very modular -- the handlers and registry go
+       together; there's no sense in using the built-in registry and
+       not the built-in handlers because if you're custom building
+       something, you can just make your own regular registry.  So now
+       we tie them together, and we don't export our handlers.  
+    */
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+    builtin_registryP = xmlrpc_registry_new(&env);
+    die_if_fault_occurred(&env);
+    xmlrpc_env_clean(&env);
+
+    xmlrpc_server_abyss_set_handlers(&globalSrv, builtin_registryP);
+}
+
+
+
+xmlrpc_registry *
+xmlrpc_server_abyss_registry(void) {
+
+    /* This is highly deprecated.  If you want to mess with a registry,
+       make your own with xmlrpc_registry_new() -- don't mess with the
+       internal one.
+    */
+    return builtin_registryP;
+}
+
+
+
+/* A quick & easy shorthand for adding a method. */
+void 
+xmlrpc_server_abyss_add_method (char *        const method_name,
+                                xmlrpc_method const method,
+                                void *        const user_data) {
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+    xmlrpc_registry_add_method(&env, builtin_registryP, NULL, method_name,
+                               method, user_data);
+    die_if_fault_occurred(&env);
+    xmlrpc_env_clean(&env);
+}
+
+
+
+void
+xmlrpc_server_abyss_add_method_w_doc (char *        const method_name,
+                                      xmlrpc_method const method,
+                                      void *        const user_data,
+                                      char *        const signature,
+                                      char *        const help) {
+
+    xmlrpc_env env;
+    xmlrpc_env_init(&env);
+    xmlrpc_registry_add_method_w_doc(
+        &env, builtin_registryP, NULL, method_name,
+        method, user_data, signature, help);
+    die_if_fault_occurred(&env);
+    xmlrpc_env_clean(&env);    
+}

+ 182 - 0
Utilities/cmxmlrpc/xmlrpc_server_abyss.h

@@ -0,0 +1,182 @@
+/* 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. */
+
+
+#ifndef  _XMLRPC_SERVER_ABYSS_H_
+#define  _XMLRPC_SERVER_ABYSS_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct _TServer;
+
+/*=========================================================================
+**  XML-RPC Server (based on Abyss)
+**=========================================================================
+**  A simple XML-RPC server based on the Abyss web server. If errors
+**  occur during server setup, the server will exit. In general, if you
+**  want to use this API, you'll need to be familiar with Abyss.
+**
+**  There are two ways to use Abyss:
+**    1) You can use the handy wrapper functions.
+**    2) You can set up Abyss yourself, and install the appropriate
+**       handlers manually.
+*/
+
+#define XMLRPC_SERVER_ABYSS_NO_FLAGS (0)
+
+
+
+
+/*=========================================================================
+**  Basic Abyss Server Functions
+**=======================================================================*/
+
+typedef void ((*runfirstFn)(void *));
+
+typedef struct {
+    const char *      config_file_name;
+    xmlrpc_registry * registryP;
+    runfirstFn        runfirst;
+    void *            runfirst_arg;
+} xmlrpc_server_abyss_parms;
+
+
+#define XMLRPC_APSIZE(MBRNAME) \
+    XMLRPC_STRUCTSIZE(xmlrpc_server_abyss_parms, MBRNAME)
+
+/* XMLRPC_APSIZE(xyz) is the minimum size a struct xmlrpc_server_abyss_parms
+   must be to include the 'xyz' member.  This is essential to forward and
+   backward compatbility, as new members will be added to the end of the
+   struct in future releases.  This is how the callee knows whether or
+   not the caller is new enough to have supplied a certain parameter.
+*/
+
+void
+xmlrpc_server_abyss(xmlrpc_env *                      const envP,
+                    const xmlrpc_server_abyss_parms * const parms,
+                    unsigned int                      const parm_size);
+
+void
+xmlrpc_server_abyss_set_handlers(struct _TServer * const srvP,
+                                 xmlrpc_registry * const registryP);
+
+
+/*=========================================================================
+**  Handy Abyss Extensions
+**=======================================================================*/
+
+/* These are functions that have nothing to do with Xmlrpc-c, but provide
+   convenient Abyss services beyond those provided by the Abyss library.
+*/
+
+/* Start an Abyss webserver running (previously created and
+** initialized).  Under Unix, this routine will attempt to do a
+** detaching fork, drop root privileges (if any) and create a pid
+** file.  Under Windows, this routine merely starts the server.  This
+** routine never returns.
+**
+** Once you call this routine, it is illegal to modify the server any
+** more, including changing any method registry.
+*/
+void
+xmlrpc_server_abyss_run(void);
+
+/* Same as xmlrpc_server_abyss_run(), except you get to specify a "runfirst"
+** function.  The server runs this just before executing the actual server
+** function, after any daemonizing.  NULL for 'runfirst' means no runfirst
+** function.  'runfirstArg' is the argument the server passes to the runfirst
+** function.
+**/
+void 
+xmlrpc_server_abyss_run_first(void (runfirst(void *)),
+                              void * const runfirstArg);
+
+/*=========================================================================
+**  Method Registry
+**=========================================================================
+   These functions are for the built-in xmlrpc_server_abyss registry.
+   It's usually simpler to skip all this and use the regular method
+   registry services (from xmlrpc_server.h) to build a registry and
+   pass it to xmlrpc_server_abyss.
+*/
+
+/* Call this function to create a new Abyss webserver with the default
+** options and the built-in method registry.  If you've already
+** initialized Abyss using Abyss functions, you can instead call
+** xmlrpc_server_abyss_init_registry() to make it an Xmlrpc-c server.
+** Or use a regular method registry and call
+** xmlrpc_server_abyss_set_handlers().
+**/
+void 
+xmlrpc_server_abyss_init(int          const flags, 
+                         const char * const config_file);
+
+/* This is called automatically by xmlrpc_server_abyss_init. */
+void xmlrpc_server_abyss_init_registry (void);
+
+/* Fetch the internal registry, if you happen to need it. 
+   If you're using this, you really shouldn't be using the built-in
+   registry at all.  It exists today only for backward compatibilty.
+*/
+extern xmlrpc_registry *
+xmlrpc_server_abyss_registry (void);
+
+/* A quick & easy shorthand for adding a method. Depending on
+** how you've configured your copy of Abyss, it's probably not safe to
+** call this method after calling xmlrpc_server_abyss_run. */
+void xmlrpc_server_abyss_add_method (char *method_name,
+                                     xmlrpc_method method,
+                                     void *user_data);
+    
+/* As above, but provide documentation (see xmlrpc_registry_add_method_w_doc
+** for more information). You should really use this one. */
+extern void
+xmlrpc_server_abyss_add_method_w_doc (char *method_name,
+                                      xmlrpc_method method,
+                                      void *user_data,
+                                      char *signature,
+                                      char *help);
+
+/*=========================================================================
+**  Content Handlers
+**=======================================================================*/
+/* Abyss contents handlers xmlrpc_server_abyss_rpc2_handler()
+   and xmlrpc_server_abyss_default_handler() were available in older
+   Xmlrpc-c, but starting with Release 1.01, they are not.  Instead,
+   call xmlrpc_server_abyss_set_handlers() to install them.
+
+   Alternatively, you can write your own handlers that do the same thing.
+   It's not hard, and if you're writing low enough level Abyss code that
+   you can't use xmlrpc_server_abyss_set_handlers(), you probably want to
+   anyway.
+*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif

+ 79 - 0
Utilities/cmxmlrpc/xmlrpc_server_abyss_int.h

@@ -0,0 +1,79 @@
+/*============================================================================
+                         xmlrpc_server_abyss_int.h
+==============================================================================
+  This header file defines the interface between client modules inside
+  xmlrpc-c.
+
+  Use this in addition to xmlrpc_server_abyss.h, which defines the external
+  interface.
+
+  Copyright information is at the end of the file.
+============================================================================*/
+
+#ifndef  _XMLRPC_SERVER_ABYSS_INT_H_
+#define  _XMLRPC_SERVER_ABYSS_INT_H_ 1
+
+#include "abyss.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*=========================================================================
+**  Abyss Content Handlers
+**=========================================================================
+**  These are Abyss handlers.  You install them into an Abyss server.
+*/
+
+/* Handler for XML-RPC requests.  Install this using ServerAddHandler
+   as the handler for all requests to /RPC2. This handler assumes that
+   it can read from the method registry without running into race
+   conditions or anything nasty like that.  
+*/
+extern xmlrpc_bool
+xmlrpc_server_abyss_rpc2_handler (TSession *r);
+
+/* A default handler.  Install this as the default handler with
+   ServerDefaultHandler if you don't want to serve any HTML or
+   GIFs from your htdocs directory. 
+ 
+   This handler always returns a "404 Not Found". 
+*/
+extern xmlrpc_bool
+xmlrpc_server_abyss_default_handler (TSession *r);
+
+
+
+
+/* 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. */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+

+ 609 - 0
Utilities/cmxmlrpc/xmlrpc_struct.c

@@ -0,0 +1,609 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+
+#define KEY_ERROR_BUFFER_SZ (32)
+
+
+void
+xmlrpc_destroyStruct(xmlrpc_value * const structP) {
+
+    _struct_member * const members = 
+        XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
+    size_t const size = 
+        XMLRPC_MEMBLOCK_SIZE(_struct_member, &structP->_block);
+
+    unsigned int i;
+
+    for (i = 0; i < size; ++i) {
+        xmlrpc_DECREF(members[i].key);
+        xmlrpc_DECREF(members[i].value);
+    }
+    XMLRPC_MEMBLOCK_CLEAN(_struct_member, &structP->_block);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_new
+**=========================================================================
+**  Create a new <struct> value. The corresponding destructor code
+**  currently lives in xmlrpc_DECREF.
+**
+**  We store the individual members in an array of _struct_member. This
+**  contains a key, a hash code, and a value. We look up keys by doing
+**  a linear search of the hash codes.
+*/
+
+xmlrpc_value *
+xmlrpc_struct_new(xmlrpc_env* env)
+{
+    xmlrpc_value *strct;
+    int strct_valid;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+
+    /* Set up error handling preconditions. */
+    strct = NULL;
+    strct_valid = 0;
+
+    /* Allocate and fill out an empty structure. */
+    strct = (xmlrpc_value*) malloc(sizeof(xmlrpc_value));
+    XMLRPC_FAIL_IF_NULL(strct, env, XMLRPC_INTERNAL_ERROR,
+                        "Could not allocate memory for struct");
+    strct->_refcount = 1;
+    strct->_type = XMLRPC_TYPE_STRUCT;
+    XMLRPC_MEMBLOCK_INIT(_struct_member, env, &strct->_block, 0);
+    XMLRPC_FAIL_IF_FAULT(env);
+    strct_valid = 1;
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (strct) {
+            if (strct_valid)
+                xmlrpc_DECREF(strct);
+            else
+                free(strct);
+        }
+        return NULL;
+    }
+    return strct;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_size
+**=========================================================================
+**  Return the number of key-value pairs contained in the struct. If the
+**  value is not a struct, return -1 and set a fault.
+*/
+
+int 
+xmlrpc_struct_size(xmlrpc_env* env, xmlrpc_value* strct)
+{
+    int retval;
+
+    /* Suppress a compiler warning about uninitialized variables. */
+    retval = 0;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_VALUE_OK(strct);
+
+    XMLRPC_TYPE_CHECK(env, strct, XMLRPC_TYPE_STRUCT);
+    retval = XMLRPC_MEMBLOCK_SIZE(_struct_member, &strct->_block);
+
+ cleanup:
+    if (env->fault_occurred)
+        return -1;
+    return retval;
+}
+
+
+
+/*=========================================================================
+**  get_hash
+**=========================================================================
+**  A mindlessly simple hash function. Please feel free to write something
+**  more clever if this produces bad results.
+*/
+
+static unsigned char 
+get_hash(const char * const key, 
+         size_t       const key_len) {
+
+    unsigned char retval;
+    size_t i;
+
+    XMLRPC_ASSERT(key != NULL);
+    
+    retval = 0;
+    for (i = 0; i < key_len; i++)
+        retval += key[i];
+    return retval;
+}
+
+
+
+/*=========================================================================
+**  find_member
+**=========================================================================
+**  Get the index of the member with the specified key, or -1 if no such
+**  member exists.
+*/
+
+static int 
+find_member(xmlrpc_value * const strctP, 
+            const char *   const key, 
+            size_t         const key_len) {
+
+    size_t size, i;
+    unsigned char hash;
+    _struct_member *contents;
+    xmlrpc_value *keyval;
+    char *keystr;
+    size_t keystr_size;
+
+    XMLRPC_ASSERT_VALUE_OK(strctP);
+    XMLRPC_ASSERT(key != NULL);
+
+    /* Look for our key. */
+    hash = get_hash(key, key_len);
+    size = XMLRPC_MEMBLOCK_SIZE(_struct_member, &strctP->_block);
+    contents = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &strctP->_block);
+    for (i = 0; i < size; i++) {
+        if (contents[i].key_hash == hash) {
+            keyval = contents[i].key;
+            keystr = XMLRPC_MEMBLOCK_CONTENTS(char, &keyval->_block);
+            keystr_size = XMLRPC_MEMBLOCK_SIZE(char, &keyval->_block)-1;
+            if (key_len == keystr_size && memcmp(key, keystr, key_len) == 0)
+                return i;
+        }   
+    }
+    return -1;
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_has_key
+**=========================================================================
+*/
+
+int 
+xmlrpc_struct_has_key(xmlrpc_env *   const envP,
+                      xmlrpc_value * const strctP,
+                      const char *   const key) {
+
+    XMLRPC_ASSERT(key != NULL);
+    return xmlrpc_struct_has_key_n(envP, strctP, key, strlen(key));
+}
+
+
+
+int 
+xmlrpc_struct_has_key_n(xmlrpc_env   * const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key, 
+                        size_t         const key_len) {
+    int index;
+
+    /* Suppress a compiler warning about uninitialized variables. */
+    index = 0;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(strctP);
+    XMLRPC_ASSERT(key != NULL);
+    
+    XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
+    index = find_member(strctP, key, key_len);
+
+ cleanup:
+    if (envP->fault_occurred)
+        return 0;
+    return (index >= 0);
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_find_value...
+**=========================================================================
+**  These functions look up a specified key value in a specified struct.
+**  If it exists, they return the value of the struct member.  If not,
+**  they return a NULL to indicate such.
+*/
+
+/* It would be a nice extension to be able to look up a key that is
+   not a text string.
+*/
+
+void
+xmlrpc_struct_find_value(xmlrpc_env *    const envP,
+                         xmlrpc_value *  const structP,
+                         const char *    const key,
+                         xmlrpc_value ** const valuePP) {
+/*----------------------------------------------------------------------------
+  Given a key, retrieve a value from the struct.  If the key is not
+  present, return NULL as *valuePP.
+-----------------------------------------------------------------------------*/
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(structP);
+    XMLRPC_ASSERT_PTR_OK(key);
+    
+    if (structP->_type != XMLRPC_TYPE_STRUCT)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TYPE_ERROR, "Value is not a struct.  It is type #%d",
+            structP->_type);
+    else {
+        int index;
+
+        /* Get our member index. */
+        index = find_member(structP, key, strlen(key));
+        if (index < 0)
+            *valuePP = NULL;
+        else {
+            _struct_member * const members =
+                XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
+            *valuePP = members[index].value;
+            
+            XMLRPC_ASSERT_VALUE_OK(*valuePP);
+            
+            xmlrpc_INCREF(*valuePP);
+        }
+    }
+}
+
+
+
+void
+xmlrpc_struct_find_value_v(xmlrpc_env *    const envP,
+                           xmlrpc_value *  const structP,
+                           xmlrpc_value *  const keyP,
+                           xmlrpc_value ** const valuePP) {
+/*----------------------------------------------------------------------------
+  Given a key, retrieve a value from the struct.  If the key is not
+  present, return NULL as *valuePP.
+-----------------------------------------------------------------------------*/
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(structP);
+    XMLRPC_ASSERT_VALUE_OK(keyP);
+    
+    if (structP->_type != XMLRPC_TYPE_STRUCT)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TYPE_ERROR, "Value is not a struct.  It is type #%d",
+            structP->_type);
+    else {
+        if (keyP->_type != XMLRPC_TYPE_STRING)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_TYPE_ERROR, "Key value is not a string.  "
+                "It is type #%d",
+                keyP->_type);
+        else {
+            int index;
+
+            /* Get our member index. */
+            index = find_member(structP, 
+                                XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block),
+                                XMLRPC_MEMBLOCK_SIZE(char, &keyP->_block)-1);
+            if (index < 0)
+                *valuePP = NULL;
+            else {
+                _struct_member * const members =
+                    XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
+                *valuePP = members[index].value;
+                
+                XMLRPC_ASSERT_VALUE_OK(*valuePP);
+                
+                xmlrpc_INCREF(*valuePP);
+            }
+        }
+    }
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_read_value...
+**=========================================================================
+**  These fail if no member with the specified key exists.
+**  Otherwise, they are the same as xmlrpc_struct_find_value...
+*/
+
+void
+xmlrpc_struct_read_value_v(xmlrpc_env *    const envP,
+                           xmlrpc_value *  const structP,
+                           xmlrpc_value *  const keyP,
+                           xmlrpc_value ** const valuePP) {
+
+    xmlrpc_struct_find_value_v(envP, structP, keyP, valuePP);
+
+    if (!envP->fault_occurred) {
+        if (*valuePP == NULL) {
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%.*s'",
+                XMLRPC_MEMBLOCK_SIZE(char, &keyP->_block),
+                XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block));
+        }
+    }
+}
+
+
+
+void
+xmlrpc_struct_read_value(xmlrpc_env *    const envP,
+                         xmlrpc_value *  const structP,
+                         const char *    const key,
+                         xmlrpc_value ** const valuePP) {
+
+    xmlrpc_struct_find_value(envP, structP, key, valuePP);
+    
+    if (!envP->fault_occurred) {
+        if (*valuePP == NULL) {
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%s'",
+                key);
+            /* We should fix the error message to format the key for display */
+        }
+    }
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_get_value...
+**=========================================================================
+**  These are for backward compatibility.  They used to be the only ones.
+**  They're deprecated because they don't acquire a reference to the
+**  value they return.
+*/
+
+xmlrpc_value * 
+xmlrpc_struct_get_value_n(xmlrpc_env *   const envP,
+                          xmlrpc_value * const structP,
+                          const char *   const key, 
+                          size_t         const keyLen) {
+
+    xmlrpc_value * retval;
+    xmlrpc_value * keyP;
+    
+    keyP = xmlrpc_build_value(envP, "s#", key, keyLen);
+    if (!envP->fault_occurred) {
+        xmlrpc_struct_find_value_v(envP, structP, keyP, &retval);
+
+        if (!envP->fault_occurred) {
+            if (retval == NULL) {
+                xmlrpc_env_set_fault_formatted(
+                    envP, XMLRPC_INDEX_ERROR, 
+                    "No member of struct has key '%.*s'",
+                    keyLen, key);
+                /* We should fix the error message to format the key
+                   for display */
+            } else
+                /* For backward compatibility.  */
+                xmlrpc_DECREF(retval);
+        }
+        xmlrpc_DECREF(keyP);
+    }
+    return retval;
+}
+
+
+
+xmlrpc_value * 
+xmlrpc_struct_get_value(xmlrpc_env *   const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key) {
+
+    XMLRPC_ASSERT(key != NULL);
+    return xmlrpc_struct_get_value_n(envP, strctP, key, strlen(key));
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_struct_set_value
+**=========================================================================
+*/
+
+void 
+xmlrpc_struct_set_value(xmlrpc_env *   const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key,
+                        xmlrpc_value * const valueP) {
+
+    XMLRPC_ASSERT(key != NULL);
+    xmlrpc_struct_set_value_n(envP, strctP, key, strlen(key), valueP);
+}
+
+
+
+void 
+xmlrpc_struct_set_value_n(xmlrpc_env *    const envP,
+                          xmlrpc_value *  const strctP,
+                          const char *    const key, 
+                          size_t          const key_len,
+                          xmlrpc_value *  const valueP) {
+
+    xmlrpc_value *keyval;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT(key != NULL);
+
+    /* Set up error handling preconditions. */
+    keyval = NULL;
+
+    XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
+
+    /* Build an xmlrpc_value from our string. */
+    keyval = xmlrpc_build_value(envP, "s#", key, key_len);
+    XMLRPC_FAIL_IF_FAULT(envP);
+
+    /* Do the actual work. */
+    xmlrpc_struct_set_value_v(envP, strctP, keyval, valueP);
+
+ cleanup:
+    if (keyval)
+        xmlrpc_DECREF(keyval);
+}
+
+
+
+void 
+xmlrpc_struct_set_value_v(xmlrpc_env *   const envP,
+                          xmlrpc_value * const strctP,
+                          xmlrpc_value * const keyvalP,
+                          xmlrpc_value * const valueP) {
+
+    char *key;
+    size_t key_len;
+    int index;
+    _struct_member *members, *member, new_member;
+    xmlrpc_value *old_value;
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(strctP);
+    XMLRPC_ASSERT_VALUE_OK(keyvalP);
+    XMLRPC_ASSERT_VALUE_OK(valueP);
+
+    XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
+    XMLRPC_TYPE_CHECK(envP, keyvalP, XMLRPC_TYPE_STRING);
+
+    key = XMLRPC_MEMBLOCK_CONTENTS(char, &keyvalP->_block);
+    key_len = XMLRPC_MEMBLOCK_SIZE(char, &keyvalP->_block) - 1;
+    index = find_member(strctP, key, key_len);
+
+    if (index >= 0) {
+        /* Change the value of an existing member. (But be careful--the
+        ** original and new values might be the same object, so watch
+        ** the order of INCREF and DECREF calls!) */
+        members = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &strctP->_block);
+        member = &members[index];
+
+        /* Juggle our references. */
+        old_value = member->value;
+        member->value = valueP;
+        xmlrpc_INCREF(member->value);
+        xmlrpc_DECREF(old_value);
+    } else {
+        /* Add a new member. */
+        new_member.key_hash = get_hash(key, key_len);
+        new_member.key      = keyvalP;
+        new_member.value    = valueP;
+        XMLRPC_MEMBLOCK_APPEND(_struct_member, envP, &strctP->_block,
+                               &new_member, 1);
+        XMLRPC_FAIL_IF_FAULT(envP);
+        xmlrpc_INCREF(keyvalP);
+        xmlrpc_INCREF(valueP);
+    }
+
+cleanup:
+    return;
+}
+
+
+
+/* Note that the order of keys and values is undefined, and may change
+   when you modify the struct.
+*/
+
+void 
+xmlrpc_struct_read_member(xmlrpc_env *    const envP,
+                          xmlrpc_value *  const structP,
+                          unsigned int    const index,
+                          xmlrpc_value ** const keyvalP,
+                          xmlrpc_value ** const valueP) {
+
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(structP);
+    XMLRPC_ASSERT_PTR_OK(keyvalP);
+    XMLRPC_ASSERT_PTR_OK(valueP);
+
+    if (structP->_type != XMLRPC_TYPE_STRUCT)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TYPE_ERROR, "Attempt to read a struct member "
+            "of something that is not a struct");
+    else {
+        _struct_member * const members =
+            XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
+        size_t const size = 
+            XMLRPC_MEMBLOCK_SIZE(_struct_member, &structP->_block);
+
+        if (index >= size)
+            xmlrpc_env_set_fault_formatted(
+                envP, XMLRPC_INDEX_ERROR, "Index %u is beyond the end of "
+                "the %u-member structure", index, (unsigned int)size);
+        else {
+            _struct_member * const memberP = &members[index];
+            *keyvalP = memberP->key;
+            xmlrpc_INCREF(memberP->key);
+            *valueP = memberP->value;
+            xmlrpc_INCREF(memberP->value);
+        }
+    }
+}
+
+
+
+void 
+xmlrpc_struct_get_key_and_value(xmlrpc_env *    const envP,
+                                xmlrpc_value *  const structP,
+                                int             const index,
+                                xmlrpc_value ** const keyvalP,
+                                xmlrpc_value ** const valueP) {
+/*----------------------------------------------------------------------------
+   Same as xmlrpc_struct_read_member(), except doesn't take a reference
+   to the returned value.
+
+   This is obsolete.
+-----------------------------------------------------------------------------*/
+    XMLRPC_ASSERT_ENV_OK(envP);
+    XMLRPC_ASSERT_VALUE_OK(structP);
+    XMLRPC_ASSERT_PTR_OK(keyvalP);
+    XMLRPC_ASSERT_PTR_OK(valueP);
+
+    if (index < 0)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_INDEX_ERROR, "Index %d is negative.");
+    else {
+        xmlrpc_struct_read_member(envP, structP, index, keyvalP, valueP);
+        if (!envP->fault_occurred) {
+            xmlrpc_DECREF(*keyvalP);
+            xmlrpc_DECREF(*valueP);
+        }
+    }
+    if (envP->fault_occurred) {
+        *keyvalP = NULL;
+        *valueP = NULL;
+    }
+}

+ 75 - 0
Utilities/cmxmlrpc/xmlrpc_strutil.c

@@ -0,0 +1,75 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_config.h"
+
+
+
+const char * 
+xmlrpc_makePrintable(const char * const input) {
+/*----------------------------------------------------------------------------
+   Convert an arbitrary string of bytes (null-terminated, though) to
+   printable ASCII.  E.g. convert newlines to "\n".
+
+   Return the result in newly malloc'ed storage.  Return NULL if we can't
+   get the storage.
+-----------------------------------------------------------------------------*/
+    char * output;
+    const unsigned int inputLength = strlen(input);
+
+    output = malloc(inputLength*4+1);
+
+    if (output != NULL) {
+        unsigned int inputCursor, outputCursor;
+
+        for (inputCursor = 0, outputCursor = 0; 
+             inputCursor < inputLength; 
+             ++inputCursor) {
+
+            if (isprint(input[inputCursor]))
+                output[outputCursor++] = input[inputCursor]; 
+            else if (input[inputCursor] == '\n') {
+                output[outputCursor++] = '\\';
+                output[outputCursor++] = 'n';
+            } else if (input[inputCursor] == '\t') {
+                output[outputCursor++] = '\\';
+                output[outputCursor++] = 't';
+            } else if (input[inputCursor] == '\a') {
+                output[outputCursor++] = '\\';
+                output[outputCursor++] = 'a';
+            } else if (input[inputCursor] == '\r') {
+                output[outputCursor++] = '\\';
+                output[outputCursor++] = 'r';
+            } else {
+                snprintf(&output[outputCursor], 4, "\\x%02x", 
+                         input[inputCursor]);
+            }
+        }
+        output[outputCursor++] = '\0';
+    }
+    return output;
+}
+
+
+
+const char *
+xmlrpc_makePrintableChar(char const input) {
+
+    const char * retval;
+
+    if (input == '\0')
+        retval = strdup("\\0");
+    else {
+        char buffer[2];
+        
+        buffer[0] = input;
+        buffer[1] = '\0';
+        
+        retval = xmlrpc_makePrintable(buffer);
+    }
+    return retval;
+}

+ 363 - 0
Utilities/cmxmlrpc/xmlrpc_support.c

@@ -0,0 +1,363 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_int.h"
+
+#ifdef EFENCE
+                /* when looking for corruption don't allocate extra slop */
+#define BLOCK_ALLOC_MIN (1)
+#else
+#define BLOCK_ALLOC_MIN (16)
+#endif
+#define BLOCK_ALLOC_MAX (128 * 1024 * 1024)
+
+#define ERROR_BUFFER_SZ (256)
+
+
+/*=========================================================================
+**  Strings
+**=======================================================================*/
+
+void
+xmlrpc_strfree(const char * const string) {
+    free((void*)string);
+}
+
+
+/*=========================================================================
+**  Assertions and Error Handling
+**=========================================================================
+**  Support code for XMLRPC_ASSERT and xmlrpc_env.
+*/ 
+
+void xmlrpc_assertion_failed (char* file, int line)
+{
+    fprintf(stderr, "%s:%d: assertion failed\n", file, line);
+    abort();
+}
+
+static char* default_fault_string = "Not enough memory for error message";
+
+void xmlrpc_env_init (xmlrpc_env* env)
+{
+    XMLRPC_ASSERT(env != NULL);
+
+    env->fault_occurred = 0;
+    env->fault_code     = 0;
+    env->fault_string   = NULL;
+}
+
+void xmlrpc_env_clean (xmlrpc_env* env)
+{
+    XMLRPC_ASSERT(env != NULL);
+
+    /* env->fault_string may be one of three things:
+    **   1) a NULL pointer
+    **   2) a pointer to the default_fault_string
+    **   3) a pointer to a malloc'd fault string
+    ** If we have case (3), we'll need to free it. */
+    if (env->fault_string && env->fault_string != default_fault_string)
+        free(env->fault_string);
+    env->fault_string = XMLRPC_BAD_POINTER;
+}
+
+
+
+void 
+xmlrpc_env_set_fault(xmlrpc_env * const env, 
+                     int          const faultCode, 
+                     const char * const faultDescription) {
+
+    XMLRPC_ASSERT(env != NULL); 
+    XMLRPC_ASSERT(faultDescription != NULL);
+
+    /* Clean up any leftover pointers. */
+    xmlrpc_env_clean(env);
+
+    env->fault_occurred = 1;
+    env->fault_code     = faultCode;
+
+    /* Try to copy the fault string. If this fails, use a default. */
+    env->fault_string = (char*) malloc(strlen(faultDescription) + 1);
+    if (env->fault_string)
+        strcpy(env->fault_string, faultDescription);
+    else
+        env->fault_string = default_fault_string;
+}
+
+
+
+void 
+xmlrpc_env_set_fault_formatted (xmlrpc_env * const envP, 
+                                int          const code,
+                                const char * const format, 
+                                ...) {
+    va_list args;
+    char buffer[ERROR_BUFFER_SZ];
+
+    XMLRPC_ASSERT(envP != NULL);
+    XMLRPC_ASSERT(format != NULL);
+
+    /* Print our error message to the buffer. */
+    va_start(args, format);
+    vsnprintf(buffer, ERROR_BUFFER_SZ, format, args);
+    va_end(args);
+
+    /* vsnprintf is guaranteed to terminate the buffer, but we're paranoid. */
+    buffer[ERROR_BUFFER_SZ - 1] = '\0';
+
+    /* Set the fault. */
+    xmlrpc_env_set_fault(envP, code, buffer);
+}
+
+
+
+void xmlrpc_fatal_error (char* file, int line, char* msg)
+{
+    fprintf(stderr, "%s:%d: %s\n", file, line, msg);
+    exit(1);
+}
+
+
+/*=========================================================================
+**  Resource Limits
+**=========================================================================
+*/ 
+
+static size_t limits[XMLRPC_LAST_LIMIT_ID + 1] = {
+    XMLRPC_NESTING_LIMIT_DEFAULT,
+    XMLRPC_XML_SIZE_LIMIT_DEFAULT
+};
+
+void xmlrpc_limit_set (int limit_id, size_t value)
+{
+    XMLRPC_ASSERT(0 <= limit_id && limit_id <= XMLRPC_LAST_LIMIT_ID);
+    limits[limit_id] = value;
+}
+
+size_t xmlrpc_limit_get (int limit_id)
+{
+    XMLRPC_ASSERT(0 <= limit_id && limit_id <= XMLRPC_LAST_LIMIT_ID);
+    return limits[limit_id];
+}
+
+
+/*=========================================================================
+**  xmlrpc_mem_block
+**=========================================================================
+**  We support resizable blocks of memory. We need these just about
+**  everywhere.
+*/
+
+xmlrpc_mem_block * 
+xmlrpc_mem_block_new(xmlrpc_env * const env, 
+                     size_t       const size) {
+    xmlrpc_mem_block* block;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+
+    block = (xmlrpc_mem_block*) malloc(sizeof(xmlrpc_mem_block));
+    XMLRPC_FAIL_IF_NULL(block, env, XMLRPC_INTERNAL_ERROR,
+                        "Can't allocate memory block");
+
+    xmlrpc_mem_block_init(env, block, size);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+                     cleanup:
+    if (env->fault_occurred) {
+        if (block)
+            free(block);
+        return NULL;
+    } else {
+        return block;
+    }
+}
+
+/* Destroy an existing xmlrpc_mem_block, and everything it contains. */
+void xmlrpc_mem_block_free (xmlrpc_mem_block* block)
+{
+    XMLRPC_ASSERT(block != NULL);
+    XMLRPC_ASSERT(block->_block != NULL);
+
+    xmlrpc_mem_block_clean(block);
+    free(block);
+}
+
+/* Initialize the contents of the provided xmlrpc_mem_block. */
+void xmlrpc_mem_block_init (xmlrpc_env* env,
+                            xmlrpc_mem_block* block,
+                            size_t size)
+{
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(block != NULL);
+
+    block->_size = size;
+    if (size < BLOCK_ALLOC_MIN)
+        block->_allocated = BLOCK_ALLOC_MIN;
+    else
+        block->_allocated = size;
+
+    block->_block = (void*) malloc(block->_allocated);
+    if (!block->_block)
+        xmlrpc_env_set_fault_formatted(
+            env, XMLRPC_INTERNAL_ERROR,
+            "Can't allocate %u-byte memory block",
+            block->_allocated);
+}
+
+/* Deallocate the contents of the provided xmlrpc_mem_block, but not the
+** block itself. */
+void xmlrpc_mem_block_clean (xmlrpc_mem_block* block)
+{
+    XMLRPC_ASSERT(block != NULL);
+    XMLRPC_ASSERT(block->_block != NULL);
+
+    free(block->_block);
+    block->_block = XMLRPC_BAD_POINTER;
+}
+
+
+
+/* Get the size of the xmlrpc_mem_block. */
+size_t 
+xmlrpc_mem_block_size(const xmlrpc_mem_block * const block) {
+
+    XMLRPC_ASSERT(block != NULL);
+    return block->_size;
+}
+
+
+
+/* Get the contents of the xmlrpc_mem_block. */
+void * 
+xmlrpc_mem_block_contents(const xmlrpc_mem_block * const block) {
+
+    XMLRPC_ASSERT(block != NULL);
+    return block->_block;
+}
+
+
+
+/* Resize an xmlrpc_mem_block, preserving as much of the contents as
+** possible. */
+void 
+xmlrpc_mem_block_resize (xmlrpc_env *       const env,
+                         xmlrpc_mem_block * const block,
+                         size_t             const size) {
+
+    size_t proposed_alloc;
+    void* new_block;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(block != NULL);
+
+    /* Check to see if we already have enough space. Maybe we'll get lucky. */
+    if (size <= block->_allocated) {
+        block->_size = size;
+        return;
+    }
+
+    /* Calculate a new allocation size. */
+#ifdef EFENCE
+    proposed_alloc = size;
+#else
+    proposed_alloc = block->_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(env, XMLRPC_INTERNAL_ERROR, "Memory block too large");
+
+    /* Allocate our new memory block. */
+    new_block = (void*) malloc(proposed_alloc);
+    XMLRPC_FAIL_IF_NULL(new_block, env, XMLRPC_INTERNAL_ERROR,
+                        "Can't resize memory block");
+
+    /* Copy over our data and update the xmlrpc_mem_block struct. */
+    memcpy(new_block, block->_block, block->_size);
+    free(block->_block);
+    block->_block     = new_block;
+    block->_size      = size;
+    block->_allocated = proposed_alloc;
+
+ cleanup:
+    return;
+}
+
+/* Append data to an existing xmlrpc_mem_block. */
+void 
+xmlrpc_mem_block_append(xmlrpc_env *       const env,
+                        xmlrpc_mem_block * const block,
+                        void *             const data, 
+                        size_t             const len)
+{
+    int size;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT(block != NULL);
+
+    size = block->_size;
+    xmlrpc_mem_block_resize(env, block, size + len);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    memcpy(((unsigned char*) block->_block) + size, data, len);
+
+ cleanup:
+    return;
+}
+
+
+
+void
+xmlrpc_traceXml(const char * const label, 
+                const char * const xml,
+                unsigned int const xmlLength) {
+
+    if (getenv("XMLRPC_TRACE_XML")) {
+        unsigned int nonPrintableCount;
+        unsigned int i;
+
+        nonPrintableCount = 0;  /* Initial value */
+
+        for (i = 0; i < xmlLength; ++i) {
+            if (!isprint(xml[i]) && xml[i] != '\n' && xml[i] != '\r')
+                ++nonPrintableCount;
+        }
+        if (nonPrintableCount > 0)
+            fprintf(stderr, "%s contains %u nonprintable characters.\n", 
+                    label, nonPrintableCount);
+
+        fprintf(stderr, "%s:\n %.*s\n", label, (int)xmlLength, xml);
+    }
+}

+ 142 - 0
Utilities/cmxmlrpc/xmlrpc_transport.c

@@ -0,0 +1,142 @@
+/* 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. */
+
+#include "xmlrpc_config.h"
+
+#undef PACKAGE
+#undef VERSION
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef WIN32
+#ifdef _DEBUG
+#       include <crtdbg.h>
+#       define new DEBUG_NEW
+#       define malloc(size) _malloc_dbg( size, _NORMAL_BLOCK, __FILE__, __LINE__)
+#       undef THIS_FILE
+        static char THIS_FILE[] = __FILE__;
+#endif
+#endif /*WIN32*/
+
+#include "xmlrpc.h"
+#include "xmlrpc_client.h"
+
+#if defined (WIN32)
+#include <process.h>
+#endif
+
+/* For debugging the xmlrpc_transport threading. */
+/* #define tdbg_printf printf */
+#define tdbg_printf (void *)
+
+/* Lacking from the abyss/thread.c implimentaion. */
+void wait_for_asynch_thread(pthread_t *thread)
+{
+#if WIN32
+        unsigned long milliseconds = INFINITE;
+        switch (WaitForSingleObject (
+        *thread /* handle to object to wait for */,
+        milliseconds /* time-out interval in milliseconds*/) )
+        {
+                /* One may want to handle these cases  */
+        case WAIT_OBJECT_0:
+        case WAIT_TIMEOUT:
+                break;
+        }
+#else
+        void * result;
+        int success;
+        success = pthread_join (*thread, &result);
+#endif
+}
+
+/* MRB-WARNING: Only call when you have successfully
+**     acquired the Lock/Unlock mutex! */
+void unregister_asynch_thread (running_thread_list *list, pthread_t *thread)
+{
+        running_thread_info * pCur = NULL;
+        XMLRPC_ASSERT_PTR_OK(thread);
+        XMLRPC_ASSERT_PTR_OK(list);
+
+        tdbg_printf("unregister_asynch_thread: &pthread_id = %08X *(%08X)\n", thread, *thread);
+        /* Removal */
+        /* Lock (); */
+        for (pCur = list->AsyncThreadHead; pCur != NULL; pCur = (running_thread_info *)pCur->Next)
+        {
+                if (pCur->_thread == *thread)
+                {
+                        if (pCur == list->AsyncThreadHead)
+                                list->AsyncThreadHead = pCur->Next;
+                        if (pCur == list->AsyncThreadTail)
+                                list->AsyncThreadTail = pCur->Last;
+                        if (pCur->Last)
+                                ((running_thread_info *)(pCur->Last))->Next = pCur->Next;
+                        if (pCur->Next)
+                                ((running_thread_info *)(pCur->Next))->Last = pCur->Last;
+                        /* Free malloc'd running_thread_info */
+                        free (pCur);
+                        return;
+                }
+        }
+
+        /* This is a serious progmatic error, since the thread
+        ** should be in that list! */
+        XMLRPC_ASSERT_PTR_OK(0x0000);
+
+        /* Unlock (); */
+}
+
+/* MRB-WARNING: Only call when you have successfully
+**     acquired the Lock/Unlock mutex! */
+void register_asynch_thread (running_thread_list *list, pthread_t *thread)
+{
+        running_thread_info* info = (running_thread_info *) malloc(sizeof(running_thread_info));
+
+        XMLRPC_ASSERT_PTR_OK(thread);
+        XMLRPC_ASSERT_PTR_OK(list);
+        
+        tdbg_printf("register_asynch_thread: &pthread_id = %08X *(%08X)\n", thread, *thread);
+
+        info->_thread = *thread;
+
+        /* Insertion */
+        /* Lock (); */
+        if (list->AsyncThreadHead == NULL)
+        {
+                list->AsyncThreadHead = list->AsyncThreadTail = info;
+                list->AsyncThreadTail->Next = list->AsyncThreadHead->Next = NULL;
+                list->AsyncThreadTail->Last = list->AsyncThreadHead->Last = NULL;
+        }
+        else
+        {
+                info->Last = list->AsyncThreadTail;
+                list->AsyncThreadTail->Next = info;
+                list->AsyncThreadTail = list->AsyncThreadTail->Next;
+                list->AsyncThreadTail->Next = NULL;
+        }
+        /* Unlock (); */
+}

+ 125 - 0
Utilities/cmxmlrpc/xmlrpc_transport.h

@@ -0,0 +1,125 @@
+/* Copyright information is at the end of the file */
+#ifndef  _XMLRPC_TRANSPORT_H_
+#define  _XMLRPC_TRANSPORT_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "xmlrpc_pthreads.h" /* For threading helpers. */
+
+struct call_info;
+
+struct clientTransport;
+
+/*=========================================================================
+**  Transport function type declarations.
+**=========================================================================
+*/
+typedef void (*transport_create)(
+    xmlrpc_env *              const envP,
+    int                       const flags,
+    const char *              const appname,
+    const char *              const appversion,
+    struct clientTransport ** const handlePP);
+    
+typedef void (*transport_destroy)(
+    struct clientTransport * const clientTransportP);
+
+typedef void (*transport_asynch_complete)(
+    struct call_info * const callInfoP,
+    xmlrpc_mem_block * const responseXmlP,
+    xmlrpc_env         const env);
+
+typedef void (*transport_send_request)(
+    xmlrpc_env *             const envP, 
+    struct clientTransport * const clientTransportP,
+    xmlrpc_server_info *     const serverP,
+    xmlrpc_mem_block *       const xmlP,
+    transport_asynch_complete      complete,
+    struct call_info *       const callInfoP);
+
+typedef void (*transport_call)(
+    xmlrpc_env *             const envP,
+    struct clientTransport * const clientTransportP,
+    xmlrpc_server_info *     const serverP,
+    xmlrpc_mem_block *       const xmlP,
+    struct call_info *       const callInfoP,
+    xmlrpc_mem_block **      const responsePP);
+
+enum timeoutType {timeout_no, timeout_yes};
+typedef void (*transport_finish_asynch)(
+    struct clientTransport * const clientTransportP,
+    enum timeoutType         const timeoutType,
+    timeout_t                const timeout);
+
+
+struct clientTransportOps {
+
+    transport_create        create;
+    transport_destroy       destroy;
+    transport_send_request  send_request;
+    transport_call          call;
+    transport_finish_asynch finish_asynch;
+};
+
+/*=========================================================================
+**  Transport Helper Functions and declarations.
+**=========================================================================
+*/
+typedef struct _running_thread_info
+{
+        struct _running_thread_info * Next;
+        struct _running_thread_info * Last;
+
+        pthread_t _thread;
+} running_thread_info;
+
+
+/* list of running Async callback functions. */
+typedef struct _running_thread_list
+{
+        running_thread_info * AsyncThreadHead;
+        running_thread_info * AsyncThreadTail;
+} running_thread_list;
+
+/* MRB-WARNING: Only call when you have successfully
+**     acquired the Lock/Unlock mutex! */
+void register_asynch_thread (running_thread_list *list, pthread_t *thread);
+
+/* MRB-WARNING: Only call when you have successfully
+**     acquired the Lock/Unlock mutex! */
+void unregister_asynch_thread (running_thread_list *list, pthread_t *thread);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/* 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. */
+
+
+#endif

+ 376 - 0
Utilities/cmxmlrpc/xmlrpc_utf8.c

@@ -0,0 +1,376 @@
+/* Copyright (C) 2001 by Eric Kidd. 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. */
+
+
+/*=========================================================================
+**  XML-RPC UTF-8 Utilities
+**=========================================================================
+**  Routines for validating, encoding and decoding UTF-8 data.  We try to
+**  be very, very strict about invalid UTF-8 data.
+**
+**  All of the code in this file assumes that your machine represents
+**  wchar_t as a 16-bit (or wider) character containing UCS-2 data.  If this
+**  assumption is incorrect, you may need to replace this file.
+**
+**  For lots of information on Unicode and UTF-8 decoding, see:
+**    http://www.cl.cam.ac.uk/~mgk25/unicode.html
+*/
+
+#include "xmlrpc_config.h"
+
+#include "xmlrpc.h"
+
+#ifdef HAVE_UNICODE_WCHAR
+
+/*=========================================================================
+**  Tables and Constants
+**=========================================================================
+**  We use a variety of tables and constants to help decode and validate
+**  UTF-8 data.
+*/
+
+/* The number of bytes in a UTF-8 sequence starting with the character used
+** as the array index.  A zero entry indicates an illegal initial byte.
+** This table was generated using a Perl script and information from the
+** UTF-8 standard.
+**
+** Fredrik Lundh's UTF-8 decoder Python 2.0 uses a similar table.  But
+** since Python 2.0 has the icky CNRI license, I regenerated this
+** table from scratch and wrote my own decoder. */
+static unsigned char utf8_seq_length[256] = {
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+    4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0
+};
+
+/* The minimum legal character value for a UTF-8 sequence of the given
+** length.  We have to check this to avoid accepting "overlong" UTF-8
+** sequences, which use more bytes than necessary to encode a given
+** character.  Such sequences are commonly used by evil people to bypass
+** filters and security checks.  This table is based on the UTF-8-test.txt
+** file by Markus Kuhn <[email protected]>. */
+static wchar_t utf8_min_char_for_length[4] = {
+    0,          /* Length 0: Not used (meaningless) */
+    0x0000,     /* Length 1: Not used (special-cased) */
+    0x0080,     /* Length 2 */
+    0x0800      /* Length 3 */
+
+#if 0
+    /* These are only useful on systems where wchar_t is 32-bits wide
+    ** and supports full UCS-4. */
+    0x00010000, /* Length 4 */
+    0x00200000, /* Length 5 */
+    0x04000000  /* Length 6 */
+#endif
+};
+
+/* This is the maximum legal 16-byte (UCS-2) character.  Again, this
+** information is based on UTF-8-test.txt. */
+#define UCS2_MAX_LEGAL_CHARACTER (0xFFFD)
+
+/* First and last UTF-16 surrogate characters.  These are *not* legal UCS-2
+** characters--they're used to code for UCS-4 characters when using
+** UTF-16.  They should never appear in decoded UTF-8 data!  Again, these
+** could hypothetically be used to bypass security measures on some machines.
+** Based on UTF-8-test.txt. */
+#define UTF16_FIRST_SURROGATE (0xD800)
+#define UTF16_LAST_SURROGATE  (0xDFFF)
+
+/* Is the character 'c' a UTF-8 continuation character? */
+#define IS_CONTINUATION(c) (((c) & 0xC0) == 0x80)
+
+/* Maximum number of bytes needed to encode a supported character. */
+#define MAX_ENCODED_BYTES (3)
+
+
+/*=========================================================================
+**  decode_utf8
+**=========================================================================
+**  Internal routine which decodes (or validates) a UTF-8 string.
+**  To validate, set io_buff and out_buff_len to NULL.  To decode, allocate
+**  a sufficiently large buffer, pass it as io_buff, and pass a pointer as
+**  as out_buff_len.  The data will be written to the buffer, and the
+**  length to out_buff_len.
+**
+**  We assume that wchar_t holds a single UCS-2 character in native-endian
+**  byte ordering.
+*/
+
+static void 
+decode_utf8(xmlrpc_env * const env,
+            const char * const utf8_data,
+            size_t       const utf8_len,
+            wchar_t *    const io_buff,
+            size_t *     const out_buff_len) {
+
+    size_t i, length, out_pos;
+    char init, con1, con2;
+    wchar_t wc;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(utf8_data);
+    XMLRPC_ASSERT((!io_buff && !out_buff_len) ||
+                  (io_buff && out_buff_len));
+
+    /* Suppress GCC warning about possibly undefined variable. */
+    wc = 0;
+
+    i = 0;
+    out_pos = 0;
+    while (i < utf8_len) {
+        init = utf8_data[i];
+        if ((init & 0x80) == 0x00) {
+            /* Convert ASCII character to wide character. */
+            wc = init;
+            i++;
+        } else {
+            /* Look up the length of this UTF-8 sequence. */
+            length = utf8_seq_length[(unsigned char) init];
+            
+            /* Check to make sure we have enough bytes to convert. */
+            if (i + length > utf8_len)
+                XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                            "Truncated UTF-8 sequence");
+            
+            /* Decode a multibyte UTF-8 sequence. */
+            switch (length) {
+            case 0:
+                XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                            "Invalid UTF-8 initial byte");
+                
+            case 2:
+                /* 110xxxxx 10xxxxxx */
+                con1 = utf8_data[i+1];
+                if (!IS_CONTINUATION(con1))
+                    XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                                "UTF-8 sequence too short");
+                wc = ((((wchar_t) (init & 0x1F)) <<  6) |
+                      (((wchar_t) (con1 & 0x3F))));
+                break;
+                
+            case 3:
+                /* 1110xxxx 10xxxxxx 10xxxxxx */
+                con1 = utf8_data[i+1];
+                con2 = utf8_data[i+2];
+                if (!IS_CONTINUATION(con1) || !IS_CONTINUATION(con2))
+                    XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                                "UTF-8 sequence too short");
+                wc = ((((wchar_t) (init & 0x0F)) << 12) |
+                      (((wchar_t) (con1 & 0x3F)) <<  6) |
+                      (((wchar_t) (con2 & 0x3F))));
+                break;
+                
+            case 4:
+                /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+            case 5:
+                /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+            case 6:
+                /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+                XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                            "UCS-4 characters not supported");
+                
+            default:
+                XMLRPC_ASSERT("Error in UTF-8 decoder tables");
+            }
+                    
+            /* Advance to the end of the sequence. */
+            i += length;
+            
+            /* Check for illegal UCS-2 characters. */
+            if (wc > UCS2_MAX_LEGAL_CHARACTER)
+                XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                            "UCS-2 characters > U+FFFD are illegal");
+            
+            /* Check for UTF-16 surrogates. */
+            if (UTF16_FIRST_SURROGATE <= wc && wc <= UTF16_LAST_SURROGATE)
+                XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                            "UTF-16 surrogates may not appear in UTF-8 data");
+            
+            /* Check for overlong sequences. */
+            if (wc < utf8_min_char_for_length[length])
+                XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
+                            "Overlong UTF-8 sequence not allowed");
+        }
+        
+        /* If we have a buffer, write our character to it. */
+        if (io_buff) {
+            io_buff[out_pos++] = wc;
+        }
+    }
+    
+    /* Record the number of characters we found. */
+    if (out_buff_len)
+        *out_buff_len = out_pos;
+    
+ cleanup:
+    if (env->fault_occurred) {
+        if (out_buff_len)
+            *out_buff_len = 0;
+    }
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_validate_utf8
+**=========================================================================
+**  Make sure that a UTF-8 string is valid.
+*/
+
+void 
+xmlrpc_validate_utf8 (xmlrpc_env * const env,
+                      const char * const utf8_data,
+                      size_t       const utf8_len) {
+
+    decode_utf8(env, utf8_data, utf8_len, NULL, NULL);
+}
+
+
+/*=========================================================================
+**  xmlrpc_utf8_to_wcs
+**=========================================================================
+**  Decode UTF-8 string to a "wide character string".  This function
+**  returns an xmlrpc_mem_block with an element type of wchar_t.  Don't
+**  try to intepret the block in a bytewise fashion--it won't work in
+**  any useful or portable fashion.
+*/
+
+xmlrpc_mem_block *xmlrpc_utf8_to_wcs (xmlrpc_env *env,
+                                      char *utf8_data,
+                                      size_t utf8_len)
+{
+    xmlrpc_mem_block *output;
+    size_t wcs_length;
+
+    /* Error-handling preconditions. */
+    output = NULL;
+
+    /* Allocate a memory block large enough to hold any possible output.
+    ** We assume that each byte of the input may decode to a whcar_t. */
+    output = XMLRPC_TYPED_MEM_BLOCK_NEW(wchar_t, env, utf8_len);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Decode the UTF-8 data. */
+    decode_utf8(env, utf8_data, utf8_len,
+                XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, output),
+                &wcs_length);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Make sure we didn't overrun our buffer. */
+    XMLRPC_ASSERT(wcs_length <= utf8_len);
+
+    /* Correct the length of the memory block. */
+    XMLRPC_TYPED_MEM_BLOCK_RESIZE(wchar_t, env, output, wcs_length);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (output)
+            xmlrpc_mem_block_free(output);
+        return NULL;
+    }
+    return output;
+}
+
+
+/*=========================================================================
+**  xmlrpc_utf8_to_wcs
+**=========================================================================
+**  Encode a "wide character string" as UTF-8.
+*/
+
+xmlrpc_mem_block *xmlrpc_wcs_to_utf8 (xmlrpc_env *env,
+                                      wchar_t *wcs_data,
+                                      size_t wcs_len)
+{
+    size_t estimate, bytes_used, i;
+    xmlrpc_mem_block *output;
+    unsigned char *buffer;
+    wchar_t wc;
+
+    XMLRPC_ASSERT_ENV_OK(env);
+    XMLRPC_ASSERT_PTR_OK(wcs_data);
+
+    /* Error-handling preconditions. */
+    output = NULL;
+
+    /* Allocate a memory block large enough to hold any possible output.
+    ** We assume that every wchar might encode to the maximum length. */
+    estimate = wcs_len * MAX_ENCODED_BYTES;
+    output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, estimate);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+    /* Output our characters. */
+    buffer = (unsigned char*) XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output);
+    bytes_used = 0;
+    for (i = 0; i < wcs_len; i++) {
+        wc = wcs_data[i];
+        if (wc <= 0x007F) {
+            buffer[bytes_used++] = wc & 0x7F;
+        } else if (wc <= 0x07FF) {
+            /* 110xxxxx 10xxxxxx */
+            buffer[bytes_used++] = 0xC0 | (wc >> 6);
+            buffer[bytes_used++] = 0x80 | (wc & 0x3F);
+        } else if (wc <= 0xFFFF) {
+            /* 1110xxxx 10xxxxxx 10xxxxxx */
+            buffer[bytes_used++] = 0xE0 | (wc >> 12);
+            buffer[bytes_used++] = 0x80 | ((wc >> 6) & 0x3F);
+            buffer[bytes_used++] = 0x80 | (wc & 0x3F);
+        } else {
+            XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
+                        "Don't know how to encode UCS-4 characters yet");
+        }
+    }
+
+    /* Make sure we didn't overrun our buffer. */
+    XMLRPC_ASSERT(bytes_used <= estimate);
+
+    /* Correct the length of the memory block. */
+    XMLRPC_TYPED_MEM_BLOCK_RESIZE(char, env, output, bytes_used);
+    XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+    if (env->fault_occurred) {
+        if (output)
+            xmlrpc_mem_block_free(output);
+        return NULL;
+    }
+    return output;
+}
+
+#endif /* HAVE_UNICODE_WCHAR */

+ 82 - 0
Utilities/cmxmlrpc/xmlrpc_xmlparser.h

@@ -0,0 +1,82 @@
+/* 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. */
+
+
+/*=========================================================================
+**  Abstract XML Parser Interface
+**=========================================================================
+**  This file provides an abstract interface to the XML parser. For now,
+**  this interface is implemented by expat, but feel free to change it
+**  if necessary.
+*/
+
+
+/*=========================================================================
+**  xml_element
+**=========================================================================
+**  This data structure represents an XML element. We provide no more API
+**  than we'll need in xmlrpc_parse.c.
+**
+**  The pointers returned by the various accessor methods belong to the
+**  xml_element structure. Do not free them, and do not use them after
+**  the xml_element has been destroyed.
+*/
+
+/* You'll need to finish defining struct _xml_element elsewhere. */
+typedef struct _xml_element xml_element;
+
+/* Destroy an xml_element. */
+void xml_element_free (xml_element *elem);
+
+/* Return a pointer to the element's name. Do not free this pointer!
+** This pointer should point to standard ASCII or UTF-8 data. */
+char *xml_element_name (xml_element *elem);
+
+/* Return the xml_element's CDATA. Do not free this pointer!
+** This pointer should point to standard ASCII or UTF-8 data.
+** The implementation is allowed to concatenate all the CDATA in the
+** element regardless of child elements. Alternatively, if there are
+** any child elements, the implementation is allowed to dispose
+** of whitespace characters.
+** The value returned by xml_element_cdata should be '\0'-terminated
+** (although it may contain other '\0' characters internally).
+** xml_element_cdata_size should not include the final '\0'. */
+size_t xml_element_cdata_size (xml_element *elem);
+char *xml_element_cdata (xml_element *elem);
+
+/* Return the xml_element's child elements. Do not free this pointer! */
+size_t xml_element_children_size (xml_element *elem);
+xml_element **xml_element_children (xml_element *elem);
+
+
+/*=========================================================================
+**  xml_parse
+**=========================================================================
+**  Parse a chunk of XML data and return the top-level element. If this
+**  routine fails, it will return NULL and set up the env appropriately.
+**  You are responsible for calling xml_element_free on the returned pointer.
+*/
+
+xml_element *xml_parse (xmlrpc_env *env, const char *xml_data, int xml_len);