add xmlrpc-c 1.03.14 to in tree libs

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3772 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris committed 2006-12-21 03:57:49 +00:00
1 parent 6d9679b164
commit 3abb7730b2
338 files changed
+98032 -2

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
Makefile
rpctest
rpctest.static
cpptest
efrpctest
efrpctest_wrapper
+145
View File
@@ -0,0 +1,145 @@
ifeq ($(SRCDIR)x,x)
SRCDIR = $(CURDIR)/..
endif
SUBDIR = src
BUILDDIR = $(SRCDIR)
VPATH = .:$(SRCDIR)
include $(BUILDDIR)/Makefile.config
default: all
SUBDIRS =
ifeq ($(ENABLE_CPLUSPLUS),yes)
SUBDIRS += cpp
endif
WININET_TRANSPORT_DIR = $(SRCDIR)/lib/wininet_transport
CURL_TRANSPORT_DIR = $(SRCDIR)/lib/curl_transport
LIBWWW_TRANSPORT_DIR = $(SRCDIR)/lib/libwww_transport
CASPRINTF_OBJ = $(BUILDDIR)/lib/util/casprintf.lo
TRANSPORT_OBJS =
ifeq ($(MUST_BUILD_WININET_CLIENT),yes)
TRANSPORT_OBJS += $(WININET_TRANSPORT_DIR)/xmlrpc_wininet_transport.lo
endif
ifeq ($(MUST_BUILD_CURL_CLIENT),yes)
TRANSPORT_OBJS += $(CURL_TRANSPORT_DIR)/xmlrpc_curl_transport.lo
endif
ifeq ($(MUST_BUILD_LIBWWW_CLIENT),yes)
TRANSPORT_OBJS += $(LIBWWW_TRANSPORT_DIR)/xmlrpc_libwww_transport.lo
endif
ifeq ($(ENABLE_LIBXML2_BACKEND),yes)
LIBXML_INCLUDES = $(LIBXML2_CFLAGS)
LIBXML = $(LIBXML2_LIBS)
else
LIBXML_INCLUDES = -I$(SRCDIR)/lib/expat/xmlparse
LIBXML = $(BUILDDIR)/lib/expat/xmlparse/libxmlrpc_xmlparse.la \
$(BUILDDIR)/lib/expat/xmltok/libxmlrpc_xmltok.la
endif
ABYSS_INCLUDES = -I$(SRCDIR)/lib/abyss/src
LIBXMLRPC_CLIENT_OBJS = xmlrpc_client.lo
LIBXMLRPC_SERVER_OBJS = xmlrpc_registry.lo
LIBXMLRPC_SERVER_ABYSS_OBJS = xmlrpc_server_abyss.lo
ifeq ($(ENABLE_LIBXML2_BACKEND),yes)
XMLRPC_XML_PARSER = xmlrpc_libxml2.lo
else
XMLRPC_XML_PARSER = xmlrpc_expat.lo
endif
LIBXMLRPC_OBJS = xmlrpc_support.lo xmlrpc_data.lo xmlrpc_builddecomp.lo \
xmlrpc_datetime.lo xmlrpc_array.lo xmlrpc_struct.lo \
$(XMLRPC_XML_PARSER) xmlrpc_parse.lo \
xmlrpc_serialize.lo xmlrpc_base64.lo \
xmlrpc_utf8.lo xmlrpc_authcookie.lo xmlrpc_strutil.lo
LIB_OBJS = $(LIBXMLRPC_CLIENT_OBJS) $(LIBXMLRPC_SERVER_OBJS)
TRANSPORT_INCLUDES = \
-I$(WININET_TRANSPORT_DIR) \
-I$(CURL_TRANSPORT_DIR) \
-I$(LIBWWW_TRANSPORT_DIR) \
INCLUDES = -I$(BUILDDIR) -I$(SRCDIR) \
-I$(SRCDIR)/include -I$(SRCDIR)/lib/util/include \
-I$(SRCDIR)/lib/abyss/src \
$(TRANSPORT_INCLUDES) \
$(LIBXML_INCLUDES) \
TARGET_LTLIBRARIES = libxmlrpc.la libxmlrpc_server.la \
libxmlrpc_server_abyss.la
ifeq ($(MUST_BUILD_CLIENT),yes)
TARGET_LTLIBRARIES += libxmlrpc_client.la
endif
ifeq ($(ENABLE_CGI_SERVER),yes)
TARGET_LTLIBRARIES += libxmlrpc_server_cgi.la
endif
all: $(TARGET_LTLIBRARIES) $(SUBDIRS:%=%/all)
LDFLAGS = $(LADD)
LIBLDFLAGS = $(LDFLAGS_VERSINFO) -rpath $(LIBINST_DIR) $(LADD)
libxmlrpc.la: $(LIBXMLRPC_OBJS)
$(LIBTOOL) --mode=link $(CCLD) -o $@ $(LIBLDFLAGS) $^
libxmlrpc_client.la: xmlrpc_client.lo $(TRANSPORT_OBJS) $(CASPRINTF_OBJ)
$(LIBTOOL) --mode=link $(CCLD) -o $@ $(LIBLDFLAGS) $^
libxmlrpc_server.la: xmlrpc_registry.lo
$(LIBTOOL) --mode=link $(CCLD) -o $@ $(LIBLDFLAGS) $^
libxmlrpc_server_abyss.la: xmlrpc_server_abyss.lo
$(LIBTOOL) --mode=link $(CCLD) -o $@ $(LIBLDFLAGS) $^
libxmlrpc_server_cgi.la: xmlrpc_server_cgi.lo
$(LIBTOOL) --mode=link $(CCLD) -o $@ $(LIBLDFLAGS) $^
CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_PERSONAL) $(CADD)
$(LIB_OBJS):%.lo:%.c
$(LIBTOOL) --mode=compile $(CC) -c $(INCLUDES) $(CFLAGS) $<
$(LIBXMLRPC_OBJS):%.lo:%.c
$(LIBTOOL) --mode=compile $(CC) -c $(INCLUDES) $(LIBXML_INCLUDES) \
$(CFLAGS) $<
$(LIBXMLRPC_SERVER_ABYSS_OBJS):%.lo:%.c
$(LIBTOOL) --mode=compile $(CC) -c $(INCLUDES) $(ABYSS_INCLUDES) \
$(CFLAGS) $<
xmlrpc_server_cgi.lo:%.lo:%.c
$(LIBTOOL) --mode=compile $(CC) -c $(INCLUDES) \
$(CFLAGS) $<
LTLIBRARIES_TO_INSTALL = $(TARGET_LTLIBRARIES)
check:
$(MAKE) -C test check
.PHONY: install
install: install-common $(SUBDIRS:%=%/install)
.PHONY: clean clean-local distclean
clean: clean-common clean-local
clean-local: cpp/clean
distclean: clean distclean-common
.PHONY: dep
dep: $(SUBDIRS:%=%/dep) $(BUILDDIR)/transport_config.h dep-common
include $(SRCDIR)/Makefile.common
xmlrpc_client.lo: $(BUILDDIR)/transport_config.h
include Makefile.depend
View File
Whitespace-only changes.
+1
View File
@@ -0,0 +1 @@
cpptest
+177
View File
@@ -0,0 +1,177 @@
ifeq ($(SRCDIR)x,x)
SRCDIR = $(CURDIR)/../..
endif
SUBDIR = src/cpp
BUILDDIR = $(SRCDIR)
VPATH = .:$(SRCDIR)
include $(BUILDDIR)/Makefile.config
default: all
WININET_TRANSPORT_DIR = $(SRCDIR)/lib/wininet_transport
CURL_TRANSPORT_DIR = $(SRCDIR)/lib/curl_transport
LIBWWW_TRANSPORT_DIR = $(SRCDIR)/lib/libwww_transport
ifeq ($(ENABLE_LIBXML2_BACKEND),yes)
LIBXML_INCLUDES = $(LIBXML2_CFLAGS)
LIBXML = $(LIBXML2_LIBS)
else
LIBXML_INCLUDES = -I$(SRCDIR)/lib/expat/xmlparse
LIBXML = $(BUILDDIR)/lib/expat/xmlparse/libxmlrpc_xmlparse.la \
$(BUILDDIR)/lib/expat/xmltok/libxmlrpc_xmltok.la
endif
ifeq ($(ENABLE_LIBXML2),yes)
XMLRPC_XML_PARSER = xmlrpc_libxml2.lo
else
XMLRPC_XML_PARSER = xmlrpc_expat.lo
endif
LIBXMLRPC = $(BUILDDIR)/src/libxmlrpc.la
LIBXMLRPC_SERVER = $(BUILDDIR)/src/libxmlrpc_server.la
LIBXMLRPC_CLIENT = $(BUILDDIR)/src/libxmlrpc_client.la
LIBXMLRPCPP_OBJS = girmem.o value.o fault.o outcome.o param_list.o xml.o
LIBXMLRPC_SERVERPP_OBJS = registry.o
LIBXMLRPC_SERVER_ABYSSPP_OBJS = server_abyss.o
LIBXMLRPC_CLIENTPP_OBJS = client.o client_simple.o
$(LIBXMLRPC_CLIENTPP_OBJS): $(BUILDDIR)/transport_config.h
TRANSPORT_OBJS =
ifeq ($(MUST_BUILD_WININET_CLIENT),yes)
TRANSPORT_OBJS += $(WININET_TRANSPORT_DIR)/xmlrpc_wininet_transport.lo
endif
ifeq ($(MUST_BUILD_CURL_CLIENT),yes)
TRANSPORT_OBJS += $(CURL_TRANSPORT_DIR)/xmlrpc_curl_transport.lo
endif
ifeq ($(MUST_BUILD_LIBWWW_CLIENT),yes)
TRANSPORT_OBJS += $(LIBWWW_TRANSPORT_DIR)/xmlrpc_libwww_transport.lo
endif
TRANSPORT_INCLUDES = \
-I$(WININET_TRANSPORT_DIR) \
-I$(CURL_TRANSPORT_DIR) \
-I$(LIBWWW_TRANSPORT_DIR) \
BASIC_INCLUDES = -I$(SRCDIR)/include -I$(BUILDDIR) -I$(SRCDIR) \
-I$(SRCDIR)/lib/util/include
# We always statically-link our C++ code because it's tiny, and C++ is
# no good at shared libraries, anyway. It's actually getting bigger,
# but since I'm determined not to expand the use of libtool (because
# it obfuscates the build process), I haven't figured out yet how to
# get the shared libraries to build for all the platforms.
# libxmlrpc_cpp.a is the legacy C++ wrapper library. The others are the
# more elaborate replacements.
TARGET_LIBRARIES = \
libxmlrpc_cpp.a \
libxmlrpc++.a \
libxmlrpc_server++.a \
libxmlrpc_server_abyss++.a \
libxmlrpc_client++.a \
TARGET_PROGS = cpptest
all: $(TARGET_LIBRARIES)
ifeq ($(MUST_BUILD_CURL_CLIENT),yes)
LDFLAGS += $(shell curl-config --libs) -lpthread
endif
ifeq ($(MUST_BUILD_LIBWWW_CLIENT),yes)
LDFLAGS += $(shell libwww-config --libs)
endif
LDFLAGS += $(LADD)
cpptest:%:%.o libxmlrpc_server_abyss++.a libxmlrpc_server++.a \
libxmlrpc_client++.a libxmlrpc++.a libxmlrpc_cpp.a \
$(LIBXMLRPC) $(LIBXMLRPC_SERVER) $(LIBXMLRPC_CLIENT) $(LIBXML)
$(LIBTOOL) --mode=link $(CXXLD) -o $@ $(LDFLAGS) $^
LIBLDFLAGS = $(LADD)
libxmlrpc_cpp.a: XmlRpcCpp.o
-rm -f $@
$(AR) cru $@ $^
$(RANLIB) $@
libxmlrpc++.a: $(LIBXMLRPCPP_OBJS)
-rm -f $@
$(AR) cru $@ $^
$(RANLIB) $@
libxmlrpc_server++.a: $(LIBXMLRPC_SERVERPP_OBJS)
-rm -f $@
$(AR) cru $@ $^
$(RANLIB) $@
libxmlrpc_server_abyss++.a: $(LIBXMLRPC_SERVER_ABYSSPP_OBJS)
-rm -f $@
$(AR) cru $@ $^
$(RANLIB) $@
libxmlrpc_client++.a: $(LIBXMLRPC_CLIENTPP_OBJS)
-rm -f $@
$(AR) cru $@ $^
$(RANLIB) $@
CXXFLAGS = $(CXXFLAGS_COMMON) $(CFLAGS_PERSONAL) $(CADD)
cpptest.o:%.o:%.cpp
$(CXX) -c $(BASIC_INCLUDES) $(CXXFLAGS) $<
XmlRpcCpp.o:%.o:%.cpp
$(CXX) -c $(BASIC_INCLUDES) $(CXXFLAGS) $<
$(LIBXMLRPCPP_OBJS):%.o:%.cpp
$(CXX) -c $(BASIC_INCLUDES) $(CXXFLAGS) $<
SERVER_INCLUDES = $(BASIC_INCLUDES) $(LIBXML_INCLUDES)
$(LIBXMLRPC_SERVERPP_OBJS):%.o:%.cpp
$(CXX) -c $(SERVER_INCLUDES) $(CXXFLAGS) $<
SERVER_ABYSS_INCLUDES = $(SERVER_INCLUDES) -I$(SRCDIR)/lib/abyss/src
$(LIBXMLRPC_SERVER_ABYSSPP_OBJS):%.o:%.cpp
$(CXX) -c $(SERVER_ABYSS_INCLUDES) $(CXXFLAGS) $<
CLIENT_INCLUDES = $(BASIC_INCLUDES) $(LIBXML_INCLUDES) $(TRANSPORT_INCLUDES)
$(LIBXMLRPC_CLIENTPP_OBJS):%.o:%.cpp
$(CXX) -c $(CLIENT_INCLUDES) $(CXXFLAGS) $<
LIBRARIES_TO_INSTALL = $(TARGET_LIBRARIES)
check: cpptest
for tst in $^; do \
./$$tst; \
done;
.PHONY: install
install: install-common
.PHONY: clean clean-local distclean
clean: clean-common clean-local
clean-local:
rm -f $(TARGET_PROGS)
distclean: clean distclean-common
# INCLUDES and DEP_SOURCES are used by dep-common target
INCLUDES = $(BASIC_INCLUDES) $(CLIENT_INCLUDES) $(LIBXML_INCLUDES) \
$(SERVER_INCLUDES) $(SERVER_ABYSS_INCLUDES) $(TRANSPORT_INCLUDES)
DEP_SOURCES = *.cpp
.PHONY: dep
dep: $(BUILDDIR)/transport_config.h dep-common
include $(SRCDIR)/Makefile.common
include Makefile.depend
Whitespace-only changes.
+393
View File
@@ -0,0 +1,393 @@
// 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 <string>
#include "xmlrpc-c/oldcppwrapper.hpp"
using std::string;
//=========================================================================
// 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;
}
+851
View File
@@ -0,0 +1,851 @@
/*=============================================================================
client.cpp
===============================================================================
This is the C++ XML-RPC client library for Xmlrpc-c.
Note that unlike most of Xmlprc-c's C++ API, this is _not_ based on the
C client library. This code is independent of the C client library, and
is based directly on the client XML transport libraries (with a little
help from internal C utility libraries).
=============================================================================*/
#include <cassert>
#include <string>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/girmem.hpp"
using girmem::autoObjectPtr;
using girmem::autoObject;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/client.h"
#include "xmlrpc-c/transport.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
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/xml.hpp"
#include "xmlrpc-c/client.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace {
class memblockStringWrapper {
public:
memblockStringWrapper(string const value) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->memblockP = XMLRPC_MEMBLOCK_NEW(char, &env, 0);
if (env.fault_occurred)
throw(error(env.fault_string));
XMLRPC_MEMBLOCK_APPEND(char, &env, this->memblockP,
value.c_str(), value.size());
if (env.fault_occurred)
throw(error(env.fault_string));
}
memblockStringWrapper(xmlrpc_mem_block * const memblockP) :
memblockP(memblockP) {};
~memblockStringWrapper() {
XMLRPC_MEMBLOCK_FREE(char, this->memblockP);
}
xmlrpc_mem_block * memblockP;
};
} // namespace
namespace xmlrpc_c {
carriageParm::carriageParm() {}
carriageParm::~carriageParm() {}
carriageParm_http0::carriageParm_http0() :
c_serverInfoP(NULL) {}
carriageParm_http0::carriageParm_http0(string const serverUrl) {
this->c_serverInfoP = NULL;
this->instantiate(serverUrl);
}
carriageParm_http0::~carriageParm_http0() {
if (this->c_serverInfoP)
xmlrpc_server_info_free(this->c_serverInfoP);
}
void
carriageParm_http0::instantiate(string const serverUrl) {
if (c_serverInfoP)
throw(error("object already instantiated"));
xmlrpc_env env;
xmlrpc_env_init(&env);
this->c_serverInfoP = xmlrpc_server_info_new(&env, serverUrl.c_str());
if (env.fault_occurred)
throw(error(env.fault_string));
}
void
carriageParm_http0::setBasicAuth(string const username,
string const password) {
if (!c_serverInfoP)
throw(error("object not instantiated"));
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_server_info_set_basic_auth(
&env, this->c_serverInfoP, username.c_str(), password.c_str());
if (env.fault_occurred)
throw(error(env.fault_string));
}
carriageParm_curl0::carriageParm_curl0(
string const serverUrl
) {
this->instantiate(serverUrl);
}
carriageParm_libwww0::carriageParm_libwww0(
string const serverUrl
) {
this->instantiate(serverUrl);
}
carriageParm_wininet0::carriageParm_wininet0(
string const serverUrl
) {
this->instantiate(serverUrl);
}
xmlTransaction::xmlTransaction() {}
void
xmlTransaction::finish(string const& responseXml) const {
xml::trace("XML-RPC RESPONSE", responseXml);
}
void
xmlTransaction::finishErr(error const&) const {
}
xmlTransactionPtr::xmlTransactionPtr() {}
xmlTransaction *
xmlTransactionPtr::operator->() const {
autoObject * const p(this->objectP);
return dynamic_cast<xmlTransaction *>(p);
}
clientXmlTransport::~clientXmlTransport() {}
void
clientXmlTransport::start(carriageParm * const carriageParmP,
string const& callXml,
xmlTransactionPtr const& xmlTranP) {
string responseXml;
this->call(carriageParmP, callXml, &responseXml);
xmlTranP->finish(responseXml);
}
void
clientXmlTransport::finishAsync(xmlrpc_c::timeout const timeout) {
if (timeout.finite == timeout.finite)
throw(error("This class does not have finishAsync()"));
}
void
clientXmlTransport::asyncComplete(
struct xmlrpc_call_info * const callInfoP,
xmlrpc_mem_block * const responseXmlMP,
xmlrpc_env const transportEnv) {
xmlTransactionPtr * const xmlTranPP =
reinterpret_cast<xmlTransactionPtr *>(callInfoP);
try {
if (transportEnv.fault_occurred) {
(*xmlTranPP)->finishErr(error(transportEnv.fault_string));
} else {
string const responseXml(
XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlMP),
XMLRPC_MEMBLOCK_SIZE(char, responseXmlMP));
(*xmlTranPP)->finish(responseXml);
}
} catch(error) {
/* We can't throw an error back to C code, and the async_complete
interface does not provide for failure, so we define ->finish()
as not being capable of throwing an error.
*/
assert(false);
}
delete(xmlTranPP);
/* Ordinarily, *xmlTranPP is the last reference to **xmlTranPP
(The xmlTransaction), so it will get destroyed too. But
->finish() could conceivably create a new reference to
**xmlTranPP, and then it would keep living.
*/
}
clientXmlTransport_http::~clientXmlTransport_http() {}
void
clientXmlTransport_http::call(
carriageParm * const carriageParmP,
string const& callXml,
string * const responseXmlP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
carriageParm_http0 * const carriageParmHttpP =
dynamic_cast<carriageParm_http0 *>(carriageParmP);
if (carriageParmHttpP == NULL)
throw(error("HTTP client XML transport called with carriage "
"parameter object not of class carriageParm_http"));
memblockStringWrapper callXmlM(callXml);
xmlrpc_mem_block * responseXmlMP;
this->c_transportOpsP->call(&env,
this->c_transportP,
carriageParmHttpP->c_serverInfoP,
callXmlM.memblockP,
&responseXmlMP);
if (env.fault_occurred)
throw(error(env.fault_string));
memblockStringWrapper responseHolder(responseXmlMP);
// Makes responseXmlMP get freed at end of scope
*responseXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlMP),
XMLRPC_MEMBLOCK_SIZE(char, responseXmlMP));
}
void
clientXmlTransport_http::start(
carriageParm * const carriageParmP,
string const& callXml,
xmlTransactionPtr const& xmlTranP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
carriageParm_http0 * const carriageParmHttpP =
dynamic_cast<carriageParm_http0 *>(carriageParmP);
if (carriageParmHttpP == NULL)
throw(error("HTTP client XML transport called with carriage "
"parameter object not of type carriageParm_http"));
memblockStringWrapper callXmlM(callXml);
/* xmlTranP2 is the reference to the XML transaction that is held by
the running transaction, in C code. It lives in dynamically allocated
storage and xmlTranP2P points to it.
*/
xmlTransactionPtr * const xmlTranP2P(new xmlTransactionPtr(xmlTranP));
try {
this->c_transportOpsP->send_request(
&env,
this->c_transportP,
carriageParmHttpP->c_serverInfoP,
callXmlM.memblockP,
&this->asyncComplete,
reinterpret_cast<xmlrpc_call_info *>(xmlTranP2P));
if (env.fault_occurred)
throw(error(env.fault_string));
} catch (...) {
delete xmlTranP2P;
throw;
}
}
void
clientXmlTransport_http::finishAsync(xmlrpc_c::timeout const timeout) {
xmlrpc_timeoutType const c_timeoutType(
timeout.finite ? timeout_yes : timeout_no);
xmlrpc_timeout const c_timeout(timeout.duration);
this->c_transportOpsP->finish_asynch(
this->c_transportP, c_timeoutType, c_timeout);
}
#if MUST_BUILD_CURL_CLIENT
clientXmlTransport_curl::clientXmlTransport_curl(
string const networkInterface,
bool const noSslVerifyPeer,
bool const noSslVerifyHost,
string const userAgent) {
struct xmlrpc_curl_xportparms transportParms;
transportParms.network_interface =
networkInterface.size() > 0 ? networkInterface.c_str() : NULL;
transportParms.no_ssl_verifypeer = noSslVerifyPeer;
transportParms.no_ssl_verifyhost = noSslVerifyHost;
transportParms.user_agent =
userAgent.size() > 0 ? userAgent.c_str() : NULL;
this->c_transportOpsP = &xmlrpc_curl_transport_ops;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_curl_transport_ops.create(
&env, 0, "", "", (xmlrpc_xportparms *)&transportParms,
XMLRPC_CXPSIZE(user_agent),
&this->c_transportP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
#else // MUST_BUILD_CURL_CLIENT
clientXmlTransport_curl::clientXmlTransport_curl(string, bool, bool, string) {
throw("There is no Curl client XML transport in this XML-RPC client "
"library");
}
#endif
clientXmlTransport_curl::~clientXmlTransport_curl() {
this->c_transportOpsP->destroy(this->c_transportP);
}
#if MUST_BUILD_LIBWWW_CLIENT
clientXmlTransport_libwww::clientXmlTransport_libwww(
string const appname,
string const appversion) {
this->c_transportOpsP = &xmlrpc_libwww_transport_ops;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_libwww_transport_ops.create(
&env, 0, appname.c_str(), appversion.c_str(), NULL, 0,
&this->c_transportP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
#else // MUST_BUILD_LIBWWW_CLIENT
clientXmlTransport_libwww::clientXmlTransport_libwww(string, string) {
throw(error("There is no Libwww client XML transport "
"in this XML-RPC client library"));
}
#endif
clientXmlTransport_libwww::~clientXmlTransport_libwww() {
this->c_transportOpsP->destroy(this->c_transportP);
}
#if MUST_BUILD_WININET_CLIENT
clientXmlTransport_wininet::clientXmlTransport_wininet(
bool const allowInvalidSslCerts
) {
struct xmlrpc_wininet_xportparms transportParms;
transportParms.allowInvalidSSLCerts = allowInvalidSslCerts;
this->c_transportOpsP = &xmlrpc_wininet_transport_ops;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_wininet_transport_ops.create(
&env, 0, "", "", &transportParms, XMLRPC_WXPSIZE(allowInvalidSSLCerts),
&this->c_transportP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
#else // MUST_BUILD_WININET_CLIENT
clientXmlTransport_wininet::clientXmlTransport_wininet(bool const) {
throw(error("There is no Wininet client XML transport "
"in this XML-RPC client library"));
}
#endif
clientXmlTransport_wininet::~clientXmlTransport_wininet() {
this->c_transportOpsP->destroy(this->c_transportP);
}
clientTransaction::clientTransaction() {}
clientTransactionPtr::clientTransactionPtr() {}
clientTransactionPtr::~clientTransactionPtr() {}
clientTransaction *
clientTransactionPtr::operator->() const {
autoObject * const p(this->objectP);
return dynamic_cast<clientTransaction *>(p);
}
client::~client() {}
void
client::start(carriageParm * const carriageParmP,
string const methodName,
paramList const& paramList,
clientTransactionPtr const& tranP) {
/*----------------------------------------------------------------------------
Start an RPC, wait for it to complete, and finish it.
Usually, a derived class overrides this with something that does
not wait for the RPC to complete, but rather arranges for something
to finish the RPC later when the RPC does complete.
-----------------------------------------------------------------------------*/
rpcOutcome outcome;
this->call(carriageParmP, methodName, paramList, &outcome);
tranP->finish(outcome);
}
client_xml::client_xml(clientXmlTransport * const transportP) :
transportP(transportP) {}
void
client_xml::call(carriageParm * const carriageParmP,
string const methodName,
paramList const& paramList,
rpcOutcome * const outcomeP) {
string callXml;
string responseXml;
xml::generateCall(methodName, paramList, &callXml);
xml::trace("XML-RPC CALL", callXml);
this->transportP->call(carriageParmP, callXml, &responseXml);
xml::trace("XML-RPC RESPONSE", responseXml);
xml::parseResponse(responseXml, outcomeP);
}
void
client_xml::start(carriageParm * const carriageParmP,
string const methodName,
paramList const& paramList,
clientTransactionPtr const& tranP) {
string callXml;
xml::generateCall(methodName, paramList, &callXml);
xml::trace("XML-RPC CALL", callXml);
xmlTransaction_clientPtr const xmlTranP(tranP);
this->transportP->start(carriageParmP, callXml, xmlTranP);
}
void
client_xml::finishAsync(xmlrpc_c::timeout const timeout) {
transportP->finishAsync(timeout);
}
connection::connection(client * const clientP,
carriageParm * const carriageParmP) :
clientP(clientP), carriageParmP(carriageParmP) {}
connection::~connection() {}
rpc::rpc(string const methodName,
xmlrpc_c::paramList const& paramList) {
this->state = STATE_UNFINISHED;
this->methodName = methodName;
this->paramList = paramList;
}
rpc::~rpc() {
if (this->state == STATE_ERROR)
delete(this->errorP);
}
void
rpc::call(client * const clientP,
carriageParm * const carriageParmP) {
if (this->state != STATE_UNFINISHED)
throw(error("Attempt to execute an RPC that has already been "
"executed"));
clientP->call(carriageParmP,
this->methodName,
this->paramList,
&this->outcome);
this->state = outcome.succeeded() ? STATE_SUCCEEDED : STATE_FAILED;
}
void
rpc::call(connection const& connection) {
this->call(connection.clientP, connection.carriageParmP);
}
void
rpc::start(client * const clientP,
carriageParm * const carriageParmP) {
if (this->state != STATE_UNFINISHED)
throw(error("Attempt to execute an RPC that has already been "
"executed"));
clientP->start(carriageParmP,
this->methodName,
this->paramList,
rpcPtr(this));
}
void
rpc::start(xmlrpc_c::connection const& connection) {
this->start(connection.clientP, connection.carriageParmP);
}
void
rpc::finish(rpcOutcome const& outcome) {
this->state = outcome.succeeded() ? STATE_SUCCEEDED : STATE_FAILED;
this->outcome = outcome;
this->notifyComplete();
}
void
rpc::finishErr(error const& error) {
this->state = STATE_ERROR;
this->errorP = new girerr::error(error);
this->notifyComplete();
}
void
rpc::notifyComplete() {
/*----------------------------------------------------------------------------
Anyone who does RPCs asynchronously and doesn't use polling will
want to make his own class derived from 'rpc' and override this
with a notifyFinish() that does something.
Typically, notifyFinish() will queue the RPC so some other thread
will deal with the fact that the RPC is finished.
In the absence of the aforementioned queueing, the RPC becomes
unreferenced as soon as our Caller releases his reference, so the
RPC gets destroyed when we return.
-----------------------------------------------------------------------------*/
}
value
rpc::getResult() const {
switch (this->state) {
case STATE_UNFINISHED:
throw(error("Attempt to get result of RPC that is not finished."));
break;
case STATE_ERROR:
throw(*this->errorP);
break;
case STATE_FAILED:
throw(error("RPC failed. " +
this->outcome.getFault().getDescription()));
break;
case STATE_SUCCEEDED: {
// All normal
}
}
return this->outcome.getResult();
}
fault
rpc::getFault() const {
switch (this->state) {
case STATE_UNFINISHED:
throw(error("Attempt to get fault from RPC that is not finished"));
break;
case STATE_ERROR:
throw(*this->errorP);
break;
case STATE_SUCCEEDED:
throw(error("Attempt to get fault from an RPC that succeeded"));
break;
case STATE_FAILED: {
// All normal
}
}
return this->outcome.getFault();
}
bool
rpc::isFinished() const {
return (this->state != STATE_UNFINISHED);
}
bool
rpc::isSuccessful() const {
return (this->state == STATE_SUCCEEDED);
}
rpcPtr::rpcPtr() {}
rpcPtr::rpcPtr(rpc * const rpcP) {
this->instantiate(rpcP);
}
rpcPtr::rpcPtr(string const methodName,
xmlrpc_c::paramList const& paramList) {
this->instantiate(new rpc(methodName, paramList));
}
rpc *
rpcPtr::operator->() const {
autoObject * const p(this->objectP);
return dynamic_cast<rpc *>(p);
}
xmlTransaction_client::xmlTransaction_client(
clientTransactionPtr const& tranP) :
tranP(tranP) {}
void
xmlTransaction_client::finish(string const& responseXml) const {
xml::trace("XML-RPC RESPONSE", responseXml);
try {
rpcOutcome outcome;
xml::parseResponse(responseXml, &outcome);
this->tranP->finish(outcome);
} catch (error const caughtError) {
this->tranP->finishErr(caughtError);
}
}
void
xmlTransaction_client::finishErr(error const& error) const {
this->tranP->finishErr(error);
}
xmlTransaction_clientPtr::xmlTransaction_clientPtr() {}
xmlTransaction_clientPtr::xmlTransaction_clientPtr(
clientTransactionPtr const& tranP) {
this->instantiate(new xmlTransaction_client(tranP));
}
xmlTransaction_client *
xmlTransaction_clientPtr::operator->() const {
autoObject * const p(this->objectP);
return dynamic_cast<xmlTransaction_client *>(p);
}
} // namespace
+180
View File
@@ -0,0 +1,180 @@
#include <string>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/client.hpp"
#include <xmlrpc-c/client.hpp>
/* transport_config.h defines XMLRPC_DEFAULT_TRANSPORT,
MUST_BUILD_WININET_CLIENT, MUST_BUILD_CURL_CLIENT,
MUST_BUILD_LIBWWW_CLIENT
*/
#include "transport_config.h"
#include "xmlrpc-c/client_simple.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace xmlrpc_c {
namespace {
/*----------------------------------------------------------------------------
Use an object of this class to set up to remove a reference to an
xmlrpc_value object (a C object with manual reference management)
at then end of a scope -- even if the scope ends with a throw.
-----------------------------------------------------------------------------*/
class cValueWrapper {
xmlrpc_value * valueP;
public:
cValueWrapper(xmlrpc_value * valueP) : valueP(valueP) {}
~cValueWrapper() { xmlrpc_DECREF(valueP); }
};
} // namespace
clientSimple::clientSimple() {
if (string(XMLRPC_DEFAULT_TRANSPORT) == string("curl"))
this->transportP = new clientXmlTransport_curl;
else if (string(XMLRPC_DEFAULT_TRANSPORT) == string("libwww"))
this->transportP = new clientXmlTransport_libwww;
else if (string(XMLRPC_DEFAULT_TRANSPORT) == string("wininet"))
this->transportP = new clientXmlTransport_wininet;
else
throw(error("INTERNAL ERROR: "
"Default client XML transport is not one we recognize"));
this->clientP = new client_xml(transportP);
}
clientSimple::~clientSimple() {
delete this->clientP;
delete this->transportP;
}
void
clientSimple::call(string const serverUrl,
string const methodName,
value * const resultP) {
carriageParm_http0 carriageParm(serverUrl);
rpcPtr rpcPtr(methodName, paramList());
rpcPtr->call(this->clientP, &carriageParm);
*resultP = rpcPtr->getResult();
}
namespace {
void
makeParamArray(string const format,
xmlrpc_value ** const paramArrayPP,
va_list args) {
xmlrpc_env env;
xmlrpc_env_init(&env);
/* The format is a sequence of parameter specifications, such as
"iiii" for 4 integer parameters. We add parentheses to make it
an array of those parameters: "(iiii)".
*/
string const arrayFormat("(" + string(format) + ")");
const char * tail;
xmlrpc_build_value_va(&env, arrayFormat.c_str(),
args, paramArrayPP, &tail);
if (env.fault_occurred)
throw(error(env.fault_string));
if (strlen(tail) != 0) {
/* xmlrpc_build_value_va() parses off a single value specification
from its format string, and 'tail' points to whatever is after
it. Our format string should have been a single array value,
meaning tail is end-of-string. If it's not, that means
something closed our array early.
*/
xmlrpc_DECREF(*paramArrayPP);
throw(error("format string is invalid. It apparently has a "
"stray right parenthesis"));
}
}
} // namespace
void
clientSimple::call(string const serverUrl,
string const methodName,
string const format,
value * const resultP,
...) {
carriageParm_http0 carriageParm(serverUrl);
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_value * paramArrayP;
va_list args;
va_start(args, resultP);
makeParamArray(format, &paramArrayP, args);
va_end(args);
if (env.fault_occurred)
throw(error(env.fault_string));
else {
cValueWrapper paramArrayWrapper(paramArrayP); // ensure destruction
unsigned int const paramCount = xmlrpc_array_size(&env, paramArrayP);
if (env.fault_occurred)
throw(error(env.fault_string));
paramList paramList;
for (unsigned int i = 0; i < paramCount; ++i) {
xmlrpc_value * paramP;
xmlrpc_array_read_item(&env, paramArrayP, i, &paramP);
if (env.fault_occurred)
throw(error(env.fault_string));
else {
cValueWrapper paramWrapper(paramP); // ensure destruction
paramList.add(value(paramP));
}
}
rpcPtr rpcPtr(methodName, paramList);
rpcPtr->call(this->clientP, &carriageParm);
*resultP = rpcPtr->getResult();
}
}
void
clientSimple::call(string const serverUrl,
string const methodName,
paramList const& paramList,
value * const resultP) {
carriageParm_http0 carriageParm(serverUrl);
rpcPtr rpcPtr(methodName, paramList);
rpcPtr->call(this->clientP, &carriageParm);
*resultP = rpcPtr->getResult();
}
} // namespace
File diff suppressed because it is too large. Load diff
+35
View File
@@ -0,0 +1,35 @@
#include <string>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.hpp"
using namespace std;
namespace xmlrpc_c {
fault::fault() : valid(false) {};
fault::fault(string const _description,
xmlrpc_c::fault::code_t const _code
) :
valid(true),
code(_code),
description(_description)
{}
xmlrpc_c::fault::code_t
fault::getCode() const {
if (!valid)
throw(error("Attempt to access placeholder xmlrpc_c::fault object"));
return this->code;
}
string
fault::getDescription() const {
if (!valid)
throw(error("Attempt to access placeholder xmlrpc_c::fault object"));
return this->description;
}
} // namespace
+123
View File
@@ -0,0 +1,123 @@
#include "pthreadx.h"
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/girmem.hpp"
using namespace std;
using namespace girmem;
namespace girmem {
void
autoObject::incref() {
pthread_mutex_lock(&this->refcountLock);
++this->refcount;
pthread_mutex_unlock(&this->refcountLock);
}
void
autoObject::decref(bool * const unreferencedP) {
if (this->refcount == 0)
throw(error("Decrementing ref count of unreferenced object"));
pthread_mutex_lock(&this->refcountLock);
--this->refcount;
*unreferencedP = (this->refcount == 0);
pthread_mutex_unlock(&this->refcountLock);
}
autoObject::autoObject() {
int rc;
rc = pthread_mutex_init(&this->refcountLock, NULL);
if (rc != 0)
throw(error("Unable to initialize pthread mutex"));
this->refcount = 0;
}
autoObject::~autoObject() {
if (this->refcount > 0)
throw(error("Destroying referenced object"));
int rc;
rc = pthread_mutex_destroy(&this->refcountLock);
if (rc != 0)
throw(error("Unable to destroy pthread mutex"));
}
autoObjectPtr::autoObjectPtr() : objectP(NULL) {}
autoObjectPtr::autoObjectPtr(autoObject * const objectP) {
this->objectP = objectP;
objectP->incref();
}
autoObjectPtr::autoObjectPtr(autoObjectPtr const& autoObjectPtr) {
// copy constructor
this->objectP = autoObjectPtr.objectP;
if (this->objectP)
this->objectP->incref();
}
autoObjectPtr::~autoObjectPtr() {
if (this->objectP) {
bool dead;
this->objectP->decref(&dead);
if (dead)
delete(this->objectP);
}
}
void
autoObjectPtr::instantiate(autoObject * const objectP) {
this->objectP = objectP;
objectP->incref();
}
autoObjectPtr
autoObjectPtr::operator=(autoObjectPtr const& autoObjectPtr) {
if (this->objectP != NULL)
throw(error("Already instantiated"));
this->objectP = autoObjectPtr.objectP;
this->objectP->incref();
return *this;
}
autoObject *
autoObjectPtr::operator->() const {
return this->objectP;
}
} // namespace
+57
View File
@@ -0,0 +1,57 @@
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.hpp"
using namespace std;
namespace xmlrpc_c {
rpcOutcome::rpcOutcome() : valid(false) {}
rpcOutcome::rpcOutcome(xmlrpc_c::value const result) :
valid(true), _succeeded(true), result(result)
{}
rpcOutcome::rpcOutcome(xmlrpc_c::fault const fault) :
valid(true), _succeeded(false), fault(fault)
{}
bool
rpcOutcome::succeeded() const {
if (!valid)
throw(error("Attempt to access rpcOutcome object before setting it"));
return _succeeded;
}
fault
rpcOutcome::getFault() const {
if (!valid)
throw(error("Attempt to access rpcOutcome object before setting it"));
if (_succeeded)
throw(error("Attempt to get fault description from a non-failure "
"RPC outcome"));
return fault;
}
value
rpcOutcome::getResult() const {
if (!valid)
throw(error("Attempt to access rpcOutcome object before setting it"));
if (!_succeeded)
throw(error("Attempt to get result from an unsuccessful RPC outcome"));
return result;
}
} // namespace
+259
View File
@@ -0,0 +1,259 @@
#include <climits>
#include <cfloat>
#include <ctime>
#include <string>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace xmlrpc_c {
paramList::paramList(unsigned int const paramCount) {
this->paramVector.reserve(paramCount);
}
void
paramList::add(xmlrpc_c::value const param) {
this->paramVector.push_back(param);
}
unsigned int
paramList::size() const {
return this->paramVector.size();
}
xmlrpc_c::value
paramList::operator[](unsigned int const subscript) const {
if (subscript >= this->paramVector.size())
throw(girerr::error(
"Subscript of xmlrpc_c::paramList out of bounds"));
return this->paramVector[subscript];
}
int
paramList::getInt(unsigned int const paramNumber,
int const minimum,
int const maximum) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
if (this->paramVector[paramNumber].type() != value::TYPE_INT)
throw(fault("Parameter that is supposed to be integer is not",
fault::CODE_TYPE));
int const intvalue(static_cast<int>(
value_int(this->paramVector[paramNumber])));
if (intvalue < minimum)
throw(fault("Integer parameter too low", fault::CODE_TYPE));
if (intvalue > maximum)
throw(fault("Integer parameter too high", fault::CODE_TYPE));
return intvalue;
}
bool
paramList::getBoolean(unsigned int const paramNumber) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
if (this->paramVector[paramNumber].type() != value::TYPE_BOOLEAN)
throw(fault("Parameter that is supposed to be boolean is not",
fault::CODE_TYPE));
return static_cast<bool>(value_boolean(this->paramVector[paramNumber]));
}
double
paramList::getDouble(unsigned int const paramNumber,
double const minimum,
double const maximum) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
if (this->paramVector[paramNumber].type() != value::TYPE_DOUBLE)
throw(fault("Parameter that is supposed to be floating point number "
"is not",
fault::CODE_TYPE));
double const doublevalue(static_cast<double>(
value_double(this->paramVector[paramNumber])));
if (doublevalue < minimum)
throw(fault("Floating point number parameter too low",
fault::CODE_TYPE));
if (doublevalue > maximum)
throw(fault("Floating point number parameter too high",
fault::CODE_TYPE));
return doublevalue;
}
time_t
paramList::getDatetime_sec(
unsigned int const paramNumber,
paramList::timeConstraint const constraint) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
const xmlrpc_c::value * const paramP(&this->paramVector[paramNumber]);
if (paramP->type() != value::TYPE_DATETIME)
throw(fault("Parameter that is supposed to be a datetime is not",
fault::CODE_TYPE));
time_t const timeValue(static_cast<time_t>(value_datetime(*paramP)));
time_t const now(time(NULL));
switch (constraint) {
case TC_ANY:
/* He'll take anything; no problem */
break;
case TC_NO_FUTURE:
if (timeValue > now)
throw(fault("Datetime parameter that is not supposed to be in "
"the future is.", fault::CODE_TYPE));
break;
case TC_NO_PAST:
if (timeValue < now)
throw(fault("Datetime parameter that is not supposed to be in "
"the past is.", fault::CODE_TYPE));
break;
}
return timeValue;
}
string
paramList::getString(unsigned int const paramNumber) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
if (this->paramVector[paramNumber].type() != value::TYPE_STRING)
throw(fault("Parameter that is supposed to be a string is not",
fault::CODE_TYPE));
return static_cast<string>(value_string(this->paramVector[paramNumber]));
}
std::vector<unsigned char>
paramList::getBytestring(unsigned int const paramNumber) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
const xmlrpc_c::value * const paramP(&this->paramVector[paramNumber]);
if (paramP->type() != value::TYPE_BYTESTRING)
throw(fault("Parameter that is supposed to be a byte string is not",
fault::CODE_TYPE));
return value_bytestring(*paramP).vectorUcharValue();
}
std::vector<xmlrpc_c::value>
paramList::getArray(unsigned int const paramNumber,
unsigned int const minSize,
unsigned int const maxSize) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
const xmlrpc_c::value * const paramP(&this->paramVector[paramNumber]);
if (paramP->type() != value::TYPE_ARRAY)
throw(fault("Parameter that is supposed to be an array is not",
fault::CODE_TYPE));
xmlrpc_c::value_array const arrayValue(*paramP);
if (arrayValue.size() < minSize)
throw(fault("Array parameter has too few elements",
fault::CODE_TYPE));
if (arrayValue.size() > maxSize)
throw(fault("Array parameter has too many elements",
fault::CODE_TYPE));
return value_array(*paramP).vectorValueValue();
}
std::map<string, xmlrpc_c::value>
paramList::getStruct(unsigned int const paramNumber) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
const xmlrpc_c::value * const paramP(&this->paramVector[paramNumber]);
if (paramP->type() != value::TYPE_STRUCT)
throw(fault("Parameter that is supposed to be a structure is not",
fault::CODE_TYPE));
return static_cast<std::map<string, xmlrpc_c::value> >(
value_struct(*paramP));
}
void
paramList::getNil(unsigned int const paramNumber) const {
if (paramNumber >= this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
if (this->paramVector[paramNumber].type() != value::TYPE_NIL)
throw(fault("Parameter that is supposed to be nil is not",
fault::CODE_TYPE));
}
void
paramList::verifyEnd(unsigned int const paramNumber) const {
if (paramNumber < this->paramVector.size())
throw(fault("Too many parameters", fault::CODE_TYPE));
if (paramNumber > this->paramVector.size())
throw(fault("Not enough parameters", fault::CODE_TYPE));
}
} // namespace
+341
View File
@@ -0,0 +1,341 @@
#include <string>
#include <memory>
#include <algorithm>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/girmem.hpp"
using girmem::autoObject;
using girmem::autoObjectPtr;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/registry.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace xmlrpc_c {
method::method() :
_signature("?"),
_help("No help is available for this method")
{};
method *
method::self() {
return this;
}
method::~method() {}
methodPtr::methodPtr(method * const methodP) {
this->instantiate(methodP);
}
method *
methodPtr::operator->() const {
autoObject * const p(this->objectP);
return dynamic_cast<method *>(p);
}
defaultMethod::~defaultMethod() {}
defaultMethodPtr::defaultMethodPtr() {}
defaultMethodPtr::defaultMethodPtr(defaultMethod * const methodP) {
this->instantiate(methodP);
}
defaultMethod *
defaultMethodPtr::operator->() const {
autoObject * const p(this->objectP);
return dynamic_cast<defaultMethod *>(p);
}
defaultMethod *
defaultMethod::self() {
return this;
}
registry::registry() {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->c_registryP = xmlrpc_registry_new(&env);
if (env.fault_occurred)
throw(error(env.fault_string));
}
registry::~registry(void) {
xmlrpc_registry_free(this->c_registryP);
}
static xmlrpc_c::paramList
pListFromXmlrpcArray(xmlrpc_value * const arrayP) {
/*----------------------------------------------------------------------------
Convert an XML-RPC array in C (not C++) form to a parameter list object
that can be passed to a method execute method.
This is glue code to allow us to hook up C++ Xmlrpc-c code to
C Xmlrpc-c code.
-----------------------------------------------------------------------------*/
xmlrpc_env env;
xmlrpc_env_init(&env);
XMLRPC_ASSERT_ARRAY_OK(arrayP);
unsigned int const arraySize = xmlrpc_array_size(&env, arrayP);
xmlrpc_c::paramList paramList(arraySize);
for (unsigned int i = 0; i < arraySize; ++i) {
xmlrpc_value * arrayItemP;
xmlrpc_array_read_item(&env, arrayP, i, &arrayItemP);
paramList.add(xmlrpc_c::value(arrayItemP));
xmlrpc_DECREF(arrayItemP);
}
return paramList;
}
static xmlrpc_value *
c_executeMethod(xmlrpc_env * const envP,
xmlrpc_value * const paramArrayP,
void * const methodPtr) {
/*----------------------------------------------------------------------------
This is a function designed to be called via a C registry to
execute an XML-RPC method, but use a C++ method object to do the
work. You register this function as the method function and a
pointer to the C++ method object as the method data in the C
registry.
If we had a pure C++ registry, this would be unnecessary.
Since we can't throw an error back to the C code, we catch anything
the XML-RPC method's execute() method throws, and any error we
encounter in processing the result it returns, and turn it into an
XML-RPC method failure. This will cause a leak if the execute()
method actually created a result, since it will not get destroyed.
-----------------------------------------------------------------------------*/
xmlrpc_c::method * const methodP =
static_cast<xmlrpc_c::method *>(methodPtr);
xmlrpc_c::paramList const paramList(pListFromXmlrpcArray(paramArrayP));
xmlrpc_value * retval;
try {
xmlrpc_c::value result;
try {
methodP->execute(paramList, &result);
} catch (xmlrpc_c::fault caughtFault) {
xmlrpc_env_set_fault(envP, caughtFault.getCode(),
caughtFault.getDescription().c_str());
} catch (girerr::error caughtError) {
xmlrpc_env_set_fault(envP, 0, caughtError.what());
}
if (envP->fault_occurred)
retval = NULL;
else
retval = result.cValue();
} catch (...) {
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
"Unexpected error executing the code for this "
"particular method, detected by the Xmlrpc-c "
"method registry code. The method did not "
"fail; rather, it did not complete at all.");
retval = NULL;
}
return retval;
}
static xmlrpc_value *
c_executeDefaultMethod(xmlrpc_env * const envP,
const char * const , // host
const char * const methodName,
xmlrpc_value * const paramArrayP,
void * const methodPtr) {
/*----------------------------------------------------------------------------
This is a function designed to be called via a C registry to
execute an XML-RPC method, but use a C++ method object to do the
work. You register this function as the default method function and a
pointer to the C++ default method object as the method data in the C
registry.
If we had a pure C++ registry, this would be unnecessary.
Since we can't throw an error back to the C code, we catch anything
the XML-RPC method's execute() method throws, and any error we
encounter in processing the result it returns, and turn it into an
XML-RPC method failure. This will cause a leak if the execute()
method actually created a result, since it will not get destroyed.
-----------------------------------------------------------------------------*/
xmlrpc_c::defaultMethod * const methodP =
static_cast<xmlrpc_c::defaultMethod *>(methodPtr);
xmlrpc_c::paramList const paramList(pListFromXmlrpcArray(paramArrayP));
xmlrpc_value * retval;
try {
xmlrpc_c::value result;
try {
methodP->execute(methodName, paramList, &result);
} catch (xmlrpc_c::fault caughtFault) {
xmlrpc_env_set_fault(envP, caughtFault.getCode(),
caughtFault.getDescription().c_str());
} catch (girerr::error caughtError) {
xmlrpc_env_set_fault(envP, 0, caughtError.what());
}
if (envP->fault_occurred)
retval = NULL;
else
retval = result.cValue();
} catch (...) {
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
"Unexpected error executing the default "
"method code, detected by the Xmlrpc-c "
"method registry code. The method did not "
"fail; rather, it did not complete at all.");
retval = NULL;
}
return retval;
}
void
registry::addMethod(string const name,
xmlrpc_c::methodPtr const methodPtr) {
this->methodList.push_back(methodPtr);
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_c::method * const methodP(methodPtr->self());
xmlrpc_registry_add_method_w_doc(
&env, this->c_registryP, NULL,
name.c_str(), &c_executeMethod,
(void*) methodP,
methodP->signature().c_str(), methodP->help().c_str());
if (env.fault_occurred)
throw(error(env.fault_string));
}
void
registry::setDefaultMethod(defaultMethodPtr const methodPtr) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->defaultMethodP = methodPtr;
xmlrpc_c::defaultMethod * const methodP(methodPtr->self());
xmlrpc_registry_set_default_method(
&env, this->c_registryP, &c_executeDefaultMethod, (void*) methodP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
void
registry::disableIntrospection() {
xmlrpc_registry_disable_introspection(this->c_registryP);
}
void
registry::processCall(string const& callXml,
string * const responseXmlP) const {
/*----------------------------------------------------------------------------
Process an XML-RPC call whose XML is 'callXml'.
Return the response XML as *responseXmlP.
If we are unable to execute the call, we throw an error. But if
the call executes and the method merely fails in an XML-RPC sense, we
don't. In that case, *responseXmlP indicates the failure.
-----------------------------------------------------------------------------*/
xmlrpc_env env;
xmlrpc_mem_block * output;
xmlrpc_env_init(&env);
// For the pure C++ version, this will have to parse 'callXml'
// into a method name and parameters, look up the method name in
// the registry, call the method's execute() method, then marshall
// the result into XML and return it as *responseXmlP. It will
// also have to execute system methods (e.g. introspection)
// itself. This will be more or less like what
// xmlrpc_registry_process_call() does.
output = xmlrpc_registry_process_call(
&env, this->c_registryP, NULL, callXml.c_str(), callXml.length());
if (env.fault_occurred)
throw(error(env.fault_string));
*responseXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, output),
XMLRPC_MEMBLOCK_SIZE(char, output));
xmlrpc_mem_block_free(output);
}
xmlrpc_registry *
registry::c_registry() const {
return this->c_registryP;
}
} // namespace
+196
View File
@@ -0,0 +1,196 @@
#include <cassert>
#include <string>
#include <memory>
#include <signal.h>
#include <sys/wait.h>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/server_abyss.h"
#include "xmlrpc-c/registry.hpp"
#include "xmlrpc-c/server_abyss.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace xmlrpc_c {
serverAbyss::serverAbyss(
xmlrpc_c::registry const& registry,
unsigned int const portNumber,
string const& logFileName,
unsigned int const keepaliveTimeout,
unsigned int const keepaliveMaxConn,
unsigned int const timeout,
bool const dontAdvertise) {
this->registryP = &registry;
this->logFileName = logFileName;
this->keepaliveTimeout = keepaliveTimeout;
this->keepaliveMaxConn = keepaliveMaxConn;
this->timeout = timeout;
this->dontAdvertise = dontAdvertise;
if (portNumber > 0xffff)
throw(error("Port number exceeds the maximum possible port number "
"(65535)"));
else {
this->portNumber = portNumber;
}
}
serverAbyss::~serverAbyss() {
}
namespace {
static void
sigterm(int const sig) {
TraceExit("Signal %d received. Exiting...\n",sig);
}
static void
sigchld(int const sig) {
/*----------------------------------------------------------------------------
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.
-----------------------------------------------------------------------------*/
#ifndef _WIN32
/* Reap zombie children until there aren't any more. */
bool zombiesExist;
bool error;
assert(sig == SIGCHLD);
zombiesExist = true; // initial assumption
error = false; // no error yet
while (zombiesExist && !error) {
int status;
pid_t const pid = waitpid((pid_t) -1, &status, WNOHANG);
if (pid == 0)
zombiesExist = false;
else if (pid < 0) {
/* because of ptrace */
if (errno == EINTR) {
// This is OK - it's a ptrace notification
} else
error = true;
}
}
#endif /* _WIN32 */
}
void
setupSignalHandlers(void) {
#ifndef _WIN32
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
setAdditionalServerParms(
unsigned int const keepaliveTimeout,
unsigned int const keepaliveMaxConn,
unsigned int const timeout,
bool const dontAdvertise,
TServer * const srvP) {
/* The following ought to be parameters on ServerCreate(), but it
looks like plugging them straight into the TServer structure is
the only way to set them.
*/
if (keepaliveTimeout > 0)
srvP->keepalivetimeout = keepaliveTimeout;
if (keepaliveMaxConn > 0)
srvP->keepalivemaxconn = keepaliveMaxConn;
if (timeout > 0)
srvP->timeout = timeout;
srvP->advertise = !dontAdvertise;
}
} // namespace
void
serverAbyss::run() {
xmlrpc_env env;
xmlrpc_env_init(&env);
DateInit();
MIMETypeInit();
TServer srv;
const char * const logfileArg(this->logFileName.length() == 0 ?
NULL : this->logFileName.c_str());
ServerCreate(&srv, "XmlRpcServer", this->portNumber,
DEFAULT_DOCS, logfileArg);
setAdditionalServerParms(
this->keepaliveTimeout, this->keepaliveMaxConn, this->timeout,
this->dontAdvertise, &srv);
xmlrpc_c::server_abyss_set_handlers(&srv, *this->registryP);
ServerInit(&srv);
setupSignalHandlers();
ServerRun(&srv);
/* We can't exist here because ServerRun doesn't return */
assert(false);
}
void
server_abyss_set_handlers(TServer * const srvP,
xmlrpc_c::registry const& registry) {
xmlrpc_server_abyss_set_handlers(srvP, registry.c_registry());
}
} // namespace
+807
View File
@@ -0,0 +1,807 @@
/*****************************************************************************
xmlrpc_value.cpp
******************************************************************************
This module provides services for dealwith XML-RPC values. Each type
of XML-RPC value is a C++ class. An object represents a particular
XML-RPC value.
Everything is based on the C services in libxmlrpc.
We could make things more efficient by using the internal interfaces
via xmlrpc_int.h. We could make them even more efficient by dumping
libxmlrpc altogether for some or all of these services.
An xmlrpc_c::value object is really just a handle for a C xmlrpc_value
object. You're not supposed to make a pointer to an xmlrpc_c::value
object, but rather copy the object around.
Because the C xmlrpc_value object does reference counting, it
disappears automatically when the last handle does. To go pure C++,
we'd have to have a C++ object for the value itself and a separate
handle object, like Boost's shared_ptr<>.
The C++ is designed so that the user never sees the C interface at
all. Unfortunately, the user can see it if he wants because some
class members had to be declared public so that other components of
the library could see them, but the user is not supposed to access
those members.
*****************************************************************************/
#include <string>
#include <vector>
#include <time.h>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/base.hpp"
using namespace std;
namespace {
class cDatetimeValueWrapper {
public:
xmlrpc_value * valueP;
cDatetimeValueWrapper(time_t const cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_datetime_new_sec(&env, cppvalue);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cDatetimeValueWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
class cStringWrapper {
public:
const char * str;
size_t length;
cStringWrapper(xmlrpc_value * valueP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_read_string_lp(&env, valueP, &length, &str);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cStringWrapper() {
free((char*)str);
}
};
} // namespace
namespace xmlrpc_c {
value::value() {
this->cValueP = NULL;
}
value::value(xmlrpc_value * const valueP) { // default constructor
this->instantiate(valueP);
}
value::value(xmlrpc_c::value const& value) { // copy constructor
this->cValueP = value.cValue();
}
xmlrpc_c::value&
value::operator=(xmlrpc_c::value const& value) {
if (this->cValueP != NULL)
throw(error("Assigning to already instantiated xmlrpc_c::value"));
this->cValueP = value.cValue();
return *this; // The result of the (a = b) expression
}
value::~value() {
if (this->cValueP) {
xmlrpc_DECREF(this->cValueP);
}
}
void
value::instantiate(xmlrpc_value * const valueP) {
xmlrpc_INCREF(valueP);
this->cValueP = valueP;
}
xmlrpc_value *
value::cValue() const {
if (this->cValueP) {
xmlrpc_INCREF(this->cValueP); // For Caller
}
return this->cValueP;
}
void
value::appendToCArray(xmlrpc_value * const arrayP) const {
/*----------------------------------------------------------------------------
Append this value to the C array 'arrayP'.
----------------------------------------------------------------------------*/
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_array_append_item(&env, arrayP, this->cValueP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
void
value::addToCStruct(xmlrpc_value * const structP,
string const key) const {
/*----------------------------------------------------------------------------
Add this value to the C array 'arrayP' with key 'key'.
----------------------------------------------------------------------------*/
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_struct_set_value_n(&env, structP,
key.c_str(), key.length(),
this->cValueP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
value::type_t
value::type() const {
/* You'd think we could just cast from xmlrpc_type to
value:type_t, but Gcc warns if we do that. So we have to do this
even messier union nonsense.
*/
union {
xmlrpc_type x;
value::type_t y;
} u;
u.x = xmlrpc_value_type(this->cValueP);
return u.y;
}
value_int::value_int(int const cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(int const cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_int_new(&env, cppvalue);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
value_int::value_int(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_INT)
throw(error("Not integer type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_int::operator int() const {
int retval;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_read_int(&env, this->cValueP, &retval);
if (env.fault_occurred)
throw(error(env.fault_string));
return retval;
}
value_double::value_double(double const cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(double const cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_double_new(&env, cppvalue);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
this->instantiate(cWrapper(cppvalue).valueP);
}
value_double::value_double(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_DOUBLE)
throw(error("Not double type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_double::operator double() const {
double retval;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_read_double(&env, this->cValueP, &retval);
if (env.fault_occurred)
throw(error(env.fault_string));
return retval;
}
value_boolean::value_boolean(bool const cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(xmlrpc_bool const cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_bool_new(&env, cppvalue);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
value_boolean::operator bool() const {
xmlrpc_bool retval;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_read_bool(&env, this->cValueP, &retval);
if (env.fault_occurred)
throw(error(env.fault_string));
return (bool)retval;
}
value_boolean::value_boolean(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_BOOLEAN)
throw(error("Not boolean type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_datetime::value_datetime(string const cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(string const cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_datetime_new_str(&env, cppvalue.c_str());
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
value_datetime::value_datetime(time_t const cppvalue) {
cDatetimeValueWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
value_datetime::value_datetime(struct timeval const& cppvalue) {
cDatetimeValueWrapper wrapper(cppvalue.tv_sec);
this->instantiate(wrapper.valueP);
}
value_datetime::value_datetime(struct timespec const& cppvalue) {
cDatetimeValueWrapper wrapper(cppvalue.tv_sec);
this->instantiate(wrapper.valueP);
}
value_datetime::value_datetime(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_DATETIME)
throw(error("Not datetime type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_datetime::operator time_t() const {
time_t retval;
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_read_datetime_sec(&env, this->cValueP, &retval);
if (env.fault_occurred)
throw(error(env.fault_string));
return retval;
}
value_string::value_string(string const& cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(string const cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_string_new(&env, cppvalue.c_str());
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
value_string::value_string(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_STRING)
throw(error("Not string type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_string::operator string() const {
xmlrpc_env env;
xmlrpc_env_init(&env);
cStringWrapper adapter(this->cValueP);
return string(adapter.str, adapter.length);
}
value_bytestring::value_bytestring(
vector<unsigned char> const& cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(vector<unsigned char> const& cppvalue) {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP =
xmlrpc_base64_new(&env, cppvalue.size(), &cppvalue[0]);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
vector<unsigned char>
value_bytestring::vectorUcharValue() const {
class cWrapper {
public:
const unsigned char * contents;
size_t length;
cWrapper(xmlrpc_value * const valueP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_read_base64(&env, valueP, &length, &contents);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
free((void*)contents);
}
};
cWrapper wrapper(this->cValueP);
return vector<unsigned char>(&wrapper.contents[0],
&wrapper.contents[wrapper.length]);
}
size_t
value_bytestring::length() const {
xmlrpc_env env;
xmlrpc_env_init(&env);
size_t length;
xmlrpc_read_base64_size(&env, this->cValueP, &length);
if (env.fault_occurred)
throw(error(env.fault_string));
return length;
}
value_bytestring::value_bytestring(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_BYTESTRING)
throw(error("Not byte string type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_array::value_array(vector<xmlrpc_c::value> const& cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper() {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_array_new(&env);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper;
vector<xmlrpc_c::value>::const_iterator i;
for (i = cppvalue.begin(); i != cppvalue.end(); ++i)
i->appendToCArray(wrapper.valueP);
this->instantiate(wrapper.valueP);
}
value_array::value_array(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_ARRAY)
throw(error("Not array type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
vector<xmlrpc_c::value>
value_array::vectorValueValue() const {
xmlrpc_env env;
xmlrpc_env_init(&env);
unsigned int arraySize;
arraySize = xmlrpc_array_size(&env, this->cValueP);
if (env.fault_occurred)
throw(error(env.fault_string));
vector<xmlrpc_c::value> retval(arraySize);
for (unsigned int i = 0; i < arraySize; ++i) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper(xmlrpc_value * const arrayP,
unsigned int const index) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_array_read_item(&env, arrayP, index, &valueP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(valueP);
}
};
cWrapper wrapper(this->cValueP, i);
retval[i].instantiate(wrapper.valueP);
}
return retval;
}
size_t
value_array::size() const {
xmlrpc_env env;
xmlrpc_env_init(&env);
unsigned int arraySize;
arraySize = xmlrpc_array_size(&env, this->cValueP);
if (env.fault_occurred)
throw(error(env.fault_string));
return arraySize;
}
value_struct::value_struct(
map<string, xmlrpc_c::value> const &cppvalue) {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper() {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_struct_new(&env);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper;
map<string, xmlrpc_c::value>::const_iterator i;
for (i = cppvalue.begin(); i != cppvalue.end(); ++i) {
xmlrpc_c::value mapvalue(i->second);
string mapkey(i->first);
mapvalue.addToCStruct(wrapper.valueP, mapkey);
}
this->instantiate(wrapper.valueP);
}
value_struct::value_struct(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_STRUCT)
throw(error("Not struct type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_struct::operator map<string, xmlrpc_c::value>() const {
xmlrpc_env env;
xmlrpc_env_init(&env);
unsigned int structSize;
structSize = xmlrpc_struct_size(&env, this->cValueP);
if (env.fault_occurred)
throw(error(env.fault_string));
map<string, xmlrpc_c::value> retval;
for (unsigned int i = 0; i < structSize; ++i) {
class cMemberWrapper {
public:
xmlrpc_value * keyP;
xmlrpc_value * valueP;
cMemberWrapper(xmlrpc_value * const structP,
unsigned int const index) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_struct_read_member(&env, structP, index,
&keyP, &valueP);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cMemberWrapper() {
xmlrpc_DECREF(keyP);
xmlrpc_DECREF(valueP);
}
};
cMemberWrapper memberWrapper(this->cValueP, i);
cStringWrapper keyWrapper(memberWrapper.keyP);
string const key(keyWrapper.str, keyWrapper.length);
retval[key] = xmlrpc_c::value(memberWrapper.valueP);
}
return retval;
}
value_nil::value_nil() {
class cWrapper {
public:
xmlrpc_value * valueP;
cWrapper() {
xmlrpc_env env;
xmlrpc_env_init(&env);
this->valueP = xmlrpc_nil_new(&env);
if (env.fault_occurred)
throw(error(env.fault_string));
}
~cWrapper() {
xmlrpc_DECREF(this->valueP);
}
};
cWrapper wrapper;
this->instantiate(wrapper.valueP);
}
value_nil::value_nil(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_NIL)
throw(error("Not nil type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
} // namespace
+166
View File
@@ -0,0 +1,166 @@
#include <string>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/xml.hpp"
using namespace std;
using namespace xmlrpc_c;
namespace {
class cValueWrapper {
/*----------------------------------------------------------------------------
Use an object of this class to set up to remove a reference to an
xmlrpc_value object (a C object with manual reference management)
at then end of a scope -- even if the scope ends with a throw.
-----------------------------------------------------------------------------*/
public:
xmlrpc_value * valueP;
cValueWrapper(xmlrpc_value * valueP) : valueP(valueP) {}
~cValueWrapper() { xmlrpc_DECREF(valueP); }
};
xmlrpc_value *
cArrayFromParamList(paramList const& paramList) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_value * paramArrayP;
paramArrayP = xmlrpc_array_new(&env);
if (!env.fault_occurred) {
for (unsigned int i = 0;
i < paramList.size() && !env.fault_occurred;
++i) {
cValueWrapper const param(paramList[i].cValue());
xmlrpc_array_append_item(&env, paramArrayP, param.valueP);
}
}
if (env.fault_occurred) {
xmlrpc_DECREF(paramArrayP);
throw(error(env.fault_string));
}
return paramArrayP;
}
} // namespace
namespace xmlrpc_c {
namespace xml {
void
generateCall(string const& methodName,
paramList const& paramList,
string * const callXmlP) {
/*----------------------------------------------------------------------------
Generate the XML for an XML-RPC call, given a method name and parameter
list.
-----------------------------------------------------------------------------*/
class memblockWrapper {
xmlrpc_mem_block * const memblockP;
public:
memblockWrapper(xmlrpc_mem_block * const memblockP) :
memblockP(memblockP) {}
~memblockWrapper() {
XMLRPC_MEMBLOCK_FREE(char, memblockP);
}
};
xmlrpc_mem_block * callXmlMP;
xmlrpc_env env;
xmlrpc_env_init(&env);
callXmlMP = XMLRPC_MEMBLOCK_NEW(char, &env, 0);
if (!env.fault_occurred) {
memblockWrapper callXmlHolder(callXmlMP);
// Makes callXmlMP get freed at end of scope
xmlrpc_value * const paramArrayP(cArrayFromParamList(paramList));
xmlrpc_serialize_call(&env, callXmlMP, methodName.c_str(),
paramArrayP);
*callXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, callXmlMP),
XMLRPC_MEMBLOCK_SIZE(char, callXmlMP));
xmlrpc_DECREF(paramArrayP);
}
if (env.fault_occurred)
throw(error(env.fault_string));
}
void
parseResponse(string const& responseXml,
rpcOutcome * const outcomeP) {
/*----------------------------------------------------------------------------
Parse the XML for an XML-RPC response into an XML-RPC result value.
-----------------------------------------------------------------------------*/
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_value * c_resultP;
c_resultP =
xmlrpc_parse_response(&env, responseXml.c_str(), responseXml.size());
/* Unfortunately, xmlrpc_parse_response() does not distinguish between
unparseable XML and XML that cleanly indicates an RPC failure or
other failure on the server end. We'll fix that some day, but for
now, we just assume any failure is an XML-RPC RPC failure.
*/
if (env.fault_occurred)
*outcomeP =
rpcOutcome(fault(env.fault_string,
static_cast<fault::code_t>(env.fault_code)));
else {
*outcomeP = rpcOutcome(value(c_resultP));
xmlrpc_DECREF(c_resultP);
}
}
void
parseSuccessfulResponse(string const& responseXml,
value * const resultP) {
/*----------------------------------------------------------------------------
Same as parseResponse(), but expects the response to indicate success;
throws an error if it doesn't.
-----------------------------------------------------------------------------*/
rpcOutcome outcome;
parseResponse(responseXml, &outcome);
if (!outcome.succeeded())
throw(error("RPC failed. " + outcome.getFault().getDescription()));
*resultP = outcome.getResult();
}
void
trace(string const& label,
string const& xml) {
xmlrpc_traceXml(label.c_str(), xml.c_str(), xml.size());
}
}} // namespace
+3
View File
@@ -0,0 +1,3 @@
Makefile
test
cgitest1
+56
View File
@@ -0,0 +1,56 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "xmlrpc_config.h"
#include "test.h"
#include "cgi.h"
static const char cgiResponse1[] =
"....Status: 200 OK\n"
"Content-type: text/xml; charset=\"utf-8\"\n"
"Content-length: 141\n"
"\n"
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><i4>12</i4></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n";
void
test_server_cgi(void) {
/*----------------------------------------------------------------------------
Here, we pretend to be a web server when someone has requested a POST
to the CGI script "cgitest1".
-----------------------------------------------------------------------------*/
FILE * cgiOutputP;
printf("Running CGI tests...\n");
cgiOutputP = popen("REQUEST_METHOD=POST "
"CONTENT_TYPE=text/xml "
"CONTENT_LENGTH=211 "
"./cgitest1 "
"<sample_add_call.xml",
"r");
if (cgiOutputP == NULL)
TEST_ERROR("Unable to run 'cgitest' program.");
else {
unsigned char cgiResponse[4096];
size_t bytesRead;
bytesRead = fread(cgiResponse, 1, sizeof(cgiResponse), cgiOutputP);
TEST(bytesRead == strlen(cgiResponse1));
TEST(memcmp(cgiResponse, cgiResponse1, bytesRead) == 0);
}
printf("\n");
printf("CGI tests done.\n");
}
+2
View File
@@ -0,0 +1,2 @@
void
test_server_cgi(void);
+79
View File
@@ -0,0 +1,79 @@
/*============================================================================
Act like a CGI script -- read POST data from Standard Input, interpret
it as an XML-RPC call, and write an XML-RPC response to Standard Output.
This is for use by a test program.
============================================================================*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/server_cgi.h"
#include "test.h"
int total_tests;
int total_failures;
static xmlrpc_value *
sample_add(xmlrpc_env * const env,
xmlrpc_value * const param_array,
void * const user_data ATTR_UNUSED) {
xmlrpc_int32 x, y, z;
/* Parse our argument array. */
xmlrpc_decompose_value(env, param_array, "(ii)", &x, &y);
if (env->fault_occurred)
return NULL;
/* Add our two numbers. */
z = x + y;
/* Return our result. */
return xmlrpc_build_value(env, "i", z);
}
int
main(int argc ATTR_UNUSED,
char ** argv ATTR_UNUSED) {
xmlrpc_env env;
xmlrpc_registry * registryP;
xmlrpc_value * argArrayP;
xmlrpc_env_init(&env);
registryP = xmlrpc_registry_new(&env);
TEST(registryP != NULL);
TEST_NO_FAULT(&env);
xmlrpc_registry_add_method(&env, registryP, NULL, "sample.add",
sample_add, NULL);
TEST_NO_FAULT(&env);
argArrayP = xmlrpc_build_value(&env, "(ii)",
(xmlrpc_int32) 25, (xmlrpc_int32) 17);
TEST_NO_FAULT(&env);
/* The following reads from Standard Input and writes to Standard
Output
*/
xmlrpc_server_cgi_process_call(registryP);
xmlrpc_DECREF(argArrayP);
xmlrpc_registry_free(registryP);
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/client.h"
#include "test.h"
#include "client.h"
static void
test_init_cleanup(void) {
xmlrpc_env env;
struct xmlrpc_clientparms clientParms1;
struct xmlrpc_curl_xportparms curlTransportParms1;
xmlrpc_env_init(&env);
xmlrpc_client_init2(&env, 0, "testprog", "1.0", NULL, 0);
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
xmlrpc_client_init2(&env, 0, "testprog", "1.0", &clientParms1, 0);
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
clientParms1.transport = "curl";
xmlrpc_client_init2(&env, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transport));
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
clientParms1.transportparmsP = NULL;
xmlrpc_client_init2(&env, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transportparmsP));
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
clientParms1.transportparmsP = (struct xmlrpc_xportparms *)
&curlTransportParms1;
{
xmlrpc_env env2;
xmlrpc_env_init(&env2);
xmlrpc_client_init2(&env2, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transportparmsP));
TEST_FAULT(&env2, XMLRPC_INTERNAL_ERROR);
}
clientParms1.transportparm_size = 0;
xmlrpc_client_init2(&env, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transportparm_size));
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
curlTransportParms1.network_interface = "eth0";
clientParms1.transportparm_size = XMLRPC_CXPSIZE(network_interface);
xmlrpc_client_init2(&env, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transportparm_size));
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
curlTransportParms1.no_ssl_verifypeer = 1;
curlTransportParms1.no_ssl_verifyhost = 1;
clientParms1.transportparm_size = XMLRPC_CXPSIZE(no_ssl_verifyhost);
xmlrpc_client_init2(&env, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transportparm_size));
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
curlTransportParms1.user_agent = "testprog/1.0";
clientParms1.transportparm_size = XMLRPC_CXPSIZE(user_agent);
xmlrpc_client_init2(&env, 0, "testprog", "1.0",
&clientParms1, XMLRPC_CPSIZE(transportparm_size));
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
xmlrpc_client_init(0, "testprog", "1.0");
TEST_NO_FAULT(&env);
xmlrpc_client_cleanup();
}
void
test_client(void) {
printf("Running client tests.");
test_init_cleanup();
printf("\n");
printf("Client tests done.\n");
}
+2
View File
@@ -0,0 +1,2 @@
void
test_client(void);
+17
View File
@@ -0,0 +1,17 @@
#!/bin/sh
echo "*** Testing for heap block overruns..."
efrpctest
if ! test $?; then exit 1; fi
echo "*** Testing for heap block underruns..."
EF_PROTECT_BELOW=1 efrpctest
if ! test $?; then exit 1; fi
echo "*** Testing for access to freed heap blocks..."
EF_PROTECT_FREE=1 efrpctest
if ! test $?; then exit 1; fi
echo "*** Testing for single-byte overruns..."
EF_ALIGNMENT=0 efrpctest
if ! test $?; then exit 1; fi
@@ -0,0 +1,11 @@
POST /cgi-bin/sample-cgi.cgi 1.0
Host: localhost
Content-Type: text/xml
Content-Length: 141
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>system.listMethods</methodName>
<params>
</params>
</methodCall>
+297
View File
@@ -0,0 +1,297 @@
#include <limits.h>
#include <stdlib.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/xmlparser.h"
#include "test.h"
#include "xml_data.h"
#include "parse_xml.h"
static void test_expat (void)
{
xmlrpc_env env;
xml_element *elem, *array, *data, *value1, *i4;
char *cdata;
size_t size;
xmlrpc_env_init(&env);
/* Parse a moderately complex XML document. */
elem = xml_parse(&env, expat_data, strlen(expat_data));
TEST_NO_FAULT(&env);
TEST(elem != NULL);
/* Verify our results. */
TEST(strcmp(xml_element_name(elem), "value") == 0);
TEST(xml_element_children_size(elem) == 1);
array = xml_element_children(elem)[0];
TEST(strcmp(xml_element_name(array), "array") == 0);
TEST(xml_element_children_size(array) == 1);
data = xml_element_children(array)[0];
TEST(strcmp(xml_element_name(data), "data") == 0);
TEST(xml_element_children_size(data) > 1);
value1 = xml_element_children(data)[0];
TEST(strcmp(xml_element_name(value1), "value") == 0);
TEST(xml_element_children_size(value1) == 1);
i4 = xml_element_children(value1)[0];
TEST(strcmp(xml_element_name(i4), "i4") == 0);
TEST(xml_element_children_size(i4) == 0);
cdata = xml_element_cdata(i4);
size = xml_element_cdata_size(i4);
TEST(size == strlen("2147483647"));
TEST(memcmp(cdata, "2147483647", strlen("2147483647")) == 0);
/* Test cleanup code (w/memprof). */
xml_element_free(elem);
/* Try to parse broken XML. We want to know that a proper error occurs,
** AND that we don't leak any memory (w/memprof). */
elem = xml_parse(&env, expat_error_data, strlen(expat_error_data));
TEST(env.fault_occurred);
TEST(elem == NULL);
xmlrpc_env_clean(&env);
}
static void
test_parse_xml_value(void) {
xmlrpc_env env, env2;
xmlrpc_value *s, *sval;
xmlrpc_int32 int_max, int_min, int_one;
xmlrpc_bool bool_false, bool_true;
char *str_hello, *str_untagged, *datetime;
unsigned char *b64_data;
size_t b64_len;
double negone, zero, one;
int size, sval_int;
const char **bad_value;
xmlrpc_env_init(&env);
{
xmlrpc_value * valueP;
/* Parse a correctly-formed response. */
valueP = xmlrpc_parse_response(&env, correct_value,
strlen(correct_value));
TEST_NO_FAULT(&env);
TEST(valueP != NULL);
/* Analyze it and make sure it contains the correct values. */
xmlrpc_decompose_value(
&env, valueP, "((iibbs68())idddSs)",
&int_max, &int_min,
&bool_false, &bool_true, &str_hello,
&b64_data, &b64_len, &datetime,
&int_one, &negone, &zero, &one, &s, &str_untagged);
xmlrpc_DECREF(valueP);
}
TEST_NO_FAULT(&env);
TEST(int_max == INT_MAX);
TEST(int_min == INT_MIN);
TEST(!bool_false);
TEST(bool_true);
TEST(strlen(str_hello) == strlen("Hello, world! <&>"));
TEST(strcmp(str_hello, "Hello, world! <&>") == 0);
TEST(b64_len == 11);
TEST(memcmp(b64_data, "base64 data", b64_len) == 0);
TEST(strcmp(datetime, "19980717T14:08:55") == 0);
TEST(int_one == 1);
TEST(negone == -1.0);
TEST(zero == 0.0);
TEST(one == 1.0);
TEST(strcmp(str_untagged, "Untagged string") == 0);
free(str_hello);
free(b64_data);
free(datetime);
free(str_untagged);
/* Analyze the contents of our struct. */
TEST(s != NULL);
size = xmlrpc_struct_size(&env, s);
TEST_NO_FAULT(&env);
TEST(size == 2);
sval = xmlrpc_struct_get_value(&env, s, "ten <&>");
TEST_NO_FAULT(&env);
xmlrpc_decompose_value(&env, sval, "i", &sval_int);
TEST_NO_FAULT(&env);
TEST(sval_int == 10);
sval = xmlrpc_struct_get_value(&env, s, "twenty");
TEST_NO_FAULT(&env);
xmlrpc_decompose_value(&env, sval, "i", &sval_int);
TEST_NO_FAULT(&env);
TEST(sval_int == 20);
xmlrpc_DECREF(s);
/* Test our error-checking code. This is exposed to potentially-malicious
** network data, so we need to handle evil data gracefully, without
** barfing or leaking memory. (w/memprof) */
{
xmlrpc_value * valueP;
/* First, test some poorly-formed XML data. */
xmlrpc_env_init(&env2);
valueP = xmlrpc_parse_response(&env2, unparseable_value,
strlen(unparseable_value));
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
TEST(valueP == NULL);
xmlrpc_env_clean(&env2);
}
/* Next, check for bogus values. These are all well-formed XML, but
** they aren't legal XML-RPC. */
for (bad_value = bad_values; *bad_value != NULL; bad_value++) {
xmlrpc_value * valueP;
xml_element *elem;
/* First, check to make sure that our test case is well-formed XML.
** (It's easy to make mistakes when writing the test cases!) */
elem = xml_parse(&env, *bad_value, strlen(*bad_value));
TEST_NO_FAULT(&env);
xml_element_free(elem);
/* Now, make sure the higher-level routine barfs appropriately. */
xmlrpc_env_init(&env2);
valueP = xmlrpc_parse_response(&env2, *bad_value, strlen(*bad_value));
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
TEST(valueP == NULL);
xmlrpc_env_clean(&env2);
}
xmlrpc_env_clean(&env);
}
static void test_parse_xml_response (void)
{
xmlrpc_env env, env2;
int i1;
const char **bad_resp;
xmlrpc_env_init(&env);
{
xmlrpc_value * v;
/* Parse a valid response. */
v = xmlrpc_parse_response(&env, serialized_response,
strlen(serialized_response));
TEST_NO_FAULT(&env);
TEST(v != NULL);
xmlrpc_decompose_value(&env, v, "i", &i1);
xmlrpc_DECREF(v);
}
TEST_NO_FAULT(&env);
TEST(i1 == 30);
{
xmlrpc_value * v;
xmlrpc_env fault;
/* Parse a valid fault. */
xmlrpc_env_init(&fault);
v = xmlrpc_parse_response(&fault, serialized_fault,
strlen(serialized_fault));
TEST(fault.fault_occurred);
TEST(fault.fault_code == 6);
TEST(strcmp(fault.fault_string, "A fault occurred") == 0);
xmlrpc_env_clean(&fault);
}
/* We don't need to test our handling of poorly formatted XML here,
** because we already did that in test_parse_xml_value. */
/* Next, check for bogus responses. These are all well-formed XML, but
** they aren't legal XML-RPC. */
for (bad_resp = bad_responses; *bad_resp != NULL; bad_resp++) {
xmlrpc_value * v;
xml_element *elem;
/* First, check to make sure that our test case is well-formed XML.
** (It's easy to make mistakes when writing the test cases!) */
elem = xml_parse(&env, *bad_resp, strlen(*bad_resp));
TEST_NO_FAULT(&env);
xml_element_free(elem);
/* Now, make sure the higher-level routine barfs appropriately. */
xmlrpc_env_init(&env2);
v = xmlrpc_parse_response(&env2, *bad_resp, strlen(*bad_resp));
TEST(env2.fault_occurred);
TEST(env2.fault_code != 0); /* We use 0 as a code in our bad faults. */
TEST(v == NULL);
xmlrpc_env_clean(&env2);
}
xmlrpc_env_clean(&env);
}
static void test_parse_xml_call (void)
{
xmlrpc_env env, env2;
const char *method_name;
xmlrpc_value *params;
int i1, i2;
const char **bad_call;
xml_element *elem;
xmlrpc_env_init(&env);
/* Parse a valid call. */
xmlrpc_parse_call(&env, serialized_call, strlen(serialized_call),
&method_name, &params);
TEST_NO_FAULT(&env);
TEST(params != NULL);
xmlrpc_decompose_value(&env, params, "(ii)", &i1, &i2);
xmlrpc_DECREF(params);
TEST_NO_FAULT(&env);
TEST(strcmp(method_name, "gloom&doom") == 0);
TEST(i1 == 10 && i2 == 20);
strfree(method_name);
/* Test some poorly-formed XML data. */
xmlrpc_env_init(&env2);
xmlrpc_parse_call(&env2, unparseable_value, strlen(unparseable_value),
&method_name, &params);
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
TEST(method_name == NULL && params == NULL);
xmlrpc_env_clean(&env2);
/* Next, check for bogus values. These are all well-formed XML, but
** they aren't legal XML-RPC. */
for (bad_call = bad_calls; *bad_call != NULL; bad_call++) {
/* First, check to make sure that our test case is well-formed XML.
** (It's easy to make mistakes when writing the test cases!) */
elem = xml_parse(&env, *bad_call, strlen(*bad_call));
TEST_NO_FAULT(&env);
xml_element_free(elem);
/* Now, make sure the higher-level routine barfs appropriately. */
xmlrpc_env_init(&env2);
xmlrpc_parse_call(&env2, *bad_call, strlen(*bad_call),
&method_name, &params);
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
TEST(method_name == NULL && params == NULL);
xmlrpc_env_clean(&env2);
}
xmlrpc_env_clean(&env);
}
void
test_parse_xml(void) {
test_expat();
test_parse_xml_value();
test_parse_xml_response();
test_parse_xml_call();
}
+2
View File
@@ -0,0 +1,2 @@
void
test_parse_xml(void);
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This may or may not be legal XML-RPC, but some old clients (including
Ruby and xmlrpc-epi) send it. This should be interpreted as a call with no
parameters. -->
<methodCall>
<methodName>foo</methodName>
</methodCall>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<params>
<param>
<value><int>2</int></value>
</param>
<param>
<value><int>2</int></value>
</param>
</params>
<methodName>add</methodName>
</methodCall>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>foo</methodName>
<params>
<param>
<value><struct>
<member>
<value><i4>0</i4></value>
<name>child elements reversed!</name>
</member>
</struct></value>
</param>
</params>
</methodCall>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>sample.add</methodName>
<params>
<param><value><i4>5</i4></value></param>
<param><value><i4>7</i4></value></param>
</params>
</methodCall>
+275
View File
@@ -0,0 +1,275 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "test.h"
#include "xml_data.h"
#include "serialize.h"
static void
test_serialize_basic(void) {
xmlrpc_env env;
xmlrpc_value * v;
xmlrpc_mem_block *output;
size_t size;
xmlrpc_env_init(&env);
/* Build a nice, messy value to serialize. We should attempt to use
** use every data type except double (which doesn't serialize in a
** portable manner. */
v = xmlrpc_build_value(&env, "(iibbs68())",
(xmlrpc_int32) INT_MAX, (xmlrpc_int32) INT_MIN,
(xmlrpc_bool) 0, (xmlrpc_bool) 1,
"Hello, world! <&>",
"base64 data", (size_t) 11,
"19980717T14:08:55");
TEST_NO_FAULT(&env);
/* Serialize the value. */
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0);
TEST_NO_FAULT(&env);
xmlrpc_serialize_value(&env, output, v);
TEST_NO_FAULT(&env);
/* Make sure we serialized the correct value. */
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
TEST(size == strlen(serialized_data));
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
serialized_data, size) == 0);
/* (Debugging code to display the value.) */
/* XMLRPC_TYPED_MEM_BLOCK_APPEND(char, &env, output, "\0", 1);
** TEST_NO_FAULT(&env);
** printf("%s\n", XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output)); */
/* Clean up our value. */
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output);
xmlrpc_DECREF(v);
xmlrpc_env_clean(&env);
}
static void
test_serialize_double(void) {
/* Test serialize of a double. */
xmlrpc_env env;
xmlrpc_value * v;
xmlrpc_mem_block *output;
char * result;
/* serialized result, as asciiz string */
size_t resultLength;
/* Length in characters of the serialized result */
float serializedValue;
char nextChar;
int itemsMatched;
xmlrpc_env_init(&env);
/* Build a double to serialize */
v = xmlrpc_build_value(&env, "d", 3.14159);
TEST_NO_FAULT(&env);
/* Serialize the value. */
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0);
TEST_NO_FAULT(&env);
xmlrpc_serialize_value(&env, output, v);
TEST_NO_FAULT(&env);
/* Make sure we serialized the correct value. Note that because
doubles aren't precise, this might serialize as 3.1415899999
or something like that. So we check it arithmetically.
*/
resultLength = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
result = malloc(resultLength + 1);
memcpy(result, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
resultLength);
result[resultLength] = '\0';
itemsMatched = sscanf(result,
"<value><double>%f</double></value>\r\n%c",
&serializedValue, &nextChar);
TEST(itemsMatched == 1);
TEST(serializedValue - 3.14159 < .000001);
/* We'd like to test more precision, but sscanf doesn't do doubles */
free(result);
/* Clean up our value. */
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output);
xmlrpc_DECREF(v);
xmlrpc_env_clean(&env);
}
static void
test_serialize_struct(void) {
/* Serialize a simple struct. */
char const serialized_struct[] =
"<value><struct>\r\n" \
"<member><name>&lt;&amp;&gt;</name>\r\n" \
"<value><i4>10</i4></value></member>\r\n" \
"</struct></value>";
xmlrpc_env env;
xmlrpc_value * v;
xmlrpc_mem_block *output;
size_t size;
xmlrpc_env_init(&env);
v = xmlrpc_build_value(&env, "{s:i}", "<&>", (xmlrpc_int32) 10);
TEST_NO_FAULT(&env);
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0);
TEST_NO_FAULT(&env);
xmlrpc_serialize_value(&env, output, v);
TEST_NO_FAULT(&env);
/* Make sure we serialized the correct value. */
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
TEST(size == strlen(serialized_struct));
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
serialized_struct, size) == 0);
/* Clean up our struct. */
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output);
xmlrpc_DECREF(v);
xmlrpc_env_clean(&env);
}
static void
test_serialize_methodResponse(void) {
/* Serialize a methodResponse. */
xmlrpc_env env;
xmlrpc_value * v;
xmlrpc_mem_block *output;
size_t size;
xmlrpc_env_init(&env);
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0);
TEST_NO_FAULT(&env);
v = xmlrpc_build_value(&env, "i", (xmlrpc_int32) 30);
TEST_NO_FAULT(&env);
xmlrpc_serialize_response(&env, output, v);
TEST_NO_FAULT(&env);
/* Make sure we serialized the correct value. */
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
TEST(size == strlen(serialized_response));
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
serialized_response, size) == 0);
/* Clean up our methodResponse. */
xmlrpc_DECREF(v);
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output);
xmlrpc_env_clean(&env);
}
static void
test_serialize_methodCall(void) {
/* Serialize a methodCall. */
xmlrpc_env env;
xmlrpc_value * v;
xmlrpc_mem_block *output;
size_t size;
xmlrpc_env_init(&env);
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0);
TEST_NO_FAULT(&env);
v = xmlrpc_build_value(&env, "(ii)", (xmlrpc_int32) 10, (xmlrpc_int32) 20);
TEST_NO_FAULT(&env);
xmlrpc_serialize_call(&env, output, "gloom&doom", v);
TEST_NO_FAULT(&env);
/* Make sure we serialized the correct value. */
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
TEST(size == strlen(serialized_call));
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
serialized_call, size) == 0);
/* Clean up our methodCall. */
xmlrpc_DECREF(v);
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output);
xmlrpc_env_clean(&env);
}
static void
test_serialize_fault(void) {
/* Serialize a fault. */
xmlrpc_env env;
xmlrpc_env fault;
xmlrpc_mem_block *output;
size_t size;
xmlrpc_env_init(&env);
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0);
TEST_NO_FAULT(&env);
xmlrpc_env_init(&fault);
xmlrpc_env_set_fault(&fault, 6, "A fault occurred");
xmlrpc_serialize_fault(&env, output, &fault);
TEST_NO_FAULT(&env);
/* Make sure we serialized the correct value. */
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output);
TEST(size == strlen(serialized_fault));
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
serialized_fault, size) == 0);
/* Clean up our fault. */
xmlrpc_env_clean(&fault);
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output);
xmlrpc_env_clean(&env);
}
void
test_serialize(void) {
printf("Running serialize tests.");
test_serialize_basic();
test_serialize_double();
test_serialize_struct();
test_serialize_methodResponse();
test_serialize_methodCall();
test_serialize_fault();
printf("\n");
printf("Serialize tests done.\n");
}
+2
View File
@@ -0,0 +1,2 @@
void
test_serialize(void);
+44
View File
@@ -0,0 +1,44 @@
#include <stdio.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/abyss.h"
#include "xmlrpc-c/server_abyss.h"
#include "test.h"
#include "server_abyss.h"
void
test_server_abyss(void) {
xmlrpc_env env;
xmlrpc_registry * registryP;
TServer abyssServer;
printf("Running Abyss server tests...\n");
xmlrpc_env_init(&env);
registryP = xmlrpc_registry_new(&env);
TEST(registryP != NULL);
TEST_NO_FAULT(&env);
ServerCreate(&abyssServer, "testserver", 8080, NULL, NULL);
xmlrpc_server_abyss_set_handlers(&abyssServer, registryP);
xmlrpc_server_abyss_set_handler(&env, &abyssServer, "/RPC3", registryP);
TEST_NO_FAULT(&env);
ServerFree(&abyssServer);
printf("\n");
printf("Abyss server tests done.\n");
}
+2
View File
@@ -0,0 +1,2 @@
void
test_server_abyss(void);
+938
View File
@@ -0,0 +1,938 @@
/* Copyright information is at the end of the file. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/server.h"
#include "test.h"
#include "value.h"
#include "serialize.h"
#include "parse_xml.h"
#include "cgi.h"
#include "xml_data.h"
#include "client.h"
#include "server_abyss.h"
/*=========================================================================
** Test Harness
**=========================================================================
** This is a super light-weight test harness. It's vaguely inspired by
** Kent Beck's book on eXtreme Programming (XP)--the output is succinct,
** new tests can be coded quickly, and the whole thing runs in a few
** second's time.
**
** To run the tests, type './rpctest'.
** To check for memory leaks, install RedHat's 'memprof' utility, and
** type 'memprof rpctest'.
**
** If you add new tests to this file, please deallocate any data
** structures you use in the appropriate fashion. This allows us to test
** various destructor code for memory leaks.
*/
int total_tests = 0;
int total_failures = 0;
/*=========================================================================
** Test Data
**=========================================================================
** Some common test data which need to be allocated at a fixed address,
** or which are inconvenient to allocate inline.
*/
static char* test_string_1 = "foo";
static char* test_string_2 = "bar";
static int test_int_array_1[5] = {1, 2, 3, 4, 5};
static int test_int_array_2[3] = {6, 7, 8};
static int test_int_array_3[8] = {1, 2, 3, 4, 5, 6, 7, 8};
/*=========================================================================
** Test Suites
**=========================================================================
*/
static void test_env(void)
{
xmlrpc_env env, env2;
char *s;
/* Test xmlrpc_env_init. */
xmlrpc_env_init(&env);
TEST(!env.fault_occurred);
TEST(env.fault_code == 0);
TEST(env.fault_string == NULL);
/* Test xmlrpc_set_fault. */
xmlrpc_env_set_fault(&env, 1, test_string_1);
TEST(env.fault_occurred);
TEST(env.fault_code == 1);
TEST(env.fault_string != test_string_1);
TEST(strcmp(env.fault_string, test_string_1) == 0);
/* Change an existing fault. */
xmlrpc_env_set_fault(&env, 2, test_string_2);
TEST(env.fault_occurred);
TEST(env.fault_code == 2);
TEST(strcmp(env.fault_string, test_string_2) == 0);
/* Set a fault with a format string. */
xmlrpc_env_set_fault_formatted(&env, 3, "a%s%d", "bar", 9);
TEST(env.fault_occurred);
TEST(env.fault_code == 3);
TEST(strcmp(env.fault_string, "abar9") == 0);
/* Set a fault with an oversized string. */
s = "12345678901234567890123456789012345678901234567890";
xmlrpc_env_set_fault_formatted(&env, 4, "%s%s%s%s%s%s", s, s, s, s, s, s);
TEST(env.fault_occurred);
TEST(env.fault_code == 4);
TEST(strlen(env.fault_string) == 255);
/* Test cleanup code (with help from memprof). */
xmlrpc_env_clean(&env);
/* Test cleanup code on in absence of xmlrpc_env_set_fault. */
xmlrpc_env_init(&env2);
xmlrpc_env_clean(&env2);
}
static void test_mem_block (void)
{
xmlrpc_env env;
xmlrpc_mem_block* block;
xmlrpc_mem_block* typed_heap_block;
xmlrpc_mem_block typed_auto_block;
void** typed_contents;
xmlrpc_env_init(&env);
/* Allocate a zero-size block. */
block = xmlrpc_mem_block_new(&env, 0);
TEST_NO_FAULT(&env);
TEST(block != NULL);
TEST(xmlrpc_mem_block_size(block) == 0);
/* Grow the block a little bit. */
xmlrpc_mem_block_resize(&env, block, strlen(test_string_1) + 1);
TEST_NO_FAULT(&env);
TEST(xmlrpc_mem_block_size(block) == strlen(test_string_1) + 1);
/* Insert a string into the block, and resize it by large amount.
** We want to cause a reallocation and copy of the block contents. */
strcpy(xmlrpc_mem_block_contents(block), test_string_1);
xmlrpc_mem_block_resize(&env, block, 10000);
TEST_NO_FAULT(&env);
TEST(xmlrpc_mem_block_size(block) == 10000);
TEST(strcmp(xmlrpc_mem_block_contents(block), test_string_1) == 0);
/* Test cleanup code (with help from memprof). */
xmlrpc_mem_block_free(block);
/* Allocate a bigger block. */
block = xmlrpc_mem_block_new(&env, 128);
TEST_NO_FAULT(&env);
TEST(block != NULL);
TEST(xmlrpc_mem_block_size(block) == 128);
/* Test cleanup code (with help from memprof). */
xmlrpc_mem_block_free(block);
/* Allocate a "typed" memory block. */
typed_heap_block = XMLRPC_TYPED_MEM_BLOCK_NEW(void*, &env, 20);
TEST_NO_FAULT(&env);
TEST(typed_heap_block != NULL);
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, typed_heap_block) == 20);
typed_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(void*, typed_heap_block);
TEST(typed_contents != NULL);
/* Resize a typed memory block. */
XMLRPC_TYPED_MEM_BLOCK_RESIZE(void*, &env, typed_heap_block, 100);
TEST_NO_FAULT(&env);
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, typed_heap_block) == 100);
/* Test cleanup code (with help from memprof). */
XMLRPC_TYPED_MEM_BLOCK_FREE(void*, typed_heap_block);
/* Test _INIT and _CLEAN for stack-based memory blocks. */
XMLRPC_TYPED_MEM_BLOCK_INIT(void*, &env, &typed_auto_block, 30);
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, &typed_auto_block) == 30);
XMLRPC_TYPED_MEM_BLOCK_CLEAN(void*, &typed_auto_block);
/* Test xmlrpc_mem_block_append. */
block = XMLRPC_TYPED_MEM_BLOCK_NEW(int, &env, 5);
TEST_NO_FAULT(&env);
memcpy(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(int, block),
test_int_array_1, sizeof(test_int_array_1));
XMLRPC_TYPED_MEM_BLOCK_APPEND(int, &env, block, test_int_array_2, 3);
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(int, block) == 8);
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(int, block),
test_int_array_3, sizeof(test_int_array_3)) == 0);
XMLRPC_TYPED_MEM_BLOCK_FREE(int, block);
xmlrpc_env_clean(&env);
}
static char *(base64_triplets[]) = {
"", "", "\r\n",
"a", "YQ==", "YQ==\r\n",
"aa", "YWE=", "YWE=\r\n",
"aaa", "YWFh", "YWFh\r\n",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY"
"2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=",
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY"
"2Rl\r\n"
"ZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\r\n",
NULL};
static void test_base64_conversion (void)
{
xmlrpc_env env, env2;
char **triplet, *bin_data, *nocrlf_ascii_data, *ascii_data;
xmlrpc_mem_block *output;
xmlrpc_env_init(&env);
for (triplet = base64_triplets; *triplet != NULL; triplet += 3) {
bin_data = *triplet;
nocrlf_ascii_data = *(triplet + 1);
ascii_data = *(triplet + 2);
/* Test our encoding routine. */
output = xmlrpc_base64_encode(&env,
(unsigned char*) bin_data,
strlen(bin_data));
TEST_NO_FAULT(&env);
TEST(output != NULL);
TEST(xmlrpc_mem_block_size(output) == strlen(ascii_data));
TEST(memcmp(xmlrpc_mem_block_contents(output), ascii_data,
strlen(ascii_data)) == 0);
xmlrpc_mem_block_free(output);
/* Test our newline-free encoding routine. */
output =
xmlrpc_base64_encode_without_newlines(&env,
(unsigned char*) bin_data,
strlen(bin_data));
TEST_NO_FAULT(&env);
TEST(output != NULL);
TEST(xmlrpc_mem_block_size(output) == strlen(nocrlf_ascii_data));
TEST(memcmp(xmlrpc_mem_block_contents(output), nocrlf_ascii_data,
strlen(nocrlf_ascii_data)) == 0);
xmlrpc_mem_block_free(output);
/* Test our decoding routine. */
output = xmlrpc_base64_decode(&env, ascii_data, strlen(ascii_data));
TEST_NO_FAULT(&env);
TEST(output != NULL);
TEST(xmlrpc_mem_block_size(output) == strlen(bin_data));
TEST(memcmp(xmlrpc_mem_block_contents(output), bin_data,
strlen(bin_data)) == 0);
xmlrpc_mem_block_free(output);
}
/* Now for something broken... */
xmlrpc_env_init(&env2);
output = xmlrpc_base64_decode(&env2, "====", 4);
TEST(output == NULL);
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
xmlrpc_env_clean(&env2);
/* Now for something broken in a really sneaky way... */
xmlrpc_env_init(&env2);
output = xmlrpc_base64_decode(&env2, "a==", 4);
TEST(output == NULL);
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
xmlrpc_env_clean(&env2);
xmlrpc_env_clean(&env);
}
static void test_bounds_checks (void)
{
xmlrpc_env env;
xmlrpc_value *array;
int i1, i2, i3, i4;
/* Get an array to work with. */
xmlrpc_env_init(&env);
array = xmlrpc_build_value(&env, "(iii)", 100, 200, 300);
TEST_NO_FAULT(&env);
xmlrpc_env_clean(&env);
/* Test xmlrpc_decompose_value with too few values. */
xmlrpc_env_init(&env);
xmlrpc_decompose_value(&env, array, "(iiii)", &i1, &i2, &i3, &i4);
TEST_FAULT(&env, XMLRPC_INDEX_ERROR);
xmlrpc_env_clean(&env);
/* Test xmlrpc_decompose_value with too many values. */
xmlrpc_env_init(&env);
xmlrpc_decompose_value(&env, array, "(ii)", &i1, &i2, &i3, &i4);
TEST_FAULT(&env, XMLRPC_INDEX_ERROR);
xmlrpc_env_clean(&env);
/* Dispose of our array. */
xmlrpc_DECREF(array);
}
/*=========================================================================
** test_method_registry
**=========================================================================
** We need to define some static callbacks to test this code.
*/
#define FOO_USER_DATA ((void*) 0xF00)
#define BAR_USER_DATA ((void*) 0xBAF)
static xmlrpc_value *test_foo (xmlrpc_env *env,
xmlrpc_value *param_array,
void *user_data)
{
xmlrpc_int32 x, y;
TEST_NO_FAULT(env);
TEST(param_array != NULL);
TEST(user_data == FOO_USER_DATA);
xmlrpc_decompose_value(env, param_array, "(ii)", &x, &y);
TEST_NO_FAULT(env);
TEST(x == 25);
TEST(y == 17);
return xmlrpc_build_value(env, "i", (xmlrpc_int32) x + y);
}
static xmlrpc_value *test_bar (xmlrpc_env *env,
xmlrpc_value *param_array,
void *user_data)
{
xmlrpc_int32 x, y;
TEST_NO_FAULT(env);
TEST(param_array != NULL);
TEST(user_data == BAR_USER_DATA);
xmlrpc_decompose_value(env, param_array, "(ii)", &x, &y);
TEST_NO_FAULT(env);
TEST(x == 25);
TEST(y == 17);
xmlrpc_env_set_fault(env, 123, "Test fault");
return NULL;
}
static xmlrpc_value *
test_default(xmlrpc_env * const env,
const char * const host ATTR_UNUSED,
const char * const method_name ATTR_UNUSED,
xmlrpc_value * const param_array,
void * const user_data) {
xmlrpc_int32 x, y;
TEST_NO_FAULT(env);
TEST(param_array != NULL);
TEST(user_data == FOO_USER_DATA);
xmlrpc_decompose_value(env, param_array, "(ii)", &x, &y);
TEST_NO_FAULT(env);
TEST(x == 25);
TEST(y == 17);
return xmlrpc_build_value(env, "i", 2 * (x + y));
}
static xmlrpc_value *
process_call_helper (xmlrpc_env *env,
xmlrpc_registry *registry,
const char *method_name,
xmlrpc_value *arg_array)
{
xmlrpc_mem_block *call, *response;
xmlrpc_value *value;
/* Build a call, and tell the registry to handle it. */
call = xmlrpc_mem_block_new(env, 0);
TEST_NO_FAULT(env);
xmlrpc_serialize_call(env, call, method_name, arg_array);
TEST_NO_FAULT(env);
response = xmlrpc_registry_process_call(env, registry, NULL,
xmlrpc_mem_block_contents(call),
xmlrpc_mem_block_size(call));
TEST_NO_FAULT(env);
TEST(response != NULL);
/* Parse the response. */
value = xmlrpc_parse_response(env, xmlrpc_mem_block_contents(response),
xmlrpc_mem_block_size(response));
xmlrpc_mem_block_free(call);
xmlrpc_mem_block_free(response);
return value;
}
static void
test_method_registry(void) {
xmlrpc_env env, env2;
xmlrpc_value *arg_array, *value;
xmlrpc_registry *registry;
xmlrpc_mem_block *response;
xmlrpc_int32 i;
xmlrpc_value *multi;
xmlrpc_int32 foo1_result, foo2_result;
xmlrpc_int32 bar_code, nosuch_code, multi_code, bogus1_code, bogus2_code;
char *bar_string, *nosuch_string, *multi_string;
char *bogus1_string, *bogus2_string;
xmlrpc_env_init(&env);
/* Create a new registry. */
registry = xmlrpc_registry_new(&env);
TEST(registry != NULL);
TEST_NO_FAULT(&env);
/* Add some test methods. */
xmlrpc_registry_add_method(&env, registry, NULL, "test.foo",
test_foo, FOO_USER_DATA);
TEST_NO_FAULT(&env);
xmlrpc_registry_add_method(&env, registry, NULL, "test.bar",
test_bar, BAR_USER_DATA);
TEST_NO_FAULT(&env);
/* Build an argument array for our calls. */
arg_array = xmlrpc_build_value(&env, "(ii)",
(xmlrpc_int32) 25, (xmlrpc_int32) 17);
TEST_NO_FAULT(&env);
/* Call test.foo and check the result. */
value = process_call_helper(&env, registry, "test.foo", arg_array);
TEST_NO_FAULT(&env);
TEST(value != NULL);
xmlrpc_decompose_value(&env, value, "i", &i);
xmlrpc_DECREF(value);
TEST_NO_FAULT(&env);
TEST(i == 42);
/* Call test.bar and check the result. */
xmlrpc_env_init(&env2);
value = process_call_helper(&env2, registry, "test.bar", arg_array);
TEST_FAULT(&env2, 123);
TEST(env2.fault_string && strcmp(env2.fault_string, "Test fault") == 0);
xmlrpc_env_clean(&env2);
/* Call a non-existant method and check the result. */
xmlrpc_env_init(&env2);
value = process_call_helper(&env2, registry, "test.nosuch", arg_array);
TEST(value == NULL);
TEST_FAULT(&env2, XMLRPC_NO_SUCH_METHOD_ERROR);
xmlrpc_env_clean(&env2);
/* Test system.multicall. */
multi = xmlrpc_build_value(&env,
"(({s:s,s:V}{s:s,s:V}{s:s,s:V}"
"{s:s,s:()}s{}{s:s,s:V}))",
"methodName", "test.foo",
"params", arg_array,
"methodName", "test.bar",
"params", arg_array,
"methodName", "test.nosuch",
"params", arg_array,
"methodName", "system.multicall",
"params",
"bogus_entry",
"methodName", "test.foo",
"params", arg_array);
TEST_NO_FAULT(&env);
value = process_call_helper(&env, registry, "system.multicall", multi);
TEST_NO_FAULT(&env);
xmlrpc_decompose_value(&env, value,
"((i){s:i,s:s,*}{s:i,s:s,*}"
"{s:i,s:s,*}{s:i,s:s,*}{s:i,s:s,*}(i))",
&foo1_result,
"faultCode", &bar_code,
"faultString", &bar_string,
"faultCode", &nosuch_code,
"faultString", &nosuch_string,
"faultCode", &multi_code,
"faultString", &multi_string,
"faultCode", &bogus1_code,
"faultString", &bogus1_string,
"faultCode", &bogus2_code,
"faultString", &bogus2_string,
&foo2_result);
xmlrpc_DECREF(value);
TEST_NO_FAULT(&env);
TEST(foo1_result == 42);
TEST(bar_code == 123);
TEST(strcmp(bar_string, "Test fault") == 0);
TEST(nosuch_code == XMLRPC_NO_SUCH_METHOD_ERROR);
TEST(multi_code == XMLRPC_REQUEST_REFUSED_ERROR);
TEST(foo2_result == 42);
xmlrpc_DECREF(multi);
free(bar_string);
free(nosuch_string);
free(multi_string);
free(bogus1_string);
free(bogus2_string);
/* PASS bogus XML data and make sure our parser pukes gracefully.
** (Because of the way the code is laid out, and the presence of other
** test suites, this lets us skip tests for invalid XML-RPC data.) */
xmlrpc_env_init(&env2);
response = xmlrpc_registry_process_call(&env, registry, NULL,
expat_error_data,
strlen(expat_error_data));
TEST_NO_FAULT(&env);
TEST(response != NULL);
value = xmlrpc_parse_response(&env2, xmlrpc_mem_block_contents(response),
xmlrpc_mem_block_size(response));
TEST(value == NULL);
TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
xmlrpc_mem_block_free(response);
xmlrpc_env_clean(&env2);
xmlrpc_registry_set_default_method(&env, registry, &test_default,
FOO_USER_DATA);
TEST_NO_FAULT(&env);
value = process_call_helper(&env, registry, "test.nosuch", arg_array);
TEST_NO_FAULT(&env);
TEST(value != NULL);
xmlrpc_decompose_value(&env, value, "i", &i);
xmlrpc_DECREF(value);
TEST_NO_FAULT(&env);
TEST(i == 84);
/* Change the default method. */
xmlrpc_registry_set_default_method(&env, registry, &test_default,
BAR_USER_DATA);
TEST_NO_FAULT(&env);
/* Test cleanup code (w/memprof). */
xmlrpc_registry_free(registry);
xmlrpc_DECREF(arg_array);
xmlrpc_env_clean(&env);
}
static void test_nesting_limit (void)
{
xmlrpc_env env;
xmlrpc_value *val;
xmlrpc_env_init(&env);
/* Test with an adequate limit for (...(...()...)...). */
xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 2);
val = xmlrpc_parse_response(&env, correct_value, strlen(correct_value));
TEST_NO_FAULT(&env);
TEST(val != NULL);
xmlrpc_DECREF(val);
/* Test with an inadequate limit. */
xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 1);
val = xmlrpc_parse_response(&env, correct_value, strlen(correct_value));
TEST_FAULT(&env, XMLRPC_PARSE_ERROR); /* BREAKME - Will change. */
TEST(val == NULL);
/* Reset the default limit. */
xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, XMLRPC_NESTING_LIMIT_DEFAULT);
TEST(xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID)
== XMLRPC_NESTING_LIMIT_DEFAULT);
xmlrpc_env_clean(&env);
}
static void test_xml_size_limit (void)
{
xmlrpc_env env;
const char *method_name;
xmlrpc_value *params, *val;
/* NOTE - This test suite only verifies the last-ditch size-checking
** code. There should also be matching code in all server (and
** preferably all client) modules as well. */
/* Set our XML size limit to something ridiculous. */
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 6);
/* Attempt to parse a call. */
xmlrpc_env_init(&env);
xmlrpc_parse_call(&env, serialized_call, strlen(serialized_call),
&method_name, &params);
TEST_FAULT(&env, XMLRPC_LIMIT_EXCEEDED_ERROR);
TEST(method_name == NULL);
TEST(params == NULL);
xmlrpc_env_clean(&env);
/* Attempt to parse a response. */
xmlrpc_env_init(&env);
val = xmlrpc_parse_response(&env, correct_value, strlen(correct_value));
TEST_FAULT(&env, XMLRPC_LIMIT_EXCEEDED_ERROR);
TEST(val == NULL);
xmlrpc_env_clean(&env);
/* Reset the default limit. */
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, XMLRPC_XML_SIZE_LIMIT_DEFAULT);
}
/*=========================================================================
** test_sample_files
**=========================================================================
** Read in a bunch of sample test files and make sure we get plausible
** results.
**
** We use these files to test strange-but-legal encodings, illegal-but-
** supported encodings, etc.
*/
static char *good_requests[] = {
"req_out_of_order.xml",
"req_no_params.xml",
"req_value_name.xml",
NULL
};
#define MAX_SAMPLE_FILE_LEN (16 * 1024)
static char file_buff [MAX_SAMPLE_FILE_LEN];
static void
read_file (char *path, char **out_data, size_t *out_size)
{
FILE *f;
size_t bytes_read;
/* Open the file. */
f = fopen(path, "r");
if (f == NULL) {
/* Since this error is fairly likely to happen, give an
** informative error message... */
fflush(stdout);
fprintf(stderr, "Could not open file '%s'. errno=%d (%s)\n",
path, errno, strerror(errno));
abort();
}
/* Read in one buffer full of data, and make sure that everything
** fit. (We perform a lazy error/no-eof/zero-length-file test using
** bytes_read.) */
bytes_read = fread(file_buff, sizeof(char), MAX_SAMPLE_FILE_LEN, f);
TEST(0 < bytes_read && bytes_read < MAX_SAMPLE_FILE_LEN);
/* Close the file and return our data. */
fclose(f);
*out_data = file_buff;
*out_size = bytes_read;
}
static void test_sample_files (void)
{
xmlrpc_env env;
char **paths, *path;
char *data;
size_t data_len;
const char *method_name;
xmlrpc_value *params;
xmlrpc_env_init(&env);
/* Test our good requests. */
for (paths = good_requests; *paths != NULL; paths++) {
path = *paths;
read_file(path, &data, &data_len);
xmlrpc_parse_call(&env, data, data_len, &method_name, &params);
TEST_NO_FAULT(&env);
strfree(method_name);
xmlrpc_DECREF(params);
}
xmlrpc_env_clean(&env);
}
/*=========================================================================
** test_utf8_coding
**=========================================================================
** We need to test our UTF-8 decoder thoroughly. Most of these test
** cases are taken from the UTF-8-test.txt file by Markus Kuhn
** <mkuhn@acm.org>:
** http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
*/
#ifdef HAVE_UNICODE_WCHAR
typedef struct {
char *utf8;
wchar_t wcs[16];
} utf8_and_wcs;
static utf8_and_wcs good_utf8[] = {
/* Greek 'kosme'. */
{"\316\272\341\275\271\317\203\316\274\316\265",
{0x03BA, 0x1F79, 0x03C3, 0x03BC, 0x03B5, 0}},
/* First sequences of a given length. */
/* '\000' is not a legal C string. */
{"\302\200", {0x0080, 0}},
{"\340\240\200", {0x0800, 0}},
/* Last sequences of a given length. */
{"\177", {0x007F, 0}},
{"\337\277", {0x07FF, 0}},
/* 0xFFFF is not a legal Unicode character. */
/* Other boundry conditions. */
{"\001", {0x0001, 0}},
{"\355\237\277", {0xD7FF, 0}},
{"\356\200\200", {0xE000, 0}},
{"\357\277\275", {0xFFFD, 0}},
/* Other random test cases. */
{"", {0}},
{"abc", {0x0061, 0x0062, 0x0063, 0}},
{"[\302\251]", {0x005B, 0x00A9, 0x005D, 0}},
{NULL, {0}}
};
static char *(bad_utf8[]) = {
/* Continuation bytes. */
"\200", "\277",
/* Lonely start characters. */
"\300", "\300x", "\300xx",
"\340", "\340x", "\340xx", "\340xxx",
/* Last byte missing. */
"\340\200", "\340\200x", "\340\200xx",
"\357\277", "\357\277x", "\357\277xx",
/* Illegal bytes. */
"\376", "\377",
/* Overlong '/'. */
"\300\257", "\340\200\257",
/* Overlong ASCII NUL. */
"\300\200", "\340\200\200",
/* Maximum overlong sequences. */
"\301\277", "\340\237\277",
/* Illegal code positions. */
"\357\277\276", /* U+FFFE */
"\357\277\277", /* U+FFFF */
/* UTF-16 surrogates (unpaired and paired). */
"\355\240\200",
"\355\277\277",
"\355\240\200\355\260\200",
"\355\257\277\355\277\277",
/* Valid UCS-4 characters (we don't handle these yet).
** On systems with UCS-4 or UTF-16 wchar_t values, we
** may eventually handle these in some fashion. */
"\360\220\200\200",
"\370\210\200\200\200",
"\374\204\200\200\200\200",
NULL
};
/* This routine is missing on certain platforms. This implementation
** *appears* to be correct. */
#if 0
#ifndef HAVE_WCSNCMP
int wcsncmp(wchar_t *wcs1, wchar_t* wcs2, size_t len)
{
size_t i;
/* XXX - 'unsigned long' should be 'uwchar_t'. */
unsigned long c1, c2;
for (i=0; i < len; i++) {
c1 = wcs1[i];
c2 = wcs2[i];
/* This clever comparison borrowed from the GNU C Library. */
if (c1 == 0 || c1 != c2)
return c1 - c2;
}
return 0;
}
#endif /* HAVE_WCSNCMP */
#endif
static void test_utf8_coding (void)
{
xmlrpc_env env, env2;
utf8_and_wcs *good_data;
char **bad_data;
char *utf8;
wchar_t *wcs;
xmlrpc_mem_block *output;
xmlrpc_env_init(&env);
/* Test each of our valid UTF-8 sequences. */
for (good_data = good_utf8; good_data->utf8 != NULL; good_data++) {
utf8 = good_data->utf8;
wcs = good_data->wcs;
/* Attempt to validate the UTF-8 string. */
xmlrpc_validate_utf8(&env, utf8, strlen(utf8));
TEST_NO_FAULT(&env);
/* Attempt to decode the UTF-8 string. */
output = xmlrpc_utf8_to_wcs(&env, utf8, strlen(utf8));
TEST_NO_FAULT(&env);
TEST(output != NULL);
TEST(wcslen(wcs) == XMLRPC_TYPED_MEM_BLOCK_SIZE(wchar_t, output));
TEST(0 ==
wcsncmp(wcs, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, output),
wcslen(wcs)));
xmlrpc_mem_block_free(output);
/* Test the UTF-8 encoder, too. */
output = xmlrpc_wcs_to_utf8(&env, wcs, wcslen(wcs));
TEST_NO_FAULT(&env);
TEST(output != NULL);
TEST(strlen(utf8) == XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output));
TEST(0 ==
strncmp(utf8, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output),
strlen(utf8)));
xmlrpc_mem_block_free(output);
}
/* Test each of our illegal UTF-8 sequences. */
for (bad_data = bad_utf8; *bad_data != NULL; bad_data++) {
utf8 = *bad_data;
/* Attempt to validate the UTF-8 string. */
xmlrpc_env_init(&env2);
xmlrpc_validate_utf8(&env2, utf8, strlen(utf8));
TEST_FAULT(&env2, XMLRPC_INVALID_UTF8_ERROR);
/* printf("Fault: %s\n", env2.fault_string); --Hand-checked */
xmlrpc_env_clean(&env2);
/* Attempt to decode the UTF-8 string. */
xmlrpc_env_init(&env2);
output = xmlrpc_utf8_to_wcs(&env2, utf8, strlen(utf8));
TEST_FAULT(&env2, XMLRPC_INVALID_UTF8_ERROR);
TEST(output == NULL);
xmlrpc_env_clean(&env2);
}
xmlrpc_env_clean(&env);
}
#endif /* HAVE_UNICODE_WCHAR */
/*=========================================================================
** Test Driver
**=========================================================================
*/
int
main(int argc,
char ** argv ATTR_UNUSED) {
if (argc-1 > 0) {
fprintf(stderr, "There are no arguments.");
exit(1);
}
/* Add your test suites here. */
test_env();
test_mem_block();
test_base64_conversion();
printf("\n");
test_value();
test_bounds_checks();
printf("\n");
test_serialize();
test_parse_xml();
test_method_registry();
test_nesting_limit();
test_xml_size_limit();
test_sample_files();
printf("\n");
#ifndef WIN32 /* CGI unsupported in Windows */
test_server_cgi();
#endif
test_server_abyss();
#ifdef HAVE_UNICODE_WCHAR
test_utf8_coding();
#endif /* HAVE_UNICODE_WCHAR */
printf("\n");
#ifndef WIN32 /* TODO: Client test uses curl... */
test_client();
#endif
/* Summarize our test run. */
printf("\nRan %d tests, %d failed, %.1f%% passed\n",
total_tests, total_failures,
100.0 - (100.0 * total_failures) / total_tests);
/* Print the final result. */
if (total_failures == 0) {
printf("OK\n");
return 0;
}
printf("FAILED\n");
return 1;
}
/* 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. */
+67
View File
@@ -0,0 +1,67 @@
#include <stdlib.h>
#include <stdio.h>
extern int total_tests;
extern int total_failures;
static __inline void
strfree(const char * const string) {
free((char*)string);
}
/* This is a good place to set a breakpoint. */
static __inline void
test_failure(const char * const file,
unsigned int const line,
const char * const label,
const char * const statement) {
++total_failures;
printf("\n%s:%u: test failure: %s (%s)\n", file, line, label, statement);
abort();
}
#define TEST(statement) \
do { \
++total_tests; \
if ((statement)) { \
printf("."); \
} else { \
test_failure(__FILE__, __LINE__, "expected", #statement); \
} \
} while (0)
#define TEST_NO_FAULT(env) \
do { \
++total_tests; \
if (!(env)->fault_occurred) { \
printf("."); \
} else { \
test_failure(__FILE__, __LINE__, "fault occurred", \
(env)->fault_string); \
} \
} while (0)
#define TEST_FAULT(env, code) \
do { \
++total_tests; \
if ((env)->fault_occurred && (env)->fault_code == (code)) { \
printf("."); \
} else { \
test_failure(__FILE__, __LINE__, "wrong/no fault occurred", \
(env)->fault_string); \
} \
} while (0)
#define TEST_ERROR(reason) \
do { \
printf("Unable to test at %s/%u. %s", __FILE__, __LINE__, reason); \
abort(); \
} while (0)
File diff suppressed because it is too large. Load diff
+2
View File
@@ -0,0 +1,2 @@
void
test_value(void);
+202
View File
@@ -0,0 +1,202 @@
#include <stddef.h>
#define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
#define RAW_STRING_DATA \
"<value><array><data>\r\n" \
"<value><i4>2147483647</i4></value>\r\n" \
"<value><i4>-2147483648</i4></value>\r\n" \
"<value><boolean>0</boolean></value>\r\n" \
"<value><boolean>1</boolean></value>\r\n" \
"<value><string>Hello, world! &lt;&amp;&gt;</string></value>\r\n" \
"<value><base64>\r\n" \
"YmFzZTY0IGRhdGE=\r\n" \
"</base64></value>\r\n" \
"<value>" \
"<dateTime.iso8601>19980717T14:08:55</dateTime.iso8601>" \
"</value>\r\n" \
"<value><array><data>\r\n" \
"</data></array></value>\r\n" \
"</data></array></value>"
char const serialized_data[] = RAW_STRING_DATA;
char const serialized_call[] =
XML_PROLOGUE
"<methodCall>\r\n"
"<methodName>gloom&amp;doom</methodName>\r\n"
"<params>\r\n"
"<param><value><i4>10</i4></value></param>\r\n"
"<param><value><i4>20</i4></value></param>\r\n"
"</params>\r\n"
"</methodCall>\r\n";
char const serialized_response[] =
XML_PROLOGUE
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><i4>30</i4></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n";
char const serialized_fault[] =
XML_PROLOGUE
"<methodResponse>\r\n"
"<fault>\r\n"
"<value><struct>\r\n"
"<member><name>faultCode</name>\r\n"
"<value><i4>6</i4></value></member>\r\n"
"<member><name>faultString</name>\r\n"
"<value><string>A fault occurred</string></value></member>\r\n"
"</struct></value>\r\n"
"</fault>\r\n"
"</methodResponse>\r\n";
char const expat_data[] = XML_PROLOGUE RAW_STRING_DATA "\r\n";
char const expat_error_data[] =
XML_PROLOGUE \
"<foo><bar>abc</bar><baz></baz>\r\n";
char const correct_value[] =
XML_PROLOGUE
"<methodResponse><params><param>\r\n"
"<value><array><data>\r\n"
RAW_STRING_DATA "\r\n"
"<value><int>1</int></value>\r\n"
"<value><double>-1.0</double></value>\r\n"
"<value><double>0.0</double></value>\r\n"
"<value><double>1.0</double></value>\r\n"
"<value><struct>\r\n"
"<member><name>ten &lt;&amp;&gt;</name>\r\n"
"<value><i4>10</i4></value></member>\r\n"
"<member><name>twenty</name>\r\n"
"<value><i4>20</i4></value></member>\r\n"
"</struct></value>\r\n"
"<value>Untagged string</value>\r\n"
"</data></array></value>\r\n"
"</param></params></methodResponse>\r\n";
#define VALUE_HEADER \
XML_PROLOGUE"<methodResponse><params><param><value>\r\n"
#define VALUE_FOOTER \
"</value></param></params></methodResponse>\r\n"
#define MEMBER_HEADER \
VALUE_HEADER"<struct><member>"
#define MEMBER_FOOTER \
"</member></struct>"VALUE_FOOTER
#define ARBITRARY_VALUE \
"<value><i4>0</i4></value>"
char const unparseable_value[] = VALUE_HEADER"<i4>"VALUE_FOOTER;
const char * bad_values[] = {
VALUE_HEADER"<i4>0</i4><i4>0</i4>"VALUE_FOOTER,
VALUE_HEADER"<foo></foo>"VALUE_FOOTER,
VALUE_HEADER"<i4><i4>4</i4></i4>"VALUE_FOOTER,
VALUE_HEADER"<i4>2147483648</i4>"VALUE_FOOTER,
VALUE_HEADER"<i4>-2147483649</i4>"VALUE_FOOTER,
VALUE_HEADER"<i4> 0</i4>"VALUE_FOOTER,
VALUE_HEADER"<i4>0 </i4>"VALUE_FOOTER,
VALUE_HEADER"<boolean>2</boolean>"VALUE_FOOTER,
VALUE_HEADER"<boolean>-1</boolean>"VALUE_FOOTER,
VALUE_HEADER"<double> 0.0</double>"VALUE_FOOTER,
VALUE_HEADER"<double>0.0 </double>"VALUE_FOOTER,
VALUE_HEADER"<array></array>"VALUE_FOOTER,
VALUE_HEADER"<array><data></data><data></data></array>"VALUE_FOOTER,
VALUE_HEADER"<array><data></data><data></data></array>"VALUE_FOOTER,
VALUE_HEADER"<array><data><foo></foo></data></array>"VALUE_FOOTER,
VALUE_HEADER"<struct><foo></foo></struct>"VALUE_FOOTER,
MEMBER_HEADER MEMBER_FOOTER,
MEMBER_HEADER"<name>a</name>"MEMBER_FOOTER,
MEMBER_HEADER"<name>a</name>"ARBITRARY_VALUE"<f></f>"MEMBER_FOOTER,
MEMBER_HEADER"<foo></foo>"ARBITRARY_VALUE MEMBER_FOOTER,
MEMBER_HEADER"<name>a</name><foo></foo>"MEMBER_FOOTER,
MEMBER_HEADER"<name><foo></foo></name>"ARBITRARY_VALUE MEMBER_FOOTER,
NULL
};
#define RESPONSE_HEADER \
XML_PROLOGUE"<methodResponse>\r\n"
#define RESPONSE_FOOTER \
"</methodResponse>\r\n"
#define PARAMS_RESP_HEADER \
RESPONSE_HEADER"<params>"
#define PARAMS_RESP_FOOTER \
"</params>"RESPONSE_FOOTER
#define FAULT_HEADER \
RESPONSE_HEADER"<fault>"
#define FAULT_FOOTER \
"</fault>"RESPONSE_FOOTER
#define FAULT_STRUCT_HEADER \
FAULT_HEADER"<value><struct>"
#define FAULT_STRUCT_FOOTER \
"</struct></value>"FAULT_FOOTER
const char * bad_responses[] = {
XML_PROLOGUE"<foo></foo>\r\n",
RESPONSE_HEADER RESPONSE_FOOTER,
RESPONSE_HEADER"<params></params><params></params>"RESPONSE_FOOTER,
RESPONSE_HEADER"<foo></foo>"RESPONSE_FOOTER,
/* Make sure we insist on only one parameter in a response. */
PARAMS_RESP_HEADER PARAMS_RESP_FOOTER,
PARAMS_RESP_HEADER
"<param><i4>0</i4></param>"
"<param><i4>0</i4></param>"
PARAMS_RESP_FOOTER,
/* Test other sorts of bad parameters. */
PARAMS_RESP_HEADER"<foo></foo>"PARAMS_RESP_FOOTER,
PARAMS_RESP_HEADER"<param></param>"PARAMS_RESP_FOOTER,
PARAMS_RESP_HEADER"<param><foo></foo></param>"PARAMS_RESP_FOOTER,
PARAMS_RESP_HEADER
"<param>"ARBITRARY_VALUE ARBITRARY_VALUE"</param>"
PARAMS_RESP_FOOTER,
/* Basic fault tests. */
FAULT_HEADER FAULT_FOOTER,
FAULT_HEADER"<foo></foo>"FAULT_FOOTER,
FAULT_HEADER"<value></value><value></value>"FAULT_FOOTER,
FAULT_HEADER"<value><i4>1</i4></value>"FAULT_FOOTER,
/* Make sure we insist on the proper members within the fault struct. */
FAULT_STRUCT_HEADER
"<member><name>faultString</name>"
"<value><string>foo</string></value></member>"
FAULT_STRUCT_FOOTER,
FAULT_STRUCT_HEADER
"<member><name>faultCode</name>"
"<value><i4>0</i4></value></member>"
FAULT_STRUCT_FOOTER,
FAULT_STRUCT_HEADER
"<member><name>faultCode</name>"
"<value><i4>0</i4></value></member>"
"<member><name>faultString</name>"
"<value><i4>0</i4></value></member>"
FAULT_STRUCT_FOOTER,
FAULT_STRUCT_HEADER
"<member><name>faultCode</name>"
"<value><string>0</string></value></member>"
"<member><name>faultString</name>"
"<value><string>foo</string></value></member>"
FAULT_STRUCT_FOOTER,
NULL};
#define CALL_HEADER \
XML_PROLOGUE"<methodCall>\r\n"
#define CALL_FOOTER \
"</methodCall>\r\n"
const char * bad_calls[] = {
XML_PROLOGUE"<foo></foo>\r\n",
CALL_HEADER CALL_FOOTER,
CALL_HEADER"<methodName>m</methodName><foo></foo>"CALL_FOOTER,
CALL_HEADER"<foo></foo><params></params>"CALL_FOOTER,
CALL_HEADER"<methodName><f></f></methodName><params></params>"CALL_FOOTER,
NULL};
+20
View File
@@ -0,0 +1,20 @@
extern char const serialized_data[];
extern char const serialized_call[];
extern char const serialized_response[];
extern char const serialized_fault[];
extern char const expat_data[];
extern char const expat_error_data[];
extern char const correct_value[];
extern char const unparseable_value[];
extern const char *(bad_values[]);
extern const char *(bad_responses[]);
extern const char *(bad_calls[]);
+225
View File
@@ -0,0 +1,225 @@
/* 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-c/base.h"
#include "xmlrpc-c/base_int.h"
void
xmlrpc_abort_if_array_bad(xmlrpc_value * const arrayP) {
if (arrayP == NULL)
abort();
else if (arrayP->_type != XMLRPC_TYPE_ARRAY)
abort();
else {
unsigned int const arraySize =
XMLRPC_MEMBLOCK_SIZE(xmlrpc_value*, &arrayP->_block);
xmlrpc_value ** const contents =
XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
if (contents == NULL)
abort();
else {
unsigned int index;
for (index = 0; index < arraySize; ++index) {
xmlrpc_value * const itemP = contents[index];
if (itemP == NULL)
abort();
else if (itemP->_refcount < 1)
abort();
}
}
}
}
void
xmlrpc_destroyArrayContents(xmlrpc_value * const arrayP) {
/*----------------------------------------------------------------------------
Dispose of the contents of an array (but not the array value itself).
The value is not valid after this.
-----------------------------------------------------------------------------*/
unsigned int const arraySize =
XMLRPC_MEMBLOCK_SIZE(xmlrpc_value*, &arrayP->_block);
xmlrpc_value ** const contents =
XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
unsigned int index;
XMLRPC_ASSERT_ARRAY_OK(arrayP);
/* Release our reference to each item in the array */
for (index = 0; index < arraySize; ++index) {
xmlrpc_value * const itemP = contents[index];
xmlrpc_DECREF(itemP);
}
XMLRPC_MEMBLOCK_CLEAN(xmlrpc_value *, &arrayP->_block);
}
int
xmlrpc_array_size(xmlrpc_env * const env,
const xmlrpc_value * const array) {
int retval;
/* Suppress a compiler warning about uninitialized variables. */
retval = 0;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_VALUE_OK(array);
XMLRPC_TYPE_CHECK(env, array, XMLRPC_TYPE_ARRAY);
retval = XMLRPC_TYPED_MEM_BLOCK_SIZE(xmlrpc_value*, &array->_block);
cleanup:
if (env->fault_occurred)
return -1;
else
return retval;
}
void
xmlrpc_array_append_item(xmlrpc_env * const envP,
xmlrpc_value * const arrayP,
xmlrpc_value * const valueP) {
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(arrayP);
if (xmlrpc_value_type(arrayP) != XMLRPC_TYPE_ARRAY)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value is not an array");
else {
size_t const size =
XMLRPC_MEMBLOCK_SIZE(xmlrpc_value *, &arrayP->_block);
XMLRPC_MEMBLOCK_RESIZE(xmlrpc_value *, envP, &arrayP->_block, size+1);
if (!envP->fault_occurred) {
xmlrpc_value ** const contents =
XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value*, &arrayP->_block);
xmlrpc_INCREF(valueP);
contents[size] = valueP;
}
}
}
void
xmlrpc_array_read_item(xmlrpc_env * const envP,
const xmlrpc_value * const arrayP,
unsigned int const index,
xmlrpc_value ** const valuePP) {
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(arrayP);
XMLRPC_ASSERT_PTR_OK(valuePP);
if (arrayP->_type != XMLRPC_TYPE_ARRAY)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Attempt to read array item from "
"a value that is not an array");
else {
xmlrpc_value ** const contents =
XMLRPC_MEMBLOCK_CONTENTS(xmlrpc_value *, &arrayP->_block);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(xmlrpc_value *, &arrayP->_block);
if (index >= size)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "Array index %u is beyond end "
"of %u-item array", index, (unsigned int)size);
else {
*valuePP = contents[index];
xmlrpc_INCREF(*valuePP);
}
}
}
xmlrpc_value *
xmlrpc_array_get_item(xmlrpc_env * const envP,
const xmlrpc_value * const arrayP,
int const index) {
xmlrpc_value * valueP;
if (index < 0)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "Index %d is negative.", index);
else {
xmlrpc_array_read_item(envP, arrayP, index, &valueP);
if (!envP->fault_occurred)
xmlrpc_DECREF(valueP);
}
if (envP->fault_occurred)
valueP = NULL;
return valueP;
}
xmlrpc_value *
xmlrpc_array_new(xmlrpc_env * const envP) {
/*----------------------------------------------------------------------------
Create an empty array xmlrpc_value.
-----------------------------------------------------------------------------*/
xmlrpc_value * arrayP;
xmlrpc_createXmlrpcValue(envP, &arrayP);
if (!envP->fault_occurred) {
arrayP->_type = XMLRPC_TYPE_ARRAY;
XMLRPC_MEMBLOCK_INIT(xmlrpc_value*, envP, &arrayP->_block, 0);
if (envP->fault_occurred)
free(arrayP);
}
return arrayP;
}
/* 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. */
+85
View File
@@ -0,0 +1,85 @@
/* 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 "mallocvar.h"
#include "xmlrpc-c/base.h"
/*****************************************************************************
I don't see how these were expected to be used. And I probably
broke it somehow at some point by removing code from somewhere else.
But I doubt that, whatever it's supposed to do, environment
variables are the right tool.
Note that on a platform that doesn't have SETENV,
xmlrpc_authcookie_set() is just a no-op.
-Bryan 2005.06.10
****************************************************************************/
void
xmlrpc_authcookie_set(xmlrpc_env * const envP,
const char * const username,
const char * const password) {
char * unencoded;
xmlrpc_mem_block * token;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_PTR_OK(username);
XMLRPC_ASSERT_PTR_OK(password);
/* Create unencoded string/hash. */
MALLOCARRAY(unencoded,(strlen(username) + strlen(password) + 1 + 1));
sprintf(unencoded, "%s:%s", username, password);
/* Create encoded string. */
token = xmlrpc_base64_encode_without_newlines(
envP, (unsigned char *)unencoded, strlen(unencoded));
if (!envP->fault_occurred) {
/* Set HTTP_COOKIE_AUTH to the character representation of the
encoded string.
*/
#ifdef HAVE_SETENV
setenv("HTTP_COOKIE_AUTH",
XMLRPC_MEMBLOCK_CONTENTS(char, token),
1);
#endif
xmlrpc_mem_block_free(token);
}
free(unencoded);
}
char *xmlrpc_authcookie ( void ) {
return getenv("HTTP_COOKIE_AUTH");
}
+266
View File
@@ -0,0 +1,266 @@
/* 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-c/base.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 * const env,
const char * const ascii_data,
size_t const 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;
const char * next_char;
size_t remaining_len;
/* 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 (remaining_len = ascii_len, next_char = ascii_data;
remaining_len > 0;
--remaining_len, ++next_char) {
/* Skip some punctuation. */
this_ch = (*next_char & 0x7f);
if ( this_ch == '\r' || this_ch == '\n' || this_ch == ' ' )
continue;
if ( this_ch == BASE64_PAD )
npad++;
this_ch = table_a2b_base64[(*next_char) & 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;
}
+969
View File
@@ -0,0 +1,969 @@
/* Copyright information is at end of file */
#include "xmlrpc_config.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "bool.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
/* Borrowed from Python 1.5.2.
** MPW pushes 'extended' for float and double types with varargs */
#ifdef MPW
typedef extended va_double;
#else
typedef double va_double;
#endif
/* Borrowed from Python 1.5.2.
** Python copies its va_list objects before using them in certain
** tricky fashions. We don't why Python does this, but since we're
** abusing our va_list objects in a similar fashion, we'll copy them
** too. */
#if VA_LIST_IS_ARRAY
#define VA_LIST_COPY(dest,src) memcpy((dest), (src), sizeof(va_list))
#else
#define VA_LIST_COPY(dest,src) ((dest) = (src))
#endif
/*=========================================================================
** Creating XML-RPC values.
**=========================================================================
** Build new XML-RPC values from a format string. This code is heavily
** inspired by Py_BuildValue from Python 1.5.2. In particular, our
** particular abuse of the va_list data type is copied from the equivalent
** Python code in modsupport.c. Since Python is portable, our code should
** (in theory) also be portable.
*/
static void
getString(xmlrpc_env * const envP,
const char ** const formatP,
va_list * const args,
xmlrpc_value ** const valPP) {
const char * str;
unsigned int len;
str = (const char*) va_arg(*args, char*);
if (**formatP == '#') {
(*formatP)++;
len = (size_t) va_arg(*args, size_t);
} else
len = strlen(str);
*valPP = xmlrpc_string_new_lp(envP, len, str);
}
#ifdef HAVE_UNICODE_WCHAR
static void
mkWideString(xmlrpc_env * const envP,
wchar_t * const wcs,
size_t const wcs_len,
xmlrpc_value ** const valPP) {
xmlrpc_value * valP;
char *contents;
wchar_t *wcs_contents;
int block_is_inited;
xmlrpc_mem_block *utf8_block;
char *utf8_contents;
size_t utf8_len;
/* Error-handling preconditions. */
valP = NULL;
utf8_block = NULL;
block_is_inited = 0;
/* Initialize our XML-RPC value. */
valP = (xmlrpc_value*) malloc(sizeof(xmlrpc_value));
XMLRPC_FAIL_IF_NULL(valP, envP, XMLRPC_INTERNAL_ERROR,
"Could not allocate memory for wide string");
valP->_refcount = 1;
valP->_type = XMLRPC_TYPE_STRING;
/* More error-handling preconditions. */
valP->_wcs_block = NULL;
/* Build our wchar_t block first. */
valP->_wcs_block =
XMLRPC_TYPED_MEM_BLOCK_NEW(wchar_t, envP, wcs_len + 1);
XMLRPC_FAIL_IF_FAULT(envP);
wcs_contents =
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, valP->_wcs_block);
memcpy(wcs_contents, wcs, wcs_len * sizeof(wchar_t));
wcs_contents[wcs_len] = '\0';
/* Convert the wcs block to UTF-8. */
utf8_block = xmlrpc_wcs_to_utf8(envP, wcs_contents, wcs_len + 1);
XMLRPC_FAIL_IF_FAULT(envP);
utf8_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, utf8_block);
utf8_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, utf8_block);
/* XXX - We need an extra memcopy to initialize _block. */
XMLRPC_TYPED_MEM_BLOCK_INIT(char, envP, &valP->_block, utf8_len);
XMLRPC_FAIL_IF_FAULT(envP);
block_is_inited = 1;
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
memcpy(contents, utf8_contents, utf8_len);
cleanup:
if (utf8_block)
xmlrpc_mem_block_free(utf8_block);
if (envP->fault_occurred) {
if (valP) {
if (valP->_wcs_block)
xmlrpc_mem_block_free(valP->_wcs_block);
if (block_is_inited)
xmlrpc_mem_block_clean(&valP->_block);
free(valP);
}
}
*valPP = valP;
}
#endif /* HAVE_UNICODE_WCHAR */
static void
getWideString(xmlrpc_env * const envP,
const char ** const formatP,
va_list * const args,
xmlrpc_value ** const valPP) {
#ifdef HAVE_UNICODE_WCHAR
wchar_t *wcs;
size_t len;
wcs = (wchar_t*) va_arg(*args, wchar_t*);
if (**formatP == '#') {
(*formatP)++;
len = (size_t) va_arg(*args, size_t);
} else
len = wcslen(wcs);
mkWideString(envP, wcs, len, valPP);
#endif /* HAVE_UNICODE_WCHAR */
}
static void
getBase64(xmlrpc_env * const envP,
va_list * const args,
xmlrpc_value ** const valPP) {
unsigned char * value;
size_t length;
value = (unsigned char*) va_arg(*args, unsigned char*);
length = (size_t) va_arg(*args, size_t);
*valPP = xmlrpc_base64_new(envP, length, value);
}
static void
getValue(xmlrpc_env * const envP,
const char** const format,
va_list * args,
xmlrpc_value ** const valPP);
static void
getArray(xmlrpc_env * const envP,
const char ** const formatP,
char const delimiter,
va_list * const args,
xmlrpc_value ** const arrayPP) {
xmlrpc_value * arrayP;
arrayP = xmlrpc_array_new(envP);
/* Add items to the array until we hit our delimiter. */
while (**formatP != delimiter && !envP->fault_occurred) {
xmlrpc_value * itemP;
if (**formatP == '\0')
xmlrpc_env_set_fault(
envP, XMLRPC_INTERNAL_ERROR,
"format string ended before closing ')'.");
else {
getValue(envP, formatP, args, &itemP);
if (!envP->fault_occurred) {
xmlrpc_array_append_item(envP, arrayP, itemP);
xmlrpc_DECREF(itemP);
}
}
}
if (envP->fault_occurred)
xmlrpc_DECREF(arrayP);
*arrayPP = arrayP;
}
static void
getStructMember(xmlrpc_env * const envP,
const char ** const formatP,
va_list * const args,
xmlrpc_value ** const keyPP,
xmlrpc_value ** const valuePP) {
/* Get the key */
getValue(envP, formatP, args, keyPP);
if (!envP->fault_occurred) {
if (**formatP != ':')
xmlrpc_env_set_fault(
envP, XMLRPC_INTERNAL_ERROR,
"format string does not have ':' after a "
"structure member key.");
else {
/* Skip over colon that separates key from value */
(*formatP)++;
/* Get the value */
getValue(envP, formatP, args, valuePP);
}
if (envP->fault_occurred)
xmlrpc_DECREF(*keyPP);
}
}
static void
getStruct(xmlrpc_env * const envP,
const char ** const formatP,
char const delimiter,
va_list * const args,
xmlrpc_value ** const structPP) {
xmlrpc_value * structP;
structP = xmlrpc_struct_new(envP);
if (!envP->fault_occurred) {
while (**formatP != delimiter && !envP->fault_occurred) {
xmlrpc_value * keyP;
xmlrpc_value * valueP;
getStructMember(envP, formatP, args, &keyP, &valueP);
if (!envP->fault_occurred) {
if (**formatP == ',')
(*formatP)++; /* Skip over the comma */
else if (**formatP == delimiter) {
/* End of the line */
} else
xmlrpc_env_set_fault(
envP, XMLRPC_INTERNAL_ERROR,
"format string does not have ',' or ')' after "
"a structure member");
if (!envP->fault_occurred)
/* Add the new member to the struct. */
xmlrpc_struct_set_value_v(envP, structP, keyP, valueP);
xmlrpc_DECREF(valueP);
xmlrpc_DECREF(keyP);
}
}
if (envP->fault_occurred)
xmlrpc_DECREF(structP);
}
*structPP = structP;
}
static void
mkArrayFromVal(xmlrpc_env * const envP,
xmlrpc_value * const value,
xmlrpc_value ** const valPP) {
if (xmlrpc_value_type(value) != XMLRPC_TYPE_ARRAY)
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
"Array format ('A'), non-array xmlrpc_value");
else
xmlrpc_INCREF(value);
*valPP = value;
}
static void
mkStructFromVal(xmlrpc_env * const envP,
xmlrpc_value * const value,
xmlrpc_value ** const valPP) {
if (xmlrpc_value_type(value) != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
"Struct format ('S'), non-struct xmlrpc_value");
else
xmlrpc_INCREF(value);
*valPP = value;
}
static void
getValue(xmlrpc_env * const envP,
const char** const formatP,
va_list * const args,
xmlrpc_value ** const valPP) {
/*----------------------------------------------------------------------------
Get the next value from the list. *formatP points to the specifier
for the next value in the format string (i.e. to the type code
character) and we move *formatP past the whole specifier for the
next value. We read the required arguments from 'args'. We return
the value as *valPP with a reference to it.
For example, if *formatP points to the "i" in the string "sis",
we read one argument from 'args' and return as *valP an integer whose
value is the argument we read. We advance *formatP to point to the
last 's' and advance 'args' to point to the argument that belongs to
that 's'.
-----------------------------------------------------------------------------*/
char const formatChar = *(*formatP)++;
switch (formatChar) {
case 'i':
*valPP =
xmlrpc_int_new(envP, (xmlrpc_int32) va_arg(*args, xmlrpc_int32));
break;
case 'b':
*valPP =
xmlrpc_bool_new(envP, (xmlrpc_bool) va_arg(*args, xmlrpc_bool));
break;
case 'd':
*valPP =
xmlrpc_double_new(envP, (double) va_arg(*args, va_double));
break;
case 's':
getString(envP, formatP, args, valPP);
break;
case 'w':
getWideString(envP, formatP, args, valPP);
break;
/* The code 't' is reserved for a better, time_t based
implementation of dateTime conversion.
*/
case '8':
*valPP =
xmlrpc_datetime_new_str(envP, (char*) va_arg(*args, char*));
break;
case '6':
getBase64(envP, args, valPP);
break;
case 'n':
*valPP =
xmlrpc_nil_new(envP);
break;
case 'p':
/* We might someday want to use the code 'p!' to read in a
cleanup function for this pointer.
*/
*valPP =
xmlrpc_cptr_new(envP, (void*) va_arg(*args, void*));
break;
case 'A':
mkArrayFromVal(envP, (xmlrpc_value*) va_arg(*args, xmlrpc_value*),
valPP);
break;
case 'S':
mkStructFromVal(envP, (xmlrpc_value*) va_arg(*args, xmlrpc_value*),
valPP);
break;
case 'V':
*valPP = (xmlrpc_value*) va_arg(*args, xmlrpc_value*);
xmlrpc_INCREF(*valPP);
break;
case '(':
getArray(envP, formatP, ')', args, valPP);
if (!envP->fault_occurred) {
XMLRPC_ASSERT(**formatP == ')');
(*formatP)++; /* Skip over closing parenthesis */
}
break;
case '{':
getStruct(envP, formatP, '}', args, valPP);
if (!envP->fault_occurred) {
XMLRPC_ASSERT(**formatP == '}');
(*formatP)++; /* Skip over closing brace */
}
break;
default: {
const char * const badCharacter = xmlrpc_makePrintableChar(formatChar);
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Unexpected character '%s' in format string", badCharacter);
xmlrpc_strfree(badCharacter);
}
}
}
void
xmlrpc_build_value_va(xmlrpc_env * const envP,
const char * const format,
va_list args,
xmlrpc_value ** const valPP,
const char ** const tailP) {
const char * formatCursor;
va_list args_copy;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(format != NULL);
if (strlen(format) == 0)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Format string is empty.");
else {
formatCursor = &format[0];
VA_LIST_COPY(args_copy, args);
getValue(envP, &formatCursor, &args_copy, valPP);
if (!envP->fault_occurred)
XMLRPC_ASSERT_VALUE_OK(*valPP);
*tailP = formatCursor;
}
}
xmlrpc_value *
xmlrpc_build_value(xmlrpc_env * const envP,
const char * const format,
...) {
va_list args;
xmlrpc_value* retval;
const char * suffix;
va_start(args, format);
xmlrpc_build_value_va(envP, format, args, &retval, &suffix);
va_end(args);
if (!envP->fault_occurred) {
if (*suffix != '\0')
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
"specifier: '%s'. There must be exactly one arument.",
suffix);
if (envP->fault_occurred)
xmlrpc_DECREF(retval);
}
return retval;
}
/*=========================================================================
** Parsing XML-RPC values.
**=========================================================================
** Parse an XML-RPC value based on a format string. This code is heavily
** inspired by Py_BuildValue from Python 1.5.2.
*/
/* Prototype for recursive invocation: */
static void
decomposeValue(xmlrpc_env * const env,
xmlrpc_value * const val,
const char ** const format,
va_list * args,
xmlrpc_bool const oldstyleMemMgmt);
static void
parsearray(xmlrpc_env * const env,
const xmlrpc_value * const array,
const char ** const format,
char const delimiter,
va_list * args,
xmlrpc_bool const oldstyleMemMgmt) {
int size, i;
xmlrpc_value *item;
/* Fetch the array size. */
size = xmlrpc_array_size(env, array);
XMLRPC_FAIL_IF_FAULT(env);
/* Loop over the items in the array. */
for (i = 0; i < size; i++) {
/* Bail out if the caller didn't care about the rest of the items. */
if (**format == '*')
break;
item = xmlrpc_array_get_item(env, array, i);
XMLRPC_FAIL_IF_FAULT(env);
XMLRPC_ASSERT(**format != '\0');
if (**format == delimiter)
XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Too many items in array");
decomposeValue(env, item, format, args, oldstyleMemMgmt);
XMLRPC_FAIL_IF_FAULT(env);
}
if (**format == '*')
(*format)++;
if (**format != delimiter)
XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Not enough items in array");
cleanup:
return;
}
static void
parsestruct(xmlrpc_env * const env,
xmlrpc_value * const strct,
const char ** const format,
char const delimiter,
va_list * args,
xmlrpc_bool const oldstyleMemMgmt) {
xmlrpc_value *key, *value;
char *keystr;
size_t keylen;
/* Set up error handling preconditions. */
key = NULL;
/* Build the members of our struct. */
while (**format != '*' && **format != delimiter && **format != '\0') {
/* Get our key, and skip over the ':' character. Notice the
** sudden call to getValue--we're going in the opposite direction. */
getValue(env, format, args, &key);
XMLRPC_FAIL_IF_FAULT(env);
XMLRPC_ASSERT(**format == ':');
(*format)++;
/* Look up the value for our key. */
xmlrpc_parse_value(env, key, "s#", &keystr, &keylen);
XMLRPC_FAIL_IF_FAULT(env);
value = xmlrpc_struct_get_value_n(env, strct, keystr, keylen);
XMLRPC_FAIL_IF_FAULT(env);
/* Get our value, and skip over the ',' character (if present). */
decomposeValue(env, value, format, args, oldstyleMemMgmt);
XMLRPC_FAIL_IF_FAULT(env);
XMLRPC_ASSERT(**format == ',' || **format == delimiter);
if (**format == ',')
(*format)++;
/* Release our reference, and restore our invariant. */
xmlrpc_DECREF(key);
key = NULL;
}
if (**format == '*') {
(*format)++;
if (**format != delimiter && **format != '\0')
XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
"* can appear only at the end "
"of a structure format specifier");
} else {
/* Here we're supposed to fail if he didn't extract all the
members. But we don't know how to determine whether he
specified all the members, so we always fail.
*/
XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR, "You must specify '*' as the "
"last member of a structure in a format specifier "
"used for parsing an xmlrpc_value");
}
XMLRPC_ASSERT(**format == delimiter || **format == '\0');
cleanup:
if (key)
xmlrpc_DECREF(key);
}
static void
readString(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP,
xmlrpc_bool const oldstyleMemMgmt) {
if (oldstyleMemMgmt) {
xmlrpc_read_string_old(envP, valueP, stringValueP);
} else
xmlrpc_read_string(envP, valueP, stringValueP);
}
static void
readStringLp(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const char ** const stringValueP,
xmlrpc_bool const oldstyleMemMgmt) {
if (oldstyleMemMgmt) {
xmlrpc_read_string_lp_old(envP, valueP, lengthP, stringValueP);
} else
xmlrpc_read_string_lp(envP, valueP, lengthP, stringValueP);
}
#ifdef HAVE_UNICODE_WCHAR
static void
readStringW(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
const wchar_t ** const stringValueP,
xmlrpc_bool const oldstyleMemMgmt) {
if (oldstyleMemMgmt) {
xmlrpc_read_string_w_old(envP, valueP, stringValueP);
} else
xmlrpc_read_string_w(envP, valueP, stringValueP);
}
static void
readStringWLp(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
size_t * const lengthP,
const wchar_t ** const stringValueP,
xmlrpc_bool const oldstyleMemMgmt) {
if (oldstyleMemMgmt) {
xmlrpc_read_string_w_lp_old(envP, valueP, lengthP, stringValueP);
} else
xmlrpc_read_string_w_lp(envP, valueP, lengthP, stringValueP);
}
#endif
static void
readDatetimeStr(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP,
xmlrpc_bool const oldstyleMemMgmt) {
if (oldstyleMemMgmt)
xmlrpc_read_datetime_str_old(envP, valueP, stringValueP);
else
xmlrpc_read_datetime_str(envP, valueP, stringValueP);
}
static void
readBase64(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const unsigned char ** const byteStringValueP,
xmlrpc_bool const oldstyleMemMgmt) {
if (oldstyleMemMgmt)
xmlrpc_read_base64_old(envP, valueP, lengthP, byteStringValueP);
else
xmlrpc_read_base64(envP, valueP, lengthP, byteStringValueP);
}
static void
decomposeValue(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
const char ** const format,
va_list * args,
xmlrpc_bool const oldstyleMemMgmt) {
char formatSpecChar;
formatSpecChar = *(*format)++;
switch (formatSpecChar) {
case 'i': {
xmlrpc_int32 * const int32ptr =
(xmlrpc_int32*) va_arg(*args, xmlrpc_int32*);
xmlrpc_read_int(envP, valueP, int32ptr);
}
break;
case 'b': {
xmlrpc_bool * const boolptr =
(xmlrpc_bool*) va_arg(*args, xmlrpc_bool*);
xmlrpc_read_bool(envP, valueP, boolptr);
}
break;
case 'd': {
double * const doubleptr = (double*) va_arg(*args, double*);
xmlrpc_read_double(envP, valueP, doubleptr);
}
break;
case '8': {
/* The code 't' is reserved for a better, time_t based
implementation of dateTime conversion.
*/
const char ** const strptr = (const char**) va_arg(*args, char**);
readDatetimeStr(envP, valueP, strptr, oldstyleMemMgmt);
}
break;
case 's': {
const char ** const strptr = (const char**) va_arg(*args, char**);
if (**format == '#') {
size_t * const sizeptr = (size_t*) va_arg(*args, size_t**);
(*format)++;
readStringLp(envP, valueP, sizeptr, strptr, oldstyleMemMgmt);
} else
readString(envP, valueP, strptr, oldstyleMemMgmt);
}
break;
case 'w': {
#ifdef HAVE_UNICODE_WCHAR
const wchar_t ** const wcsptr =
(const wchar_t**) va_arg(*args, wchar_t**);
if (**format == '#') {
size_t * const sizeptr = (size_t*) va_arg(*args, size_t**);
(*format)++;
readStringWLp(envP, valueP, sizeptr, wcsptr, oldstyleMemMgmt);
} else
readStringW(envP, valueP, wcsptr, oldstyleMemMgmt);
#else
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"This XML-RPC For C/C++ library was built without Unicode "
"wide character capability. 'w' isn't available.");
#endif /* HAVE_UNICODE_WCHAR */
}
break;
case '6': {
const unsigned char ** const binptr =
(const unsigned char**) va_arg(*args, unsigned char**);
size_t * const sizeptr = (size_t*) va_arg(*args, size_t**);
readBase64(envP, valueP, sizeptr, binptr, oldstyleMemMgmt);
}
break;
case 'n': {
xmlrpc_read_nil(envP, valueP);
}
break;
case 'p': {
void ** const voidptrptr = (void**) va_arg(*args, void**);
xmlrpc_read_cptr(envP, valueP, voidptrptr);
}
break;
case 'V': {
xmlrpc_value ** const valptr =
(xmlrpc_value**) va_arg(*args, xmlrpc_value**);
*valptr = valueP;
if (!oldstyleMemMgmt)
xmlrpc_INCREF(valueP);
}
break;
case 'A':
if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_ARRAY)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Non-array type supplied for "
"'A' specifier");
else {
xmlrpc_value ** const valptr =
(xmlrpc_value**) va_arg(*args, xmlrpc_value**);
*valptr = valueP;
if (!oldstyleMemMgmt)
xmlrpc_INCREF(valueP);
}
break;
case 'S':
if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Non-struct type supplied for "
"'S' specifier");
else {
xmlrpc_value ** const valptr =
(xmlrpc_value**) va_arg(*args, xmlrpc_value**);
*valptr = valueP;
if (!oldstyleMemMgmt)
xmlrpc_INCREF(valueP);
}
break;
case '(':
if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_ARRAY)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Non-array type supplied for "
"'()' specifier");
else {
parsearray(envP, valueP, format, ')', args, oldstyleMemMgmt);
(*format)++;
}
break;
case '{':
if (xmlrpc_value_type(valueP) != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Non-struct type supplied for "
"'{}' specifier");
else {
parsestruct(envP, valueP, format, '}', args, oldstyleMemMgmt);
(*format)++;
}
break;
default:
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Invalid format character '%c'",
formatSpecChar);
}
}
void
xmlrpc_decompose_value_va(xmlrpc_env * const envP,
xmlrpc_value * const value,
const char * const format,
va_list args) {
const char *format_copy;
va_list args_copy;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(value);
XMLRPC_ASSERT(format != NULL);
format_copy = format;
VA_LIST_COPY(args_copy, args);
decomposeValue(envP, value, &format_copy, &args_copy, FALSE);
if (!envP->fault_occurred) {
XMLRPC_ASSERT(*format_copy == '\0');
}
}
void
xmlrpc_decompose_value(xmlrpc_env * const envP,
xmlrpc_value * const value,
const char * const format,
...) {
va_list args;
va_start(args, format);
xmlrpc_decompose_value_va(envP, value, format, args);
va_end(args);
}
void
xmlrpc_parse_value_va(xmlrpc_env * const envP,
xmlrpc_value * const value,
const char * const format,
va_list args) {
const char *format_copy;
va_list args_copy;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(value);
XMLRPC_ASSERT(format != NULL);
format_copy = format;
VA_LIST_COPY(args_copy, args);
decomposeValue(envP, value, &format_copy, &args_copy, TRUE);
if (!envP->fault_occurred) {
XMLRPC_ASSERT(*format_copy == '\0');
}
}
void
xmlrpc_parse_value(xmlrpc_env * const envP,
xmlrpc_value * const value,
const char * const format,
...) {
va_list args;
va_start(args, format);
xmlrpc_parse_value_va(envP, value, format, args);
va_end(args);
}
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
** Copyright (C) 2001 by Eric Kidd. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
File diff suppressed because it is too large. Load diff
+861
View File
@@ -0,0 +1,861 @@
/* Copyright information is at end of file */
#include "xmlrpc_config.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "bool.h"
#include "mallocvar.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
static void
destroyValue(xmlrpc_value * const valueP) {
/* First, we need to destroy this value's contents, if any. */
switch (valueP->_type) {
case XMLRPC_TYPE_INT:
break;
case XMLRPC_TYPE_BOOL:
break;
case XMLRPC_TYPE_DOUBLE:
break;
case XMLRPC_TYPE_DATETIME:
xmlrpc_mem_block_clean(&valueP->_block);
break;
case XMLRPC_TYPE_STRING:
#ifdef HAVE_UNICODE_WCHAR
if (valueP->_wcs_block)
xmlrpc_mem_block_free(valueP->_wcs_block);
#endif /* HAVE_UNICODE_WCHAR */
xmlrpc_mem_block_clean(&valueP->_block);
break;
case XMLRPC_TYPE_BASE64:
xmlrpc_mem_block_clean(&valueP->_block);
break;
case XMLRPC_TYPE_ARRAY:
xmlrpc_destroyArrayContents(valueP);
break;
case XMLRPC_TYPE_STRUCT:
xmlrpc_destroyStruct(valueP);
break;
case XMLRPC_TYPE_C_PTR:
break;
case XMLRPC_TYPE_NIL:
break;
case XMLRPC_TYPE_DEAD:
XMLRPC_ASSERT(FALSE); /* Can't happen, per entry conditions */
default:
XMLRPC_ASSERT(FALSE); /* There are no other possible values */
}
/* Next, we mark this value as invalid, to help catch refcount
** errors. */
valueP->_type = XMLRPC_TYPE_DEAD;
/* Finally, we destroy the value itself. */
free(valueP);
}
/*=========================================================================
** Reference Counting
**=========================================================================
** Some simple reference-counting code. The xmlrpc_DECREF routine is in
** charge of destroying values when their reference count equals zero.
*/
void
xmlrpc_INCREF (xmlrpc_value * const valueP) {
XMLRPC_ASSERT_VALUE_OK(valueP);
XMLRPC_ASSERT(valueP->_refcount > 0);
++valueP->_refcount;
}
void
xmlrpc_DECREF (xmlrpc_value * const valueP) {
XMLRPC_ASSERT_VALUE_OK(valueP);
XMLRPC_ASSERT(valueP->_refcount > 0);
XMLRPC_ASSERT(valueP->_type != XMLRPC_TYPE_DEAD);
valueP->_refcount--;
/* If we have no more refs, we need to deallocate this value. */
if (valueP->_refcount == 0)
destroyValue(valueP);
}
/*=========================================================================
Utiltiies
=========================================================================*/
const char *
xmlrpc_typeName(xmlrpc_type const type) {
switch(type) {
case XMLRPC_TYPE_INT: return "INT";
case XMLRPC_TYPE_BOOL: return "BOOL";
case XMLRPC_TYPE_DOUBLE: return "DOUBLE";
case XMLRPC_TYPE_DATETIME: return "DATETIME";
case XMLRPC_TYPE_STRING: return "STRING";
case XMLRPC_TYPE_BASE64: return "BASE64";
case XMLRPC_TYPE_ARRAY: return "ARRAY";
case XMLRPC_TYPE_STRUCT: return "STRUCT";
case XMLRPC_TYPE_C_PTR: return "C_PTR";
case XMLRPC_TYPE_NIL: return "NIL";
case XMLRPC_TYPE_DEAD: return "DEAD";
default: return "???";
}
}
static void
verifyNoNulls(xmlrpc_env * const envP,
const char * const contents,
unsigned int const len) {
/*----------------------------------------------------------------------------
Verify that the character array 'contents', which is 'len' bytes long,
does not contain any NUL characters, which means it can be made into
a passable ASCIIZ string just by adding a terminating NUL.
Fail if the array contains a NUL.
-----------------------------------------------------------------------------*/
unsigned int i;
for (i = 0; i < len && !envP->fault_occurred; i++)
if (contents[i] == '\0')
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR,
"String must not contain NUL characters");
}
#ifdef HAVE_UNICODE_WCHAR
static void
verifyNoNullsW(xmlrpc_env * const envP,
const wchar_t * const contents,
unsigned int const len) {
/*----------------------------------------------------------------------------
Same as verifyNoNulls(), but for wide characters.
-----------------------------------------------------------------------------*/
unsigned int i;
for (i = 0; i < len && !envP->fault_occurred; i++)
if (contents[i] == '\0')
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR,
"String must not contain NUL characters");
}
#endif
static void
validateType(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
xmlrpc_type const expectedType) {
if (valueP->_type != expectedType) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
"type %s was expected.",
xmlrpc_typeName(valueP->_type), xmlrpc_typeName(expectedType));
}
}
/*=========================================================================
Extracting XML-RPC value
===========================================================================
These routines extract XML-RPC values into ordinary C data types.
For array and struct values, see the separates files xmlrpc_array.c
and xmlrpc_struct.c.
=========================================================================*/
void
xmlrpc_read_int(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
xmlrpc_int32 * const intValueP) {
validateType(envP, valueP, XMLRPC_TYPE_INT);
if (!envP->fault_occurred)
*intValueP = valueP->_value.i;
}
void
xmlrpc_read_bool(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
xmlrpc_bool * const boolValueP) {
validateType(envP, valueP, XMLRPC_TYPE_BOOL);
if (!envP->fault_occurred)
*boolValueP = valueP->_value.b;
}
void
xmlrpc_read_double(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
xmlrpc_double * const doubleValueP) {
validateType(envP, valueP, XMLRPC_TYPE_DOUBLE);
if (!envP->fault_occurred)
*doubleValueP = valueP->_value.d;
}
/* datetime stuff is in xmlrpc_datetime.c */
static void
accessStringValue(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const char ** const contentsP) {
validateType(envP, valueP, XMLRPC_TYPE_STRING);
if (!envP->fault_occurred) {
unsigned int const size =
XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
unsigned int const len = size - 1;
/* The memblock has a null character added to the end */
verifyNoNulls(envP, contents, len);
*lengthP = len;
*contentsP = contents;
}
}
void
xmlrpc_read_string(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP) {
/*----------------------------------------------------------------------------
Read the value of an XML-RPC string as an ASCIIZ string.
Return the string in newly malloc'ed storage that Caller must free.
Fail if the string contains null characters (which means it wasn't
really a string, but XML-RPC doesn't seem to understand what a string
is, and such values are possible).
-----------------------------------------------------------------------------*/
size_t length;
const char * contents;
accessStringValue(envP, valueP, &length, &contents);
if (!envP->fault_occurred) {
char * stringValue;
stringValue = malloc(length+1);
if (stringValue == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate space "
"for %u-character string", length);
else {
memcpy(stringValue, contents, length);
stringValue[length] = '\0';
*stringValueP = stringValue;
}
}
}
void
xmlrpc_read_string_old(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP) {
size_t length;
accessStringValue(envP, valueP, &length, stringValueP);
}
void
xmlrpc_read_string_lp(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const char ** const stringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_STRING);
if (!envP->fault_occurred) {
unsigned int const size =
XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
char * stringValue;
stringValue = malloc(size);
if (stringValue == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate %u bytes "
"for string.", size);
else {
memcpy(stringValue, contents, size);
*stringValueP = stringValue;
*lengthP = size - 1; /* Size includes terminating NUL */
}
}
}
void
xmlrpc_read_string_lp_old(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const char ** const stringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_STRING);
if (!envP->fault_occurred) {
*lengthP = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block) - 1;
*stringValueP = XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
}
}
#ifdef HAVE_UNICODE_WCHAR
static void
setupWcsBlock(xmlrpc_env * const envP,
xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
Add a wcs block (wchar_t string) to the indicated xmlrpc_value if it
doesn't have one already.
-----------------------------------------------------------------------------*/
if (!valueP->_wcs_block) {
char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
size_t const len =
XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block) - 1;
valueP->_wcs_block =
xmlrpc_utf8_to_wcs(envP, contents, len + 1);
}
}
static void
accessStringValueW(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
size_t * const lengthP,
const wchar_t ** const stringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_STRING);
if (!envP->fault_occurred) {
setupWcsBlock(envP, valueP);
if (!envP->fault_occurred) {
wchar_t * const wcontents =
XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);
size_t const len =
XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block) - 1;
verifyNoNullsW(envP, wcontents, len);
*lengthP = len;
*stringValueP = wcontents;
}
}
}
void
xmlrpc_read_string_w(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
const wchar_t ** const stringValueP) {
size_t length;
const wchar_t * wcontents;
accessStringValueW(envP, valueP, &length, &wcontents);
if (!envP->fault_occurred) {
wchar_t * stringValue;
stringValue = malloc((length + 1) * sizeof(wchar_t));
if (stringValue == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Unable to allocate space for %u-byte string",
length);
else {
memcpy(stringValue, wcontents, length * sizeof(wchar_t));
stringValue[length] = '\0';
*stringValueP = stringValue;
}
}
}
void
xmlrpc_read_string_w_old(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
const wchar_t ** const stringValueP) {
size_t length;
accessStringValueW(envP, valueP, &length, stringValueP);
}
void
xmlrpc_read_string_w_lp(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
size_t * const lengthP,
const wchar_t ** const stringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_STRING);
if (!envP->fault_occurred) {
setupWcsBlock(envP, valueP);
if (!envP->fault_occurred) {
wchar_t * const wcontents =
XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block);
wchar_t * stringValue;
stringValue = malloc(size * sizeof(wchar_t));
if (stringValue == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Unable to allocate space for %u-byte string",
size);
else {
memcpy(stringValue, wcontents, size * sizeof(wchar_t));
*lengthP = size - 1; /* size includes terminating NUL */
*stringValueP = stringValue;
}
}
}
}
void
xmlrpc_read_string_w_lp_old(xmlrpc_env * const envP,
xmlrpc_value * const valueP,
size_t * const lengthP,
const wchar_t ** const stringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_STRING);
if (!envP->fault_occurred) {
setupWcsBlock(envP, valueP);
if (!envP->fault_occurred) {
wchar_t * const wcontents =
XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valueP->_wcs_block);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(wchar_t, valueP->_wcs_block);
*lengthP = size - 1; /* size includes terminatnig NUL */
*stringValueP = wcontents;
}
}
}
#endif
void
xmlrpc_read_base64(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const unsigned char ** const byteStringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_BASE64);
if (!envP->fault_occurred) {
size_t const size =
XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
char * byteStringValue;
byteStringValue = malloc(size);
if (byteStringValue == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate %u bytes "
"for byte string.", size);
else {
memcpy(byteStringValue, contents, size);
*byteStringValueP = (const unsigned char *)byteStringValue;
*lengthP = size;
}
}
}
void
xmlrpc_read_base64_old(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP,
const unsigned char ** const byteStringValueP) {
validateType(envP, valueP, XMLRPC_TYPE_BASE64);
if (!envP->fault_occurred) {
*lengthP =
XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
*byteStringValueP = (const unsigned char *)
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
}
}
void
xmlrpc_read_base64_size(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
size_t * const lengthP) {
validateType(envP, valueP, XMLRPC_TYPE_BASE64);
if (!envP->fault_occurred)
*lengthP = XMLRPC_MEMBLOCK_SIZE(char, &valueP->_block);
}
void
xmlrpc_read_nil(xmlrpc_env * const envP,
xmlrpc_value * const valueP) {
/*----------------------------------------------------------------------------
Read out the value of a nil value. It doesn't have one, of course, so
this is essentially a no-op. But it does validate the type and is
necessary to match all the other types.
-----------------------------------------------------------------------------*/
validateType(envP, valueP, XMLRPC_TYPE_NIL);
}
void
xmlrpc_read_cptr(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
void ** const ptrValueP) {
validateType(envP, valueP, XMLRPC_TYPE_C_PTR);
if (!envP->fault_occurred)
*ptrValueP = valueP->_value.c_ptr;
}
xmlrpc_type xmlrpc_value_type (xmlrpc_value* value)
{
XMLRPC_ASSERT_VALUE_OK(value);
return value->_type;
}
void
xmlrpc_createXmlrpcValue(xmlrpc_env * const envP,
xmlrpc_value ** const valPP) {
/*----------------------------------------------------------------------------
Create a blank xmlrpc_value to be filled in.
Set the reference count to 1.
-----------------------------------------------------------------------------*/
xmlrpc_value * valP;
MALLOCVAR(valP);
if (!valP)
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
"Could not allocate memory for xmlrpc_value");
else
valP->_refcount = 1;
*valPP = valP;
}
xmlrpc_value *
xmlrpc_int_new(xmlrpc_env * const envP,
xmlrpc_int32 const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_INT;
valP->_value.i = value;
}
return valP;
}
xmlrpc_value *
xmlrpc_bool_new(xmlrpc_env * const envP,
xmlrpc_bool const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_BOOL;
valP->_value.b = value;
}
return valP;
}
xmlrpc_value *
xmlrpc_double_new(xmlrpc_env * const envP,
double const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_DOUBLE;
valP->_value.d = value;
}
return valP;
}
#ifdef HAVE_UNICODE_WCHAR
#define MAKE_WCS_BLOCK_NULL(val) ((val)->_wcs_block = NULL)
#else
#define MAKE_WCS_BLOCK_NULL(val) while (0) do {};
#endif
xmlrpc_value *
xmlrpc_string_new_lp(xmlrpc_env * const envP,
size_t const length,
const char * const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_STRING;
MAKE_WCS_BLOCK_NULL(valP);
XMLRPC_MEMBLOCK_INIT(char, envP, &valP->_block, length + 1);
if (!envP->fault_occurred) {
char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valP->_block);
memcpy(contents, value, length);
contents[length] = '\0';
}
if (envP->fault_occurred)
free(valP);
}
return valP;
}
xmlrpc_value *
xmlrpc_string_new(xmlrpc_env * const envP,
const char * const value) {
return xmlrpc_string_new_lp(envP, strlen(value), value);
}
#ifdef HAVE_UNICODE_WCHAR
xmlrpc_value *
xmlrpc_string_w_new_lp(xmlrpc_env * const envP,
size_t const length,
const wchar_t * const value) {
xmlrpc_value * valP;
/* Initialize our XML-RPC value. */
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_STRING;
/* Build our wchar_t block first. */
valP->_wcs_block =
XMLRPC_MEMBLOCK_NEW(wchar_t, envP, length + 1);
if (!envP->fault_occurred) {
wchar_t * const wcs_contents =
XMLRPC_MEMBLOCK_CONTENTS(wchar_t, valP->_wcs_block);
xmlrpc_mem_block * utf8_block;
memcpy(wcs_contents, value, length * sizeof(wchar_t));
wcs_contents[length] = '\0';
/* Convert the wcs block to UTF-8. */
utf8_block = xmlrpc_wcs_to_utf8(envP, wcs_contents, length + 1);
if (!envP->fault_occurred) {
char * const utf8_contents =
XMLRPC_MEMBLOCK_CONTENTS(char, utf8_block);
size_t const utf8_len = XMLRPC_MEMBLOCK_SIZE(char, utf8_block);
/* XXX - We need an extra memcopy to initialize _block. */
XMLRPC_MEMBLOCK_INIT(char, envP, &valP->_block, utf8_len);
if (!envP->fault_occurred) {
char * contents;
contents = XMLRPC_MEMBLOCK_CONTENTS(char, &valP->_block);
memcpy(contents, utf8_contents, utf8_len);
}
XMLRPC_MEMBLOCK_FREE(char, utf8_block);
}
if (envP->fault_occurred)
XMLRPC_MEMBLOCK_FREE(wchar_t, valP->_wcs_block);
}
if (envP->fault_occurred)
free(valP);
}
return valP;
}
xmlrpc_value *
xmlrpc_string_w_new(xmlrpc_env * const envP,
const wchar_t * const value) {
return xmlrpc_string_w_new_lp(envP, wcslen(value), value);
}
#endif
xmlrpc_value *
xmlrpc_base64_new(xmlrpc_env * const envP,
size_t const length,
const unsigned char * const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_BASE64;
xmlrpc_mem_block_init(envP, &valP->_block, length);
if (!envP->fault_occurred) {
char * const contents =
xmlrpc_mem_block_contents(&valP->_block);
memcpy(contents, value, length);
}
if (envP->fault_occurred)
free(valP);
}
return valP;
}
/* array stuff is in xmlrpc_array.c */
xmlrpc_value *
xmlrpc_cptr_new(xmlrpc_env * const envP,
void * const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_C_PTR;
valP->_value.c_ptr = value;
}
return valP;
}
xmlrpc_value *
xmlrpc_nil_new(xmlrpc_env * const envP) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred)
valP->_type = XMLRPC_TYPE_NIL;
return valP;
}
/* 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. */
+458
View File
@@ -0,0 +1,458 @@
#include "xmlrpc_config.h"
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "bool.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
/* Future work: the XMLRPC_TYPE_DATETIME xmlrpc_value should store the
datetime as something computation-friendly, not as a string. The
client library should parse the string value and reject the XML if
it isn't valid.
But this file should remain the authority on datetimes, so the XML
parser and builder should call on routines in here to do that.
time_t won't work because it can't represent times before 1970 or
after 2038. We need to figure out something better.
*/
#ifdef WIN32
static const __int64 SECS_BETWEEN_EPOCHS = 11644473600;
static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */
void UnixTimeToFileTime(const time_t t, LPFILETIME pft)
{
// Note that LONGLONG is a 64-bit value
LONGLONG ll;
ll = Int32x32To64(t, SECS_TO_100NS) + SECS_BETWEEN_EPOCHS * SECS_TO_100NS;
pft->dwLowDateTime = (DWORD)ll;
pft->dwHighDateTime = ll >> 32;
}
void UnixTimeToSystemTime(const time_t t, LPSYSTEMTIME pst)
{
FILETIME ft;
UnixTimeToFileTime(t, &ft);
FileTimeToSystemTime(&ft, pst);
}
static void UnixTimeFromFileTime(xmlrpc_env * const envP, LPFILETIME pft, time_t * const timeValueP)
{
LONGLONG ll;
ll = ((LONGLONG)pft->dwHighDateTime << 32) + pft->dwLowDateTime;
/* convert to the Unix epoch */
ll -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
/* now convert to seconds */
ll /= SECS_TO_100NS;
if ( (time_t)ll != ll )
{
//fail - value is too big for a time_t
xmlrpc_faultf(envP, "Does not indicate a valid date");
*timeValueP = (time_t)-1;
return;
}
*timeValueP = (time_t)ll;
}
static void UnixTimeFromSystemTime(xmlrpc_env * const envP, LPSYSTEMTIME pst, time_t * const timeValueP)
{
FILETIME filetime;
SystemTimeToFileTime(pst, &filetime);
UnixTimeFromFileTime(envP, &filetime, timeValueP);
}
#endif
static void
validateDatetimeType(xmlrpc_env * const envP,
const xmlrpc_value * const valueP) {
if (valueP->_type != XMLRPC_TYPE_DATETIME) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value of type %s supplied where "
"type %s was expected.",
xmlrpc_typeName(valueP->_type),
xmlrpc_typeName(XMLRPC_TYPE_DATETIME));
}
}
void
xmlrpc_read_datetime_str(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP) {
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred) {
const char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
*stringValueP = strdup(contents);
if (*stringValueP == NULL)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR, "Unable to allocate space "
"for datetime string");
}
}
void
xmlrpc_read_datetime_str_old(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
const char ** const stringValueP) {
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred) {
*stringValueP = XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block);
}
}
static void
parseDateNumbers(const char * const t,
unsigned int * const YP,
unsigned int * const MP,
unsigned int * const DP,
unsigned int * const hP,
unsigned int * const mP,
unsigned int * const sP) {
char year[4+1];
char month[2+1];
char day[2+1];
char hour[2+1];
char minute[2+1];
char second[2+1];
assert(strlen(t) == 17);
year[0] = t[ 0];
year[1] = t[ 1];
year[2] = t[ 2];
year[3] = t[ 3];
year[4] = '\0';
month[0] = t[ 4];
month[1] = t[ 5];
month[2] = '\0';
day[0] = t[ 6];
day[1] = t[ 7];
day[2] = '\0';
assert(t[ 8] == 'T');
hour[0] = t[ 9];
hour[1] = t[10];
hour[2] = '\0';
assert(t[11] == ':');
minute[0] = t[12];
minute[1] = t[13];
minute[2] = '\0';
assert(t[14] == ':');
second[0] = t[15];
second[1] = t[16];
second[2] = '\0';
*YP = atoi(year);
*MP = atoi(month);
*DP = atoi(day);
*hP = atoi(hour);
*mP = atoi(minute);
*sP = atoi(second);
}
#ifdef HAVE_SETENV
xmlrpc_bool const haveSetenv = TRUE;
#else
xmlrpc_bool const haveSetenv = FALSE;
static void
setenv(const char * const name ATTR_UNUSED,
const char * const value ATTR_UNUSED,
int const replace ATTR_UNUSED) {
assert(FALSE);
}
#endif
static void
makeTimezoneUtc(xmlrpc_env * const envP,
const char ** const oldTzP) {
const char * const tz = getenv("TZ");
#ifdef WIN32
/* Windows implementation does not exist */
assert(TRUE);
#endif
if (haveSetenv) {
if (tz) {
*oldTzP = strdup(tz);
if (*oldTzP == NULL)
xmlrpc_faultf(envP, "Unable to get memory to save TZ "
"environment variable.");
} else
*oldTzP = NULL;
if (!envP->fault_occurred)
setenv("TZ", "", 1);
} else {
if (tz && strlen(tz) == 0) {
/* Everything's fine. Nothing to change or restore */
} else {
/* Note that putenv() is not sufficient. You can't restore
the original value with that, because it sets a pointer into
your own storage.
*/
xmlrpc_faultf(envP, "Your TZ environment variable is not a "
"null string and your C library does not have "
"setenv(), so we can't change it.");
}
}
}
static void
restoreTimezone(const char * const oldTz) {
if (haveSetenv) {
setenv("TZ", oldTz, 1);
free((char*)oldTz);
}
}
static void
mkAbsTime(xmlrpc_env * const envP,
struct tm const brokenTime,
time_t * const timeValueP) {
#ifdef WIN32
/* Windows Implementation */
SYSTEMTIME stbrokenTime;
stbrokenTime.wHour = brokenTime.tm_hour;
stbrokenTime.wMinute = brokenTime.tm_min;
stbrokenTime.wSecond = brokenTime.tm_sec;
stbrokenTime.wMonth = brokenTime.tm_mon;
stbrokenTime.wDay = brokenTime.tm_mday;
stbrokenTime.wYear = brokenTime.tm_year;
stbrokenTime.wMilliseconds = 0;
/* When the date string is parsed into the tm structure, it was
modified to decrement the month count by one and convert the
4 digit year to a two digit year. We undo what the parser
did to make it a true SYSTEMTIME structure, then convert this
structure into a UNIX time_t structure
*/
stbrokenTime.wYear+=1900;
stbrokenTime.wMonth+=1;
UnixTimeFromSystemTime(envP, &stbrokenTime,timeValueP);
#else
time_t mktimeResult;
const char * oldTz;
struct tm mktimeWork;
/* We use mktime() to create the time_t because it's the
best we have available, but mktime() takes a local time
argument, and we have absolute time. So we fake it out
by temporarily setting the timezone to UTC.
*/
makeTimezoneUtc(envP, &oldTz);
if (!envP->fault_occurred) {
mktimeWork = brokenTime;
mktimeResult = mktime(&mktimeWork);
restoreTimezone(oldTz);
if (mktimeResult == (time_t)-1)
xmlrpc_faultf(envP, "Does not indicate a valid date");
else
*timeValueP = mktimeResult;
}
#endif
}
static void
validateFormat(xmlrpc_env * const envP,
const char * const t) {
if (strlen(t) != 17)
xmlrpc_faultf(envP, "%u characters instead of 15.", strlen(t));
else if (t[8] != 'T')
xmlrpc_faultf(envP, "9th character is '%c', not 'T'", t[8]);
else {
unsigned int i;
for (i = 0; i < 8 && !envP->fault_occurred; ++i)
if (!isdigit(t[i]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[i]);
if (!isdigit(t[9]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[9]);
if (!isdigit(t[10]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[10]);
if (t[11] != ':')
xmlrpc_faultf(envP, "Not a colon: '%c'", t[11]);
if (!isdigit(t[12]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[12]);
if (!isdigit(t[13]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[13]);
if (t[14] != ':')
xmlrpc_faultf(envP, "Not a colon: '%c'", t[14]);
if (!isdigit(t[15]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[15]);
if (!isdigit(t[16]))
xmlrpc_faultf(envP, "Not a digit: '%c'", t[16]);
}
}
static void
parseDatetime(xmlrpc_env * const envP,
const char * const t,
time_t * const timeValueP) {
/*----------------------------------------------------------------------------
Parse a time in the format stored in an xmlrpc_value and return the
time that it represents.
t[] is the input time string. We return the result as *timeValueP.
Example of the format we parse: "19980717T14:08:55"
Note that this is not quite ISO 8601. It's a bizarre combination of
two ISO 8601 formats.
-----------------------------------------------------------------------------*/
validateFormat(envP, t);
if (!envP->fault_occurred) {
unsigned int Y, M, D, h, m, s;
parseDateNumbers(t, &Y, &M, &D, &h, &m, &s);
if (Y < 1900)
xmlrpc_faultf(envP, "Year is too early to represent as "
"a standard Unix time");
else {
struct tm brokenTime;
brokenTime.tm_sec = s;
brokenTime.tm_min = m;
brokenTime.tm_hour = h;
brokenTime.tm_mday = D;
brokenTime.tm_mon = M - 1;
brokenTime.tm_year = Y - 1900;
mkAbsTime(envP, brokenTime, timeValueP);
}
}
}
void
xmlrpc_read_datetime_sec(xmlrpc_env * const envP,
const xmlrpc_value * const valueP,
time_t * const timeValueP) {
validateDatetimeType(envP, valueP);
if (!envP->fault_occurred)
parseDatetime(envP,
XMLRPC_MEMBLOCK_CONTENTS(char, &valueP->_block),
timeValueP);
}
xmlrpc_value *
xmlrpc_datetime_new_str(xmlrpc_env * const envP,
const char * const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_DATETIME;
XMLRPC_TYPED_MEM_BLOCK_INIT(
char, envP, &valP->_block, strlen(value) + 1);
if (!envP->fault_occurred) {
char * const contents =
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
strcpy(contents, value);
}
if (envP->fault_occurred)
free(valP);
}
return valP;
}
xmlrpc_value *
xmlrpc_datetime_new_sec(xmlrpc_env * const envP,
time_t const value) {
xmlrpc_value * valP;
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
struct tm brokenTime;
char timeString[64];
valP->_type = XMLRPC_TYPE_DATETIME;
gmtime_r(&value, &brokenTime);
/* Note that this format is NOT ISO 8601 -- it's a bizarre
hybrid of two ISO 8601 formats.
*/
strftime(timeString, sizeof(timeString), "%Y%m%dT%H:%M:%S",
&brokenTime);
XMLRPC_TYPED_MEM_BLOCK_INIT(
char, envP, &valP->_block, strlen(timeString) + 1);
if (!envP->fault_occurred) {
char * const contents =
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &valP->_block);
strcpy(contents, timeString);
}
if (envP->fault_occurred)
free(valP);
}
return valP;
}
+395
View File
@@ -0,0 +1,395 @@
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
#include "xmlrpc_config.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <xmlparse.h>
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/xmlparser.h"
/* Define the contents of our internal structure. */
struct _xml_element {
struct _xml_element *_parent;
char *_name;
xmlrpc_mem_block _cdata; /* char */
xmlrpc_mem_block _children; /* xml_element* */
};
/* Check that we're using expat in UTF-8 mode, not wchar_t mode.
** If you need to use expat in wchar_t mode, write a subroutine to
** copy a wchar_t string to a char string & return an error for
** any non-ASCII characters. Then call this subroutine on all
** XML_Char strings passed to our event handlers before using the
** data. */
/* #if sizeof(char) != sizeof(XML_Char)
** #error expat must define XML_Char to be a regular char.
** #endif
*/
#define XMLRPC_ASSERT_ELEM_OK(elem) \
XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
/*=========================================================================
** xml_element_new
**=========================================================================
** Create a new xml_element. This routine isn't exported, because the
** arguments are implementation-dependent.
*/
static xml_element *xml_element_new (xmlrpc_env *env, char *name)
{
xml_element *retval;
int name_valid, cdata_valid, children_valid;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(name != NULL);
/* Set up our error-handling preconditions. */
retval = NULL;
name_valid = cdata_valid = children_valid = 0;
/* Allocate our xml_element structure. */
retval = (xml_element*) malloc(sizeof(xml_element));
XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for XML element");
/* Set our parent field to NULL. */
retval->_parent = NULL;
/* Copy over the element name. */
retval->_name = (char*) malloc(strlen(name) + 1);
XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
"Couldn't allocate memory for XML element");
name_valid = 1;
strcpy(retval->_name, name);
/* Initialize a block to hold our CDATA. */
XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
XMLRPC_FAIL_IF_FAULT(env);
cdata_valid = 1;
/* Initialize a block to hold our child elements. */
XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
XMLRPC_FAIL_IF_FAULT(env);
children_valid = 1;
cleanup:
if (env->fault_occurred) {
if (retval) {
if (name_valid)
free(retval->_name);
if (cdata_valid)
xmlrpc_mem_block_clean(&retval->_cdata);
if (children_valid)
xmlrpc_mem_block_clean(&retval->_children);
free(retval);
}
return NULL;
} else {
return retval;
}
}
/*=========================================================================
** xml_element_free
**=========================================================================
** Blow away an existing element & all of its child elements.
*/
void xml_element_free (xml_element *elem)
{
xmlrpc_mem_block *children;
int size, i;
xml_element **contents;
XMLRPC_ASSERT_ELEM_OK(elem);
free(elem->_name);
elem->_name = XMLRPC_BAD_POINTER;
xmlrpc_mem_block_clean(&elem->_cdata);
/* Deallocate all of our children recursively. */
children = &elem->_children;
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, children);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, children);
for (i = 0; i < size; i++)
xml_element_free(contents[i]);
xmlrpc_mem_block_clean(&elem->_children);
free(elem);
}
/*=========================================================================
** Miscellaneous Accessors
**=========================================================================
** Return the fields of the xml_element. See the header for more
** documentation on each function works.
*/
char *xml_element_name (xml_element *elem)
{
XMLRPC_ASSERT_ELEM_OK(elem);
return elem->_name;
}
/* The result of this function is NOT VALID until the end_element handler
** has been called! */
size_t xml_element_cdata_size (xml_element *elem)
{
XMLRPC_ASSERT_ELEM_OK(elem);
return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
}
char *xml_element_cdata (xml_element *elem)
{
XMLRPC_ASSERT_ELEM_OK(elem);
return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
}
size_t xml_element_children_size (xml_element *elem)
{
XMLRPC_ASSERT_ELEM_OK(elem);
return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
}
xml_element **xml_element_children (xml_element *elem)
{
XMLRPC_ASSERT_ELEM_OK(elem);
return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
}
/*=========================================================================
** Internal xml_element Utility Functions
**=========================================================================
*/
static void xml_element_append_cdata (xmlrpc_env *env,
xml_element *elem,
char *cdata,
size_t size)
{
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_ELEM_OK(elem);
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
}
/* Whether or not this function succeeds, it takes ownership of the 'child'
** argument.
** WARNING - This is the exact opposite of the usual memory ownership
** rules for xmlrpc_value! So please pay attention. */
static void xml_element_append_child (xmlrpc_env *env,
xml_element *elem,
xml_element *child)
{
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_ELEM_OK(elem);
XMLRPC_ASSERT_ELEM_OK(child);
XMLRPC_ASSERT(child->_parent == NULL);
XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
&child, 1);
if (!env->fault_occurred)
child->_parent = elem;
else
xml_element_free(child);
}
/*=========================================================================
** Our parse context. We pass this around as expat user data.
**=========================================================================
*/
typedef struct {
xmlrpc_env *env;
xml_element *root;
xml_element *current;
} parse_context;
/*=========================================================================
** Expat Event Handler Functions
**=========================================================================
*/
static void
start_element (void *user_data, XML_Char *name, XML_Char **atts ATTR_UNUSED)
{
parse_context *context;
xml_element *elem, *new_current;
XMLRPC_ASSERT(user_data != NULL && name != NULL);
/* Get our context and see if an error has already occured. */
context = (parse_context*) user_data;
if (!context->env->fault_occurred) {
/* Set up our error-handling preconditions. */
elem = NULL;
/* Build a new element. */
elem = xml_element_new(context->env, name);
XMLRPC_FAIL_IF_FAULT(context->env);
/* Insert it in the appropriate place. */
if (!context->root) {
context->root = elem;
context->current = elem;
elem = NULL;
} else {
XMLRPC_ASSERT(context->current != NULL);
/* (We need to watch our error handling invariants very carefully
** here. Read the docs for xml_element_append_child. */
new_current = elem;
xml_element_append_child(context->env, context->current, elem);
elem = NULL;
XMLRPC_FAIL_IF_FAULT(context->env);
context->current = new_current;
}
cleanup:
if (elem)
xml_element_free(elem);
}
}
static void end_element (void *user_data, XML_Char *name)
{
parse_context *context;
XMLRPC_ASSERT(user_data != NULL && name != NULL);
/* Get our context and see if an error has already occured. */
context = (parse_context*) user_data;
if (!context->env->fault_occurred) {
/* XXX - I think expat enforces these facts, but I want to be sure.
** If one of these assertion ever fails, it should be replaced by a
** non-assertion runtime error check. */
XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
XMLRPC_ASSERT(context->current->_parent != NULL ||
context->current == context->root);
/* Add a trailing '\0' to our cdata. */
xml_element_append_cdata(context->env, context->current, "\0", 1);
XMLRPC_FAIL_IF_FAULT(context->env);
/* Pop our "stack" of elements. */
context->current = context->current->_parent;
cleanup:
return;
}
}
static void character_data (void *user_data, XML_Char *s, int len)
{
parse_context *context;
XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
/* Get our context and see if an error has already occured. */
context = (parse_context*) user_data;
if (!context->env->fault_occurred) {
XMLRPC_ASSERT(context->current != NULL);
xml_element_append_cdata(context->env, context->current, s, len);
XMLRPC_FAIL_IF_FAULT(context->env);
cleanup:
return;
}
}
/*=========================================================================
** Expat Driver
**=========================================================================
** XXX - We should allow the user to specify the encoding of our xml_data.
*/
xml_element *xml_parse (xmlrpc_env *env, const char *xml_data, int xml_len)
{
parse_context context;
XML_Parser parser;
int ok;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(xml_data != NULL && xml_len >= 0);
/* Set up our error-handling preconditions. */
parser = NULL;
context.root = NULL;
/* Set up the rest of our parse context. */
context.env = env;
context.current = NULL;
/* Set up our XML parser. */
parser = XML_ParserCreate(NULL);
XMLRPC_FAIL_IF_NULL(parser, env, XMLRPC_INTERNAL_ERROR,
"Could not create expat parser");
XML_SetUserData(parser, &context);
XML_SetElementHandler(parser,
(XML_StartElementHandler) start_element,
(XML_EndElementHandler) end_element);
XML_SetCharacterDataHandler(parser,
(XML_CharacterDataHandler) character_data);
/* Parse our data. */
ok = XML_Parse(parser, xml_data, xml_len, 1);
if (!ok)
XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
(char*) XML_ErrorString(XML_GetErrorCode(parser)));
XMLRPC_FAIL_IF_FAULT(env);
/* Perform some sanity checks. */
XMLRPC_ASSERT(context.root != NULL);
XMLRPC_ASSERT(context.current == NULL);
cleanup:
if (parser)
XML_ParserFree(parser);
if (env->fault_occurred) {
if (context.root)
xml_element_free(context.root);
return NULL;
} else {
return context.root;
}
}
+418
View File
@@ -0,0 +1,418 @@
/* -*- 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-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/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 */
NULL, /* getParameterEntity */
NULL, /* cdataBlock */
NULL, /* externalSubset */
1 /* initialized */
/* Following are SAX2 fields. Any ifdef here? */
,NULL, /* _private */
NULL, /* startElementNs */
NULL, /* endElementNs */
NULL /* serror */
};
xml_element *xml_parse (xmlrpc_env *env, const 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;
}
}
+795
View File
@@ -0,0 +1,795 @@
/* 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-c/base.h"
#include "xmlrpc-c/base_int.h"
#include "xmlrpc-c/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|
** nil|struct|array)
** array data
** data value*
** struct member*
** member name, value
**
** Contain CDATA: methodName, i4, int, boolean, string, double,
** dateTime.iso8601, base64, name
**
** We attempt to validate the structure of the XML document carefully.
** We also try *very* hard to handle malicious data gracefully, and without
** leaking memory.
**
** The CHECK_NAME and CHECK_CHILD_COUNT macros examine an XML element, and
** invoke XMLRPC_FAIL if something looks wrong.
*/
#define CHECK_NAME(env,elem,name) \
do \
if (strcmp((name), xml_element_name(elem)) != 0) \
XMLRPC_FAIL2(env, XMLRPC_PARSE_ERROR, \
"Expected element of type <%s>, found <%s>", \
(name), xml_element_name(elem)); \
while (0)
#define CHECK_CHILD_COUNT(env,elem,count) \
do \
if (xml_element_children_size(elem) != (count)) \
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR, \
"Expected <%s> to have %d children, found %d", \
xml_element_name(elem), (count), \
xml_element_children_size(elem)); \
while (0)
static xml_element *
get_child_by_name (xmlrpc_env *env, xml_element *parent, char *name)
{
size_t child_count, i;
xml_element **children;
children = xml_element_children(parent);
child_count = xml_element_children_size(parent);
for (i = 0; i < child_count; i++) {
if (0 == strcmp(xml_element_name(children[i]), name))
return children[i];
}
xmlrpc_env_set_fault_formatted(env, XMLRPC_PARSE_ERROR,
"Expected <%s> to have child <%s>",
xml_element_name(parent), name);
return NULL;
}
/*=========================================================================
** Number-Parsing Functions
**=========================================================================
** These functions mirror atoi, atof, etc., but provide better
** error-handling. These routines may reset errno to zero.
*/
static xmlrpc_int32
xmlrpc_atoi(xmlrpc_env *env, char *str, size_t strlen,
xmlrpc_int32 min, xmlrpc_int32 max)
{
long i;
char *end;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(str);
/* Suppress compiler warnings. */
i = 0;
/* Check for leading white space. */
if (isspace(str[0]))
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" must not contain whitespace", str);
/* Convert the value. */
end = str + strlen;
errno = 0;
i = strtol(str, &end, 10);
/* Look for ERANGE. */
if (errno != 0)
/* XXX - Do all operating systems have thread-safe strerror? */
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
"error parsing \"%s\": %s (%d)",
str, strerror(errno), errno);
/* Look for out-of-range errors which didn't produce ERANGE. */
if (i < min || i > max)
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
"\"%s\" must be in range %d to %d", str, min, max);
/* Check for unused characters. */
if (end != str + strlen)
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" contained trailing data", str);
cleanup:
errno = 0;
if (env->fault_occurred)
return 0;
return (xmlrpc_int32) i;
}
static double
xmlrpc_atod(xmlrpc_env *env, char *str, size_t strlen)
{
double d;
char *end;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(str);
/* Suppress compiler warnings. */
d = 0.0;
/* Check for leading white space. */
if (isspace(str[0]))
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" must not contain whitespace", str);
/* Convert the value. */
end = str + strlen;
errno = 0;
d = strtod(str, &end);
/* Look for ERANGE. */
if (errno != 0)
/* XXX - Do all operating systems have thread-safe strerror? */
XMLRPC_FAIL3(env, XMLRPC_PARSE_ERROR,
"error parsing \"%s\": %s (%d)",
str, strerror(errno), errno);
/* Check for unused characters. */
if (end != str + strlen)
XMLRPC_FAIL1(env, XMLRPC_PARSE_ERROR,
"\"%s\" contained trailing data", str);
cleanup:
errno = 0;
if (env->fault_occurred)
return 0.0;
return d;
}
/*=========================================================================
** make_string
**=========================================================================
** Make an XML-RPC string.
**
** SECURITY: We validate our UTF-8 first. This incurs a performance
** penalty, but ensures that we will never pass maliciously malformed
** UTF-8 data back up to the user layer, where it could wreak untold
** damange. Don't comment out this check unless you know *exactly* what
** you're doing. (Win32 developers who remove this check are *begging*
** to wind up on BugTraq, because many of the Win32 filesystem routines
** rely on an insecure UTF-8 decoder.)
**
** XXX - This validation is redundant if the user chooses to convert
** UTF-8 data into a wchar_t string.
*/
static xmlrpc_value *
make_string(xmlrpc_env *env, char *cdata, size_t cdata_size)
{
#ifdef HAVE_UNICODE_WCHAR
xmlrpc_validate_utf8(env, cdata, cdata_size);
#endif
if (env->fault_occurred)
return NULL;
return xmlrpc_build_value(env, "s#", cdata, cdata_size);
}
/*=========================================================================
** convert_value
**=======================================================================*/
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 void
convertBase64(xmlrpc_env * const envP,
const char * const cdata,
size_t const cdata_size,
xmlrpc_value ** const valuePP) {
xmlrpc_mem_block *decoded;
decoded = xmlrpc_base64_decode(envP, cdata, cdata_size);
if (!envP->fault_occurred) {
unsigned char * const asciiData =
XMLRPC_MEMBLOCK_CONTENTS(unsigned char, decoded);
size_t const asciiLen =
XMLRPC_MEMBLOCK_SIZE(unsigned char, decoded);
*valuePP = xmlrpc_build_value(envP, "6", asciiData, asciiLen);
XMLRPC_MEMBLOCK_FREE(unsigned char, decoded);
}
}
static xmlrpc_value *
convert_value(xmlrpc_env * const envP,
unsigned * const depthP,
xml_element * const elem) {
/*----------------------------------------------------------------------------
Compute the xmlrpc_value represented by the XML <value> element 'elem'.
Return that xmlrpc_value.
Assume we are running at recursion depth *depthP. (TODO: this is a
modularity violation. We have to replace this with a "maximum
depth" argument that tells how much additional recursion depth
we're allowed to use).
-----------------------------------------------------------------------------*/
int child_count;
char * child_name;
xmlrpc_value *retval;
XMLRPC_ASSERT_ENV_OK(envP);
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;
/* Make sure we haven't recursed too deeply. */
if (*depthP > xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID))
XMLRPC_FAIL(envP, XMLRPC_PARSE_ERROR,
"Nested data structure too deep.");
/* Validate our structure, and see whether we have a child element. */
CHECK_NAME(envP, 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. */
char * const cdata = xml_element_cdata(elem);
size_t const cdata_size = xml_element_cdata_size(elem);
retval = make_string(envP, cdata, cdata_size);
} else {
/* We should have a type tag inside our value tag. */
xml_element *child;
CHECK_CHILD_COUNT(envP, 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) {
retval = convert_struct(envP, depthP, child);
} else if (strcmp(child_name, "array") == 0) {
CHECK_CHILD_COUNT(envP, child, 1);
retval = convert_array(envP, depthP, child);
} else {
char * cdata;
size_t cdata_size;
CHECK_CHILD_COUNT(envP, 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) {
xmlrpc_int32 const i =
xmlrpc_atoi(envP, cdata, strlen(cdata),
XMLRPC_INT32_MIN, XMLRPC_INT32_MAX);
XMLRPC_FAIL_IF_FAULT(envP);
retval = xmlrpc_build_value(envP, "i", i);
} else if (strcmp(child_name, "string") == 0) {
retval = make_string(envP, cdata, cdata_size);
} else if (strcmp(child_name, "boolean") == 0) {
xmlrpc_int32 const i =
xmlrpc_atoi(envP, cdata, strlen(cdata), 0, 1);
XMLRPC_FAIL_IF_FAULT(envP);
retval = xmlrpc_build_value(envP, "b", (xmlrpc_bool) i);
} else if (strcmp(child_name, "double") == 0) {
double const d = xmlrpc_atod(envP, cdata, strlen(cdata));
XMLRPC_FAIL_IF_FAULT(envP);
retval = xmlrpc_build_value(envP, "d", d);
} else if (strcmp(child_name, "dateTime.iso8601") == 0) {
retval = xmlrpc_build_value(envP, "8", cdata);
} else if (strcmp(child_name, "nil") == 0) {
retval = xmlrpc_build_value(envP, "n");
} else if (strcmp(child_name, "base64") == 0) {
/* No more tail calls once we do this! */
convertBase64(envP, cdata, cdata_size, &retval);
if (envP->fault_occurred)
/* Just for cleanup code: */
retval = NULL;
} else {
XMLRPC_FAIL1(envP, XMLRPC_PARSE_ERROR,
"Unknown value type -- XML element is named "
"<%s>", child_name);
}
}
}
cleanup:
if (envP->fault_occurred) {
if (retval)
xmlrpc_DECREF(retval);
retval = NULL;
}
return retval;
}
/*=========================================================================
** convert_array
**=========================================================================
** Convert an XML element representing an array into an xmlrpc_value.
*/
static xmlrpc_value *
convert_array(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xml_element *data, **values, *value;
xmlrpc_value *array, *item;
int size, i;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Set up our error-handling preconditions. */
array = item = NULL;
(*depth)++;
/* Allocate an array to hold our values. */
array = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
/* We don't need to check our element name--our callers do that. */
CHECK_CHILD_COUNT(env, elem, 1);
data = xml_element_children(elem)[0];
CHECK_NAME(env, data, "data");
/* Iterate over our children. */
values = xml_element_children(data);
size = xml_element_children_size(data);
for (i = 0; i < size; i++) {
value = values[i];
item = convert_value(env, depth, value);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, array, item);
xmlrpc_DECREF(item);
item = NULL;
XMLRPC_FAIL_IF_FAULT(env);
}
cleanup:
(*depth)--;
if (item)
xmlrpc_DECREF(item);
if (env->fault_occurred) {
if (array)
xmlrpc_DECREF(array);
return NULL;
}
return array;
}
/*=========================================================================
** convert_struct
**=========================================================================
** Convert an XML element representing a struct into an xmlrpc_value.
*/
static xmlrpc_value *
convert_struct(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xmlrpc_value *strct, *key, *value;
xml_element **members, *member, *name_elem, *value_elem;
int size, i;
char *cdata;
size_t cdata_size;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Set up our error-handling preconditions. */
strct = key = value = NULL;
(*depth)++;
/* Allocate an array to hold our members. */
strct = xmlrpc_struct_new(env);
XMLRPC_FAIL_IF_FAULT(env);
/* Iterate over our children, extracting key/value pairs. */
/* We don't need to check our element name--our callers do that. */
members = xml_element_children(elem);
size = xml_element_children_size(elem);
for (i = 0; i < size; i++) {
member = members[i];
CHECK_NAME(env, member, "member");
CHECK_CHILD_COUNT(env, member, 2);
/* Get our key. */
name_elem = get_child_by_name(env, member, "name");
XMLRPC_FAIL_IF_FAULT(env);
CHECK_CHILD_COUNT(env, name_elem, 0);
cdata = xml_element_cdata(name_elem);
cdata_size = xml_element_cdata_size(name_elem);
key = make_string(env, cdata, cdata_size);
XMLRPC_FAIL_IF_FAULT(env);
/* Get our value. */
value_elem = get_child_by_name(env, member, "value");
XMLRPC_FAIL_IF_FAULT(env);
value = convert_value(env, depth, value_elem);
XMLRPC_FAIL_IF_FAULT(env);
/* Add the key/value pair to our struct. */
xmlrpc_struct_set_value_v(env, strct, key, value);
XMLRPC_FAIL_IF_FAULT(env);
/* Release our references & memory, and restore our invariants. */
xmlrpc_DECREF(key);
key = NULL;
xmlrpc_DECREF(value);
value = NULL;
}
cleanup:
(*depth)--;
if (key)
xmlrpc_DECREF(key);
if (value)
xmlrpc_DECREF(value);
if (env->fault_occurred) {
if (strct)
xmlrpc_DECREF(strct);
return NULL;
}
return strct;
}
/*=========================================================================
** convert_params
**=========================================================================
** Convert an XML element representing a list of params into an
** xmlrpc_value (of type array).
*/
static xmlrpc_value *
convert_params(xmlrpc_env *env, unsigned *depth, xml_element *elem)
{
xmlrpc_value *array, *item;
int size, i;
xml_element **params, *param, *value;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(elem != NULL);
/* Set up our error-handling preconditions. */
array = item = NULL;
/* Allocate an array to hold our parameters. */
array = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
/* We're responsible for checking our own element name. */
CHECK_NAME(env, elem, "params");
/* Iterate over our children. */
size = xml_element_children_size(elem);
params = xml_element_children(elem);
for (i = 0; i < size; i++) {
param = params[i];
CHECK_NAME(env, param, "param");
CHECK_CHILD_COUNT(env, param, 1);
value = xml_element_children(param)[0];
item = convert_value(env, depth, value);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, array, item);
xmlrpc_DECREF(item);
item = NULL;
XMLRPC_FAIL_IF_FAULT(env);
}
cleanup:
if (env->fault_occurred) {
if (array)
xmlrpc_DECREF(array);
if (item)
xmlrpc_DECREF(item);
return NULL;
}
return array;
}
static void
parseCallXml(xmlrpc_env * const envP,
const char * const xmlData,
size_t const xmlLen,
xml_element ** const callElemP) {
xmlrpc_env env;
xmlrpc_env_init(&env);
*callElemP = xml_parse(&env, xmlData, xmlLen);
if (env.fault_occurred)
xmlrpc_env_set_fault_formatted(
envP, env.fault_code, "Call is not valid XML. %s",
env.fault_string);
xmlrpc_env_clean(&env);
}
/*=========================================================================
** xmlrpc_parse_call
**=========================================================================
** Given some XML text, attempt to parse it as an XML-RPC call. Return
** a newly allocated xmlrpc_call structure (or NULL, if an error occurs).
** The two output variables will contain either valid values (which
** must free() and xmlrpc_DECREF(), respectively) or NULLs (if an error
** occurs).
*/
void
xmlrpc_parse_call(xmlrpc_env * const envP,
const char * const xml_data,
size_t const xml_len,
const char ** const out_method_nameP,
xmlrpc_value ** const out_param_arrayPP) {
xml_element *call_elem, *name_elem, *params_elem;
char *cdata;
unsigned depth;
size_t call_child_count;
char * outMethodName;
xmlrpc_value * outParamArrayP;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(xml_data != NULL);
XMLRPC_ASSERT(out_method_nameP != NULL && out_param_arrayPP != NULL);
/* Set up our error-handling preconditions. */
outMethodName = NULL;
outParamArrayP = NULL;
call_elem = NULL;
/* SECURITY: Last-ditch attempt to make sure our content length is legal.
** XXX - This check occurs too late to prevent an attacker from creating
** an enormous memory block in RAM, so you should try to enforce it
** *before* reading any data off the network. */
if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
XMLRPC_FAIL(envP, XMLRPC_LIMIT_EXCEEDED_ERROR,
"XML-RPC request too large");
parseCallXml(envP, xml_data, xml_len, &call_elem);
XMLRPC_FAIL_IF_FAULT(envP);
/* Pick apart and verify our structure. */
CHECK_NAME(envP, call_elem, "methodCall");
call_child_count = xml_element_children_size(call_elem);
if (call_child_count != 2 && call_child_count != 1)
XMLRPC_FAIL1(envP, XMLRPC_PARSE_ERROR,
"Expected <methodCall> to have 1 or 2 children, found %d",
call_child_count);
/* Extract the method name.
** SECURITY: We make sure the method name is valid UTF-8. */
name_elem = get_child_by_name(envP, call_elem, "methodName");
XMLRPC_FAIL_IF_FAULT(envP);
CHECK_CHILD_COUNT(envP, name_elem, 0);
cdata = xml_element_cdata(name_elem);
#ifdef HAVE_UNICODE_WCHAR
xmlrpc_validate_utf8(envP, cdata, strlen(cdata));
XMLRPC_FAIL_IF_FAULT(envP);
#endif /* HAVE_UNICODE_WCHAR */
outMethodName = malloc(strlen(cdata) + 1);
XMLRPC_FAIL_IF_NULL(outMethodName, envP, XMLRPC_INTERNAL_ERROR,
"Could not allocate memory for method name");
strcpy(outMethodName, cdata);
/* Convert our parameters. */
if (call_child_count == 1) {
/* Workaround for Ruby XML-RPC and old versions of xmlrpc-epi. */
outParamArrayP = xmlrpc_build_value(envP, "()");
XMLRPC_FAIL_IF_FAULT(envP);
} else {
params_elem = get_child_by_name(envP, call_elem, "params");
XMLRPC_FAIL_IF_FAULT(envP);
depth = 0;
outParamArrayP = convert_params(envP, &depth, params_elem);
XMLRPC_ASSERT(depth == 0);
XMLRPC_FAIL_IF_FAULT(envP);
}
cleanup:
if (call_elem)
xml_element_free(call_elem);
if (envP->fault_occurred) {
if (outMethodName)
free(outMethodName);
if (outParamArrayP)
xmlrpc_DECREF(outParamArrayP);
outMethodName = NULL;
outParamArrayP = NULL;
}
*out_method_nameP = outMethodName;
*out_param_arrayPP = outParamArrayP;
}
/*=========================================================================
** xmlrpc_parse_response
**=========================================================================
** Given some XML text, attempt to parse it as an XML-RPC response.
** If the response is a regular, valid response, return a new reference
** to the appropriate value. If the response is a fault, or an error
** occurs during processing, return NULL and set up env appropriately.
*/
xmlrpc_value *
xmlrpc_parse_response(xmlrpc_env *env,
const char *xml_data,
size_t xml_len) {
xml_element *response, *child, *value;
unsigned depth;
xmlrpc_value *params, *retval, *fault;
int retval_incremented;
xmlrpc_value *fault_code_value, *fault_str_value;
xmlrpc_int32 fault_code;
char *fault_str;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(xml_data != NULL);
/* Set up our error-handling preconditions. */
response = NULL;
params = fault = NULL;
retval_incremented = 0;
/* SECURITY: Last-ditch attempt to make sure our content length is legal.
** XXX - This check occurs too late to prevent an attacker from creating
** an enormous memory block in RAM, so you should try to enforce it
** *before* reading any data off the network. */
if (xml_len > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
XMLRPC_FAIL(env, XMLRPC_LIMIT_EXCEEDED_ERROR,
"XML-RPC response too large");
/* SECURITY: Set up our recursion depth counter. */
depth = 0;
/* Parse our XML data. */
response = xml_parse(env, xml_data, xml_len);
XMLRPC_FAIL_IF_FAULT(env);
/* Pick apart and verify our structure. */
CHECK_NAME(env, response, "methodResponse");
CHECK_CHILD_COUNT(env, response, 1);
child = xml_element_children(response)[0];
/* Parse the response itself. */
if (strcmp("params", xml_element_name(child)) == 0) {
/* Convert our parameter list. */
params = convert_params(env, &depth, child);
XMLRPC_FAIL_IF_FAULT(env);
/* Extract the return value, and jiggle our reference counts. */
xmlrpc_parse_value(env, params, "(V)", &retval);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_INCREF(retval);
retval_incremented = 1;
} else if (strcmp("fault", xml_element_name(child)) == 0) {
/* Convert our fault structure. */
CHECK_CHILD_COUNT(env, child, 1);
value = xml_element_children(child)[0];
fault = convert_value(env, &depth, value);
XMLRPC_FAIL_IF_FAULT(env);
XMLRPC_TYPE_CHECK(env, fault, XMLRPC_TYPE_STRUCT);
/* Get our fault code. */
fault_code_value = xmlrpc_struct_get_value(env, fault, "faultCode");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_parse_value(env, fault_code_value, "i", &fault_code);
XMLRPC_FAIL_IF_FAULT(env);
/* Get our fault string. */
fault_str_value = xmlrpc_struct_get_value(env, fault, "faultString");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_parse_value(env, fault_str_value, "s", &fault_str);
XMLRPC_FAIL_IF_FAULT(env);
/* Return our fault. */
XMLRPC_FAIL(env, fault_code, fault_str);
} else {
XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
"Expected <params> or <fault> in <methodResponse>");
}
/* Sanity-check our depth-counting code. */
XMLRPC_ASSERT(depth == 0);
cleanup:
if (response)
xml_element_free(response);
if (params)
xmlrpc_DECREF(params);
if (fault)
xmlrpc_DECREF(fault);
if (env->fault_occurred) {
if (retval_incremented)
xmlrpc_DECREF(retval);
return NULL;
}
return retval;
}
+830
View File
@@ -0,0 +1,830 @@
/* 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-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/base_int.h"
/*=========================================================================
** XML-RPC Server Method Registry
**=========================================================================
** A method registry maintains a list of functions, and handles
** dispatching. To build an XML-RPC server, just add a communications
** protocol. :-)
*/
static void
install_system_methods (xmlrpc_env *env, xmlrpc_registry *registry);
xmlrpc_registry *
xmlrpc_registry_new(xmlrpc_env *env) {
xmlrpc_value *methods;
xmlrpc_registry *registry;
int registry_valid;
XMLRPC_ASSERT_ENV_OK(env);
/* Error-handling preconditions. */
methods = NULL;
registry = NULL;
registry_valid = 0;
/* Allocate our memory. */
methods = xmlrpc_struct_new(env);
XMLRPC_FAIL_IF_FAULT(env);
registry = (xmlrpc_registry*) malloc(sizeof(xmlrpc_registry));
XMLRPC_FAIL_IF_NULL(registry, env, XMLRPC_INTERNAL_ERROR,
"Could not allocate memory for registry");
/* Set everything up. */
registry->_introspection_enabled = 1;
registry->_methods = methods;
registry->_default_method = NULL;
registry->_preinvoke_method = NULL;
registry_valid = 1;
/* Install our system methods. */
install_system_methods(env, registry);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (env->fault_occurred) {
if (registry_valid) {
xmlrpc_registry_free(registry);
} else {
if (methods)
xmlrpc_DECREF(methods);
if (registry)
free(registry);
}
return NULL;
}
return registry;
}
void
xmlrpc_registry_free(xmlrpc_registry * registry) {
XMLRPC_ASSERT_PTR_OK(registry);
XMLRPC_ASSERT(registry->_methods != XMLRPC_BAD_POINTER);
xmlrpc_DECREF(registry->_methods);
registry->_methods = XMLRPC_BAD_POINTER;
if (registry->_default_method != NULL)
xmlrpc_DECREF(registry->_default_method);
if (registry->_preinvoke_method != NULL)
xmlrpc_DECREF(registry->_preinvoke_method);
free(registry);
}
/*=========================================================================
** xmlrpc_registry_disable_introspection
**=========================================================================
** See xmlrpc.h for more documentation.
*/
void
xmlrpc_registry_disable_introspection(xmlrpc_registry * registry) {
XMLRPC_ASSERT_PTR_OK(registry);
registry->_introspection_enabled = 0;
}
/*=========================================================================
** xmlrpc_registry_add_method
**=========================================================================
** See xmlrpc.h for more documentation.
*/
void
xmlrpc_registry_add_method(xmlrpc_env *env,
xmlrpc_registry *registry,
const char *host,
const char *method_name,
xmlrpc_method method,
void *user_data) {
xmlrpc_registry_add_method_w_doc (env, registry, host, method_name,
method, user_data, "?",
"No help is available for this method.");
}
void
xmlrpc_registry_add_method_w_doc(xmlrpc_env *env,
xmlrpc_registry *registry,
const char *host,
const char *method_name,
xmlrpc_method method,
void *user_data,
const char *signature,
const char *help) {
xmlrpc_value *method_info;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(registry);
XMLRPC_ASSERT(host == NULL);
XMLRPC_ASSERT_PTR_OK(method_name);
XMLRPC_ASSERT_PTR_OK(method);
/* Error-handling preconditions. */
method_info = NULL;
/* Store our method and user data into our hash table. */
method_info = xmlrpc_build_value(env, "(ppss)", (void*) method, user_data,
signature, help);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_struct_set_value(env, registry->_methods, method_name, method_info);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (method_info)
xmlrpc_DECREF(method_info);
}
/*=========================================================================
** xmlrpc_registry_set_default_method
**=========================================================================
** See xmlrpc.h for more documentation.
*/
void
xmlrpc_registry_set_default_method(xmlrpc_env *env,
xmlrpc_registry *registry,
xmlrpc_default_method handler,
void *user_data) {
xmlrpc_value *method_info;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(registry);
XMLRPC_ASSERT_PTR_OK(handler);
/* Error-handling preconditions. */
method_info = NULL;
/* Store our method and user data into our hash table. */
method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
XMLRPC_FAIL_IF_FAULT(env);
/* Dispose of any pre-existing default method and install ours. */
if (registry->_default_method)
xmlrpc_DECREF(registry->_default_method);
registry->_default_method = method_info;
cleanup:
if (env->fault_occurred) {
if (method_info)
xmlrpc_DECREF(method_info);
}
}
/*=========================================================================
** xmlrpc_registry_set_preinvoke_method
**=========================================================================
** See xmlrpc.h for more documentation.
*/
void
xmlrpc_registry_set_preinvoke_method(xmlrpc_env *env,
xmlrpc_registry *registry,
xmlrpc_preinvoke_method handler,
void *user_data) {
xmlrpc_value *method_info;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(registry);
XMLRPC_ASSERT_PTR_OK(handler);
/* Error-handling preconditions. */
method_info = NULL;
/* Store our method and user data into our hash table. */
method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
XMLRPC_FAIL_IF_FAULT(env);
/* Dispose of any pre-existing preinvoke method and install ours. */
if (registry->_preinvoke_method)
xmlrpc_DECREF(registry->_preinvoke_method);
registry->_preinvoke_method = method_info;
cleanup:
if (env->fault_occurred) {
if (method_info)
xmlrpc_DECREF(method_info);
}
}
/*=========================================================================
** dispatch_call
**=========================================================================
** An internal method which actually does the dispatch. This may get
** prettified and exported at some point in the future.
*/
static void
callPreinvokeMethodIfAny(xmlrpc_env * const envP,
xmlrpc_registry * const registryP,
const char * const methodName,
xmlrpc_value * const paramArrayP) {
/* Get the preinvoke method, if it is set. */
if (registryP->_preinvoke_method) {
xmlrpc_preinvoke_method preinvoke_method;
void * user_data;
xmlrpc_parse_value(envP, registryP->_preinvoke_method, "(pp)",
&preinvoke_method, &user_data);
if (!envP->fault_occurred)
(*preinvoke_method)(envP, methodName,
paramArrayP, user_data);
}
}
static void
callDefaultMethod(xmlrpc_env * const envP,
xmlrpc_value * const defaultMethodInfo,
const char * const methodName,
xmlrpc_value * const paramArrayP,
xmlrpc_value ** const resultPP) {
xmlrpc_default_method default_method;
void * user_data;
xmlrpc_parse_value(envP, defaultMethodInfo, "(pp)",
&default_method, &user_data);
if (!envP->fault_occurred)
*resultPP = (*default_method)(envP, NULL, methodName,
paramArrayP, user_data);
}
static void
callNamedMethod(xmlrpc_env * const envP,
xmlrpc_value * const methodInfo,
xmlrpc_value * const paramArrayP,
xmlrpc_value ** const resultPP) {
xmlrpc_method method;
void * user_data;
xmlrpc_parse_value(envP, methodInfo, "(pp*)", &method, &user_data);
if (!envP->fault_occurred)
*resultPP = (*method)(envP, paramArrayP, user_data);
}
static void
dispatch_call(xmlrpc_env * const envP,
xmlrpc_registry * const registryP,
const char * const methodName,
xmlrpc_value * const paramArrayP,
xmlrpc_value ** const resultPP) {
callPreinvokeMethodIfAny(envP, registryP, methodName, paramArrayP);
if (!envP->fault_occurred) {
xmlrpc_value * method_info;
/* Look up the method info for the named method. */
xmlrpc_struct_find_value(envP, registryP->_methods,
methodName, &method_info);
if (!envP->fault_occurred) {
if (method_info) {
callNamedMethod(envP, method_info, paramArrayP, resultPP);
xmlrpc_DECREF(method_info);
} else {
if (registryP->_default_method)
callDefaultMethod(envP, registryP->_default_method,
methodName, paramArrayP,
resultPP);
else {
/* No matching method, and no default. */
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_NO_SUCH_METHOD_ERROR,
"Method '%s' not defined", methodName);
}
}
}
}
/* For backward compatibility, for sloppy users: */
if (envP->fault_occurred)
*resultPP = NULL;
}
/*=========================================================================
** xmlrpc_registry_process_call
**=========================================================================
**
*/
xmlrpc_mem_block *
xmlrpc_registry_process_call(xmlrpc_env * const envP,
xmlrpc_registry * const registryP,
const char * const host ATTR_UNUSED,
const char * const xml_data,
size_t const xml_len) {
xmlrpc_mem_block * output;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_PTR_OK(xml_data);
xmlrpc_traceXml("XML-RPC CALL", xml_data, xml_len);
/* Allocate our output buffer.
** If this fails, we need to die in a special fashion. */
output = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
if (!envP->fault_occurred) {
const char * methodName;
xmlrpc_value * paramArray;
xmlrpc_env fault;
xmlrpc_env_init(&fault);
xmlrpc_parse_call(&fault, xml_data, xml_len,
&methodName, &paramArray);
if (!fault.fault_occurred) {
xmlrpc_value * result;
dispatch_call(&fault, registryP, methodName, paramArray, &result);
if (!fault.fault_occurred) {
xmlrpc_serialize_response(envP, output, result);
/* A comment here used to say that
xmlrpc_serialize_response() could fail and "leave
stuff in the buffer." Don't know what that means,
but it sounds like something that needs to be
fixed. The old code aborted the program here if
xmlrpc_serialize_repsonse() failed. 04.11.17
*/
xmlrpc_DECREF(result);
}
xmlrpc_strfree(methodName);
xmlrpc_DECREF(paramArray);
}
if (!envP->fault_occurred && fault.fault_occurred)
xmlrpc_serialize_fault(envP, output, &fault);
xmlrpc_env_clean(&fault);
if (envP->fault_occurred)
XMLRPC_MEMBLOCK_FREE(char, output);
else
xmlrpc_traceXml("XML-RPC RESPONSE",
XMLRPC_MEMBLOCK_CONTENTS(char, output),
XMLRPC_MEMBLOCK_SIZE(char, output));
}
return output;
}
/*=========================================================================
** system.multicall
**=========================================================================
** Low-tech support for transparent, boxed methods.
*/
static char *multicall_help =
"Process an array of calls, and return an array of results. Calls should "
"be structs of the form {'methodName': string, 'params': array}. Each "
"result will either be a single-item array containg the result value, or "
"a struct of the form {'faultCode': int, 'faultString': string}. This "
"is useful when you need to make lots of small calls without lots of "
"round trips.";
static xmlrpc_value *
call_one_method(xmlrpc_env *env, xmlrpc_registry *registry,
xmlrpc_value *method_info) {
xmlrpc_value *result_val, *result;
char *method_name;
xmlrpc_value *param_array;
/* Error-handling preconditions. */
result = result_val = NULL;
/* Extract our method name and parameters. */
xmlrpc_parse_value(env, method_info, "{s:s,s:A,*}",
"methodName", &method_name,
"params", &param_array);
XMLRPC_FAIL_IF_FAULT(env);
/* Watch out for a deep recursion attack. */
if (strcmp(method_name, "system.multicall") == 0)
XMLRPC_FAIL(env, XMLRPC_REQUEST_REFUSED_ERROR,
"Recursive system.multicall strictly forbidden");
/* Perform the call. */
dispatch_call(env, registry, method_name, param_array, &result_val);
XMLRPC_FAIL_IF_FAULT(env);
/* Build our one-item result array. */
result = xmlrpc_build_value(env, "(V)", result_val);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (result_val)
xmlrpc_DECREF(result_val);
if (env->fault_occurred) {
if (result)
xmlrpc_DECREF(result);
return NULL;
}
return result;
}
static xmlrpc_value *
system_multicall(xmlrpc_env *env,
xmlrpc_value *param_array,
void *user_data) {
xmlrpc_registry *registry;
xmlrpc_value *methlist, *methinfo, *results, *result;
size_t size, i;
xmlrpc_env env2;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_VALUE_OK(param_array);
XMLRPC_ASSERT_PTR_OK(user_data);
/* Error-handling preconditions. */
results = result = NULL;
xmlrpc_env_init(&env2);
/* Turn our arguments into something more useful. */
registry = (xmlrpc_registry*) user_data;
xmlrpc_parse_value(env, param_array, "(A)", &methlist);
XMLRPC_FAIL_IF_FAULT(env);
/* Create an empty result list. */
results = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
/* Loop over our input list, calling each method in turn. */
size = xmlrpc_array_size(env, methlist);
XMLRPC_ASSERT_ENV_OK(env);
for (i = 0; i < size; i++) {
methinfo = xmlrpc_array_get_item(env, methlist, i);
XMLRPC_ASSERT_ENV_OK(env);
/* Call our method. */
xmlrpc_env_clean(&env2);
xmlrpc_env_init(&env2);
result = call_one_method(&env2, registry, methinfo);
/* Turn any fault into a structure. */
if (env2.fault_occurred) {
XMLRPC_ASSERT(result == NULL);
result =
xmlrpc_build_value(env, "{s:i,s:s}",
"faultCode", (xmlrpc_int32) env2.fault_code,
"faultString", env2.fault_string);
XMLRPC_FAIL_IF_FAULT(env);
}
/* Append this method result to our master array. */
xmlrpc_array_append_item(env, results, result);
xmlrpc_DECREF(result);
result = NULL;
XMLRPC_FAIL_IF_FAULT(env);
}
cleanup:
xmlrpc_env_clean(&env2);
if (result)
xmlrpc_DECREF(result);
if (env->fault_occurred) {
if (results)
xmlrpc_DECREF(results);
return NULL;
}
return results;
}
/*=========================================================================
** system.listMethods
**=========================================================================
** List all available methods by name.
*/
static char *listMethods_help =
"Return an array of all available XML-RPC methods on this server.";
static xmlrpc_value *
system_listMethods(xmlrpc_env *env,
xmlrpc_value *param_array,
void *user_data) {
xmlrpc_registry *registry;
xmlrpc_value *method_names, *method_name, *method_info;
size_t size, i;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_VALUE_OK(param_array);
XMLRPC_ASSERT_PTR_OK(user_data);
/* Error-handling preconditions. */
method_names = NULL;
/* Turn our arguments into something more useful. */
registry = (xmlrpc_registry*) user_data;
xmlrpc_parse_value(env, param_array, "()");
XMLRPC_FAIL_IF_FAULT(env);
/* Make sure we're allowed to introspect. */
if (!registry->_introspection_enabled)
XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
"Introspection disabled for security reasons");
/* Iterate over all the methods in the registry, adding their names
** to a list. */
method_names = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
size = xmlrpc_struct_size(env, registry->_methods);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
xmlrpc_struct_get_key_and_value(env, registry->_methods, i,
&method_name, &method_info);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, method_names, method_name);
XMLRPC_FAIL_IF_FAULT(env);
}
cleanup:
if (env->fault_occurred) {
if (method_names)
xmlrpc_DECREF(method_names);
return NULL;
}
return method_names;
}
/*=========================================================================
** system.methodHelp
**=========================================================================
** Get the help string for a particular method.
*/
static char *methodHelp_help =
"Given the name of a method, return a help string.";
static xmlrpc_value *
system_methodHelp(xmlrpc_env *env,
xmlrpc_value *param_array,
void *user_data) {
xmlrpc_registry *registry;
char *method_name;
xmlrpc_value *ignored1, *ignored2, *ignored3, *help;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_VALUE_OK(param_array);
XMLRPC_ASSERT_PTR_OK(user_data);
/* Turn our arguments into something more useful. */
registry = (xmlrpc_registry*) user_data;
xmlrpc_parse_value(env, param_array, "(s)", &method_name);
XMLRPC_FAIL_IF_FAULT(env);
/* Make sure we're allowed to introspect. */
if (!registry->_introspection_enabled)
XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
"Introspection disabled for security reasons");
/* Get our documentation string. */
xmlrpc_parse_value(env, registry->_methods, "{s:(VVVV*),*}",
method_name, &ignored1, &ignored2, &ignored3, &help);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (env->fault_occurred)
return NULL;
xmlrpc_INCREF(help);
return help;
}
/*=========================================================================
** system.methodSignature
**=========================================================================
** Return an array of arrays describing possible signatures for this
** method.
**
** XXX - This is the ugliest function in the entire library.
*/
static char *methodSignature_help =
"Given the name of a method, return an array of legal signatures. "
"Each signature is an array of strings. The first item of each signature "
"is the return type, and any others items are parameter types.";
static char *bad_sig_str =
"Application has incorrect method signature information";
#define BAD_SIG(env) \
XMLRPC_FAIL((env), XMLRPC_INTERNAL_ERROR, bad_sig_str);
static xmlrpc_value *
system_methodSignature(xmlrpc_env *env,
xmlrpc_value *param_array,
void *user_data) {
xmlrpc_registry *registry;
char *method_name;
xmlrpc_value *ignored1, *ignored2, *ignored3;
xmlrpc_value *item, *current, *result;
int at_sig_start;
char *sig, *code;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_VALUE_OK(param_array);
XMLRPC_ASSERT_PTR_OK(user_data);
/* Error-handling preconditions. */
item = current = result = NULL;
/* Turn our arguments into something more useful. */
registry = (xmlrpc_registry*) user_data;
xmlrpc_parse_value(env, param_array, "(s)", &method_name);
XMLRPC_FAIL_IF_FAULT(env);
/* Make sure we're allowed to introspect. */
if (!registry->_introspection_enabled)
XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
"Introspection disabled for security reasons");
/* Get our signature string. */
xmlrpc_parse_value(env, registry->_methods, "{s:(VVsV*),*}",
method_name, &ignored1, &ignored2, &sig, &ignored3);
XMLRPC_FAIL_IF_FAULT(env);
if (sig[0] == '?' && sig[1] == '\0') {
/* No signature supplied. */
result = xmlrpc_build_value(env, "s", "undef");
XMLRPC_FAIL_IF_FAULT(env);
} else {
/* Build an array of arrays. */
current = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
result = xmlrpc_build_value(env, "(V)", current);
XMLRPC_FAIL_IF_FAULT(env);
at_sig_start = 1;
do {
next_loop:
/* Process the current code. */
switch (*(sig++)) {
case 'i': code = "int"; break;
case 'b': code = "boolean"; break;
case 'd': code = "double"; break;
case 's': code = "string"; break;
case '8': code = "dateTime.iso8601"; break;
case '6': code = "base64"; break;
case 'S': code = "struct"; break;
case 'A': code = "array"; break;
case ',':
/* Start a new signature array. */
if (at_sig_start)
BAD_SIG(env);
xmlrpc_DECREF(current);
current = xmlrpc_build_value(env, "()");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, result, current);
XMLRPC_FAIL_IF_FAULT(env);
at_sig_start = 1;
goto next_loop;
default:
BAD_SIG(env);
}
/* Append the appropriate string to our current signature. */
item = xmlrpc_build_value(env, "s", code);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_array_append_item(env, current, item);
xmlrpc_DECREF(item);
item = NULL;
XMLRPC_FAIL_IF_FAULT(env);
/* Advance to the next code, and skip over ':' if necessary. */
if (at_sig_start) {
if (*sig != ':')
BAD_SIG(env);
sig++;
at_sig_start = 0;
}
} while (*sig != '\0');
}
cleanup:
if (item)
xmlrpc_DECREF(item);
if (current)
xmlrpc_DECREF(current);
if (env->fault_occurred) {
if (result)
xmlrpc_DECREF(result);
return NULL;
}
return result;
}
/*=========================================================================
** install_system_methods
**=========================================================================
** Install the standard methods under system.*.
** This particular function is highly experimental, and may disappear
** without warning.
*/
static void
install_system_methods(xmlrpc_env *env, xmlrpc_registry *registry) {
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
"system.listMethods",
&system_listMethods, registry,
"A:", listMethods_help);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
"system.methodSignature",
&system_methodSignature, registry,
"A:s", methodSignature_help);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
"system.methodHelp",
&system_methodHelp, registry,
"s:s", methodHelp_help);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
"system.multicall",
&system_multicall, registry,
"A:A", multicall_help);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
+625
View File
@@ -0,0 +1,625 @@
/* 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-c/base.h"
#include "xmlrpc-c/base_int.h"
#define CRLF "\015\012"
#define SMALL_BUFFER_SZ (128)
#define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF
/*=========================================================================
** format_out
**=========================================================================
** A lightweight print routine for use with various serialization
** functions. Only use this routine for printing small objects--it uses
** a fixed-size internal buffer and returns an error on overflow.
** In particular, do NOT use this routine to print XML-RPC string values!
*/
static void
format_out(xmlrpc_env *env,
xmlrpc_mem_block *output,
char *format_string,
...) {
va_list args;
char buffer[SMALL_BUFFER_SZ];
int count;
XMLRPC_ASSERT_ENV_OK(env);
va_start(args, format_string);
/* We assume that this function is present and works correctly. Right. */
count = vsnprintf(buffer, SMALL_BUFFER_SZ, format_string, args);
/* Old C libraries return -1 if vsnprintf overflows its buffer.
** New C libraries return the number of characters which *would* have
** been printed if the error did not occur. This is impressively vile.
** Thank the C99 committee for this bright idea. But wait! We also
** need to keep track of the trailing NULL. */
if (count < 0 || count >= (SMALL_BUFFER_SZ - 1))
XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
"format_out overflowed internal buffer");
/* Append our new data to our output. */
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, buffer, count);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
va_end(args);
}
/*=========================================================================
** Warnings About Invalid UTF-8
**=========================================================================
** We claim to send UTF-8 data to the network. But we rely on application
** programs to pass us correctly-formed UTF-8 data, which is very naive
** and optimistic.
**
** In debudding mode, we call this routine to issue dire-sounding
** warnings. For the sake of safety, this routine never exits the
** program or does anything else drastic.
**
** This routine almost certainly slows down our output.
*/
#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
static void
sanity_check_utf8(const char * const str,
size_t const len) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_validate_utf8(&env, str, len);
if (env.fault_occurred)
fprintf(stderr, "*** xmlrpc-c WARNING ***: %s (%s)\n",
"Application sending corrupted UTF-8 data to network",
env.fault_string);
xmlrpc_env_clean(&env);
}
#endif
/*=========================================================================
** Escaping Strings
**=========================================================================
*/
static xmlrpc_mem_block *
escape_string(xmlrpc_env * const env,
const char * const str,
size_t const len) {
xmlrpc_mem_block *retval;
size_t i, needed;
char *out;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(str != NULL);
/* Set up our error-handling preconditions. */
retval = NULL;
/* Sanity-check this string before we print it. */
#if !defined NDEBUG && defined HAVE_UNICODE_WCHAR
sanity_check_utf8(str, len);
#endif
/* Calculate the amount of space we'll need. */
needed = 0;
for (i = 0; i < len; i++) {
if (str[i] == '<')
needed += 4; /* &lt; */
else if (str[i] == '>')
needed += 4; /* &gt; */
else if (str[i] == '&')
needed += 5; /* &amp; */
else
needed++;
}
/* Allocate our memory block. */
retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
XMLRPC_FAIL_IF_FAULT(env);
/* Copy over the newly-allocated data. */
out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
for (i = 0; i < len; i++) {
if (str[i] == '<') {
*out++ = '&';
*out++ = 'l';
*out++ = 't';
*out++ = ';';
} else if (str[i] == '>') {
*out++ = '&';
*out++ = 'g';
*out++ = 't';
*out++ = ';';
} else if (str[i] == '&') {
*out++ = '&';
*out++ = 'a';
*out++ = 'm';
*out++ = 'p';
*out++ = ';';
} else {
*out++ = str[i];
}
}
cleanup:
if (env->fault_occurred) {
if (retval)
XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
retval = NULL;
}
return retval;
}
static xmlrpc_mem_block*
escape_block (xmlrpc_env *env,
xmlrpc_mem_block *block) {
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(block != NULL);
return escape_string(env,
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
}
/*=========================================================================
** xmlrpc_serialize_string_data
**=========================================================================
** Escape and print the contents of a string.
*/
static void
xmlrpc_serialize_string_data(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *string) {
xmlrpc_mem_block *escaped;
char *contents;
size_t size;
/* Since this routine can only be called internally, we only need
** an assertion here, not a runtime type check.
** XXX - Temporarily disabled because we're using this code to
** print <dateTime.iso8601> values as well. */
/* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
/* Escape any '&' and '<' characters in the string. */
escaped = escape_block(env, &string->_block);
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
/* Print the string. */
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (escaped)
XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
}
static void
xmlrpc_serialize_base64_data(xmlrpc_env * const envP,
xmlrpc_mem_block * const output,
unsigned char * const data,
size_t const len) {
/*----------------------------------------------------------------------------
Encode the 'len' bytes at 'data' in base64 ASCII and append the result to
'output'.
-----------------------------------------------------------------------------*/
xmlrpc_mem_block * encoded;
encoded = xmlrpc_base64_encode(envP, data, len);
if (!envP->fault_occurred) {
unsigned char * const contents =
XMLRPC_MEMBLOCK_CONTENTS(unsigned char, encoded);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(unsigned char, encoded);
XMLRPC_MEMBLOCK_APPEND(char, envP, output, contents, size);
XMLRPC_MEMBLOCK_FREE(char, encoded);
}
}
/*=========================================================================
** xmlrpc_serialize_struct
**=========================================================================
** Dump the contents of a struct.
*/
static void
xmlrpc_serialize_struct(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *strct) {
size_t size;
size_t i;
xmlrpc_value *key, *value;
format_out(env, output, "<struct>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
size = xmlrpc_struct_size(env, strct);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
xmlrpc_struct_get_key_and_value(env, strct, i, &key, &value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<member><name>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_string_data(env, output, key);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</name>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</member>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
}
format_out(env, output, "</struct>");
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_value
**=========================================================================
** Dump a value in the appropriate fashion.
*/
void
xmlrpc_serialize_value(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *value) {
xmlrpc_value *item;
size_t size;
unsigned char* contents;
size_t i;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT_VALUE_OK(value);
/* Print our ubiquitous header. */
format_out(env, output, "<value>");
XMLRPC_FAIL_IF_FAULT(env);
switch (value->_type) {
case XMLRPC_TYPE_INT:
/* XXX - We assume that '%i' is the appropriate format specifier
** for an xmlrpc_int32 value. We should add some test cases to
** make sure this works. */
format_out(env, output, "<i4>%i</i4>", value->_value.i);
break;
case XMLRPC_TYPE_BOOL:
/* XXX - We assume that '%i' is the appropriate format specifier
** for an xmlrpc_bool value. */
format_out(env, output, "<boolean>%i</boolean>",
(value->_value.b) ? 1 : 0);
break;
case XMLRPC_TYPE_DOUBLE:
/* We must output a number of the form [+-]?\d*.\d*. */
format_out(env, output, "<double>%.17g</double>", value->_value.d);
break;
case XMLRPC_TYPE_STRING:
format_out(env, output, "<string>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_string_data(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</string>");
break;
case XMLRPC_TYPE_ARRAY:
format_out(env, output, "<array><data>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
/* Serialize each item. */
size = xmlrpc_array_size(env, value);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
item = xmlrpc_array_get_item(env, value, i);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, item);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, CRLF);
XMLRPC_FAIL_IF_FAULT(env);
}
format_out(env, output, "</data></array>");
break;
case XMLRPC_TYPE_STRUCT:
xmlrpc_serialize_struct(env, output, value);
break;
case XMLRPC_TYPE_BASE64:
format_out(env, output, "<base64>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char,
&value->_block);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(unsigned char, &value->_block);
xmlrpc_serialize_base64_data(env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</base64>");
break;
case XMLRPC_TYPE_DATETIME:
format_out(env, output, "<dateTime.iso8601>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_string_data(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</dateTime.iso8601>");
break;
case XMLRPC_TYPE_C_PTR:
xmlrpc_env_set_fault_formatted(
env, XMLRPC_INTERNAL_ERROR,
"Tried to serialize a C pointer value.");
break;
case XMLRPC_TYPE_NIL:
format_out(env, output, "<nil/>");
XMLRPC_FAIL_IF_FAULT(env);
break;
case XMLRPC_TYPE_DEAD:
xmlrpc_env_set_fault_formatted(
env, XMLRPC_INTERNAL_ERROR,
"Tried to serialize a deaad value.");
break;
default:
xmlrpc_env_set_fault_formatted(
env, XMLRPC_INTERNAL_ERROR,
"Invalid xmlrpc_value type: %d", value->_type);
}
XMLRPC_FAIL_IF_FAULT(env);
/* Print our ubiquitous footer. */
format_out(env, output, "</value>");
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_params
**=========================================================================
** Serialize a list as a set of parameters.
*/
void
xmlrpc_serialize_params(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *param_array) {
size_t size, i;
xmlrpc_value *item;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT_VALUE_OK(param_array);
format_out(env, output, "<params>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
/* Dump each parameter. */
size = xmlrpc_array_size(env, param_array);
XMLRPC_FAIL_IF_FAULT(env);
for (i = 0; i < size; i++) {
format_out(env, output, "<param>");
XMLRPC_FAIL_IF_FAULT(env);
item = xmlrpc_array_get_item(env, param_array, i);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, item);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</param>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
}
format_out(env, output, "</params>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_call
**=========================================================================
** Serialize an XML-RPC call.
*/
void
xmlrpc_serialize_call(xmlrpc_env * const env,
xmlrpc_mem_block * const output,
const char * const method_name,
xmlrpc_value * const param_array) {
xmlrpc_mem_block *escaped;
char *contents;
size_t size;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT(method_name != NULL);
XMLRPC_ASSERT_VALUE_OK(param_array);
/* Set up our error-handling preconditions. */
escaped = NULL;
/* Dump our header. */
format_out(env, output, XML_PROLOGUE);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<methodCall>"CRLF"<methodName>");
XMLRPC_FAIL_IF_FAULT(env);
/* Dump the method name. */
escaped = escape_string(env, method_name, strlen(method_name));
XMLRPC_FAIL_IF_FAULT(env);
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped);
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
XMLRPC_FAIL_IF_FAULT(env);
/* Dump our parameters and footer. */
format_out(env, output, "</methodName>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_params(env, output, param_array);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "</methodCall>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (escaped)
xmlrpc_mem_block_free(escaped);
}
/*=========================================================================
** xmlrpc_serialize_response
**=========================================================================
** Serialize the (non-fault) response to an XML-RPC call.
*/
void
xmlrpc_serialize_response (xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_value *value) {
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT_VALUE_OK(value);
format_out(env, output, XML_PROLOGUE);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<methodResponse>"CRLF"<params>"CRLF"<param>");
XMLRPC_FAIL_IF_FAULT(env);
xmlrpc_serialize_value(env, output, value);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output,
"</param>"CRLF"</params>"CRLF"</methodResponse>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
return;
}
/*=========================================================================
** xmlrpc_serialize_fault
**=========================================================================
** Serialize an XML-RPC fault.
**
** If this function fails, it will set up the first env argument. You'll
** need to take some other drastic action to produce a serialized fault
** of your own. (This function should only fail in an out-of-memory
** situation, AFAIK.)
*/
void
xmlrpc_serialize_fault(xmlrpc_env *env,
xmlrpc_mem_block *output,
xmlrpc_env *fault) {
xmlrpc_value *strct;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(output != NULL);
XMLRPC_ASSERT(fault != NULL && fault->fault_occurred);
/* Set up our error-handling preconditions. */
strct = NULL;
/* Build a fault structure. */
strct = xmlrpc_build_value(env, "{s:i,s:s}",
"faultCode", (xmlrpc_int32) fault->fault_code,
"faultString", fault->fault_string);
XMLRPC_FAIL_IF_FAULT(env);
/* Output our header. */
format_out(env, output, XML_PROLOGUE);
XMLRPC_FAIL_IF_FAULT(env);
format_out(env, output, "<methodResponse>"CRLF"<fault>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
/* Serialize our fault structure. */
xmlrpc_serialize_value(env, output, strct);
XMLRPC_FAIL_IF_FAULT(env);
/* Output our footer. */
format_out(env, output, CRLF"</fault>"CRLF"</methodResponse>"CRLF);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (strct)
xmlrpc_DECREF(strct);
}
File diff suppressed because it is too large. Load diff
+323
View File
@@ -0,0 +1,323 @@
/* 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-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/server_cgi.h"
/*=========================================================================
** Output Routines
**=========================================================================
** These routines send various kinds of responses to the server.
*/
static void
send_xml(const char * const xml_data,
size_t const xml_len) {
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
/* 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 const code,
const char * const message,
xmlrpc_env * const env) {
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
/* 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 * const 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 * globalRegistryP;
/*=========================================================================
** get_body
**=========================================================================
** Slurp the body of the request into an xmlrpc_mem_block.
*/
static xmlrpc_mem_block *
get_body(xmlrpc_env * const env,
size_t const length) {
xmlrpc_mem_block *result;
char *contents;
size_t count;
XMLRPC_ASSERT_ENV_OK(env);
/* Error-handling preconditions. */
result = NULL;
#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 the file handle to binary.
*/
_setmode(_fileno(stdin), _O_BINARY);
#endif
/* 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;
}
void
xmlrpc_server_cgi_process_call(xmlrpc_registry * const registryP) {
/*----------------------------------------------------------------------------
Get the XML-RPC call from Standard Input and environment variables,
parse it, find the right method, call it, prepare an XML-RPC
response with the result, and write it to Standard Output.
-----------------------------------------------------------------------------*/
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, registryP, 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);
}
void
xmlrpc_cgi_init(int const flags ATTR_UNUSED) {
xmlrpc_env env;
xmlrpc_env_init(&env);
globalRegistryP = xmlrpc_registry_new(&env);
die_if_fault_occurred(&env);
xmlrpc_env_clean(&env);
}
void
xmlrpc_cgi_cleanup(void) {
xmlrpc_registry_free(globalRegistryP);
}
xmlrpc_registry *
xmlrpc_cgi_registry(void) {
return globalRegistryP;
}
void
xmlrpc_cgi_add_method(const char * const method_name,
xmlrpc_method const method,
void * const user_data) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_registry_add_method(&env, globalRegistryP, NULL, method_name,
method, user_data);
die_if_fault_occurred(&env);
xmlrpc_env_clean(&env);
}
void
xmlrpc_cgi_add_method_w_doc(const char * const method_name,
xmlrpc_method const method,
void * const user_data,
const char * const signature,
const char * const help) {
xmlrpc_env env;
xmlrpc_env_init(&env);
xmlrpc_registry_add_method_w_doc(&env, globalRegistryP, NULL, method_name,
method, user_data, signature, help);
die_if_fault_occurred(&env);
xmlrpc_env_clean(&env);
}
void
xmlrpc_cgi_process_call(void) {
xmlrpc_server_cgi_process_call(globalRegistryP);
}
@@ -0,0 +1,924 @@
/* Copyright (C) 2005 by Steven A. Bone, sbone@pobox.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. */
/* COMPILATION NOTE:
Note that the Platform SDK headers and
link libraries for Windows XP SP2 or newer are required to compile
xmlrpc-c for this module. If you are not using this server, it is
safe to exclude the xmlrpc_server_w32httpsys.c file from the xmlrpc
project and these dependencies will not be required. You can get the
latest platform SDK at
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/
Be sure after installation to choose the program to "register the PSDK
directories with Visual Studio" so the newer headers are found.
*/
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/server.h"
#include "xmlrpc-c/server_w32httpsys.h"
#include "version.h"
#if MUST_BUILD_HTTP_SYS_SERVER > 0
/* See compilation note above if this header is not found! */
#include <http.h>
#include <windows.h>
#include <strsafe.h>
#pragma comment( lib, "httpapi" )
/* 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;
//set TRUE if you want a log
static BOOL g_bDebug;
//set log filename
static char g_fLogFile[MAX_PATH];
//do you want OutputDebugString() to be called?
static BOOL g_bDebugString;
//
// Macros.
//
#define INITIALIZE_HTTP_RESPONSE( resp, status, reason ) \
do \
{ \
RtlZeroMemory( (resp), sizeof(*(resp)) ); \
(resp)->StatusCode = (status); \
(resp)->pReason = (reason); \
(resp)->ReasonLength = (USHORT) strlen(reason); \
} while (FALSE)
#define ADD_KNOWN_HEADER(Response, HeaderId, RawValue) \
do \
{ \
(Response).Headers.KnownHeaders[(HeaderId)].pRawValue = (RawValue); \
(Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \
(USHORT) strlen(RawValue); \
} while(FALSE)
#define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb))
#define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
//
// Prototypes for Internal Functions.
//
DWORD
DoReceiveRequests(
HANDLE hReqQueue,
const xmlrpc_server_httpsys_parms * const parmsP
);
DWORD
SendHttpResponse(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest,
IN USHORT StatusCode,
IN PSTR pReason,
IN PSTR pEntity
);
DWORD
SendHttpResponseAuthRequired(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest
);
void
processRPCCall(
xmlrpc_env * const envP,
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest
);
__inline void TraceA(const char *format, ...);
__inline void TraceW(const wchar_t *format, ...);
//
// External Function Implementation.
//
void
xmlrpc_server_httpsys(
xmlrpc_env * const envP,
const xmlrpc_server_httpsys_parms * const parmsP,
unsigned int const parm_size
)
{
ULONG retCode;
HANDLE hReqQueue = NULL;
HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
WCHAR wszURL[35];
XMLRPC_ASSERT_ENV_OK(envP);
if (parm_size < XMLRPC_HSSIZE(authfn))
{
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"You must specify members at least up through "
"'authfn' in the server parameters argument. "
"That would mean the parameter size would be >= %u "
"but you specified a size of %u",
XMLRPC_HSSIZE(authfn), parm_size);
return;
}
//Set logging options
if (parmsP->logLevel>0)
g_bDebug=TRUE;
else
g_bDebug=FALSE;
if (parmsP->logLevel>1)
g_bDebugString=TRUE;
else
g_bDebugString=FALSE;
if (!parmsP->logFile)
g_bDebug=FALSE;
else
StringCchPrintfA(g_fLogFile,MAX_PATH,parmsP->logFile);
//construct the URL we are listening on
if (parmsP->useSSL!=0)
StringCchPrintf(wszURL,35,L"https://+:%u/RPC2",parmsP->portNum);
else
StringCchPrintf(wszURL,35,L"http://+:%u/RPC2",parmsP->portNum);
global_registryP = parmsP->registryP;
// Initialize HTTP APIs.
retCode = HttpInitialize(
HttpApiVersion,
HTTP_INITIALIZE_SERVER, // Flags
NULL // Reserved
);
if (retCode != NO_ERROR)
{
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"HttpInitialize failed with %lu \n ",
retCode);
return;
}
// Create a Request Queue Handle
retCode = HttpCreateHttpHandle(
&hReqQueue, // Req Queue
0 // Reserved
);
if (retCode != NO_ERROR)
{
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"HttpCreateHttpHandle failed with %lu \n ",
retCode);
goto CleanUp;
}
retCode = HttpAddUrl(
hReqQueue, // Req Queue
wszURL, // Fully qualified URL
NULL // Reserved
);
if (retCode != NO_ERROR)
{
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"HttpAddUrl failed with %lu \n ",
retCode);
goto CleanUp;
}
TraceW( L"we are listening for requests on the following url: %ws\n", wszURL);
// Loop while receiving requests
for(;;)
{
TraceW( L"Calling DoReceiveRequests()\n");
retCode = DoReceiveRequests(hReqQueue, parmsP);
if(NO_ERROR == retCode)
{
TraceW( L"DoReceiveRequests() returned NO_ERROR, breaking");
break;
}
}
CleanUp:
TraceW( L"Tearing down the server.\n", wszURL);
// Call HttpRemoveUrl for the URL that we added.
HttpRemoveUrl( hReqQueue, wszURL );
// Close the Request Queue handle.
if(hReqQueue)
CloseHandle(hReqQueue);
// Call HttpTerminate.
HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
return;
}
//
// Internal Function Implementations.
//
__inline void TraceA(const char *format, ...)
{
if(g_bDebug)
{
if (format)
{
va_list arglist;
char str[4096];
va_start(arglist, format);
if (g_fLogFile)
{
FILE *fout = fopen(g_fLogFile, "a+t");
if (fout)
{
vfprintf(fout, format, arglist);
fclose(fout);
}
}
StringCchVPrintfA(str,4096, format, arglist);
printf(str);
if (g_bDebugString)
{
OutputDebugStringA(str);
}
va_end(arglist);
}
}
}
__inline void TraceW(const wchar_t *format, ...)
{
if(g_bDebug)
{
if (format)
{
va_list arglist;
wchar_t str[4096];
va_start(arglist, format);
if (g_fLogFile)
{
FILE *fout = fopen(g_fLogFile, "a+t");
if (fout)
{
vfwprintf(fout, format, arglist);
fclose(fout);
}
}
StringCchVPrintfW(str, 4096, format, arglist);
wprintf(str);
if (g_bDebugString)
{
OutputDebugStringW(str);
}
va_end(arglist);
}
}
}
/*
* This is a blocking function that merely sits on the request queue
* for our URI and processes them one at a time. Once a request comes
* in, we check it for content-type, content-length, and verb. As long
* as the initial validations are done, we pass the request to the
* processRPCCall() function, which collects the body of the request
* and processes it. If we get an error back other than network type,
* we are responsible for notifing the client.
*/
DWORD
DoReceiveRequests(
IN HANDLE hReqQueue,
const xmlrpc_server_httpsys_parms * const parmsP
)
{
ULONG result;
HTTP_REQUEST_ID requestId;
DWORD bytesRead;
PHTTP_REQUEST pRequest;
PCHAR pRequestBuffer;
ULONG RequestBufferLength;
xmlrpc_env env;
char szHeaderBuf[255];
long lContentLength;
// Allocate a 2K buffer. Should be good for most requests, we'll grow
// this if required. We also need space for a HTTP_REQUEST structure.
RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength );
if (pRequestBuffer == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
pRequest = (PHTTP_REQUEST)pRequestBuffer;
// Wait for a new request -- This is indicated by a NULL request ID.
HTTP_SET_NULL_ID( &requestId );
for(;;)
{
RtlZeroMemory(pRequest, RequestBufferLength);
result = HttpReceiveHttpRequest(
hReqQueue, // Req Queue
requestId, // Req ID
0, // Flags
pRequest, // HTTP request buffer
RequestBufferLength,// req buffer length
&bytesRead, // bytes received
NULL // LPOVERLAPPED
);
if(NO_ERROR == result)
{
// Got a request with a filled buffer.
switch(pRequest->Verb)
{
case HttpVerbPOST:
TraceW(L"Got a POST request for %ws \n",pRequest->CookedUrl.pFullUrl);
//Check if we need use authorization.
if(parmsP->authfn)
{
xmlrpc_env_init(&env);
if(pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength<6)
{
xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR,
"Authorization header too short.");
}
else
{
//unencode the headers
if(_strnicmp("basic ",pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].pRawValue,6)!=0)
{
xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR,
"Authorization header is not of type basic.");
}
else
{
xmlrpc_mem_block * decoded;
decoded = xmlrpc_base64_decode(&env,pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].pRawValue+6,pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength-6);
if(!env.fault_occurred)
{
char *pDecodedStr;
char *pUser;
char *pPass;
char *pColon;
pDecodedStr = (char*)malloc(decoded->_size+1);
memcpy(pDecodedStr,decoded->_block,decoded->_size);
pDecodedStr[decoded->_size]='\0';
pUser = pPass = pDecodedStr;
pColon=strchr(pDecodedStr,':');
if(pColon)
{
*pColon='\0';
pPass=pColon+1;
//The authfn should set env to fail if auth is denied.
parmsP->authfn(&env,pUser,pPass);
}
else
{
xmlrpc_env_set_fault( &env, XMLRPC_REQUEST_REFUSED_ERROR,
"Decoded auth not of the correct format.");
}
free(pDecodedStr);
}
if(decoded)
XMLRPC_MEMBLOCK_FREE(char, decoded);
}
}
if(env.fault_occurred)
{
//request basic authorization, as the user did not provide it.
xmlrpc_env_clean(&env);
TraceW(L"POST request did not provide valid authorization header.");
result = SendHttpResponseAuthRequired( hReqQueue, pRequest);
break;
}
xmlrpc_env_clean(&env);
}
//Check content type to make sure it is text/xml.
memcpy(szHeaderBuf,pRequest->Headers.KnownHeaders[HttpHeaderContentType].pRawValue,pRequest->Headers.KnownHeaders[HttpHeaderContentType].RawValueLength);
szHeaderBuf[pRequest->Headers.KnownHeaders[HttpHeaderContentType].RawValueLength]='\0';
if (_stricmp(szHeaderBuf,"text/xml")!=0)
{
//We only handle text/xml data. Anything else is not valid.
TraceW(L"POST request had an unsupported content-type: %s \n", szHeaderBuf);
result = SendHttpResponse(
hReqQueue,
pRequest,
400,
"Bad Request",
NULL
);
break;
}
//Check content length to make sure it exists and is not too big.
memcpy(szHeaderBuf,pRequest->Headers.KnownHeaders[HttpHeaderContentLength].pRawValue,pRequest->Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength);
szHeaderBuf[pRequest->Headers.KnownHeaders[HttpHeaderContentLength].RawValueLength]='\0';
lContentLength = atol(szHeaderBuf);
if (lContentLength<=0)
{
//Make sure a content length was supplied.
TraceW(L"POST request did not include a content-length \n", szHeaderBuf);
result = SendHttpResponse(
hReqQueue,
pRequest,
411,
"Length Required",
NULL
);
break;
}
if((size_t) lContentLength > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
{
//Content-length is too big for us to handle
TraceW(L"POST request content-length is too big for us to handle: %d bytes \n", lContentLength);
result = SendHttpResponse(
hReqQueue,
pRequest,
500,
"content-length too large",
NULL
);
break;
}
//our initial validations of POST, content-type, and content-length
//all check out. Collect and pass the complete buffer to the
//XMLRPC-C library
xmlrpc_env_init(&env);
processRPCCall(&env,hReqQueue, pRequest);
if (env.fault_occurred)
{
//if we fail and it is anything other than a network error,
//we should return a failure response to the client.
if (env.fault_code != XMLRPC_NETWORK_ERROR)
{
if (env.fault_string)
result = SendHttpResponse(
hReqQueue,
pRequest,
500,
env.fault_string,
NULL
);
else
result = SendHttpResponse(
hReqQueue,
pRequest,
500,
"Unknown Error",
NULL
);
}
}
xmlrpc_env_clean(&env);
break;
default:
//We only handle POST data. Anything else is not valid.
TraceW(L"Got an unsupported Verb request for URI %ws \n", pRequest->CookedUrl.pFullUrl);
result = SendHttpResponse(
hReqQueue,
pRequest,
405,
"Method Not Allowed",
NULL
);
break;
}
if(result != NO_ERROR)
{
break;
}
// Reset the Request ID so that we pick up the next request.
HTTP_SET_NULL_ID( &requestId );
}
else if(result == ERROR_MORE_DATA)
{
// The input buffer was too small to hold the request headers
// We have to allocate more buffer & call the API again.
//
// When we call the API again, we want to pick up the request
// that just failed. This is done by passing a RequestID.
// This RequestID is picked from the old buffer.
requestId = pRequest->RequestId;
// Free the old buffer and allocate a new one.
RequestBufferLength = bytesRead;
FREE_MEM( pRequestBuffer );
pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength );
if (pRequestBuffer == NULL)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
pRequest = (PHTTP_REQUEST)pRequestBuffer;
}
else if(ERROR_CONNECTION_INVALID == result &&
!HTTP_IS_NULL_ID(&requestId))
{
// The TCP connection got torn down by the peer when we were
// trying to pick up a request with more buffer. We'll just move
// onto the next request.
HTTP_SET_NULL_ID( &requestId );
}
else
{
break;
}
} // for(;;)
Cleanup:
if(pRequestBuffer)
{
FREE_MEM( pRequestBuffer );
}
return result;
}
/*
* SendHttpResponse sends a text/html content type back with
* the user specified status code and reason. Used for returning
* errors to clients.
*/
DWORD
SendHttpResponse(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest,
IN USHORT StatusCode,
IN PSTR pReason,
IN PSTR pEntityString
)
{
HTTP_RESPONSE response;
HTTP_DATA_CHUNK dataChunk;
DWORD result;
DWORD bytesSent;
CHAR szServerHeader[20];
// Initialize the HTTP response structure.
INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason);
ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html");
StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);
ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
if(pEntityString)
{
// Add an entity chunk
dataChunk.DataChunkType = HttpDataChunkFromMemory;
dataChunk.FromMemory.pBuffer = pEntityString;
dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString);
response.EntityChunkCount = 1;
response.pEntityChunks = &dataChunk;
}
// Since we are sending all the entity body in one call, we don't have
// to specify the Content-Length.
result = HttpSendHttpResponse(
hReqQueue, // ReqQueueHandle
pRequest->RequestId, // Request ID
0, // Flags
&response, // HTTP response
NULL, // pReserved1
&bytesSent, // bytes sent (OPTIONAL)
NULL, // pReserved2 (must be NULL)
0, // Reserved3 (must be 0)
NULL, // LPOVERLAPPED (OPTIONAL)
NULL // pReserved4 (must be NULL)
);
if(result != NO_ERROR)
{
TraceW(L"HttpSendHttpResponse failed with %lu \n", result);
}
return result;
}
/*
* SendHttpResponseAuthRequired sends a 401 status code requesting authorization
*/
DWORD
SendHttpResponseAuthRequired(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest
)
{
HTTP_RESPONSE response;
DWORD result;
DWORD bytesSent;
CHAR szServerHeader[20];
// Initialize the HTTP response structure.
INITIALIZE_HTTP_RESPONSE(&response, 401, "Authentication Required");
// Add the WWW_Authenticate header.
ADD_KNOWN_HEADER(response, HttpHeaderWwwAuthenticate, "Basic realm=\"xmlrpc\"");
StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);
ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
// Since we are sending all the entity body in one call, we don't have
// to specify the Content-Length.
result = HttpSendHttpResponse(
hReqQueue, // ReqQueueHandle
pRequest->RequestId, // Request ID
0, // Flags
&response, // HTTP response
NULL, // pReserved1
&bytesSent, // bytes sent (OPTIONAL)
NULL, // pReserved2 (must be NULL)
0, // Reserved3 (must be 0)
NULL, // LPOVERLAPPED (OPTIONAL)
NULL // pReserved4 (must be NULL)
);
if(result != NO_ERROR)
{
TraceW(L"SendHttpResponseAuthRequired failed with %lu \n", result);
}
return result;
}
/*
* processRPCCall() is called after some validations. The assumption is that
* the request is an HTTP post of content-type text/xml with a content-length
* that is less than the maximum the library can handle.
*
* The caller should check the error status, and if the error was other than
* a network type, respond back to the client to let them know the call failed.
*/
void
processRPCCall(
xmlrpc_env * const envP,
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest
)
{
HTTP_RESPONSE response;
DWORD result;
DWORD bytesSent;
PUCHAR pEntityBuffer;
ULONG EntityBufferLength;
ULONG BytesRead;
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
CHAR szContentLength[MAX_ULONG_STR];
CHAR szServerHeader[20];
HTTP_DATA_CHUNK dataChunk;
ULONG TotalBytesRead = 0;
xmlrpc_mem_block * body;
xmlrpc_mem_block * output;
BytesRead = 0;
body = NULL;
output = NULL;
// Allocate some space for an entity buffer.
EntityBufferLength = 2048;
pEntityBuffer = (PUCHAR) ALLOC_MEM( EntityBufferLength );
if (pEntityBuffer == NULL)
{
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INTERNAL_ERROR,
"Out of Memory");
goto Done;
}
// NOTE: If we had passed the HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
// flag with HttpReceiveHttpRequest(), the entity would have
// been a part of HTTP_REQUEST (using the pEntityChunks field).
// Since we have not passed that flag, we can be assured that
// there are no entity bodies in HTTP_REQUEST.
if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
{
//Allocate some space for an XMLRPC memory block.
body = xmlrpc_mem_block_new(envP, 0);
if (envP->fault_occurred)
goto Done;
// The entity body can be sent over multiple calls. Let's collect all
// of these in a buffer and send the buffer to the xmlrpc-c library
do
{
// Read the entity chunk from the request.
BytesRead = 0;
result = HttpReceiveRequestEntityBody(
hReqQueue,
pRequest->RequestId,
0,
pEntityBuffer,
EntityBufferLength,
&BytesRead,
NULL
);
switch(result)
{
case NO_ERROR:
if(BytesRead != 0)
{
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body,
pEntityBuffer, BytesRead);
if(envP->fault_occurred)
goto Done;
}
break;
case ERROR_HANDLE_EOF:
// We have read the last request entity body. We can now
// process the suppossed XMLRPC data.
if(BytesRead != 0)
{
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body,
pEntityBuffer, BytesRead);
if(envP->fault_occurred)
goto Done;
}
// We will send the response over multiple calls.
// This is achieved by passing the
// HTTP_SEND_RESPONSE_FLAG_MORE_DATA flag.
// NOTE: Since we are accumulating the TotalBytesRead in
// a ULONG, this will not work for entity bodies that
// are larger than 4 GB. For supporting large entity
// bodies, we would have to use a ULONGLONG.
TraceA("xmlrpc_server RPC2 handler processing RPC request.\n");
// Process the RPC.
output = xmlrpc_registry_process_call(
envP, global_registryP, NULL,
XMLRPC_MEMBLOCK_CONTENTS(char, body),
XMLRPC_MEMBLOCK_SIZE(char, body));
if (envP->fault_occurred)
goto Done;
// Initialize the HTTP response structure.
INITIALIZE_HTTP_RESPONSE(&response, 200, "OK");
//Add the content-length
StringCchPrintfA(szContentLength,MAX_ULONG_STR, "%lu",
XMLRPC_MEMBLOCK_SIZE(char, output));
ADD_KNOWN_HEADER(
response,
HttpHeaderContentLength,
szContentLength );
//Add the content-type
ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/xml");
StringCchPrintfA(szServerHeader,20, "xmlrpc-c %s",XMLRPC_C_VERSION);
ADD_KNOWN_HEADER(response, HttpHeaderServer, szServerHeader);
//send the response
result = HttpSendHttpResponse(
hReqQueue, // ReqQueueHandle
pRequest->RequestId, // Request ID
HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
&response, // HTTP response
NULL, // pReserved1
&bytesSent, // bytes sent (optional)
NULL, // pReserved2
0, // Reserved3
NULL, // LPOVERLAPPED
NULL // pReserved4
);
if(result != NO_ERROR)
{
TraceW(L"HttpSendHttpResponse failed with %lu \n", result);
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_NETWORK_ERROR,
"HttpSendHttpResponse failed with %lu", result);
goto Done;
}
// Send entity body from a memory chunk.
dataChunk.DataChunkType = HttpDataChunkFromMemory;
dataChunk.FromMemory.BufferLength = (ULONG)XMLRPC_MEMBLOCK_SIZE(char, output);
dataChunk.FromMemory.pBuffer = XMLRPC_MEMBLOCK_CONTENTS(char, output);
result = HttpSendResponseEntityBody(
hReqQueue,
pRequest->RequestId,
0, // This is the last send.
1, // Entity Chunk Count.
&dataChunk,
NULL,
NULL,
0,
NULL,
NULL
);
if(result != NO_ERROR)
{
TraceW(L"HttpSendResponseEntityBody failed with %lu \n", result);
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_NETWORK_ERROR,
"HttpSendResponseEntityBody failed with %lu", result);
goto Done;
}
goto Done;
break;
default:
TraceW(L"HttpReceiveRequestEntityBody failed with %lu \n", result);
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_NETWORK_ERROR,
"HttpReceiveRequestEntityBody failed with %lu", result);
goto Done;
}
} while(TRUE);
}
else
{
// This request does not have an entity body.
TraceA("Received a bad request (no body in HTTP post).\n");
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_PARSE_ERROR,
"Bad POST request (no body)");
goto Done;
}
Done:
if(pEntityBuffer)
FREE_MEM(pEntityBuffer);
if(output)
XMLRPC_MEMBLOCK_FREE(char, output);
if(body)
XMLRPC_MEMBLOCK_FREE(char, body);
return;
}
#endif /* #if MUST_BUILD_HTTP_SYS_SERVER <> 0 */
+593
View File
@@ -0,0 +1,593 @@
/* 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-c/base.h"
#include "xmlrpc-c/base_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 * const envP) {
xmlrpc_value * valP;
XMLRPC_ASSERT_ENV_OK(envP);
xmlrpc_createXmlrpcValue(envP, &valP);
if (!envP->fault_occurred) {
valP->_type = XMLRPC_TYPE_STRUCT;
XMLRPC_MEMBLOCK_INIT(_struct_member, envP, &valP->_block, 0);
if (envP->fault_occurred)
free(valP);
}
return valP;
}
/*=========================================================================
** xmlrpc_struct_size
**=========================================================================
** Return the number of key-value pairs contained in the struct. If the
** value is not a struct, return -1 and set a fault.
*/
int
xmlrpc_struct_size(xmlrpc_env* env, xmlrpc_value* strct)
{
int retval;
/* Suppress a compiler warning about uninitialized variables. */
retval = 0;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_VALUE_OK(strct);
XMLRPC_TYPE_CHECK(env, strct, XMLRPC_TYPE_STRUCT);
retval = XMLRPC_MEMBLOCK_SIZE(_struct_member, &strct->_block);
cleanup:
if (env->fault_occurred)
return -1;
return retval;
}
/*=========================================================================
** get_hash
**=========================================================================
** A mindlessly simple hash function. Please feel free to write something
** more clever if this produces bad results.
*/
static unsigned char
get_hash(const char * const key,
size_t const key_len) {
unsigned char retval;
size_t i;
XMLRPC_ASSERT(key != NULL);
retval = 0;
for (i = 0; i < key_len; i++)
retval += key[i];
return retval;
}
/*=========================================================================
** find_member
**=========================================================================
** Get the index of the member with the specified key, or -1 if no such
** member exists.
*/
static int
find_member(xmlrpc_value * const strctP,
const char * const key,
size_t const key_len) {
size_t size, i;
unsigned char hash;
_struct_member *contents;
xmlrpc_value *keyval;
char *keystr;
size_t keystr_size;
XMLRPC_ASSERT_VALUE_OK(strctP);
XMLRPC_ASSERT(key != NULL);
/* Look for our key. */
hash = get_hash(key, key_len);
size = XMLRPC_MEMBLOCK_SIZE(_struct_member, &strctP->_block);
contents = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &strctP->_block);
for (i = 0; i < size; i++) {
if (contents[i].key_hash == hash) {
keyval = contents[i].key;
keystr = XMLRPC_MEMBLOCK_CONTENTS(char, &keyval->_block);
keystr_size = XMLRPC_MEMBLOCK_SIZE(char, &keyval->_block)-1;
if (key_len == keystr_size && memcmp(key, keystr, key_len) == 0)
return i;
}
}
return -1;
}
/*=========================================================================
** xmlrpc_struct_has_key
**=========================================================================
*/
int
xmlrpc_struct_has_key(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key) {
XMLRPC_ASSERT(key != NULL);
return xmlrpc_struct_has_key_n(envP, strctP, key, strlen(key));
}
int
xmlrpc_struct_has_key_n(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key,
size_t const key_len) {
int index;
/* Suppress a compiler warning about uninitialized variables. */
index = 0;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(strctP);
XMLRPC_ASSERT(key != NULL);
XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
index = find_member(strctP, key, key_len);
cleanup:
if (envP->fault_occurred)
return 0;
return (index >= 0);
}
/*=========================================================================
** xmlrpc_struct_find_value...
**=========================================================================
** These functions look up a specified key value in a specified struct.
** If it exists, they return the value of the struct member. If not,
** they return a NULL to indicate such.
*/
/* It would be a nice extension to be able to look up a key that is
not a text string.
*/
void
xmlrpc_struct_find_value(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
Given a key, retrieve a value from the struct. If the key is not
present, return NULL as *valuePP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_PTR_OK(key);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value is not a struct. It is type #%d",
structP->_type);
else {
int index;
/* Get our member index. */
index = find_member(structP, key, strlen(key));
if (index < 0)
*valuePP = NULL;
else {
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
*valuePP = members[index].value;
XMLRPC_ASSERT_VALUE_OK(*valuePP);
xmlrpc_INCREF(*valuePP);
}
}
}
void
xmlrpc_struct_find_value_v(xmlrpc_env * const envP,
xmlrpc_value * const structP,
xmlrpc_value * const keyP,
xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
Given a key, retrieve a value from the struct. If the key is not
present, return NULL as *valuePP.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_VALUE_OK(keyP);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Value is not a struct. It is type #%d",
structP->_type);
else {
if (keyP->_type != XMLRPC_TYPE_STRING)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Key value is not a string. "
"It is type #%d",
keyP->_type);
else {
int index;
/* Get our member index. */
index = find_member(structP,
XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block),
XMLRPC_MEMBLOCK_SIZE(char, &keyP->_block)-1);
if (index < 0)
*valuePP = NULL;
else {
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
*valuePP = members[index].value;
XMLRPC_ASSERT_VALUE_OK(*valuePP);
xmlrpc_INCREF(*valuePP);
}
}
}
}
/*=========================================================================
** xmlrpc_struct_read_value...
**=========================================================================
** These fail if no member with the specified key exists.
** Otherwise, they are the same as xmlrpc_struct_find_value...
*/
void
xmlrpc_struct_read_value_v(xmlrpc_env * const envP,
xmlrpc_value * const structP,
xmlrpc_value * const keyP,
xmlrpc_value ** const valuePP) {
xmlrpc_struct_find_value_v(envP, structP, keyP, valuePP);
if (!envP->fault_occurred) {
if (*valuePP == NULL) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%.*s'",
XMLRPC_MEMBLOCK_SIZE(char, &keyP->_block),
XMLRPC_MEMBLOCK_CONTENTS(char, &keyP->_block));
}
}
}
void
xmlrpc_struct_read_value(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
xmlrpc_value ** const valuePP) {
xmlrpc_struct_find_value(envP, structP, key, valuePP);
if (!envP->fault_occurred) {
if (*valuePP == NULL) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "No member of struct has key '%s'",
key);
/* We should fix the error message to format the key for display */
}
}
}
/*=========================================================================
** xmlrpc_struct_get_value...
**=========================================================================
** These are for backward compatibility. They used to be the only ones.
** They're deprecated because they don't acquire a reference to the
** value they return.
*/
xmlrpc_value *
xmlrpc_struct_get_value_n(xmlrpc_env * const envP,
xmlrpc_value * const structP,
const char * const key,
size_t const keyLen) {
xmlrpc_value * retval;
xmlrpc_value * keyP;
keyP = xmlrpc_build_value(envP, "s#", key, keyLen);
if (!envP->fault_occurred) {
xmlrpc_struct_find_value_v(envP, structP, keyP, &retval);
if (!envP->fault_occurred) {
if (retval == NULL) {
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR,
"No member of struct has key '%.*s'",
keyLen, key);
/* We should fix the error message to format the key
for display */
} else
/* For backward compatibility. */
xmlrpc_DECREF(retval);
}
xmlrpc_DECREF(keyP);
}
return retval;
}
xmlrpc_value *
xmlrpc_struct_get_value(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key) {
XMLRPC_ASSERT(key != NULL);
return xmlrpc_struct_get_value_n(envP, strctP, key, strlen(key));
}
/*=========================================================================
** xmlrpc_struct_set_value
**=========================================================================
*/
void
xmlrpc_struct_set_value(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key,
xmlrpc_value * const valueP) {
XMLRPC_ASSERT(key != NULL);
xmlrpc_struct_set_value_n(envP, strctP, key, strlen(key), valueP);
}
void
xmlrpc_struct_set_value_n(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
const char * const key,
size_t const key_len,
xmlrpc_value * const valueP) {
xmlrpc_value *keyval;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT(key != NULL);
/* Set up error handling preconditions. */
keyval = NULL;
XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
/* Build an xmlrpc_value from our string. */
keyval = xmlrpc_build_value(envP, "s#", key, key_len);
XMLRPC_FAIL_IF_FAULT(envP);
/* Do the actual work. */
xmlrpc_struct_set_value_v(envP, strctP, keyval, valueP);
cleanup:
if (keyval)
xmlrpc_DECREF(keyval);
}
void
xmlrpc_struct_set_value_v(xmlrpc_env * const envP,
xmlrpc_value * const strctP,
xmlrpc_value * const keyvalP,
xmlrpc_value * const valueP) {
char *key;
size_t key_len;
int index;
_struct_member *members, *member, new_member;
xmlrpc_value *old_value;
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(strctP);
XMLRPC_ASSERT_VALUE_OK(keyvalP);
XMLRPC_ASSERT_VALUE_OK(valueP);
XMLRPC_TYPE_CHECK(envP, strctP, XMLRPC_TYPE_STRUCT);
XMLRPC_TYPE_CHECK(envP, keyvalP, XMLRPC_TYPE_STRING);
key = XMLRPC_MEMBLOCK_CONTENTS(char, &keyvalP->_block);
key_len = XMLRPC_MEMBLOCK_SIZE(char, &keyvalP->_block) - 1;
index = find_member(strctP, key, key_len);
if (index >= 0) {
/* Change the value of an existing member. (But be careful--the
** original and new values might be the same object, so watch
** the order of INCREF and DECREF calls!) */
members = XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &strctP->_block);
member = &members[index];
/* Juggle our references. */
old_value = member->value;
member->value = valueP;
xmlrpc_INCREF(member->value);
xmlrpc_DECREF(old_value);
} else {
/* Add a new member. */
new_member.key_hash = get_hash(key, key_len);
new_member.key = keyvalP;
new_member.value = valueP;
XMLRPC_MEMBLOCK_APPEND(_struct_member, envP, &strctP->_block,
&new_member, 1);
XMLRPC_FAIL_IF_FAULT(envP);
xmlrpc_INCREF(keyvalP);
xmlrpc_INCREF(valueP);
}
cleanup:
return;
}
/* Note that the order of keys and values is undefined, and may change
when you modify the struct.
*/
void
xmlrpc_struct_read_member(xmlrpc_env * const envP,
xmlrpc_value * const structP,
unsigned int const index,
xmlrpc_value ** const keyvalP,
xmlrpc_value ** const valueP) {
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_PTR_OK(keyvalP);
XMLRPC_ASSERT_PTR_OK(valueP);
if (structP->_type != XMLRPC_TYPE_STRUCT)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_TYPE_ERROR, "Attempt to read a struct member "
"of something that is not a struct");
else {
_struct_member * const members =
XMLRPC_MEMBLOCK_CONTENTS(_struct_member, &structP->_block);
size_t const size =
XMLRPC_MEMBLOCK_SIZE(_struct_member, &structP->_block);
if (index >= size)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "Index %u is beyond the end of "
"the %u-member structure", index, (unsigned int)size);
else {
_struct_member * const memberP = &members[index];
*keyvalP = memberP->key;
xmlrpc_INCREF(memberP->key);
*valueP = memberP->value;
xmlrpc_INCREF(memberP->value);
}
}
}
void
xmlrpc_struct_get_key_and_value(xmlrpc_env * const envP,
xmlrpc_value * const structP,
int const index,
xmlrpc_value ** const keyvalP,
xmlrpc_value ** const valueP) {
/*----------------------------------------------------------------------------
Same as xmlrpc_struct_read_member(), except doesn't take a reference
to the returned value.
This is obsolete.
-----------------------------------------------------------------------------*/
XMLRPC_ASSERT_ENV_OK(envP);
XMLRPC_ASSERT_VALUE_OK(structP);
XMLRPC_ASSERT_PTR_OK(keyvalP);
XMLRPC_ASSERT_PTR_OK(valueP);
if (index < 0)
xmlrpc_env_set_fault_formatted(
envP, XMLRPC_INDEX_ERROR, "Index %d is negative.");
else {
xmlrpc_struct_read_member(envP, structP, index, keyvalP, valueP);
if (!envP->fault_occurred) {
xmlrpc_DECREF(*keyvalP);
xmlrpc_DECREF(*valueP);
}
}
if (envP->fault_occurred) {
*keyvalP = NULL;
*valueP = NULL;
}
}
+75
View File
@@ -0,0 +1,75 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_int.h"
const char *
xmlrpc_makePrintable(const char * const input) {
/*----------------------------------------------------------------------------
Convert an arbitrary string of bytes (null-terminated, though) to
printable ASCII. E.g. convert newlines to "\n".
Return the result in newly malloc'ed storage. Return NULL if we can't
get the storage.
-----------------------------------------------------------------------------*/
char * output;
const unsigned int inputLength = strlen(input);
output = malloc(inputLength*4+1);
if (output != NULL) {
unsigned int inputCursor, outputCursor;
for (inputCursor = 0, outputCursor = 0;
inputCursor < inputLength;
++inputCursor) {
if (isprint(input[inputCursor]))
output[outputCursor++] = input[inputCursor];
else if (input[inputCursor] == '\n') {
output[outputCursor++] = '\\';
output[outputCursor++] = 'n';
} else if (input[inputCursor] == '\t') {
output[outputCursor++] = '\\';
output[outputCursor++] = 't';
} else if (input[inputCursor] == '\a') {
output[outputCursor++] = '\\';
output[outputCursor++] = 'a';
} else if (input[inputCursor] == '\r') {
output[outputCursor++] = '\\';
output[outputCursor++] = 'r';
} else {
snprintf(&output[outputCursor], 4, "\\x%02x",
input[inputCursor]);
}
}
output[outputCursor++] = '\0';
}
return output;
}
const char *
xmlrpc_makePrintableChar(char const input) {
const char * retval;
if (input == '\0')
retval = strdup("\\0");
else {
char buffer[2];
buffer[0] = input;
buffer[1] = '\0';
retval = xmlrpc_makePrintable(buffer);
}
return retval;
}
+394
View File
@@ -0,0 +1,394 @@
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
#include "xmlrpc_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/base_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;
}
static void
set_fault_formatted_v(xmlrpc_env * const envP,
int const code,
const char * const format,
va_list const args) {
char buffer[ERROR_BUFFER_SZ];
vsnprintf(buffer, ERROR_BUFFER_SZ, format, 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_env_set_fault_formatted(xmlrpc_env * const envP,
int const code,
const char * const format,
...) {
va_list args;
XMLRPC_ASSERT(envP != NULL);
XMLRPC_ASSERT(format != NULL);
/* Print our error message to the buffer. */
va_start(args, format);
set_fault_formatted_v(envP, code, format, args);
va_end(args);
}
void
xmlrpc_faultf(xmlrpc_env * const envP,
const char * const format,
...) {
va_list args;
XMLRPC_ASSERT(envP != NULL);
XMLRPC_ASSERT(format != NULL);
/* Print our error message to the buffer. */
va_start(args, format);
set_fault_formatted_v(envP, XMLRPC_INTERNAL_ERROR, format, args);
va_end(args);
}
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
**=========================================================================
*/
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;
}
void
xmlrpc_mem_block_append(xmlrpc_env * const env,
xmlrpc_mem_block * const block,
const void * const data,
size_t const len) {
int size;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT(block != NULL);
size = block->_size;
xmlrpc_mem_block_resize(env, block, size + len);
XMLRPC_FAIL_IF_FAULT(env);
memcpy(((unsigned char*) block->_block) + size, data, len);
cleanup:
return;
}
void
xmlrpc_traceXml(const char * const label,
const char * const xml,
unsigned int const xmlLength) {
if (getenv("XMLRPC_TRACE_XML")) {
unsigned int nonPrintableCount;
unsigned int i;
nonPrintableCount = 0; /* Initial value */
for (i = 0; i < xmlLength; ++i) {
if (!isprint(xml[i]) && xml[i] != '\n' && xml[i] != '\r')
++nonPrintableCount;
}
if (nonPrintableCount > 0)
fprintf(stderr, "%s contains %u nonprintable characters.\n",
label, nonPrintableCount);
fprintf(stderr, "%s:\n\n", label);
fprintf(stderr, "%.*s\n", (int)xmlLength, xml);
}
}
+143
View File
@@ -0,0 +1,143 @@
/* 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 "pthreadx.h"
#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 (); */
}
+376
View File
@@ -0,0 +1,376 @@
/* Copyright (C) 2001 by Eric Kidd. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE. */
/*=========================================================================
** XML-RPC UTF-8 Utilities
**=========================================================================
** Routines for validating, encoding and decoding UTF-8 data. We try to
** be very, very strict about invalid UTF-8 data.
**
** All of the code in this file assumes that your machine represents
** wchar_t as a 16-bit (or wider) character containing UCS-2 data. If this
** assumption is incorrect, you may need to replace this file.
**
** For lots of information on Unicode and UTF-8 decoding, see:
** http://www.cl.cam.ac.uk/~mgk25/unicode.html
*/
#include "xmlrpc_config.h"
#include "xmlrpc-c/base.h"
#ifdef HAVE_UNICODE_WCHAR
/*=========================================================================
** Tables and Constants
**=========================================================================
** We use a variety of tables and constants to help decode and validate
** UTF-8 data.
*/
/* The number of bytes in a UTF-8 sequence starting with the character used
** as the array index. A zero entry indicates an illegal initial byte.
** This table was generated using a Perl script and information from the
** UTF-8 standard.
**
** Fredrik Lundh's UTF-8 decoder Python 2.0 uses a similar table. But
** since Python 2.0 has the icky CNRI license, I regenerated this
** table from scratch and wrote my own decoder. */
static unsigned char utf8_seq_length[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0
};
/* The minimum legal character value for a UTF-8 sequence of the given
** length. We have to check this to avoid accepting "overlong" UTF-8
** sequences, which use more bytes than necessary to encode a given
** character. Such sequences are commonly used by evil people to bypass
** filters and security checks. This table is based on the UTF-8-test.txt
** file by Markus Kuhn <mkuhn@acm.org>. */
static wchar_t utf8_min_char_for_length[4] = {
0, /* Length 0: Not used (meaningless) */
0x0000, /* Length 1: Not used (special-cased) */
0x0080, /* Length 2 */
0x0800 /* Length 3 */
#if 0
/* These are only useful on systems where wchar_t is 32-bits wide
** and supports full UCS-4. */
0x00010000, /* Length 4 */
0x00200000, /* Length 5 */
0x04000000 /* Length 6 */
#endif
};
/* This is the maximum legal 16-byte (UCS-2) character. Again, this
** information is based on UTF-8-test.txt. */
#define UCS2_MAX_LEGAL_CHARACTER (0xFFFD)
/* First and last UTF-16 surrogate characters. These are *not* legal UCS-2
** characters--they're used to code for UCS-4 characters when using
** UTF-16. They should never appear in decoded UTF-8 data! Again, these
** could hypothetically be used to bypass security measures on some machines.
** Based on UTF-8-test.txt. */
#define UTF16_FIRST_SURROGATE (0xD800)
#define UTF16_LAST_SURROGATE (0xDFFF)
/* Is the character 'c' a UTF-8 continuation character? */
#define IS_CONTINUATION(c) (((c) & 0xC0) == 0x80)
/* Maximum number of bytes needed to encode a supported character. */
#define MAX_ENCODED_BYTES (3)
/*=========================================================================
** decode_utf8
**=========================================================================
** Internal routine which decodes (or validates) a UTF-8 string.
** To validate, set io_buff and out_buff_len to NULL. To decode, allocate
** a sufficiently large buffer, pass it as io_buff, and pass a pointer as
** as out_buff_len. The data will be written to the buffer, and the
** length to out_buff_len.
**
** We assume that wchar_t holds a single UCS-2 character in native-endian
** byte ordering.
*/
static void
decode_utf8(xmlrpc_env * const env,
const char * const utf8_data,
size_t const utf8_len,
wchar_t * const io_buff,
size_t * const out_buff_len) {
size_t i, length, out_pos;
char init, con1, con2;
wchar_t wc;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(utf8_data);
XMLRPC_ASSERT((!io_buff && !out_buff_len) ||
(io_buff && out_buff_len));
/* Suppress GCC warning about possibly undefined variable. */
wc = 0;
i = 0;
out_pos = 0;
while (i < utf8_len) {
init = utf8_data[i];
if ((init & 0x80) == 0x00) {
/* Convert ASCII character to wide character. */
wc = init;
i++;
} else {
/* Look up the length of this UTF-8 sequence. */
length = utf8_seq_length[(unsigned char) init];
/* Check to make sure we have enough bytes to convert. */
if (i + length > utf8_len)
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"Truncated UTF-8 sequence");
/* Decode a multibyte UTF-8 sequence. */
switch (length) {
case 0:
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"Invalid UTF-8 initial byte");
case 2:
/* 110xxxxx 10xxxxxx */
con1 = utf8_data[i+1];
if (!IS_CONTINUATION(con1))
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"UTF-8 sequence too short");
wc = ((((wchar_t) (init & 0x1F)) << 6) |
(((wchar_t) (con1 & 0x3F))));
break;
case 3:
/* 1110xxxx 10xxxxxx 10xxxxxx */
con1 = utf8_data[i+1];
con2 = utf8_data[i+2];
if (!IS_CONTINUATION(con1) || !IS_CONTINUATION(con2))
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"UTF-8 sequence too short");
wc = ((((wchar_t) (init & 0x0F)) << 12) |
(((wchar_t) (con1 & 0x3F)) << 6) |
(((wchar_t) (con2 & 0x3F))));
break;
case 4:
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
case 5:
/* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
case 6:
/* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"UCS-4 characters not supported");
default:
XMLRPC_ASSERT("Error in UTF-8 decoder tables");
}
/* Advance to the end of the sequence. */
i += length;
/* Check for illegal UCS-2 characters. */
if (wc > UCS2_MAX_LEGAL_CHARACTER)
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"UCS-2 characters > U+FFFD are illegal");
/* Check for UTF-16 surrogates. */
if (UTF16_FIRST_SURROGATE <= wc && wc <= UTF16_LAST_SURROGATE)
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"UTF-16 surrogates may not appear in UTF-8 data");
/* Check for overlong sequences. */
if (wc < utf8_min_char_for_length[length])
XMLRPC_FAIL(env, XMLRPC_INVALID_UTF8_ERROR,
"Overlong UTF-8 sequence not allowed");
}
/* If we have a buffer, write our character to it. */
if (io_buff) {
io_buff[out_pos++] = wc;
}
}
/* Record the number of characters we found. */
if (out_buff_len)
*out_buff_len = out_pos;
cleanup:
if (env->fault_occurred) {
if (out_buff_len)
*out_buff_len = 0;
}
}
/*=========================================================================
** xmlrpc_validate_utf8
**=========================================================================
** Make sure that a UTF-8 string is valid.
*/
void
xmlrpc_validate_utf8 (xmlrpc_env * const env,
const char * const utf8_data,
size_t const utf8_len) {
decode_utf8(env, utf8_data, utf8_len, NULL, NULL);
}
/*=========================================================================
** xmlrpc_utf8_to_wcs
**=========================================================================
** Decode UTF-8 string to a "wide character string". This function
** returns an xmlrpc_mem_block with an element type of wchar_t. Don't
** try to intepret the block in a bytewise fashion--it won't work in
** any useful or portable fashion.
*/
xmlrpc_mem_block *xmlrpc_utf8_to_wcs (xmlrpc_env *env,
char *utf8_data,
size_t utf8_len)
{
xmlrpc_mem_block *output;
size_t wcs_length;
/* Error-handling preconditions. */
output = NULL;
/* Allocate a memory block large enough to hold any possible output.
** We assume that each byte of the input may decode to a whcar_t. */
output = XMLRPC_TYPED_MEM_BLOCK_NEW(wchar_t, env, utf8_len);
XMLRPC_FAIL_IF_FAULT(env);
/* Decode the UTF-8 data. */
decode_utf8(env, utf8_data, utf8_len,
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, output),
&wcs_length);
XMLRPC_FAIL_IF_FAULT(env);
/* Make sure we didn't overrun our buffer. */
XMLRPC_ASSERT(wcs_length <= utf8_len);
/* Correct the length of the memory block. */
XMLRPC_TYPED_MEM_BLOCK_RESIZE(wchar_t, env, output, wcs_length);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (env->fault_occurred) {
if (output)
xmlrpc_mem_block_free(output);
return NULL;
}
return output;
}
/*=========================================================================
** xmlrpc_utf8_to_wcs
**=========================================================================
** Encode a "wide character string" as UTF-8.
*/
xmlrpc_mem_block *xmlrpc_wcs_to_utf8 (xmlrpc_env *env,
wchar_t *wcs_data,
size_t wcs_len)
{
size_t estimate, bytes_used, i;
xmlrpc_mem_block *output;
unsigned char *buffer;
wchar_t wc;
XMLRPC_ASSERT_ENV_OK(env);
XMLRPC_ASSERT_PTR_OK(wcs_data);
/* Error-handling preconditions. */
output = NULL;
/* Allocate a memory block large enough to hold any possible output.
** We assume that every wchar might encode to the maximum length. */
estimate = wcs_len * MAX_ENCODED_BYTES;
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, estimate);
XMLRPC_FAIL_IF_FAULT(env);
/* Output our characters. */
buffer = (unsigned char*) XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output);
bytes_used = 0;
for (i = 0; i < wcs_len; i++) {
wc = wcs_data[i];
if (wc <= 0x007F) {
buffer[bytes_used++] = wc & 0x7F;
} else if (wc <= 0x07FF) {
/* 110xxxxx 10xxxxxx */
buffer[bytes_used++] = 0xC0 | (wc >> 6);
buffer[bytes_used++] = 0x80 | (wc & 0x3F);
} else if (wc <= 0xFFFF) {
/* 1110xxxx 10xxxxxx 10xxxxxx */
buffer[bytes_used++] = 0xE0 | (wc >> 12);
buffer[bytes_used++] = 0x80 | ((wc >> 6) & 0x3F);
buffer[bytes_used++] = 0x80 | (wc & 0x3F);
} else {
XMLRPC_FAIL(env, XMLRPC_INTERNAL_ERROR,
"Don't know how to encode UCS-4 characters yet");
}
}
/* Make sure we didn't overrun our buffer. */
XMLRPC_ASSERT(bytes_used <= estimate);
/* Correct the length of the memory block. */
XMLRPC_TYPED_MEM_BLOCK_RESIZE(char, env, output, bytes_used);
XMLRPC_FAIL_IF_FAULT(env);
cleanup:
if (env->fault_occurred) {
if (output)
xmlrpc_mem_block_free(output);
return NULL;
}
return output;
}
#endif /* HAVE_UNICODE_WCHAR */