ENH: Remove Utilities/cmxmlrpc source tree
We never build this source tree anymore, so remove it.
This commit is contained in:
parent
8f5af6172a
commit
3615950f12
|
@ -1,46 +0,0 @@
|
||||||
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}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src2.c"
|
|
||||||
"${src}\n")
|
|
||||||
EXEC_PROGRAM("${CMAKE_COMMAND}"
|
|
||||||
"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp"
|
|
||||||
ARGS -E copy src2.c src.c)
|
|
||||||
MESSAGE(STATUS "Performing Test ${VAR}")
|
|
||||||
TRY_COMPILE(${VAR}
|
|
||||||
${CMAKE_BINARY_DIR}
|
|
||||||
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/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")
|
|
||||||
FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
|
|
||||||
"Performing C SOURCE FILE Test ${VAR} succeded with the following output:\n"
|
|
||||||
"${OUTPUT}\n"
|
|
||||||
"Source file was:\n${src}\n")
|
|
||||||
ELSE(${VAR})
|
|
||||||
MESSAGE(STATUS "Performing Test ${VAR} - Failed")
|
|
||||||
SET(${VAR} "" CACHE INTERNAL "Test ${FUNCTION}")
|
|
||||||
FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
|
|
||||||
"Performing C SOURCE FILE Test ${VAR} failed with the following output:\n"
|
|
||||||
"${OUTPUT}\n"
|
|
||||||
"Source file was:\n${src}\n")
|
|
||||||
ENDIF(${VAR})
|
|
||||||
ENDIF("${VAR}" MATCHES "^${VAR}$" OR "${VAR}" MATCHES "UNKNOWN")
|
|
||||||
ENDMACRO(TRY_COMPILE_FROM_SOURCE)
|
|
|
@ -1,128 +0,0 @@
|
||||||
PROJECT(XMLRPC)
|
|
||||||
|
|
||||||
INCLUDE_REGULAR_EXPRESSION("^.*$")
|
|
||||||
|
|
||||||
# Disable warnings on Borland to avoid changing 3rd party code.
|
|
||||||
IF(BORLAND)
|
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w-")
|
|
||||||
ENDIF(BORLAND)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
IF(HAVE_WCHAR_H)
|
|
||||||
OPTION(HAVE_UNICODE_WCHAR "Enable Unicode Support" YES)
|
|
||||||
MARK_AS_ADVANCED(HAVE_UNICODE_WCHAR)
|
|
||||||
ELSE(HAVE_WCHAR_H)
|
|
||||||
SET(HAVE_UNICODE_WCHAR 0)
|
|
||||||
ENDIF(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_ISNOT_ARRAY_DEFINE)
|
|
||||||
TRY_COMPILE_FROM_SOURCE("va_list list1, list2; va_copy(list1, list2);"
|
|
||||||
HAS_VA_COPY)
|
|
||||||
SET(VA_LIST_IS_ARRAY_DEFINE 0)
|
|
||||||
IF(NOT VA_LIST_ISNOT_ARRAY_DEFINE)
|
|
||||||
SET(VA_LIST_IS_ARRAY_DEFINE 1)
|
|
||||||
ENDIF(NOT VA_LIST_ISNOT_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 "/")
|
|
||||||
|
|
||||||
SET(HAVE_PTHREADS 0)
|
|
||||||
IF(CMAKE_BUILD_XMLRPC_WITH_THREADS)
|
|
||||||
FIND_PACKAGE(Threads)
|
|
||||||
IF(WIN32 OR CMAKE_USE_PTHREADS_INIT)
|
|
||||||
SET(HAVE_PTHREADS 1)
|
|
||||||
ENDIF(WIN32 OR CMAKE_USE_PTHREADS_INIT)
|
|
||||||
ENDIF(CMAKE_BUILD_XMLRPC_WITH_THREADS)
|
|
||||||
|
|
||||||
INCLUDE_DIRECTORIES(
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
|
||||||
${CMAKE_EXPAT_INCLUDES}
|
|
||||||
${CMAKE_CURL_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_curl_transport.c
|
|
||||||
#xmlrpc_server_abyss.c
|
|
||||||
xmlrpc_struct.c
|
|
||||||
xmlrpc_strutil.c
|
|
||||||
xmlrpc_support.c
|
|
||||||
casprintf.c
|
|
||||||
)
|
|
||||||
IF(HAVE_UNICODE_WCHAR)
|
|
||||||
SET(xmlrpc_SRCS
|
|
||||||
${xmlrpc_SRCS}
|
|
||||||
xmlrpc_utf8.c
|
|
||||||
)
|
|
||||||
ENDIF(HAVE_UNICODE_WCHAR)
|
|
||||||
IF(WIN32)
|
|
||||||
SET(xmlrpc_SRCS ${xmlrpc_SRCS}
|
|
||||||
win32_pthreads.c
|
|
||||||
)
|
|
||||||
ENDIF(WIN32)
|
|
||||||
|
|
||||||
ADD_LIBRARY(cmXMLRPC ${xmlrpc_SRCS})
|
|
||||||
TARGET_LINK_LIBRARIES(cmXMLRPC ${CMAKE_EXPAT_LIBRARIES} ${CMAKE_CURL_LIBRARIES})
|
|
||||||
IF(HAVE_PTHREADS)
|
|
||||||
TARGET_LINK_LIBRARIES(cmXMLRPC ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
ENDIF(HAVE_PTHREADS)
|
|
||||||
|
|
||||||
ADD_EXECUTABLE(xrtest synch_client.c)
|
|
||||||
TARGET_LINK_LIBRARIES(xrtest cmXMLRPC ${CMAKE_DL_LIBS})
|
|
||||||
|
|
||||||
# comment out test since it doesn pass and Andy doesn;t seem to be fixing it,
|
|
||||||
# for now assume that XMLRPC is not production quality code - Ken
|
|
||||||
# ADD_TEST(Test-XMLRPC ${EXECUTABLE_OUTPUT_PATH}/xrtest)
|
|
|
@ -1,391 +0,0 @@
|
||||||
// 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;
|
|
||||||
}
|
|
|
@ -1,428 +0,0 @@
|
||||||
// -*- 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_ */
|
|
|
@ -1,18 +0,0 @@
|
||||||
#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
|
|
|
@ -1,35 +0,0 @@
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "xmlrpc_config.h" /* For HAVE_ASPRINTF */
|
|
||||||
#include "casprintf.h"
|
|
||||||
|
|
||||||
void GNU_PRINTF_ATTR(2,3)
|
|
||||||
casprintf(const char ** const retvalP, const char * const fmt, ...) {
|
|
||||||
|
|
||||||
char *retval;
|
|
||||||
|
|
||||||
va_list varargs; /* mysterious structure used by variable arg facility */
|
|
||||||
|
|
||||||
va_start(varargs, fmt); /* start up the mysterious variable arg facility */
|
|
||||||
|
|
||||||
#if HAVE_ASPRINTF
|
|
||||||
vasprintf(&retval, fmt, varargs);
|
|
||||||
#else
|
|
||||||
retval = malloc(8192);
|
|
||||||
vsnprintf(retval, 8192, fmt, varargs);
|
|
||||||
#endif
|
|
||||||
*retvalP = retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
strfree(const char * const string) {
|
|
||||||
free((void *)string);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef CASPRINTF_H_INCLUDED
|
|
||||||
|
|
||||||
/* GNU_PRINTF_ATTR lets the GNU compiler check pm_message() and pm_error()
|
|
||||||
calls to be sure the arguments match the format string, thus preventing
|
|
||||||
runtime segmentation faults and incorrect messages.
|
|
||||||
*/
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define GNU_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
|
|
||||||
#else
|
|
||||||
#define GNU_PRINTF_ATTR(a,b)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GNU_PRINTF_ATTR(2,3)
|
|
||||||
casprintf(const char ** const retvalP, const char * const fmt, ...);
|
|
||||||
|
|
||||||
void
|
|
||||||
strfree(const char * const string);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef XMLRPC_INLINE_H_INCLUDED
|
|
||||||
#define XMLRPC_INLINE_H_INCLUDED
|
|
||||||
|
|
||||||
/* Xmlrpc-c uses __inline__ to declare functions that should be
|
|
||||||
compiled as inline code. Some compilers, e.g. GNU, recognize the
|
|
||||||
__inline__ keyword.
|
|
||||||
*/
|
|
||||||
#ifndef __GNUC__
|
|
||||||
#ifndef __inline__
|
|
||||||
#ifdef __sgi
|
|
||||||
#define __inline__ __inline
|
|
||||||
#else
|
|
||||||
#define __inline__
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,193 +0,0 @@
|
||||||
#ifndef LINKLIST_H_INCLUDED
|
|
||||||
#define LINKLIST_H_INCLUDED
|
|
||||||
|
|
||||||
#include "inline.h"
|
|
||||||
|
|
||||||
struct list_head {
|
|
||||||
/*----------------------------------------------------------------------------
|
|
||||||
This is a header for an element of a doubly linked list, or an anchor
|
|
||||||
for such a list.
|
|
||||||
|
|
||||||
itemP == NULL means it's an anchor; otherwise it's a header.
|
|
||||||
|
|
||||||
Initialize a list header with list_init_header(). You don't have to
|
|
||||||
do anything to terminate a list header.
|
|
||||||
|
|
||||||
Initialize an anchor with list_make_emtpy(). You don't have to do anything
|
|
||||||
to terminate a list header.
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
struct list_head * nextP;
|
|
||||||
/* For a header, this is the address of the list header for
|
|
||||||
the next element in the list. If there is no next element,
|
|
||||||
it points to the anchor. If the header is not in a list at
|
|
||||||
all, it is NULL.
|
|
||||||
|
|
||||||
For an anchor, it is the address of the list header of the
|
|
||||||
first element. If the list is empty, it points to the
|
|
||||||
anchor itself.
|
|
||||||
*/
|
|
||||||
struct list_head * prevP;
|
|
||||||
/* For a header, this is the address of the list header for
|
|
||||||
the previous element in the list. If there is no previous element,
|
|
||||||
it points to the anchor. If the header is not in a list at
|
|
||||||
all, it is NULL.
|
|
||||||
|
|
||||||
For an anchor, it is the address of the list header of the
|
|
||||||
last element. If the list is empty, it points to the
|
|
||||||
anchor itself.
|
|
||||||
*/
|
|
||||||
void * itemP;
|
|
||||||
/* For a header, this is the address of the list element to which it
|
|
||||||
belongs. For an anchor, this is NULL.
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_init_header(struct list_head * const headerP,
|
|
||||||
void * const itemP) {
|
|
||||||
|
|
||||||
headerP->prevP = NULL;
|
|
||||||
headerP->nextP = NULL;
|
|
||||||
headerP->itemP = itemP;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ int
|
|
||||||
list_is_linked(struct list_head * headerP) {
|
|
||||||
return headerP->prevP != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ int
|
|
||||||
list_is_empty(struct list_head * const anchorP) {
|
|
||||||
return anchorP->nextP == anchorP;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ unsigned int
|
|
||||||
list_count(struct list_head * const anchorP) {
|
|
||||||
unsigned int count;
|
|
||||||
|
|
||||||
struct list_head * p;
|
|
||||||
|
|
||||||
for (p = anchorP->nextP, count = 0;
|
|
||||||
p != anchorP;
|
|
||||||
p = p->nextP, ++count);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_make_empty(struct list_head * const anchorP) {
|
|
||||||
anchorP->prevP = anchorP;
|
|
||||||
anchorP->nextP = anchorP;
|
|
||||||
anchorP->itemP = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_insert_after(struct list_head * const beforeHeaderP,
|
|
||||||
struct list_head * const newHeaderP) {
|
|
||||||
newHeaderP->prevP = beforeHeaderP;
|
|
||||||
newHeaderP->nextP = beforeHeaderP->nextP;
|
|
||||||
|
|
||||||
beforeHeaderP->nextP = newHeaderP;
|
|
||||||
newHeaderP->nextP->prevP = newHeaderP;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_add_tail(struct list_head * const anchorP,
|
|
||||||
struct list_head * const headerP) {
|
|
||||||
list_insert_after(anchorP->prevP, headerP);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_add_head(struct list_head * const anchorP,
|
|
||||||
struct list_head * const headerP) {
|
|
||||||
list_insert_after(anchorP, headerP);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_remove(struct list_head * const headerP) {
|
|
||||||
headerP->prevP->nextP = headerP->nextP;
|
|
||||||
headerP->nextP->prevP = headerP->prevP;
|
|
||||||
headerP->prevP = NULL;
|
|
||||||
headerP->nextP = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ struct list_head *
|
|
||||||
list_remove_head(struct list_head * const anchorP) {
|
|
||||||
struct list_head * retval;
|
|
||||||
|
|
||||||
if (list_is_empty(anchorP))
|
|
||||||
retval = NULL;
|
|
||||||
else {
|
|
||||||
retval = anchorP->nextP;
|
|
||||||
list_remove(retval);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ struct list_head *
|
|
||||||
list_remove_tail(struct list_head * const anchorP) {
|
|
||||||
struct list_head * retval;
|
|
||||||
|
|
||||||
if (list_is_empty(anchorP))
|
|
||||||
retval = NULL;
|
|
||||||
else {
|
|
||||||
retval = anchorP->prevP;
|
|
||||||
list_remove(retval);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ void *
|
|
||||||
list_foreach(struct list_head * const anchorP,
|
|
||||||
void * functionP(struct list_head * const, void * const),
|
|
||||||
void * const context) {
|
|
||||||
|
|
||||||
struct list_head * p;
|
|
||||||
struct list_head * nextP;
|
|
||||||
void * result;
|
|
||||||
|
|
||||||
for (p = anchorP->nextP, nextP = p->nextP, result=NULL;
|
|
||||||
p != anchorP && result == NULL;
|
|
||||||
p = nextP, nextP = p->nextP)
|
|
||||||
result = (*functionP)(p, context);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static __inline__ void
|
|
||||||
list_append(struct list_head * const newAnchorP,
|
|
||||||
struct list_head * const baseAnchorP) {
|
|
||||||
|
|
||||||
if (!list_is_empty(newAnchorP)) {
|
|
||||||
baseAnchorP->prevP->nextP = newAnchorP->nextP;
|
|
||||||
newAnchorP->nextP->prevP = baseAnchorP->prevP;
|
|
||||||
newAnchorP->prevP->nextP = baseAnchorP;
|
|
||||||
baseAnchorP->prevP = newAnchorP->prevP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/* 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
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/* A simple synchronous XML-RPC client written in C. */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <xmlrpc.h>
|
|
||||||
#include <xmlrpc_client.h>
|
|
||||||
|
|
||||||
#define NAME "XML-RPC C Test Client"
|
|
||||||
#define VERSION "0.1"
|
|
||||||
|
|
||||||
static void die_if_fault_occurred (xmlrpc_env *env)
|
|
||||||
{
|
|
||||||
if (env->fault_occurred) {
|
|
||||||
fprintf(stderr, "XML-RPC Fault: %s (%d)\n",
|
|
||||||
env->fault_string, env->fault_code);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int const argc,
|
|
||||||
const char ** const argv ATTR_UNUSED) {
|
|
||||||
|
|
||||||
xmlrpc_env env;
|
|
||||||
char *state_name;
|
|
||||||
int cc;
|
|
||||||
|
|
||||||
if (argc-1 > 0) {
|
|
||||||
fprintf(stderr, "No arguments");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start up our XML-RPC client library. */
|
|
||||||
xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION);
|
|
||||||
|
|
||||||
/* Initialize our error-handling environment. */
|
|
||||||
xmlrpc_env_init(&env);
|
|
||||||
|
|
||||||
/* Call the famous server at UserLand. */
|
|
||||||
for ( cc = 30; cc < 35; cc ++ )
|
|
||||||
{
|
|
||||||
xmlrpc_value *result;
|
|
||||||
result = xmlrpc_client_call(&env, "http://betty.userland.com/RPC2",
|
|
||||||
"examples.getStateName",
|
|
||||||
"(i)", (xmlrpc_int32) cc);
|
|
||||||
die_if_fault_occurred(&env);
|
|
||||||
|
|
||||||
/* Get our state name and print it out. */
|
|
||||||
xmlrpc_parse_value(&env, result, "s", &state_name);
|
|
||||||
die_if_fault_occurred(&env);
|
|
||||||
printf("%d: %s\n", cc, state_name);
|
|
||||||
|
|
||||||
/* Dispose of our result value. */
|
|
||||||
xmlrpc_DECREF(result);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clean up our error-handling environment. */
|
|
||||||
xmlrpc_env_clean(&env);
|
|
||||||
|
|
||||||
/* Shutdown our XML-RPC client library. */
|
|
||||||
xmlrpc_client_cleanup();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
/* 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";
|
|
|
@ -1,109 +0,0 @@
|
||||||
/* 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"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#define HAVE_PTHREADS 1
|
|
||||||
#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;
|
|
||||||
unsigned 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
|
|
|
@ -1,784 +0,0 @@
|
||||||
/* 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>
|
|
||||||
#include <cmxmlrpc/xmlrpc_config.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)); if(*(str)) goto cleanup; } while (0)
|
|
||||||
|
|
||||||
#define XMLRPC_FAIL1(env,code,str,arg1) \
|
|
||||||
do { \
|
|
||||||
xmlrpc_env_set_fault_formatted((env),(code),(str),(arg1)); \
|
|
||||||
if(*(str)) goto cleanup; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define XMLRPC_FAIL2(env,code,str,arg1,arg2) \
|
|
||||||
do { \
|
|
||||||
xmlrpc_env_set_fault_formatted((env),(code),(str),(arg1),(arg2)); \
|
|
||||||
if(*(str)) 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)); \
|
|
||||||
if(*(str)) 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* const env, size_t const 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* const env, xmlrpc_mem_block* const block, size_t const size);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
#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* const value);
|
|
||||||
|
|
||||||
/* Decrement the reference count of an xmlrpc_value. If there
|
|
||||||
** are no more references, free it. */
|
|
||||||
extern void xmlrpc_DECREF (xmlrpc_value* const 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* const env,
|
|
||||||
xmlrpc_value* const array,
|
|
||||||
xmlrpc_value* const value);
|
|
||||||
|
|
||||||
void
|
|
||||||
xmlrpc_array_read_item(xmlrpc_env * const envP,
|
|
||||||
const xmlrpc_value * const arrayP,
|
|
||||||
unsigned int const xmIndex,
|
|
||||||
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 xmIndex);
|
|
||||||
|
|
||||||
/* 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 xmIndex,
|
|
||||||
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 * const env,
|
|
||||||
xmlrpc_value * const strct,
|
|
||||||
int const xmIndex,
|
|
||||||
xmlrpc_value ** const out_keyval,
|
|
||||||
xmlrpc_value ** const 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 <cmxmlrpc/xmlrpc_server.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
/* 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@
|
|
|
@ -1,206 +0,0 @@
|
||||||
/* 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 {
|
|
||||||
size_t 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 xmIndex;
|
|
||||||
|
|
||||||
for (xmIndex = 0; xmIndex < arraySize; ++xmIndex) {
|
|
||||||
xmlrpc_value * const itemP = contents[xmIndex];
|
|
||||||
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.
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
size_t const arraySize =
|
|
||||||
XMLRPC_MEMBLOCK_SIZE(xmlrpc_value*, &arrayP->_block);
|
|
||||||
xmlrpc_value ** const contents =
|
|
||||||
XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
|
|
||||||
|
|
||||||
unsigned int xmIndex;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ARRAY_OK(arrayP);
|
|
||||||
|
|
||||||
/* Release our reference to each item in the array */
|
|
||||||
for (xmIndex = 0; xmIndex < arraySize; ++xmIndex) {
|
|
||||||
xmlrpc_value * const itemP = contents[xmIndex];
|
|
||||||
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 = (int)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 xmIndex,
|
|
||||||
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 (xmIndex >= size)
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, XMLRPC_INDEX_ERROR, "Array index %u is beyond end "
|
|
||||||
"of %u-item array", xmIndex, (unsigned int)size);
|
|
||||||
else {
|
|
||||||
*valuePP = contents[xmIndex];
|
|
||||||
xmlrpc_INCREF(*valuePP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
xmlrpc_value *
|
|
||||||
xmlrpc_array_get_item(xmlrpc_env * const envP,
|
|
||||||
const xmlrpc_value * const arrayP,
|
|
||||||
int const xmIndex) {
|
|
||||||
|
|
||||||
xmlrpc_value * valueP;
|
|
||||||
|
|
||||||
if (xmIndex < 0)
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, XMLRPC_INDEX_ERROR, "Index %d is negative.");
|
|
||||||
else {
|
|
||||||
xmlrpc_array_read_item(envP, arrayP, xmIndex, &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. */
|
|
|
@ -1,71 +0,0 @@
|
||||||
/* 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;
|
|
||||||
static char env_buffer[1024];
|
|
||||||
const char* block;
|
|
||||||
|
|
||||||
/* 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 * ) );
|
|
||||||
|
|
||||||
/* Create unencoded string/hash. */
|
|
||||||
sprintf(unencoded, "%s:%s", username, password);
|
|
||||||
|
|
||||||
/* Create encoded string. */
|
|
||||||
token = xmlrpc_base64_encode_without_newlines(env, (unsigned char*)unencoded,
|
|
||||||
strlen(unencoded));
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
/* Set HTTP_COOKIE_AUTH to the character representation of the
|
|
||||||
** encoded string. */
|
|
||||||
block = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, token);
|
|
||||||
sprintf(env_buffer, "HTTP_COOKIE_AUTH=%s", block);
|
|
||||||
putenv(env_buffer);
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (token) xmlrpc_mem_block_free(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *xmlrpc_authcookie ( void ) {
|
|
||||||
return getenv("HTTP_COOKIE_AUTH");
|
|
||||||
}
|
|
|
@ -1,262 +0,0 @@
|
||||||
/* 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;
|
|
||||||
}
|
|
|
@ -1,295 +0,0 @@
|
||||||
/* 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);
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
/* 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_ */
|
|
|
@ -1,977 +0,0 @@
|
||||||
/* 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;
|
|
||||||
else if (strcmp(transportName, "libcurl") == 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),
|
|
||||||
(unsigned int)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),
|
|
||||||
(unsigned int)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 = 0;
|
|
||||||
xmlrpc_env argenv;
|
|
||||||
const char * suffix;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(envP);
|
|
||||||
XMLRPC_ASSERT_PTR_OK(format);
|
|
||||||
|
|
||||||
/* Build our argument value. */
|
|
||||||
xmlrpc_env_init(&argenv);
|
|
||||||
xmlrpc_build_value_va(&argenv, format, args, &argP, &suffix);
|
|
||||||
if (argenv.fault_occurred) {
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, argenv.fault_code, "Invalid RPC arguments. "
|
|
||||||
"The format argument must indicate a single array, and the "
|
|
||||||
"following arguments must correspond to that format argument. "
|
|
||||||
"The failure is: %s",
|
|
||||||
argenv.fault_string);
|
|
||||||
xmlrpc_env_clean(&argenv);
|
|
||||||
} else {
|
|
||||||
XMLRPC_ASSERT_VALUE_OK(argP);
|
|
||||||
|
|
||||||
if (*suffix != '\0')
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
||||||
"specifier: '%s'. There must be exactly one arument.",
|
|
||||||
suffix);
|
|
||||||
else {
|
|
||||||
/* Perform the actual XML-RPC call. */
|
|
||||||
retval = xmlrpc_client_call_params(
|
|
||||||
envP, server_url, method_name, argP);
|
|
||||||
if (!envP->fault_occurred)
|
|
||||||
XMLRPC_ASSERT_VALUE_OK(retval);
|
|
||||||
}
|
|
||||||
xmlrpc_DECREF(argP);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
xmlrpc_value *
|
|
||||||
xmlrpc_client_call(xmlrpc_env * const envP,
|
|
||||||
const char * const server_url,
|
|
||||||
const char * const method_name,
|
|
||||||
const char * const format,
|
|
||||||
...) {
|
|
||||||
|
|
||||||
xmlrpc_value * result;
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, format);
|
|
||||||
result = xmlrpc_client_call_va(envP, server_url,
|
|
||||||
method_name, format, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
xmlrpc_value *
|
|
||||||
xmlrpc_client_call_server(xmlrpc_env * const envP,
|
|
||||||
xmlrpc_server_info * const serverP,
|
|
||||||
const char * const methodName,
|
|
||||||
const char * const format,
|
|
||||||
...) {
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
xmlrpc_value * paramArrayP;
|
|
||||||
xmlrpc_value * retval;
|
|
||||||
const char * suffix;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(envP);
|
|
||||||
XMLRPC_ASSERT_PTR_OK(format);
|
|
||||||
|
|
||||||
/* Build our argument */
|
|
||||||
va_start(args, format);
|
|
||||||
xmlrpc_build_value_va(envP, format, args, ¶mArrayP, &suffix);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
if (!envP->fault_occurred) {
|
|
||||||
if (*suffix != '\0')
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
||||||
"specifier: '%s'. There must be exactly one arument.",
|
|
||||||
suffix);
|
|
||||||
else
|
|
||||||
clientCallServerParams(envP, client.transportP, serverP,
|
|
||||||
methodName, paramArrayP,
|
|
||||||
&retval);
|
|
||||||
|
|
||||||
xmlrpc_DECREF(paramArrayP);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
xmlrpc_client_event_loop_finish_asynch(void) {
|
|
||||||
XMLRPC_ASSERT(clientInitialized);
|
|
||||||
clientTransportOps.finish_asynch(client.transportP, timeout_no, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
xmlrpc_client_event_loop_finish_asynch_timeout(timeout_t const timeout) {
|
|
||||||
XMLRPC_ASSERT(clientInitialized);
|
|
||||||
clientTransportOps.finish_asynch(client.transportP, timeout_yes, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
call_info_set_asynch_data(xmlrpc_env * const env,
|
|
||||||
call_info * const info,
|
|
||||||
const char * const server_url,
|
|
||||||
const char * const method_name,
|
|
||||||
xmlrpc_value * const argP,
|
|
||||||
xmlrpc_response_handler callback,
|
|
||||||
void * const user_data) {
|
|
||||||
|
|
||||||
xmlrpc_value *holder;
|
|
||||||
|
|
||||||
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. */
|
|
||||||
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. */
|
|
||||||
url_copy = NULL;
|
|
||||||
auth_copy = NULL;
|
|
||||||
|
|
||||||
/* Allocate our memory blocks. */
|
|
||||||
server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
|
|
||||||
XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
|
|
||||||
"Couldn't allocate memory for xmlrpc_server_info");
|
|
||||||
url_copy = (char*) malloc(strlen(aserver->_server_url) + 1);
|
|
||||||
XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
|
|
||||||
"Couldn't allocate memory for server URL");
|
|
||||||
auth_copy = (char*) malloc(strlen(aserver->_http_basic_auth) + 1);
|
|
||||||
XMLRPC_FAIL_IF_NULL(auth_copy, env, XMLRPC_INTERNAL_ERROR,
|
|
||||||
"Couldn't allocate memory for authentication info");
|
|
||||||
|
|
||||||
/* Build our object. */
|
|
||||||
strcpy(url_copy, aserver->_server_url);
|
|
||||||
server->_server_url = url_copy;
|
|
||||||
strcpy(auth_copy, aserver->_http_basic_auth);
|
|
||||||
server->_http_basic_auth = auth_copy;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (env->fault_occurred) {
|
|
||||||
if (url_copy)
|
|
||||||
free(url_copy);
|
|
||||||
if (auth_copy)
|
|
||||||
free(auth_copy);
|
|
||||||
if (server)
|
|
||||||
free(server);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return server;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void xmlrpc_server_info_free (xmlrpc_server_info *server)
|
|
||||||
{
|
|
||||||
XMLRPC_ASSERT_PTR_OK(server);
|
|
||||||
XMLRPC_ASSERT(server->_server_url != XMLRPC_BAD_POINTER);
|
|
||||||
|
|
||||||
if (server->_http_basic_auth)
|
|
||||||
free(server->_http_basic_auth);
|
|
||||||
free(server->_server_url);
|
|
||||||
server->_server_url = XMLRPC_BAD_POINTER;
|
|
||||||
free(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*=========================================================================
|
|
||||||
** xmlrpc_client_call_asynch
|
|
||||||
**=========================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
xmlrpc_client_call_asynch(const char * const serverUrl,
|
|
||||||
const char * const methodName,
|
|
||||||
xmlrpc_response_handler callback,
|
|
||||||
void * const userData,
|
|
||||||
const char * const format,
|
|
||||||
...) {
|
|
||||||
|
|
||||||
xmlrpc_env env;
|
|
||||||
va_list args;
|
|
||||||
xmlrpc_value * paramArrayP;
|
|
||||||
const char * suffix;
|
|
||||||
|
|
||||||
xmlrpc_env_init(&env);
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
||||||
XMLRPC_ASSERT_PTR_OK(format);
|
|
||||||
|
|
||||||
/* Build our argument array. */
|
|
||||||
va_start(args, format);
|
|
||||||
xmlrpc_build_value_va(&env, format, args, ¶mArrayP, &suffix);
|
|
||||||
va_end(args);
|
|
||||||
if (env.fault_occurred) {
|
|
||||||
/* Unfortunately, we have no way to return an error and the
|
|
||||||
regular callback for a failed RPC is designed to have the
|
|
||||||
parameter array passed to it. This was probably an oversight
|
|
||||||
of the original asynch design, but now we have to be as
|
|
||||||
backward compatible as possible, so we do this:
|
|
||||||
*/
|
|
||||||
(*callback)(serverUrl, methodName, NULL, userData, &env, NULL);
|
|
||||||
} else {
|
|
||||||
if (*suffix != '\0')
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
&env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
||||||
"specifier: '%s'. There must be exactly one arument.",
|
|
||||||
suffix);
|
|
||||||
else {
|
|
||||||
xmlrpc_server_info * serverP;
|
|
||||||
serverP = xmlrpc_server_info_new(&env, serverUrl);
|
|
||||||
if (!env.fault_occurred) {
|
|
||||||
xmlrpc_client_call_server_asynch_params(
|
|
||||||
serverP, methodName, callback, userData,
|
|
||||||
paramArrayP);
|
|
||||||
}
|
|
||||||
xmlrpc_server_info_free(serverP);
|
|
||||||
}
|
|
||||||
if (env.fault_occurred)
|
|
||||||
(*callback)(serverUrl, methodName, paramArrayP, userData,
|
|
||||||
&env, NULL);
|
|
||||||
xmlrpc_DECREF(paramArrayP);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlrpc_env_clean(&env);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
xmlrpc_client_call_asynch_params(const char * const serverUrl,
|
|
||||||
const char * const methodName,
|
|
||||||
xmlrpc_response_handler callback,
|
|
||||||
void * const userData,
|
|
||||||
xmlrpc_value * const paramArrayP) {
|
|
||||||
|
|
||||||
xmlrpc_env env;
|
|
||||||
xmlrpc_server_info *serverP;
|
|
||||||
|
|
||||||
xmlrpc_env_init(&env);
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
||||||
|
|
||||||
serverP = xmlrpc_server_info_new(&env, serverUrl);
|
|
||||||
if (!env.fault_occurred) {
|
|
||||||
xmlrpc_client_call_server_asynch_params(
|
|
||||||
serverP, methodName, callback, userData, paramArrayP);
|
|
||||||
|
|
||||||
xmlrpc_server_info_free(serverP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.fault_occurred)
|
|
||||||
/* We have no way to return failure; we report the failure
|
|
||||||
as it happened after we successfully started the RPC.
|
|
||||||
*/
|
|
||||||
(*callback)(serverUrl, methodName, paramArrayP, userData,
|
|
||||||
&env, NULL);
|
|
||||||
|
|
||||||
xmlrpc_env_clean(&env);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
xmlrpc_client_call_server_asynch(xmlrpc_server_info * const serverP,
|
|
||||||
const char * const methodName,
|
|
||||||
xmlrpc_response_handler callback,
|
|
||||||
void * const userData,
|
|
||||||
const char * const format,
|
|
||||||
...) {
|
|
||||||
|
|
||||||
xmlrpc_env env;
|
|
||||||
va_list args;
|
|
||||||
xmlrpc_value * paramArrayP;
|
|
||||||
const char * suffix;
|
|
||||||
|
|
||||||
xmlrpc_env_init(&env);
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_PTR_OK(format);
|
|
||||||
|
|
||||||
/* Build our parameter array. */
|
|
||||||
va_start(args, format);
|
|
||||||
xmlrpc_build_value_va(&env, format, args, ¶mArrayP, &suffix);
|
|
||||||
va_end(args);
|
|
||||||
if (env.fault_occurred) {
|
|
||||||
/* Unfortunately, we have no way to return an error and the
|
|
||||||
regular callback for a failed RPC is designed to have the
|
|
||||||
parameter array passed to it. This was probably an oversight
|
|
||||||
of the original asynch design, but now we have to be as
|
|
||||||
backward compatible as possible, so we do this:
|
|
||||||
*/
|
|
||||||
(*callback)(serverP->_server_url, methodName, NULL, userData,
|
|
||||||
&env, NULL);
|
|
||||||
} else {
|
|
||||||
if (*suffix != '\0')
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
&env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
||||||
"specifier: '%s'. There must be exactly one arument.",
|
|
||||||
suffix);
|
|
||||||
else {
|
|
||||||
xmlrpc_client_call_server_asynch_params(
|
|
||||||
serverP, methodName, callback, userData, paramArrayP);
|
|
||||||
}
|
|
||||||
xmlrpc_DECREF(paramArrayP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.fault_occurred)
|
|
||||||
(*callback)(serverP->_server_url, methodName, paramArrayP, userData,
|
|
||||||
&env, NULL);
|
|
||||||
|
|
||||||
xmlrpc_env_clean(&env);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
asynchComplete(call_info * const callInfoP,
|
|
||||||
xmlrpc_mem_block * const responseXmlP,
|
|
||||||
xmlrpc_env const transportEnv) {
|
|
||||||
/*----------------------------------------------------------------------------
|
|
||||||
Complete an asynchronous XML-RPC call request.
|
|
||||||
|
|
||||||
This includes calling the user's RPC completion routine.
|
|
||||||
|
|
||||||
'transportEnv' describes the an error that the transport
|
|
||||||
encountered in processing the call. If the transport successfully
|
|
||||||
sent the call to the server and processed the response but the
|
|
||||||
server failed the call, 'transportEnv' indicates no error, and the
|
|
||||||
response in *callInfoP might very well indicate that the server
|
|
||||||
failed the request.
|
|
||||||
-----------------------------------------------------------------------------*/
|
|
||||||
xmlrpc_env env;
|
|
||||||
xmlrpc_value * responseP = 0;
|
|
||||||
|
|
||||||
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. */
|
|
||||||
token = NULL;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,261 +0,0 @@
|
||||||
/*============================================================================
|
|
||||||
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 long)(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(timeout_t const 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_ */
|
|
|
@ -1,102 +0,0 @@
|
||||||
/*============================================================================
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
/*=========================================================================
|
|
||||||
** 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
/* 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"
|
|
||||||
|
|
||||||
#ifndef __xmlrpc_config_h__
|
|
||||||
#define __xmlrpc_config_h__
|
|
||||||
|
|
||||||
|
|
||||||
#define VA_LIST_IS_ARRAY @VA_LIST_IS_ARRAY_DEFINE@
|
|
||||||
|
|
||||||
#cmakedefine HAS_VA_COPY @HAS_VA_COPY@
|
|
||||||
|
|
||||||
#define HAVE_LIBWWW_SSL @HAVE_LIBWWW_SSL_DEFINE@
|
|
||||||
|
|
||||||
#define ATTR_UNUSED @ATTR_UNUSED@
|
|
||||||
|
|
||||||
#cmakedefine HAVE_UNICODE_WCHAR @HAVE_UNICODE_WCHAR@
|
|
||||||
|
|
||||||
#define DIRECTORY_SEPARATOR "@DIRECTORY_SEPARATOR@"
|
|
||||||
|
|
||||||
#cmakedefine HAVE_PTHREADS @HAVE_PTHREADS@
|
|
||||||
|
|
||||||
/* 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:
|
|
||||||
*/
|
|
||||||
#ifdef _WIN32
|
|
||||||
# define __inline__ __inline
|
|
||||||
#else
|
|
||||||
# ifndef __GNUC__
|
|
||||||
# ifndef __inline__
|
|
||||||
# ifdef __sgi
|
|
||||||
# define __inline__ __inline
|
|
||||||
# else
|
|
||||||
# define __inline__
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* A timeout in milliseconds. */
|
|
||||||
typedef unsigned long timeout_t;
|
|
||||||
|
|
||||||
#if !defined(WIN32) && defined(_WIN32)
|
|
||||||
# define WIN32
|
|
||||||
#endif
|
|
||||||
#if defined(WIN32)
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#if !defined (vsnprintf)
|
|
||||||
#define vsnprintf _vsnprintf
|
|
||||||
#endif
|
|
||||||
#if !defined (snprintf)
|
|
||||||
#define snprintf _snprintf
|
|
||||||
#endif
|
|
||||||
#include <time.h>
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <direct.h> /* for _chdir() */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
__inline BOOL setenv(const char* name, const char* value, int i)
|
|
||||||
{
|
|
||||||
return (SetEnvironmentVariable(name, value) != 0) ? TRUE : FALSE;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,759 +0,0 @@
|
||||||
/*=============================================================================
|
|
||||||
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"
|
|
||||||
|
|
||||||
#if defined(__BEOS__)
|
|
||||||
/* Some helpful system header has char==bool, then bool.h does int==bool. */
|
|
||||||
#define HAVE_BOOL 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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>
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
# include "xmlrpc_pthreads.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cmcurl/curl/curl.h>
|
|
||||||
#include <cmcurl/curl/types.h>
|
|
||||||
#include <cmcurl/curl/easy.h>
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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*/
|
|
||||||
|
|
||||||
static void xmlrpc_abort(void)
|
|
||||||
{
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clientTransport {
|
|
||||||
#if defined (HAVE_PTHREADS)
|
|
||||||
pthread_mutex_t listLock;
|
|
||||||
#endif
|
|
||||||
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;
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
pthread_t thread;
|
|
||||||
#endif
|
|
||||||
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);
|
|
||||||
(void)err;
|
|
||||||
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 {
|
|
||||||
#ifdef HAVE_PTHREADS
|
|
||||||
pthread_mutex_init(&transportP->listLock, NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
pthread_mutex_destroy(&clientTransportP->listLock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
static char proxy[1024];
|
|
||||||
static char proxyUser[1024];
|
|
||||||
int proxy_type = 0;
|
|
||||||
|
|
||||||
if ( getenv("HTTP_PROXY") )
|
|
||||||
{
|
|
||||||
proxy_type = 1;
|
|
||||||
if (getenv("HTTP_PROXY_PORT") )
|
|
||||||
{
|
|
||||||
sprintf(proxy, "%s:%s", getenv("HTTP_PROXY"), getenv("HTTP_PROXY_PORT"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(proxy, "%s", getenv("HTTP_PROXY"));
|
|
||||||
}
|
|
||||||
if ( getenv("HTTP_PROXY_TYPE") )
|
|
||||||
{
|
|
||||||
/* HTTP/SOCKS4/SOCKS5 */
|
|
||||||
if ( strcmp(getenv("HTTP_PROXY_TYPE"), "HTTP") == 0 )
|
|
||||||
{
|
|
||||||
proxy_type = 1;
|
|
||||||
}
|
|
||||||
else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS4") == 0 )
|
|
||||||
{
|
|
||||||
proxy_type = 2;
|
|
||||||
}
|
|
||||||
else if ( strcmp(getenv("HTTP_PROXY_TYPE"), "SOCKS5") == 0 )
|
|
||||||
{
|
|
||||||
proxy_type = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( getenv("HTTP_PROXY_USER") )
|
|
||||||
{
|
|
||||||
strcpy(proxyUser, getenv("HTTP_PROXY_USER"));
|
|
||||||
}
|
|
||||||
if ( getenv("HTTP_PROXY_PASSWD") )
|
|
||||||
{
|
|
||||||
strcat(proxyUser, ":");
|
|
||||||
strcat(proxyUser, getenv("HTTP_PROXY_PASSWD"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Using proxy */
|
|
||||||
if ( proxy_type > 0 )
|
|
||||||
{
|
|
||||||
curl_easy_setopt(curlSessionP, CURLOPT_PROXY, proxy);
|
|
||||||
switch (proxy_type)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
curl_easy_setopt(curlSessionP, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
|
||||||
if (*proxyUser)
|
|
||||||
{
|
|
||||||
curl_easy_setopt(curlSessionP, CURLOPT_PROXYUSERPWD, proxyUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
free(curlTransactionP);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 (%d)",
|
|
||||||
curlTransactionP->curlError, res);
|
|
||||||
else {
|
|
||||||
CURLcode crRes;
|
|
||||||
long http_result;
|
|
||||||
crRes = curl_easy_getinfo(curlSessionP, CURLINFO_HTTP_CODE,
|
|
||||||
&http_result);
|
|
||||||
|
|
||||||
if (crRes != 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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) {
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
createRpcThread(envP, rpcP, &rpcP->thread);
|
|
||||||
#else
|
|
||||||
xmlrpc_abort();
|
|
||||||
#endif
|
|
||||||
if (!envP->fault_occurred)
|
|
||||||
rpcP->threadExists = TRUE;
|
|
||||||
}
|
|
||||||
if (!envP->fault_occurred) {
|
|
||||||
list_init_header(&rpcP->link, rpcP);
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
pthread_mutex_lock(&clientTransportP->listLock);
|
|
||||||
#endif
|
|
||||||
list_add_head(&clientTransportP->rpcList, &rpcP->link);
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
pthread_mutex_unlock(&clientTransportP->listLock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (envP->fault_occurred)
|
|
||||||
destroyCurlTransaction(rpcP->curlTransactionP);
|
|
||||||
}
|
|
||||||
if (envP->fault_occurred)
|
|
||||||
{
|
|
||||||
free(rpcP);
|
|
||||||
rpcP = 0; /* set this to null as it is used later on */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*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) {
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
void *status;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
result = pthread_join(rpcP->thread, &status);
|
|
||||||
(void)result;
|
|
||||||
#else
|
|
||||||
xmlrpc_abort();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
pthread_mutex_lock(&clientTransportP->listLock);
|
|
||||||
#else
|
|
||||||
xmlrpc_abort();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
list_foreach(&clientTransportP->rpcList, finishRpc, NULL);
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
pthread_mutex_unlock(&clientTransportP->listLock);
|
|
||||||
#else
|
|
||||||
xmlrpc_abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
#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
|
@ -1,390 +0,0 @@
|
||||||
/* 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 <cm_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. */
|
|
||||||
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 = (int)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) {
|
|
||||||
|
|
||||||
/* 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. */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*============================================================================
|
|
||||||
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
|
|
|
@ -1,406 +0,0 @@
|
||||||
/* -*- 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,767 +0,0 @@
|
||||||
/* 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 stringLength,
|
|
||||||
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((int)(str[0])))
|
|
||||||
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
|
|
||||||
"\"%s\" must not contain whitespace", str);
|
|
||||||
|
|
||||||
/* Convert the value. */
|
|
||||||
end = str + stringLength;
|
|
||||||
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 + stringLength)
|
|
||||||
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 stringLength)
|
|
||||||
{
|
|
||||||
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((int)(str[0])))
|
|
||||||
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
|
|
||||||
"\"%s\" must not contain whitespace", str);
|
|
||||||
|
|
||||||
/* Convert the value. */
|
|
||||||
end = str + stringLength;
|
|
||||||
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 + stringLength)
|
|
||||||
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;
|
|
||||||
size_t 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;
|
|
||||||
size_t size, i;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
XMLRPC_ASSERT(elem != NULL);
|
|
||||||
|
|
||||||
/* Set up our error-handling preconditions. */
|
|
||||||
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;
|
|
||||||
size_t size, i;
|
|
||||||
char *cdata;
|
|
||||||
size_t cdata_size;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
XMLRPC_ASSERT(elem != NULL);
|
|
||||||
|
|
||||||
/* Set up our error-handling preconditions. */
|
|
||||||
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;
|
|
||||||
size_t size, i;
|
|
||||||
xml_element **params, *param, *value;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
XMLRPC_ASSERT(elem != NULL);
|
|
||||||
|
|
||||||
/* Set up our error-handling preconditions. */
|
|
||||||
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, (int)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, (int)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;
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
/* 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_pthreads_h_
|
|
||||||
#define xmlrpc_pthreads_h_
|
|
||||||
|
|
||||||
#if !defined(HAVE_PTHREADS)
|
|
||||||
# error This system does not have PThreads
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
# define _REENTRANT
|
|
||||||
# include <pthread.h>
|
|
||||||
#elif defined (WIN32)
|
|
||||||
/*typedef PVOID HANDLE; */
|
|
||||||
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
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,819 +0,0 @@
|
||||||
/* 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. */
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* 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, (unsigned int)xml_len);
|
|
||||||
|
|
||||||
/* Allocate our output buffer.
|
|
||||||
** If this fails, we need to die in a special fashion. */
|
|
||||||
output = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
|
|
||||||
if (!envP->fault_occurred) {
|
|
||||||
const char * methodName;
|
|
||||||
xmlrpc_value * paramArray;
|
|
||||||
xmlrpc_env fault;
|
|
||||||
|
|
||||||
xmlrpc_env_init(&fault);
|
|
||||||
|
|
||||||
xmlrpc_parse_call(&fault, xml_data, xml_len,
|
|
||||||
&methodName, ¶mArray);
|
|
||||||
|
|
||||||
if (!fault.fault_occurred) {
|
|
||||||
xmlrpc_value * result;
|
|
||||||
|
|
||||||
dispatch_call(&fault, registryP, methodName, paramArray, &result);
|
|
||||||
|
|
||||||
if (!fault.fault_occurred) {
|
|
||||||
xmlrpc_serialize_response(envP, output, result);
|
|
||||||
|
|
||||||
/* A comment here used to say that
|
|
||||||
xmlrpc_serialize_response() could fail and "leave
|
|
||||||
stuff in the buffer." Don't know what that means,
|
|
||||||
but it sounds like something that needs to be
|
|
||||||
fixed. The old code aborted the program here if
|
|
||||||
xmlrpc_serialize_repsonse() failed. 04.11.17
|
|
||||||
*/
|
|
||||||
xmlrpc_DECREF(result);
|
|
||||||
}
|
|
||||||
xmlrpc_strfree(methodName);
|
|
||||||
xmlrpc_DECREF(paramArray);
|
|
||||||
}
|
|
||||||
if (!envP->fault_occurred && fault.fault_occurred)
|
|
||||||
xmlrpc_serialize_fault(envP, output, &fault);
|
|
||||||
|
|
||||||
xmlrpc_env_clean(&fault);
|
|
||||||
|
|
||||||
if (envP->fault_occurred)
|
|
||||||
XMLRPC_MEMBLOCK_FREE(char, output);
|
|
||||||
else
|
|
||||||
xmlrpc_traceXml("XML-RPC RESPONSE",
|
|
||||||
XMLRPC_MEMBLOCK_CONTENTS(char, output),
|
|
||||||
(unsigned int)XMLRPC_MEMBLOCK_SIZE(char, output));
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*=========================================================================
|
|
||||||
** system.multicall
|
|
||||||
**=========================================================================
|
|
||||||
** Low-tech support for transparent, boxed methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static char *multicall_help =
|
|
||||||
"Process an array of calls, and return an array of results. Calls should "
|
|
||||||
"be structs of the form {'methodName': string, 'params': array}. Each "
|
|
||||||
"result will either be a single-item array containg the result value, or "
|
|
||||||
"a struct of the form {'faultCode': int, 'faultString': string}. This "
|
|
||||||
"is useful when you need to make lots of small calls without lots of "
|
|
||||||
"round trips.";
|
|
||||||
|
|
||||||
static xmlrpc_value *
|
|
||||||
call_one_method(xmlrpc_env *env, xmlrpc_registry *registry,
|
|
||||||
xmlrpc_value *method_info) {
|
|
||||||
|
|
||||||
xmlrpc_value *result_val, *result;
|
|
||||||
char *method_name;
|
|
||||||
xmlrpc_value *param_array;
|
|
||||||
|
|
||||||
/* Error-handling preconditions. */
|
|
||||||
result = result_val = NULL;
|
|
||||||
|
|
||||||
/* Extract our method name and parameters. */
|
|
||||||
xmlrpc_parse_value(env, method_info, "{s:s,s:A,*}",
|
|
||||||
"methodName", &method_name,
|
|
||||||
"params", ¶m_array);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
/* Watch out for a deep recursion attack. */
|
|
||||||
if (strcmp(method_name, "system.multicall") == 0)
|
|
||||||
XMLRPC_FAIL(env, XMLRPC_REQUEST_REFUSED_ERROR,
|
|
||||||
"Recursive system.multicall strictly forbidden");
|
|
||||||
|
|
||||||
/* Perform the call. */
|
|
||||||
dispatch_call(env, registry, method_name, param_array, &result_val);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
/* Build our one-item result array. */
|
|
||||||
result = xmlrpc_build_value(env, "(V)", result_val);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (result_val)
|
|
||||||
xmlrpc_DECREF(result_val);
|
|
||||||
if (env->fault_occurred) {
|
|
||||||
if (result)
|
|
||||||
xmlrpc_DECREF(result);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static xmlrpc_value *
|
|
||||||
system_multicall(xmlrpc_env *env,
|
|
||||||
xmlrpc_value *param_array,
|
|
||||||
void *user_data) {
|
|
||||||
|
|
||||||
xmlrpc_registry *registry;
|
|
||||||
xmlrpc_value *methlist, *methinfo, *results, *result;
|
|
||||||
size_t size, i;
|
|
||||||
xmlrpc_env env2;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
XMLRPC_ASSERT_VALUE_OK(param_array);
|
|
||||||
XMLRPC_ASSERT_PTR_OK(user_data);
|
|
||||||
|
|
||||||
/* Error-handling preconditions. */
|
|
||||||
results = result = NULL;
|
|
||||||
xmlrpc_env_init(&env2);
|
|
||||||
|
|
||||||
/* Turn our arguments into something more useful. */
|
|
||||||
registry = (xmlrpc_registry*) user_data;
|
|
||||||
xmlrpc_parse_value(env, param_array, "(A)", &methlist);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
/* Create an empty result list. */
|
|
||||||
results = xmlrpc_build_value(env, "()");
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
/* Loop over our input list, calling each method in turn. */
|
|
||||||
size = xmlrpc_array_size(env, methlist);
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
methinfo = xmlrpc_array_get_item(env, methlist, (int)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, (int)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 = 0;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,620 +0,0 @@
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Sanity-check this string before we print it. */
|
|
||||||
#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
|
|
||||||
sanity_check_utf8(str, len);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Calculate the amount of space we'll need. */
|
|
||||||
needed = 0;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (str[i] == '<')
|
|
||||||
needed += 4; /* < */
|
|
||||||
else if (str[i] == '>')
|
|
||||||
needed += 4; /* > */
|
|
||||||
else if (str[i] == '&')
|
|
||||||
needed += 5; /* & */
|
|
||||||
else
|
|
||||||
needed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate our memory block. */
|
|
||||||
retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
/* Copy over the newly-allocated data. */
|
|
||||||
out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (str[i] == '<') {
|
|
||||||
*out++ = '&';
|
|
||||||
*out++ = 'l';
|
|
||||||
*out++ = 't';
|
|
||||||
*out++ = ';';
|
|
||||||
} else if (str[i] == '>') {
|
|
||||||
*out++ = '&';
|
|
||||||
*out++ = 'g';
|
|
||||||
*out++ = 't';
|
|
||||||
*out++ = ';';
|
|
||||||
} else if (str[i] == '&') {
|
|
||||||
*out++ = '&';
|
|
||||||
*out++ = 'a';
|
|
||||||
*out++ = 'm';
|
|
||||||
*out++ = 'p';
|
|
||||||
*out++ = ';';
|
|
||||||
} else {
|
|
||||||
*out++ = str[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (env->fault_occurred) {
|
|
||||||
if (retval)
|
|
||||||
XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
|
|
||||||
retval = NULL;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static xmlrpc_mem_block*
|
|
||||||
escape_block (xmlrpc_env *env,
|
|
||||||
xmlrpc_mem_block *block) {
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
XMLRPC_ASSERT(block != NULL);
|
|
||||||
|
|
||||||
return escape_string(env,
|
|
||||||
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
|
|
||||||
XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*=========================================================================
|
|
||||||
** xmlrpc_serialize_string_data
|
|
||||||
**=========================================================================
|
|
||||||
** Escape and print the contents of a string.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
xmlrpc_serialize_string_data(xmlrpc_env *env,
|
|
||||||
xmlrpc_mem_block *output,
|
|
||||||
xmlrpc_value *string) {
|
|
||||||
|
|
||||||
xmlrpc_mem_block *escaped;
|
|
||||||
char *contents;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
/* Since this routine can only be called internally, we only need
|
|
||||||
** an assertion here, not a runtime type check.
|
|
||||||
** XXX - Temporarily disabled because we're using this code to
|
|
||||||
** print <dateTime.iso8601> values as well. */
|
|
||||||
/* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
|
|
||||||
|
|
||||||
/* Escape any '&' and '<' characters in the string. */
|
|
||||||
escaped = escape_block(env, &string->_block);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
|
|
||||||
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
|
|
||||||
|
|
||||||
/* Print the string. */
|
|
||||||
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
|
|
||||||
XMLRPC_FAIL_IF_FAULT(env);
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (escaped)
|
|
||||||
XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*=========================================================================
|
|
||||||
** xmlrpc_serialize_base64_data
|
|
||||||
**=========================================================================
|
|
||||||
** Print the contents of a memory block as well-formed Base64 data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
xmlrpc_serialize_base64_data (xmlrpc_env *env,
|
|
||||||
xmlrpc_mem_block *output,
|
|
||||||
unsigned char* data, size_t len) {
|
|
||||||
|
|
||||||
xmlrpc_mem_block *encoded;
|
|
||||||
unsigned char *contents;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
/* Encode the data. */
|
|
||||||
encoded = xmlrpc_base64_encode_without_newlines(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, (int)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, (int)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, (int)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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
/* 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 <cmxmlrpc/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
|
|
|
@ -1,799 +0,0 @@
|
||||||
/* 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);
|
|
||||||
}
|
|
|
@ -1,182 +0,0 @@
|
||||||
/* 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
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*============================================================================
|
|
||||||
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
|
|
||||||
|
|
|
@ -1,608 +0,0 @@
|
||||||
/* 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_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 = (int)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 (int)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 xmIndex;
|
|
||||||
|
|
||||||
/* Suppress a compiler warning about uninitialized variables. */
|
|
||||||
xmIndex = 0;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(envP);
|
|
||||||
XMLRPC_ASSERT_VALUE_OK(strctP);
|
|
||||||
XMLRPC_ASSERT(key != NULL);
|
|
||||||
|
|
||||||
XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
|
|
||||||
xmIndex = find_member(strctP, key, key_len);
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (envP->fault_occurred)
|
|
||||||
return 0;
|
|
||||||
return (xmIndex >= 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 xmIndex;
|
|
||||||
|
|
||||||
/* Get our member index. */
|
|
||||||
xmIndex = find_member(structP, key, strlen(key));
|
|
||||||
if (xmIndex < 0)
|
|
||||||
*valuePP = NULL;
|
|
||||||
else {
|
|
||||||
_struct_member * const members =
|
|
||||||
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
|
|
||||||
*valuePP = members[xmIndex].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 xmIndex;
|
|
||||||
|
|
||||||
/* Get our member index. */
|
|
||||||
xmIndex = find_member(structP,
|
|
||||||
XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block),
|
|
||||||
XMLRPC_MEMBLOCK_SIZE(char, &keyP->_block)-1);
|
|
||||||
if (xmIndex < 0)
|
|
||||||
*valuePP = NULL;
|
|
||||||
else {
|
|
||||||
_struct_member * const members =
|
|
||||||
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
|
|
||||||
*valuePP = members[xmIndex].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 xmIndex;
|
|
||||||
_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;
|
|
||||||
xmIndex = find_member(strctP, key, key_len);
|
|
||||||
|
|
||||||
if (xmIndex >= 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[xmIndex];
|
|
||||||
|
|
||||||
/* 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 xmIndex,
|
|
||||||
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 (xmIndex >= size)
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, XMLRPC_INDEX_ERROR, "Index %u is beyond the end of "
|
|
||||||
"the %u-member structure", xmIndex, (unsigned int)size);
|
|
||||||
else {
|
|
||||||
_struct_member * const memberP = &members[xmIndex];
|
|
||||||
*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 xmIndex,
|
|
||||||
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 (xmIndex < 0)
|
|
||||||
xmlrpc_env_set_fault_formatted(
|
|
||||||
envP, XMLRPC_INDEX_ERROR, "Index %d is negative.");
|
|
||||||
else {
|
|
||||||
xmlrpc_struct_read_member(envP, structP, xmIndex, keyvalP, valueP);
|
|
||||||
if (!envP->fault_occurred) {
|
|
||||||
xmlrpc_DECREF(*keyvalP);
|
|
||||||
xmlrpc_DECREF(*valueP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (envP->fault_occurred) {
|
|
||||||
*keyvalP = NULL;
|
|
||||||
*valueP = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
#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 size_t 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((int)(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+1] = '\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;
|
|
||||||
}
|
|
|
@ -1,367 +0,0 @@
|
||||||
/* 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>
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
# define vsnprintf _vsnprintf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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)
|
|
||||||
{
|
|
||||||
size_t 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((int)(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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,142 +0,0 @@
|
||||||
/* 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 (); */
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
/* Copyright information is at the end of the file */
|
|
||||||
#ifndef _XMLRPC_TRANSPORT_H_
|
|
||||||
#define _XMLRPC_TRANSPORT_H_ 1
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
# include "xmlrpc_pthreads.h" /* For threading helpers. */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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.
|
|
||||||
**=========================================================================
|
|
||||||
*/
|
|
||||||
#if defined(HAVE_PTHREADS)
|
|
||||||
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);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#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
|
|
|
@ -1,372 +0,0 @@
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
int cwc;
|
|
||||||
|
|
||||||
XMLRPC_ASSERT_ENV_OK(env);
|
|
||||||
XMLRPC_ASSERT_PTR_OK(wcs_data);
|
|
||||||
|
|
||||||
/* 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];
|
|
||||||
cwc = wc;
|
|
||||||
if (cwc <= 0x007F) {
|
|
||||||
buffer[bytes_used++] = wc & 0x7F;
|
|
||||||
} else if (cwc <= 0x07FF) {
|
|
||||||
/* 110xxxxx 10xxxxxx */
|
|
||||||
buffer[bytes_used++] = 0xC0 | (wc >> 6);
|
|
||||||
buffer[bytes_used++] = 0x80 | (wc & 0x3F);
|
|
||||||
} else if (cwc <= 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 */
|
|
|
@ -1,82 +0,0 @@
|
||||||
/* 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);
|
|
Loading…
Reference in New Issue