diff options
40 files changed, 12478 insertions, 0 deletions
diff --git a/Utilities/cmxmlrpc/CMake/TryCompileFromSource.cmake b/Utilities/cmxmlrpc/CMake/TryCompileFromSource.cmake new file mode 100644 index 0000000..2eab76f --- /dev/null +++ b/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) diff --git a/Utilities/cmxmlrpc/CMakeLists.txt b/Utilities/cmxmlrpc/CMakeLists.txt new file mode 100644 index 0000000..eb41764 --- /dev/null +++ b/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}) diff --git a/Utilities/cmxmlrpc/XmlRpcCpp.cpp b/Utilities/cmxmlrpc/XmlRpcCpp.cpp new file mode 100644 index 0000000..119b695 --- /dev/null +++ b/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; +} diff --git a/Utilities/cmxmlrpc/XmlRpcCpp.h b/Utilities/cmxmlrpc/XmlRpcCpp.h new file mode 100644 index 0000000..05a3ad0 --- /dev/null +++ b/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_ */ diff --git a/Utilities/cmxmlrpc/bool.h b/Utilities/cmxmlrpc/bool.h new file mode 100644 index 0000000..8bf54f3 --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/mallocvar.h b/Utilities/cmxmlrpc/mallocvar.h new file mode 100644 index 0000000..f2022e9 --- /dev/null +++ b/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 + diff --git a/Utilities/cmxmlrpc/transport_config.h b/Utilities/cmxmlrpc/transport_config.h new file mode 100644 index 0000000..7900703 --- /dev/null +++ b/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"; diff --git a/Utilities/cmxmlrpc/win32_pthreads.c b/Utilities/cmxmlrpc/win32_pthreads.c new file mode 100644 index 0000000..fd0d2db --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc.h b/Utilities/cmxmlrpc/xmlrpc.h new file mode 100644 index 0000000..e32730a --- /dev/null +++ b/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 + diff --git a/Utilities/cmxmlrpc/xmlrpc_amconfig.h.in b/Utilities/cmxmlrpc/xmlrpc_amconfig.h.in new file mode 100644 index 0000000..0b3552a --- /dev/null +++ b/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@ diff --git a/Utilities/cmxmlrpc/xmlrpc_array.c b/Utilities/cmxmlrpc/xmlrpc_array.c new file mode 100644 index 0000000..fc56382 --- /dev/null +++ b/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. */ diff --git a/Utilities/cmxmlrpc/xmlrpc_authcookie.c b/Utilities/cmxmlrpc/xmlrpc_authcookie.c new file mode 100644 index 0000000..83541b9 --- /dev/null +++ b/Utilities/cmxmlrpc/xmlrpc_authcookie.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2002 by jeff@ourexchange.net. 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"); +} diff --git a/Utilities/cmxmlrpc/xmlrpc_base64.c b/Utilities/cmxmlrpc/xmlrpc_base64.c new file mode 100644 index 0000000..edcb09b --- /dev/null +++ b/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; +} diff --git a/Utilities/cmxmlrpc/xmlrpc_cgi.c b/Utilities/cmxmlrpc/xmlrpc_cgi.c new file mode 100644 index 0000000..e3a8b42 --- /dev/null +++ b/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); +} diff --git a/Utilities/cmxmlrpc/xmlrpc_cgi.h b/Utilities/cmxmlrpc/xmlrpc_cgi.h new file mode 100644 index 0000000..0b9fc6d --- /dev/null +++ b/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_ */ diff --git a/Utilities/cmxmlrpc/xmlrpc_client.c b/Utilities/cmxmlrpc/xmlrpc_client.c new file mode 100644 index 0000000..3603a21 --- /dev/null +++ b/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, ¶mArrayP, &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, ¶mArrayP, &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, ¶mArrayP, &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); + } +} + diff --git a/Utilities/cmxmlrpc/xmlrpc_client.h b/Utilities/cmxmlrpc/xmlrpc_client.h new file mode 100644 index 0000000..1460c74 --- /dev/null +++ b/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_ */ diff --git a/Utilities/cmxmlrpc/xmlrpc_client_int.h b/Utilities/cmxmlrpc/xmlrpc_client_int.h new file mode 100644 index 0000000..77d491e --- /dev/null +++ b/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 + + + diff --git a/Utilities/cmxmlrpc/xmlrpc_config.h.in b/Utilities/cmxmlrpc/xmlrpc_config.h.in new file mode 100644 index 0000000..7e74253 --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc_curl_transport.c b/Utilities/cmxmlrpc/xmlrpc_curl_transport.c new file mode 100644 index 0000000..37d3dea --- /dev/null +++ b/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, +}; diff --git a/Utilities/cmxmlrpc/xmlrpc_curl_transport.h b/Utilities/cmxmlrpc/xmlrpc_curl_transport.h new file mode 100644 index 0000000..8fc6f68 --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc_data.c b/Utilities/cmxmlrpc/xmlrpc_data.c new file mode 100644 index 0000000..76713c9 --- /dev/null +++ b/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. */ diff --git a/Utilities/cmxmlrpc/xmlrpc_expat.c b/Utilities/cmxmlrpc/xmlrpc_expat.c new file mode 100644 index 0000000..f1f4523 --- /dev/null +++ b/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; + } +} diff --git a/Utilities/cmxmlrpc/xmlrpc_int.h b/Utilities/cmxmlrpc/xmlrpc_int.h new file mode 100644 index 0000000..3abb6d3 --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc_libxml2.c b/Utilities/cmxmlrpc/xmlrpc_libxml2.c new file mode 100644 index 0000000..eb3ed72 --- /dev/null +++ b/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; + } +} diff --git a/Utilities/cmxmlrpc/xmlrpc_parse.c b/Utilities/cmxmlrpc/xmlrpc_parse.c new file mode 100644 index 0000000..8a1f172 --- /dev/null +++ b/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; +} diff --git a/Utilities/cmxmlrpc/xmlrpc_pthreads.h b/Utilities/cmxmlrpc/xmlrpc_pthreads.h new file mode 100644 index 0000000..81b0e80 --- /dev/null +++ b/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 + diff --git a/Utilities/cmxmlrpc/xmlrpc_registry.c b/Utilities/cmxmlrpc/xmlrpc_registry.c new file mode 100644 index 0000000..41e5ac1 --- /dev/null +++ b/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, ¶mArray); + + 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", ¶m_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; +} diff --git a/Utilities/cmxmlrpc/xmlrpc_serialize.c b/Utilities/cmxmlrpc/xmlrpc_serialize.c new file mode 100644 index 0000000..6a62ed1 --- /dev/null +++ b/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; /* < */ + else if (str[i] == '>') + needed += 4; /* > */ + else if (str[i] == '&') + needed += 5; /* & */ + 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); +} diff --git a/Utilities/cmxmlrpc/xmlrpc_server.h b/Utilities/cmxmlrpc/xmlrpc_server.h new file mode 100644 index 0000000..e51d960 --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc_server_abyss.c b/Utilities/cmxmlrpc/xmlrpc_server_abyss.c new file mode 100644 index 0000000..5964c33 --- /dev/null +++ b/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 <mmoez@bigfoot.com>. +** 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); +} diff --git a/Utilities/cmxmlrpc/xmlrpc_server_abyss.h b/Utilities/cmxmlrpc/xmlrpc_server_abyss.h new file mode 100644 index 0000000..95676fd --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc_server_abyss_int.h b/Utilities/cmxmlrpc/xmlrpc_server_abyss_int.h new file mode 100644 index 0000000..06cb17c --- /dev/null +++ b/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 + diff --git a/Utilities/cmxmlrpc/xmlrpc_struct.c b/Utilities/cmxmlrpc/xmlrpc_struct.c new file mode 100644 index 0000000..d31b8c3 --- /dev/null +++ b/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; + } +} diff --git a/Utilities/cmxmlrpc/xmlrpc_strutil.c b/Utilities/cmxmlrpc/xmlrpc_strutil.c new file mode 100644 index 0000000..8b4ca5d --- /dev/null +++ b/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; +} diff --git a/Utilities/cmxmlrpc/xmlrpc_support.c b/Utilities/cmxmlrpc/xmlrpc_support.c new file mode 100644 index 0000000..f69b7e2 --- /dev/null +++ b/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); + } +} diff --git a/Utilities/cmxmlrpc/xmlrpc_transport.c b/Utilities/cmxmlrpc/xmlrpc_transport.c new file mode 100644 index 0000000..3ae6e69 --- /dev/null +++ b/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 (); */ +} diff --git a/Utilities/cmxmlrpc/xmlrpc_transport.h b/Utilities/cmxmlrpc/xmlrpc_transport.h new file mode 100644 index 0000000..e84832f --- /dev/null +++ b/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 diff --git a/Utilities/cmxmlrpc/xmlrpc_utf8.c b/Utilities/cmxmlrpc/xmlrpc_utf8.c new file mode 100644 index 0000000..4e31362 --- /dev/null +++ b/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 <mkuhn@acm.org>. */ +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 */ diff --git a/Utilities/cmxmlrpc/xmlrpc_xmlparser.h b/Utilities/cmxmlrpc/xmlrpc_xmlparser.h new file mode 100644 index 0000000..e575e82 --- /dev/null +++ b/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); |