ENH: Initial import

This commit is contained in:
Andy Cedilnik 2005-02-22 13:08:27 -05:00
parent bfcb4b6937
commit b9b4ea0f7b
40 changed files with 12478 additions and 0 deletions

View File

@ -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)

View File

@ -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})

View File

@ -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;
}

View File

@ -0,0 +1,428 @@
// -*- C++ -*- <-- an Emacs control
// Copyright information is at the bottom of the file.
//=========================================================================
// XML-RPC C++ API
//=========================================================================
#ifndef _XMLRPCCPP_H_
#define _XMLRPCCPP_H_ 1
// The C++ standard says we should either include <string.h> (which gets
// us the wrong header), or say 'using namespace std;' (which doesn't
// work with our version of g++). So this header name is technically wrong.
// Tell me what your compiler does; I can provide some autoconf magic to the
// Right Thing on most platforms.
//
// 2004.12.22 Bryan: This looks like a problem with whatever g++ he was
// using, so if anything, the special case should be for that. In any case,
// we've already added using namespace std to other files, without knowing
// there was an issue, so I'm just going to do the "using namespace"
// unconditionally and see who complains. If there are complaints, we can
// improve the documentation here.
//
// Formerly, the "using namespace std" was under
// "#if defined(__GNUC__) && (__GNUC__ >= 3)".
#include <string>
using namespace std;
#include <xmlrpc_config.h>
#include <xmlrpc.h>
#include <xmlrpc_client.h>
#include <xmlrpc_server.h>
#define XMLRPC_NO_ASSIGNMENT \
XMLRPC_FATAL_ERROR("Assignment operator not available"); return *this;
//=========================================================================
// XmlRpcFault
//=========================================================================
// A C++ exception class representing an XML-RPC fault.
class XmlRpcFault {
private:
xmlrpc_env mFault;
XmlRpcFault& operator= (const XmlRpcFault& f)
{ (void) f; XMLRPC_NO_ASSIGNMENT }
public:
XmlRpcFault (const XmlRpcFault &fault);
XmlRpcFault (const int faultCode, const string faultString);
XmlRpcFault (const xmlrpc_env *env);
~XmlRpcFault (void);
int getFaultCode (void) const;
string getFaultString (void) const;
xmlrpc_env *getFaultEnv (void);
};
inline int XmlRpcFault::getFaultCode (void) const {
return mFault.fault_code;
}
inline xmlrpc_env *XmlRpcFault::getFaultEnv (void) {
return &mFault;
}
//=========================================================================
// XmlRpcEnv
//=========================================================================
// This class can be used to wrap xmlrpc_env object. Use it as follows:
//
// XmlRpcEnv env;
// xmlrpc_parse_value(env, v, "(i)", &i);
// env.throwIfFaultOccurred();
class XmlRpcEnv {
private:
xmlrpc_env mEnv;
void throwMe (void) const;
XmlRpcEnv& operator= (const XmlRpcEnv& e)
{ (void) e; XMLRPC_NO_ASSIGNMENT }
public:
XmlRpcEnv (const XmlRpcEnv &env);
XmlRpcEnv (void) { xmlrpc_env_init(&mEnv); }
~XmlRpcEnv (void) { xmlrpc_env_clean(&mEnv); }
bool faultOccurred (void) const { return mEnv.fault_occurred; };
bool hasFaultOccurred (void) const { return faultOccurred(); };
/* hasFaultOccurred() is for backward compatibility.
faultOccurred() is a superior name for this.
*/
XmlRpcFault getFault (void) const;
void throwIfFaultOccurred (void) const;
operator xmlrpc_env * (void) { return &mEnv; }
};
inline void XmlRpcEnv::throwIfFaultOccurred (void) const {
if (faultOccurred())
throwMe();
}
//=========================================================================
// XmlRpcValue
//=========================================================================
// An object in this class is an XML-RPC value.
//
// We have a complex structure to allow C code mixed in with C++ code
// which uses this class to refer to the same XML-RPC value object.
// This is especially important because there aren't proper C++ facilities
// for much of Xmlrpc-c; you have to use the C facilities even if you'd
// rather use proper C++.
//
// The XmlRpcValue object internally represents the value as an
// xmlrpc_value. It hold one reference to the xmlrpc_value. Users
// of XmlRpcValue never see that xmlrpc_value, but C code can. the
// C code might create the xmlrpc_value and then bind it to an XmlRpcValue,
// or it might get the xmlrpc_value handle from the XmlRpcValue. Finally,
// C code can simply use the XmlRpcValue where an xmlrpc_value handle is
// required and it gets converted automatically.
//
// So reference counting for the xmlrpc_value is quite a nightmare.
class XmlRpcValue {
private:
xmlrpc_value *mValue;
public:
enum ReferenceBehavior {
MAKE_REFERENCE,
CONSUME_REFERENCE
};
typedef xmlrpc_int32 int32;
XmlRpcValue (void);
XmlRpcValue (xmlrpc_value *value,
ReferenceBehavior behavior = MAKE_REFERENCE);
XmlRpcValue (const XmlRpcValue& value);
~XmlRpcValue (void);
XmlRpcValue& operator= (const XmlRpcValue& value);
// Accessing the value's type (think of this as lightweight RTTI).
xmlrpc_type getType(void) const;
// We don't supply an automatic conversion operator--you need to say
// whether you want to make or borrow this object's reference.
// XXX - Is it really OK for these to be const?
xmlrpc_value *makeReference (void) const;
xmlrpc_value *borrowReference (void) const;
// Some static "constructor" functions.
static XmlRpcValue makeInt (const XmlRpcValue::int32 i);
static XmlRpcValue makeBool (const bool b);
static XmlRpcValue makeDouble (const double d);
static XmlRpcValue makeDateTime (const string& dateTime);
static XmlRpcValue makeString (const string& str);
static XmlRpcValue makeString (const char *const str);
static XmlRpcValue makeString (const char *const str, size_t len);
static XmlRpcValue makeArray (void);
static XmlRpcValue makeStruct (void);
static XmlRpcValue makeBase64 (const unsigned char *const data,
size_t len);
/*
// An interface to xmlrpc_build_value.
static XmlRpcValue buildValue (const char *const format, ...);
*/
// Some functions to get the underlying data.
// These will throw an XmlRpcFault if the data is the wrong type.
XmlRpcValue::int32 getInt (void) const;
bool getBool (void) const;
double getDouble (void) const;
string getRawDateTime (void) const;
string getString (void) const;
XmlRpcValue getArray (void) const;
XmlRpcValue getStruct (void) const;
// This returns an internal pointer which will become invalid when
// all references to the underlying value are destroyed.
void getBase64 (const unsigned char *& out_data,
size_t& out_len) const;
/*
// An interface to xmlrpc_parse_value.
void parseValue (const char *const format, ...);
*/
// Array functions. These will throw an XmlRpcFault if the value
// isn't an array.
size_t arraySize (void) const;
void arrayAppendItem (const XmlRpcValue& value);
XmlRpcValue arrayGetItem (int index) const;
// Struct functions. These will throw an XmlRpcFault if the value
// isn't a struct.
size_t structSize (void) const;
bool structHasKey (const string& key) const;
XmlRpcValue structGetValue (const string& key) const;
void structSetValue (const string& key, const XmlRpcValue& value);
void structGetKeyAndValue (const int index,
string& out_key,
XmlRpcValue& out_value) const;
};
inline XmlRpcValue::XmlRpcValue (xmlrpc_value *value,
ReferenceBehavior behavior)
{
mValue = value;
if (behavior == MAKE_REFERENCE)
xmlrpc_INCREF(value);
}
inline XmlRpcValue::XmlRpcValue (const XmlRpcValue& value) {
mValue = value.mValue;
xmlrpc_INCREF(mValue);
}
inline XmlRpcValue::~XmlRpcValue (void) {
xmlrpc_DECREF(mValue);
}
inline XmlRpcValue& XmlRpcValue::operator= (const XmlRpcValue& value) {
// Must increment before we decrement, in case of assignment to self.
xmlrpc_INCREF(value.mValue);
xmlrpc_DECREF(mValue);
mValue = value.mValue;
return *this;
}
inline xmlrpc_type XmlRpcValue::getType (void) const {
return xmlrpc_value_type(mValue);
}
inline xmlrpc_value *XmlRpcValue::makeReference (void) const {
xmlrpc_INCREF(mValue);
return mValue;
}
inline xmlrpc_value *XmlRpcValue::borrowReference (void) const {
return mValue;
}
//=========================================================================
// XmlRpcClient
//=========================================================================
class XmlRpcClient {
private:
string mServerUrl;
public:
static void Initialize (string appname, string appversion);
static void Terminate (void);
XmlRpcClient (const string& server_url) : mServerUrl(server_url) {}
~XmlRpcClient (void) {}
XmlRpcClient (const XmlRpcClient& client);
XmlRpcClient& operator= (const XmlRpcClient& client);
XmlRpcValue call (string method_name, XmlRpcValue param_array);
void call_asynch (string method_name,
XmlRpcValue param_array,
xmlrpc_response_handler callback,
void* user_data);
void event_loop_asynch (unsigned long milliseconds);
};
inline void XmlRpcClient::call_asynch(string method_name,
XmlRpcValue param_array,
xmlrpc_response_handler callback,
void* user_data)
{
xmlrpc_client_call_asynch_params(
mServerUrl.c_str(),
method_name.c_str(),
callback,
user_data,
param_array.borrowReference());
}
inline void XmlRpcClient::event_loop_asynch(unsigned long milliseconds)
{
xmlrpc_client_event_loop_finish_asynch_timeout(milliseconds);
}
//=========================================================================
// XmlRpcClient Methods
//=========================================================================
// These are inline for now, so we don't need to screw with linker issues
// and build a separate client library.
inline XmlRpcClient::XmlRpcClient (const XmlRpcClient& client)
: mServerUrl(client.mServerUrl)
{
}
inline XmlRpcClient& XmlRpcClient::operator= (const XmlRpcClient& client) {
if (this != &client)
mServerUrl = client.mServerUrl;
return *this;
}
inline void XmlRpcClient::Initialize (string appname, string appversion) {
xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS,
appname.c_str(),
appversion.c_str());
}
inline void XmlRpcClient::Terminate (void) {
xmlrpc_client_cleanup();
}
inline XmlRpcValue XmlRpcClient::call (string method_name,
XmlRpcValue param_array)
{
XmlRpcEnv env;
xmlrpc_value *result =
xmlrpc_client_call_params(env,
mServerUrl.c_str(),
method_name.c_str(),
param_array.borrowReference());
env.throwIfFaultOccurred();
return XmlRpcValue(result, XmlRpcValue::CONSUME_REFERENCE);
}
//=========================================================================
// XmlRpcGenSrv
//=========================================================================
class XmlRpcGenSrv {
private:
xmlrpc_registry* mRegistry;
xmlrpc_mem_block* alloc (XmlRpcEnv& env, const string& body) const;
public:
XmlRpcGenSrv (int flags);
~XmlRpcGenSrv (void);
xmlrpc_registry* getRegistry (void) const;
XmlRpcGenSrv& addMethod (const string& name,
xmlrpc_method method,
void *data);
XmlRpcGenSrv& addMethod (const string& name,
xmlrpc_method method,
void* data,
const string& signature,
const string& help);
string handle (const string& body) const;
};
inline XmlRpcGenSrv::XmlRpcGenSrv (int flags) {
XmlRpcEnv env;
if (flags == flags){};
mRegistry = xmlrpc_registry_new (env);
env.throwIfFaultOccurred();
}
inline XmlRpcGenSrv::~XmlRpcGenSrv (void) {
xmlrpc_registry_free (mRegistry);
}
inline xmlrpc_registry* XmlRpcGenSrv::getRegistry () const {
return mRegistry;
}
#undef XMLRPC_NO_ASSIGNMENT
// Copyright (C) 2001 by Eric Kidd. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
#endif /* _XMLRPCCPP_H_ */

18
Utilities/cmxmlrpc/bool.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -0,0 +1,108 @@
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
#include "xmlrpc_config.h"
#if WIN32
#include "xmlrpc_pthreads.h"
#include <process.h>
#undef PACKAGE
#undef VERSION
#include "xmlrpc.h"
int pthread_create(pthread_t *new_thread_ID,
const pthread_attr_t * attr,
pthread_func start_func, void *arg)
{
HANDLE hThread;
DWORD dwThreadID;
XMLRPC_ASSERT (attr == NULL); /* unimplemented. */
XMLRPC_ASSERT_PTR_OK(new_thread_ID);
XMLRPC_ASSERT_PTR_OK(start_func);
XMLRPC_ASSERT_PTR_OK(arg);
hThread = (HANDLE) _beginthreadex (NULL, 0,
start_func, (LPVOID)arg, CREATE_SUSPENDED, &dwThreadID);
SetThreadPriority (hThread, THREAD_PRIORITY_NORMAL);
ResumeThread (hThread);
*new_thread_ID = hThread;
return hThread ? 0 : -1;
}
/* Just kill it. */
int pthread_cancel(pthread_t target_thread)
{
CloseHandle (target_thread);
return 0;
}
/* Waits for the thread to exit before continuing. */
int pthread_join(pthread_t target_thread, void **status)
{
DWORD dwResult = WaitForSingleObject(target_thread, INFINITE);
(*status) = (void *)dwResult;
return 0;
}
/* Stubbed. Do nothing. */
int pthread_detach(pthread_t target_thread)
{
return 0;
}
int pthread_mutex_init(pthread_mutex_t *mp,
const pthread_mutexattr_t * attr)
{
InitializeCriticalSection(mp);
return 0;
}
int pthread_mutex_lock(pthread_mutex_t *mp)
{
EnterCriticalSection(mp);
return 0;
}
int pthread_mutex_unlock(pthread_mutex_t *mp)
{
LeaveCriticalSection(mp);
return 0;
}
int pthread_mutex_destroy(pthread_mutex_t *mp)
{
DeleteCriticalSection(mp);
return 0;
}
#endif

783
Utilities/cmxmlrpc/xmlrpc.h Normal file
View File

@ -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

View File

@ -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@

View File

@ -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. */

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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_ */

View File

@ -0,0 +1,981 @@
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
#include "xmlrpc_config.h"
#undef PACKAGE
#undef VERSION
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include "bool.h"
#include "mallocvar.h"
#include "xmlrpc.h"
#include "xmlrpc_int.h"
#include "xmlrpc_client.h"
#include "xmlrpc_client_int.h"
/* transport_config.h defines XMLRPC_DEFAULT_TRANSPORT,
MUST_BUILD_WININET_CLIENT, MUST_BUILD_CURL_CLIENT,
MUST_BUILD_LIBWWW_CLIENT
*/
#include "transport_config.h"
#if MUST_BUILD_WININET_CLIENT
#include "xmlrpc_wininet_transport.h"
#endif
#if MUST_BUILD_CURL_CLIENT
#include "xmlrpc_curl_transport.h"
#endif
#if MUST_BUILD_LIBWWW_CLIENT
#include "xmlrpc_libwww_transport.h"
#endif
struct xmlrpc_client {
/*----------------------------------------------------------------------------
This represents a client object.
-----------------------------------------------------------------------------*/
struct clientTransport * transportP;
};
typedef struct call_info
{
/* These fields are used when performing asynchronous calls.
** The _asynch_data_holder contains server_url, method_name and
** param_array, so it's the only thing we need to free. */
xmlrpc_value *_asynch_data_holder;
char *server_url;
char *method_name;
xmlrpc_value *param_array;
xmlrpc_response_handler callback;
void *user_data;
/* The serialized XML data passed to this call. We keep this around
** for use by our source_anchor field. */
xmlrpc_mem_block *serialized_xml;
} call_info;
static bool clientInitialized = FALSE;
/*=========================================================================
** Initialization and Shutdown
**=========================================================================
*/
static struct clientTransportOps clientTransportOps;
static struct xmlrpc_client client;
/* Some day, we need to make this dynamically allocated, so there can
be more than one client per program and just generally to provide
a cleaner interface.
*/
extern void
xmlrpc_client_init(int const flags,
const char * const appname,
const char * const appversion) {
struct xmlrpc_clientparms clientparms;
/* As our interface does not allow for failure, we just fail silently ! */
xmlrpc_env env;
xmlrpc_env_init(&env);
clientparms.transport = XMLRPC_DEFAULT_TRANSPORT;
xmlrpc_client_init2(&env, flags,
appname, appversion,
&clientparms, XMLRPC_CPSIZE(transport));
xmlrpc_env_clean(&env);
}
const char *
xmlrpc_client_get_default_transport(xmlrpc_env * const env ATTR_UNUSED) {
return XMLRPC_DEFAULT_TRANSPORT;
}
static void
setupTransport(xmlrpc_env * const envP,
const char * const transportName) {
if (FALSE) {
}
#if MUST_BUILD_WININET_CLIENT
else if (strcmp(transportName, "wininet") == 0)
clientTransportOps = xmlrpc_wininet_transport_ops;
#endif
#if MUST_BUILD_CURL_CLIENT
else if (strcmp(transportName, "curl") == 0)
clientTransportOps = xmlrpc_curl_transport_ops;
#endif
#if MUST_BUILD_LIBWWW_CLIENT
else if (strcmp(transportName, "libwww") == 0)
clientTransportOps = xmlrpc_libwww_transport_ops;
#endif
else
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Unrecognized XML transport name '%s'", transportName);
}
void
xmlrpc_client_init2(xmlrpc_env * const envP,
int const flags,
const char * const appname,
const char * const appversion,
struct xmlrpc_clientparms * const clientparmsP,
unsigned int const parm_size) {
if (clientInitialized)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Xmlrpc-c client instance has already been initialized "
"(need to call xmlrpc_client_cleanup() before you can "
"reinitialize).");
else {
const char * transportName;
if (parm_size < XMLRPC_CPSIZE(transport) ||
clientparmsP->transport == NULL) {
/* He didn't specify a transport. Use the default */
transportName = xmlrpc_client_get_default_transport(envP);
} else
transportName = clientparmsP->transport;
if (!envP->fault_occurred) {
setupTransport(envP, transportName);
if (!envP->fault_occurred) {
clientTransportOps.create(envP, flags, appname, appversion,
&client.transportP);
if (!envP->fault_occurred)
clientInitialized = TRUE;
}
}
}
}
void
xmlrpc_client_cleanup() {
XMLRPC_ASSERT(clientInitialized);
clientTransportOps.destroy(client.transportP);
clientInitialized = FALSE;
}
static void
call_info_free(call_info * const callInfoP) {
/* Assume the worst.. That only parts of the call_info are valid. */
XMLRPC_ASSERT_PTR_OK(callInfoP);
/* If this has been allocated, we're responsible for destroying it. */
if (callInfoP->_asynch_data_holder)
xmlrpc_DECREF(callInfoP->_asynch_data_holder);
/* Now we can blow away the XML data. */
if (callInfoP->serialized_xml)
xmlrpc_mem_block_free(callInfoP->serialized_xml);
free(callInfoP);
}
static void
call_info_new(xmlrpc_env * const envP,
xmlrpc_server_info * const server,
const char * const method_name,
xmlrpc_value * const argP,
call_info ** const callInfoPP) {
/*----------------------------------------------------------------------------
Create a call_info object. A call_info object represents an XML-RPC
call.
-----------------------------------------------------------------------------*/
call_info * callInfoP;
XMLRPC_ASSERT_PTR_OK(argP);
XMLRPC_ASSERT_PTR_OK(callInfoPP);
if (method_name == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"method name argument is NULL pointer");
else if (server == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"server info argument is NULL pointer");
else {
MALLOCVAR(callInfoP);
if (callInfoP == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for xmlrpc_call_info");
else {
xmlrpc_mem_block * callXmlP;
/* Clear contents. */
memset(callInfoP, 0, sizeof(*callInfoP));
/* Make the XML for our call */
callXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
if (!envP->fault_occurred) {
xmlrpc_serialize_call(envP, callXmlP, method_name, argP);
if (!envP->fault_occurred) {
xmlrpc_traceXml("XML-RPC CALL",
XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP),
XMLRPC_MEMBLOCK_SIZE(char, callXmlP));
callInfoP->serialized_xml = callXmlP;
*callInfoPP = callInfoP;
}
if (envP->fault_occurred)
XMLRPC_MEMBLOCK_FREE(char, callXmlP);
}
if (envP->fault_occurred)
free(callInfoP);
}
}
}
static void
clientCallServerParams(xmlrpc_env * const envP,
struct clientTransport * const transportP,
xmlrpc_server_info * const serverP,
const char * const methodName,
xmlrpc_value * const paramArrayP,
xmlrpc_value ** const resultPP) {
call_info * callInfoP;
if (!clientInitialized)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Xmlrpc-c client instance has not been initialized "
"(need to call xmlrpc_client_init2()).");
else {
call_info_new(envP, serverP, methodName, paramArrayP, &callInfoP);
if (!envP->fault_occurred) {
xmlrpc_mem_block * respXmlP;
clientTransportOps.call(envP, transportP, serverP,
callInfoP->serialized_xml, callInfoP,
&respXmlP);
if (!envP->fault_occurred) {
xmlrpc_traceXml("XML-RPC RESPONSE",
XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
*resultPP = xmlrpc_parse_response(
envP,
XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
XMLRPC_MEMBLOCK_FREE(char, respXmlP);
}
call_info_free(callInfoP);
}
}
}
xmlrpc_value *
xmlrpc_client_call_params(xmlrpc_env * const envP,
const char * const serverUrl,
const char * const methodName,
xmlrpc_value * const paramArrayP) {
xmlrpc_value *retval;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_PTR_OK(serverUrl);
if (!clientInitialized)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Xmlrpc-c client instance has not been initialized "
"(need to call xmlrpc_client_init2()).");
else {
xmlrpc_server_info * serverP;
/* Build a server info object and make our call. */
serverP = xmlrpc_server_info_new(envP, serverUrl);
if (!envP->fault_occurred) {
clientCallServerParams(envP, client.transportP, serverP,
methodName, paramArrayP,
&retval);
xmlrpc_server_info_free(serverP);
}
}
if (!envP->fault_occurred)
XMLRPC_ASSERT_VALUE_OK(retval);
return retval;
}
static xmlrpc_value *
xmlrpc_client_call_va(xmlrpc_env * const envP,
const char * const server_url,
const char * const method_name,
const char * const format,
va_list args) {
xmlrpc_value * argP;
xmlrpc_value * retval;
xmlrpc_env argenv;
const char * suffix;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_PTR_OK(format);
/* Build our argument value. */
xmlrpc_env_init(&argenv);
xmlrpc_build_value_va(&argenv, format, args, &argP, &suffix);
if (argenv.fault_occurred) {
xmlrpc_env_set_fault_formatted(
envP, argenv.fault_code, "Invalid RPC arguments. "
"The format argument must indicate a single array, and the "
"following arguments must correspond to that format argument. "
"The failure is: %s",
argenv.fault_string);
xmlrpc_env_clean(&argenv);
} else {
XMLRPC_ASSERT_VALUE_OK(argP);
if (*suffix != '\0')
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
"specifier: '%s'. There must be exactly one arument.",
suffix);
else {
/* Perform the actual XML-RPC call. */
retval = xmlrpc_client_call_params(
envP, server_url, method_name, argP);
if (!envP->fault_occurred)
XMLRPC_ASSERT_VALUE_OK(retval);
}
xmlrpc_DECREF(argP);
}
return retval;
}
xmlrpc_value *
xmlrpc_client_call(xmlrpc_env * const envP,
const char * const server_url,
const char * const method_name,
const char * const format,
...) {
xmlrpc_value * result;
va_list args;
va_start(args, format);
result = xmlrpc_client_call_va(envP, server_url,
method_name, format, args);
va_end(args);
return result;
}
xmlrpc_value *
xmlrpc_client_call_server(xmlrpc_env * const envP,
xmlrpc_server_info * const serverP,
const char * const methodName,
const char * const format,
...) {
va_list args;
xmlrpc_value * paramArrayP;
xmlrpc_value * retval;
const char * suffix;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_PTR_OK(format);
/* Build our argument */
va_start(args, format);
xmlrpc_build_value_va(envP, format, args, &paramArrayP, &suffix);
va_end(args);
if (!envP->fault_occurred) {
if (*suffix != '\0')
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
"specifier: '%s'. There must be exactly one arument.",
suffix);
else
clientCallServerParams(envP, client.transportP, serverP,
methodName, paramArrayP,
&retval);
xmlrpc_DECREF(paramArrayP);
}
return retval;
}
void
xmlrpc_client_event_loop_finish_asynch(void) {
XMLRPC_ASSERT(clientInitialized);
clientTransportOps.finish_asynch(client.transportP, timeout_no, 0);
}
void
xmlrpc_client_event_loop_finish_asynch_timeout(timeout_t const timeout) {
XMLRPC_ASSERT(clientInitialized);
clientTransportOps.finish_asynch(client.transportP, timeout_yes, timeout);
}
static void
call_info_set_asynch_data(xmlrpc_env * const env,
call_info * const info,
const char * const server_url,
const char * const method_name,
xmlrpc_value * const argP,
xmlrpc_response_handler callback,
void * const user_data) {
xmlrpc_value *holder;
/* Error-handling preconditions. */
holder = NULL;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(info);
XMLRPC_ASSERT(info->_asynch_data_holder == NULL);
XMLRPC_ASSERT_PTR_OK(server_url);
XMLRPC_ASSERT_PTR_OK(method_name);
XMLRPC_ASSERT_VALUE_OK(argP);
/* Install our callback and user_data.
** (We're not responsible for destroying the user_data.) */
info->callback = callback;
info->user_data = user_data;
/* Build an XML-RPC data structure to hold our other data. This makes
** copies of server_url and method_name, and increments the reference
** to the argument *argP. */
holder = xmlrpc_build_value(env, "(ssV)",
server_url, method_name, argP);
XMLRPC_FAIL_IF_FAULT(env);
/* Parse the newly-allocated structure into our public member variables.
** This doesn't make any new references, so we can dispose of the whole
** thing by DECREF'ing the one master reference. Nifty, huh? */
xmlrpc_parse_value(env, holder, "(ssV)",
&info->server_url,
&info->method_name,
&info->param_array);
XMLRPC_FAIL_IF_FAULT(env);
/* Hand over ownership of the holder to the call_info struct. */
info->_asynch_data_holder = holder;
holder = NULL;
cleanup:
if (env->fault_occurred) {
if (holder)
xmlrpc_DECREF(holder);
}
}
/*=========================================================================
** xmlrpc_server_info
**=========================================================================
*/
xmlrpc_server_info *
xmlrpc_server_info_new (xmlrpc_env * const env,
const char * const server_url) {
xmlrpc_server_info *server;
char *url_copy;
/* Error-handling preconditions. */
server = NULL;
url_copy = NULL;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(server_url);
/* Allocate our memory blocks. */
server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for xmlrpc_server_info");
memset(server, 0, sizeof(xmlrpc_server_info));
url_copy = (char*) malloc(strlen(server_url) + 1);
XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for server URL");
/* Build our object. */
strcpy(url_copy, server_url);
server->_server_url = url_copy;
server->_http_basic_auth = NULL;
cleanup:
if (env->fault_occurred) {
if (url_copy)
free(url_copy);
if (server)
free(server);
return NULL;
}
return server;
}
xmlrpc_server_info * xmlrpc_server_info_copy(xmlrpc_env *env,
xmlrpc_server_info *aserver)
{
xmlrpc_server_info *server;
char *url_copy, *auth_copy;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(aserver);
/* Error-handling preconditions. */
server = NULL;
url_copy = NULL;
auth_copy = NULL;
/* Allocate our memory blocks. */
server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for xmlrpc_server_info");
url_copy = (char*) malloc(strlen(aserver->_server_url) + 1);
XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for server URL");
auth_copy = (char*) malloc(strlen(aserver->_http_basic_auth) + 1);
XMLRPC_FAIL_IF_NULL(auth_copy, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for authentication info");
/* Build our object. */
strcpy(url_copy, aserver->_server_url);
server->_server_url = url_copy;
strcpy(auth_copy, aserver->_http_basic_auth);
server->_http_basic_auth = auth_copy;
cleanup:
if (env->fault_occurred) {
if (url_copy)
free(url_copy);
if (auth_copy)
free(auth_copy);
if (server)
free(server);
return NULL;
}
return server;
}
void xmlrpc_server_info_free (xmlrpc_server_info *server)
{
XMLRPC_ASSERT_PTR_OK(server);
XMLRPC_ASSERT(server->_server_url != XMLRPC_BAD_POINTER);
if (server->_http_basic_auth)
free(server->_http_basic_auth);
free(server->_server_url);
server->_server_url = XMLRPC_BAD_POINTER;
free(server);
}
/*=========================================================================
** xmlrpc_client_call_asynch
**=========================================================================
*/
void
xmlrpc_client_call_asynch(const char * const serverUrl,
const char * const methodName,
xmlrpc_response_handler callback,
void * const userData,
const char * const format,
...) {
xmlrpc_env env;
va_list args;
xmlrpc_value * paramArrayP;
const char * suffix;
xmlrpc_env_init(&env);
XMLRPC_ASSERT_PTR_OK(serverUrl);
XMLRPC_ASSERT_PTR_OK(format);
/* Build our argument array. */
va_start(args, format);
xmlrpc_build_value_va(&env, format, args, &paramArrayP, &suffix);
va_end(args);
if (env.fault_occurred) {
/* Unfortunately, we have no way to return an error and the
regular callback for a failed RPC is designed to have the
parameter array passed to it. This was probably an oversight
of the original asynch design, but now we have to be as
backward compatible as possible, so we do this:
*/
(*callback)(serverUrl, methodName, NULL, userData, &env, NULL);
} else {
if (*suffix != '\0')
xmlrpc_env_set_fault_formatted(
&env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
"specifier: '%s'. There must be exactly one arument.",
suffix);
else {
xmlrpc_server_info * serverP;
serverP = xmlrpc_server_info_new(&env, serverUrl);
if (!env.fault_occurred) {
xmlrpc_client_call_server_asynch_params(
serverP, methodName, callback, userData,
paramArrayP);
}
xmlrpc_server_info_free(serverP);
}
if (env.fault_occurred)
(*callback)(serverUrl, methodName, paramArrayP, userData,
&env, NULL);
xmlrpc_DECREF(paramArrayP);
}
xmlrpc_env_clean(&env);
}
void
xmlrpc_client_call_asynch_params(const char * const serverUrl,
const char * const methodName,
xmlrpc_response_handler callback,
void * const userData,
xmlrpc_value * const paramArrayP) {
xmlrpc_env env;
xmlrpc_server_info *serverP;
xmlrpc_env_init(&env);
XMLRPC_ASSERT_PTR_OK(serverUrl);
serverP = xmlrpc_server_info_new(&env, serverUrl);
if (!env.fault_occurred) {
xmlrpc_client_call_server_asynch_params(
serverP, methodName, callback, userData, paramArrayP);
xmlrpc_server_info_free(serverP);
}
if (env.fault_occurred)
/* We have no way to return failure; we report the failure
as it happened after we successfully started the RPC.
*/
(*callback)(serverUrl, methodName, paramArrayP, userData,
&env, NULL);
xmlrpc_env_clean(&env);
}
void
xmlrpc_client_call_server_asynch(xmlrpc_server_info * const serverP,
const char * const methodName,
xmlrpc_response_handler callback,
void * const userData,
const char * const format,
...) {
xmlrpc_env env;
va_list args;
xmlrpc_value * paramArrayP;
const char * suffix;
xmlrpc_env_init(&env);
XMLRPC_ASSERT_PTR_OK(format);
/* Build our parameter array. */
va_start(args, format);
xmlrpc_build_value_va(&env, format, args, &paramArrayP, &suffix);
va_end(args);
if (env.fault_occurred) {
/* Unfortunately, we have no way to return an error and the
regular callback for a failed RPC is designed to have the
parameter array passed to it. This was probably an oversight
of the original asynch design, but now we have to be as
backward compatible as possible, so we do this:
*/
(*callback)(serverP->_server_url, methodName, NULL, userData,
&env, NULL);
} else {
if (*suffix != '\0')
xmlrpc_env_set_fault_formatted(
&env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
"specifier: '%s'. There must be exactly one arument.",
suffix);
else {
xmlrpc_client_call_server_asynch_params(
serverP, methodName, callback, userData, paramArrayP);
}
xmlrpc_DECREF(paramArrayP);
}
if (env.fault_occurred)
(*callback)(serverP->_server_url, methodName, paramArrayP, userData,
&env, NULL);
xmlrpc_env_clean(&env);
}
static void
asynchComplete(call_info * const callInfoP,
xmlrpc_mem_block * const responseXmlP,
xmlrpc_env const transportEnv) {
/*----------------------------------------------------------------------------
Complete an asynchronous XML-RPC call request.
This includes calling the user's RPC completion routine.
'transportEnv' describes the an error that the transport
encountered in processing the call. If the transport successfully
sent the call to the server and processed the response but the
server failed the call, 'transportEnv' indicates no error, and the
response in *callInfoP might very well indicate that the server
failed the request.
-----------------------------------------------------------------------------*/
xmlrpc_env env;
xmlrpc_value * responseP;
xmlrpc_env_init(&env);
if (transportEnv.fault_occurred)
xmlrpc_env_set_fault_formatted(
&env, transportEnv.fault_code,
"Client transport failed to execute the RPC. %s",
transportEnv.fault_string);
if (!env.fault_occurred)
responseP = xmlrpc_parse_response(
&env,
XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlP),
XMLRPC_MEMBLOCK_SIZE(char, responseXmlP));
/* Call the user's callback function with the result */
(*callInfoP->callback)(callInfoP->server_url,
callInfoP->method_name,
callInfoP->param_array,
callInfoP->user_data, &env, responseP);
if (!env.fault_occurred)
xmlrpc_DECREF(responseP);
call_info_free(callInfoP);
xmlrpc_env_clean(&env);
}
static void
sendRequest(xmlrpc_env * const envP,
struct clientTransport * const transportP,
xmlrpc_server_info * const serverP,
const char * const methodName,
xmlrpc_response_handler responseHandler,
void * const userData,
xmlrpc_value * const argP) {
call_info * callInfoP;
call_info_new(envP, serverP, methodName, argP, &callInfoP);
if (!envP->fault_occurred) {
call_info_set_asynch_data(envP, callInfoP,
serverP->_server_url, methodName,
argP, responseHandler, userData);
if (!envP->fault_occurred)
clientTransportOps.send_request(
envP, transportP, serverP, callInfoP->serialized_xml,
&asynchComplete, callInfoP);
if (envP->fault_occurred)
call_info_free(callInfoP);
else {
/* asynchComplete() will free *callInfoP */
}
}
if (envP->fault_occurred) {
/* Transport did not start the call. Report the call complete
(with error) now.
*/
(*responseHandler)(serverP->_server_url, methodName, argP, userData,
envP, NULL);
} else {
/* The transport will call *responseHandler() when it has completed
the call
*/
}
}
void
xmlrpc_client_call_server_asynch_params(
xmlrpc_server_info * const serverP,
const char * const methodName,
xmlrpc_response_handler responseHandler,
void * const userData,
xmlrpc_value * const argP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
XMLRPC_ASSERT_PTR_OK(serverP);
XMLRPC_ASSERT_PTR_OK(methodName);
XMLRPC_ASSERT_PTR_OK(responseHandler);
XMLRPC_ASSERT_VALUE_OK(argP);
if (!clientInitialized)
xmlrpc_env_set_fault_formatted(
&env, XMLRPC_INTERNAL_ERROR,
"Xmlrpc-c client instance has not been initialized "
"(need to call xmlrpc_client_init2()).");
else
sendRequest(&env, client.transportP, serverP,
methodName, responseHandler, userData,
argP);
xmlrpc_env_clean(&env);
}
void
xmlrpc_server_info_set_basic_auth(xmlrpc_env * const envP,
xmlrpc_server_info * const serverP,
const char * const username,
const char * const password) {
size_t username_len, password_len, raw_token_len;
char *raw_token;
xmlrpc_mem_block *token;
char *token_data, *auth_type, *auth_header;
size_t token_len, auth_type_len, auth_header_len;
/* Error-handling preconditions. */
raw_token = NULL;
token = NULL;
token_data = auth_type = auth_header = NULL;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_PTR_OK(serverP);
XMLRPC_ASSERT_PTR_OK(username);
XMLRPC_ASSERT_PTR_OK(password);
/* Calculate some lengths. */
username_len = strlen(username);
password_len = strlen(password);
raw_token_len = username_len + password_len + 1;
/* Build a raw token of the form 'username:password'. */
raw_token = (char*) malloc(raw_token_len + 1);
XMLRPC_FAIL_IF_NULL(raw_token, envP, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for auth token");
strcpy(raw_token, username);
raw_token[username_len] = ':';
strcpy(&raw_token[username_len + 1], password);
/* Encode our raw token using Base64. */
token = xmlrpc_base64_encode_without_newlines(envP,
(unsigned char*) raw_token,
raw_token_len);
XMLRPC_FAIL_IF_FAULT(envP);
token_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, token);
token_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, token);
/* Build our actual header value. (I hate string processing in C.) */
auth_type = "Basic ";
auth_type_len = strlen(auth_type);
auth_header_len = auth_type_len + token_len;
auth_header = (char*) malloc(auth_header_len + 1);
XMLRPC_FAIL_IF_NULL(auth_header, envP, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for auth header");
memcpy(auth_header, auth_type, auth_type_len);
memcpy(&auth_header[auth_type_len], token_data, token_len);
auth_header[auth_header_len] = '\0';
/* Clean up any pre-existing authentication information, and install
** the new value. */
if (serverP->_http_basic_auth)
free(serverP->_http_basic_auth);
serverP->_http_basic_auth = auth_header;
cleanup:
if (raw_token)
free(raw_token);
if (token)
xmlrpc_mem_block_free(token);
if (envP->fault_occurred) {
if (auth_header)
free(auth_header);
}
}

View File

@ -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_ */

View File

@ -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

View File

@ -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

View File

@ -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,
};

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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

View File

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

View File

@ -0,0 +1,626 @@
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
#include "xmlrpc_config.h"
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "xmlrpc.h"
#include "xmlrpc_int.h"
#define CRLF "\015\012"
#define SMALL_BUFFER_SZ (128)
#define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF
/*=========================================================================
** format_out
**=========================================================================
** A lightweight print routine for use with various serialization
** functions. Only use this routine for printing small objects--it uses
** a fixed-size internal buffer and returns an error on overflow.
** In particular, do NOT use this routine to print XML-RPC string values!
*/
static void
format_out(xmlrpc_env *env,
xmlrpc_mem_block *output,
char *format_string,
...) {
va_list args;
char buffer[SMALL_BUFFER_SZ];
int count;
XMLRPC_ASSERT_ENV_OK(env);
va_start(args, format_string);
/* We assume that this function is present and works correctly. Right. */
count = vsnprintf(buffer, SMALL_BUFFER_SZ, format_string, args);
/* Old C libraries return -1 if vsnprintf overflows its buffer.
** New C libraries return the number of characters which *would* have
** been printed if the error did not occur. This is impressively vile.
** Thank the C99 committee for this bright idea. But wait! We also
** need to keep track of the trailing NULL. */
if (count < 0 || count >= (SMALL_BUFFER_SZ - 1))
XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
"format_out overflowed internal buffer");
/* Append our new data to our output. */
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, buffer, count);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
va_end(args);
}
/*=========================================================================
** Warnings About Invalid UTF-8
**=========================================================================
** We claim to send UTF-8 data to the network. But we rely on application
** programs to pass us correctly-formed UTF-8 data, which is very naive
** and optimistic.
**
** In debudding mode, we call this routine to issue dire-sounding
** warnings. For the sake of safety, this routine never exits the
** program or does anything else drastic.
**
** This routine almost certainly slows down our output.
*/
#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
static void
sanity_check_utf8(const char * const str,
size_t const len) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_validate_utf8(&env, str, len);
if (env.fault_occurred)
fprintf(stderr, "*** xmlrpc-c WARNING ***: %s (%s)\n",
"Application sending corrupted UTF-8 data to network",
env.fault_string);
xmlrpc_env_clean(&env);
}
#endif
/*=========================================================================
** Escaping Strings
**=========================================================================
*/
static xmlrpc_mem_block *
escape_string(xmlrpc_env * const env,
const char * const str,
size_t const len) {
xmlrpc_mem_block *retval;
size_t i, needed;
char *out;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(str != NULL);
/* Set up our error-handling preconditions. */
retval = NULL;
/* Sanity-check this string before we print it. */
#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
sanity_check_utf8(str, len);
#endif
/* Calculate the amount of space we'll need. */
needed = 0;
for (i = 0; i < len; i++) {
if (str[i] == '<')
needed += 4; /* &lt; */
else if (str[i] == '>')
needed += 4; /* &gt; */
else if (str[i] == '&')
needed += 5; /* &amp; */
else
needed++;
}
/* Allocate our memory block. */
retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
XMLRPC_FAIL_IF_FAULT(env);
/* Copy over the newly-allocated data. */
out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
for (i = 0; i < len; i++) {
if (str[i] == '<') {
*out++ = '&';
*out++ = 'l';
*out++ = 't';
*out++ = ';';
} else if (str[i] == '>') {
*out++ = '&';
*out++ = 'g';
*out++ = 't';
*out++ = ';';
} else if (str[i] == '&') {
*out++ = '&';
*out++ = 'a';
*out++ = 'm';
*out++ = 'p';
*out++ = ';';
} else {
*out++ = str[i];
}
}
cleanup:
if (env->fault_occurred) {
if (retval)
XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
retval = NULL;
}
return retval;
}
static xmlrpc_mem_block*
escape_block (xmlrpc_env *env,
xmlrpc_mem_block *block) {
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(block != NULL);
return escape_string(env,
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
}
/*=========================================================================
** xmlrpc_serialize_string_data
**=========================================================================
** Escape and print the contents of a string.
*/
static void
xmlrpc_serialize_string_data(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *string) {
xmlrpc_mem_block *escaped;
char *contents;
size_t size;
/* Since this routine can only be called internally, we only need
** an assertion here, not a runtime type check.
** XXX - Temporarily disabled because we're using this code to
** print <dateTime.iso8601> values as well. */
/* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
/* Escape any '&' and '<' characters in the string. */
escaped = escape_block(env, &string->_block);
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
/* Print the string. */
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (escaped)
XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
}
/*=========================================================================
** xmlrpc_serialize_base64_data
**=========================================================================
** Print the contents of a memory block as well-formed Base64 data.
*/
static void
xmlrpc_serialize_base64_data (xmlrpc_env *env,
xmlrpc_mem_block *output,
unsigned char* data, size_t len) {
xmlrpc_mem_block *encoded;
unsigned char *contents;
size_t size;
/* Encode the data. */
encoded = xmlrpc_base64_encode(env, data, len);
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, encoded);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, encoded);
/* Print the data. */
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (encoded)
XMLRPC_TYPED_MEM_BLOCK_FREE(char, encoded);
}
/*=========================================================================
** xmlrpc_serialize_struct
**=========================================================================
** Dump the contents of a struct.
*/
static void
xmlrpc_serialize_struct(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *strct) {
size_t size;
size_t i;
xmlrpc_value *key, *value;
format_out(env, output, "<struct>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
size = xmlrpc_struct_size(env, strct);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
xmlrpc_struct_get_key_and_value(env, strct, i, &key, &value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<member><name>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_string_data(env, output, key);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</name>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</member>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
}
format_out(env, output, "</struct>");
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_value
**=========================================================================
** Dump a value in the appropriate fashion.
*/
void
xmlrpc_serialize_value(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *value) {
xmlrpc_value *item;
size_t size;
unsigned char* contents;
size_t i;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT_VALUE_OK(value);
/* Print our ubiquitous header. */
format_out(env, output, "<value>");
XMLRPC_FAIL_IF_FAULT(env);
switch (value->_type) {
case XMLRPC_TYPE_INT:
/* XXX - We assume that '%i' is the appropriate format specifier
** for an xmlrpc_int32 value. We should add some test cases to
** make sure this works. */
format_out(env, output, "<i4>%i</i4>", value->_value.i);
break;
case XMLRPC_TYPE_BOOL:
/* XXX - We assume that '%i' is the appropriate format specifier
** for an xmlrpc_bool value. */
format_out(env, output, "<boolean>%i</boolean>",
(value->_value.b) ? 1 : 0);
break;
case XMLRPC_TYPE_DOUBLE:
/* We must output a number of the form [+-]?\d*.\d*. */
format_out(env, output, "<double>%.17g</double>", value->_value.d);
break;
case XMLRPC_TYPE_STRING:
format_out(env, output, "<string>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_string_data(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</string>");
break;
case XMLRPC_TYPE_ARRAY:
format_out(env, output, "<array><data>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
/* Serialize each item. */
size = xmlrpc_array_size(env, value);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
item = xmlrpc_array_get_item(env, value, i);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, item);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, CRLF);
XMLRPC_FAIL_IF_FAULT(env);
}
format_out(env, output, "</data></array>");
break;
case XMLRPC_TYPE_STRUCT:
xmlrpc_serialize_struct(env, output, value);
break;
case XMLRPC_TYPE_BASE64:
format_out(env, output, "<base64>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
&value->_block);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, &value->_block);
xmlrpc_serialize_base64_data(env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</base64>");
break;
case XMLRPC_TYPE_DATETIME:
format_out(env, output, "<dateTime.iso8601>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_string_data(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</dateTime.iso8601>");
break;
case XMLRPC_TYPE_C_PTR:
xmlrpc_env_set_fault_formatted(
env, XMLRPC_INTERNAL_ERROR,
"Tried to serialize a C pointer value.");
break;
case XMLRPC_TYPE_DEAD:
xmlrpc_env_set_fault_formatted(
env, XMLRPC_INTERNAL_ERROR,
"Tried to serialize a deaad value.");
break;
default:
xmlrpc_env_set_fault_formatted(
env, XMLRPC_INTERNAL_ERROR,
"Invalid xmlrpc_value type: %d", value->_type);
}
XMLRPC_FAIL_IF_FAULT(env);
/* Print our ubiquitous footer. */
format_out(env, output, "</value>");
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_params
**=========================================================================
** Serialize a list as a set of parameters.
*/
void
xmlrpc_serialize_params(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *param_array) {
size_t size, i;
xmlrpc_value *item;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT_VALUE_OK(param_array);
format_out(env, output, "<params>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
/* Dump each parameter. */
size = xmlrpc_array_size(env, param_array);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
format_out(env, output, "<param>");
XMLRPC_FAIL_IF_FAULT(env);
item = xmlrpc_array_get_item(env, param_array, i);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, item);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</param>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
}
format_out(env, output, "</params>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_call
**=========================================================================
** Serialize an XML-RPC call.
*/
void
xmlrpc_serialize_call(xmlrpc_env * const env,
xmlrpc_mem_block * const output,
const char * const method_name,
xmlrpc_value * const param_array) {
xmlrpc_mem_block *escaped;
char *contents;
size_t size;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT(method_name != NULL);
XMLRPC_ASSERT_VALUE_OK(param_array);
/* Set up our error-handling preconditions. */
escaped = NULL;
/* Dump our header. */
format_out(env, output, XML_PROLOGUE);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<methodCall>"CRLF"<methodName>");
XMLRPC_FAIL_IF_FAULT(env);
/* Dump the method name. */
escaped = escape_string(env, method_name, strlen(method_name));
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped);
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
/* Dump our parameters and footer. */
format_out(env, output, "</methodName>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_params(env, output, param_array);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</methodCall>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (escaped)
xmlrpc_mem_block_free(escaped);
}
/*=========================================================================
** xmlrpc_serialize_response
**=========================================================================
** Serialize the (non-fault) response to an XML-RPC call.
*/
void
xmlrpc_serialize_response (xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *value) {
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT_VALUE_OK(value);
format_out(env, output, XML_PROLOGUE);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<methodResponse>"CRLF"<params>"CRLF"<param>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output,
"</param>"CRLF"</params>"CRLF"</methodResponse>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_fault
**=========================================================================
** Serialize an XML-RPC fault.
**
** If this function fails, it will set up the first env argument. You'll
** need to take some other drastic action to produce a serialized fault
** of your own. (This function should only fail in an out-of-memory
** situation, AFAIK.)
*/
void
xmlrpc_serialize_fault(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_env *fault) {
xmlrpc_value *strct;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT(fault != NULL && fault->fault_occurred);
/* Set up our error-handling preconditions. */
strct = NULL;
/* Build a fault structure. */
strct = xmlrpc_build_value(env, "{s:i,s:s}",
"faultCode", (xmlrpc_int32) fault->fault_code,
"faultString", fault->fault_string);
XMLRPC_FAIL_IF_FAULT(env);
/* Output our header. */
format_out(env, output, XML_PROLOGUE);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<methodResponse>"CRLF"<fault>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
/* Serialize our fault structure. */
xmlrpc_serialize_value(env, output, strct);
XMLRPC_FAIL_IF_FAULT(env);
/* Output our footer. */
format_out(env, output, CRLF"</fault>"CRLF"</methodResponse>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (strct)
xmlrpc_DECREF(strct);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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 (); */
}

View File

@ -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

View File

@ -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 */

View File

@ -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);