mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-10-30 07:04:17 +00:00
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:
1 parent
6d9679b164
commit
3abb7730b2
338 files changed
+98032
-2
No files matched your search
@@ -0,0 +1,6 @@
|
||||
Makefile
|
||||
rpctest
|
||||
rpctest.static
|
||||
cpptest
|
||||
efrpctest
|
||||
efrpctest_wrapper
|
||||
@@ -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
|
||||
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
cpptest
|
||||
@@ -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.
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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, ¶mArrayP, 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, ¶mP);
|
||||
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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 = ®istry;
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
Makefile
|
||||
test
|
||||
cgitest1
|
||||
@@ -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");
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
void
|
||||
test_server_cgi(void);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
void
|
||||
test_client(void);
|
||||
@@ -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>
|
||||
@@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
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, ¶ms);
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
void
|
||||
test_parse_xml(void);
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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><&></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");
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
void
|
||||
test_serialize(void);
|
||||
@@ -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");
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
void
|
||||
test_server_abyss(void);
|
||||
@@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
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. */
|
||||
@@ -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
@@ -0,0 +1,2 @@
|
||||
void
|
||||
test_value(void);
|
||||
@@ -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! <&></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&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 <&></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};
|
||||
|
||||
|
||||
@@ -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[]);
|
||||
@@ -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. */
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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. */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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, ¶mArray);
|
||||
|
||||
if (!fault.fault_occurred) {
|
||||
xmlrpc_value * result;
|
||||
|
||||
dispatch_call(&fault, registryP, methodName, paramArray, &result);
|
||||
|
||||
if (!fault.fault_occurred) {
|
||||
xmlrpc_serialize_response(envP, output, result);
|
||||
|
||||
/* A comment here used to say that
|
||||
xmlrpc_serialize_response() could fail and "leave
|
||||
stuff in the buffer." Don't know what that means,
|
||||
but it sounds like something that needs to be
|
||||
fixed. The old code aborted the program here if
|
||||
xmlrpc_serialize_repsonse() failed. 04.11.17
|
||||
*/
|
||||
xmlrpc_DECREF(result);
|
||||
}
|
||||
xmlrpc_strfree(methodName);
|
||||
xmlrpc_DECREF(paramArray);
|
||||
}
|
||||
if (!envP->fault_occurred && fault.fault_occurred)
|
||||
xmlrpc_serialize_fault(envP, output, &fault);
|
||||
|
||||
xmlrpc_env_clean(&fault);
|
||||
|
||||
if (envP->fault_occurred)
|
||||
XMLRPC_MEMBLOCK_FREE(char, output);
|
||||
else
|
||||
xmlrpc_traceXml("XML-RPC RESPONSE",
|
||||
XMLRPC_MEMBLOCK_CONTENTS(char, output),
|
||||
XMLRPC_MEMBLOCK_SIZE(char, output));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
** system.multicall
|
||||
**=========================================================================
|
||||
** Low-tech support for transparent, boxed methods.
|
||||
*/
|
||||
|
||||
static char *multicall_help =
|
||||
"Process an array of calls, and return an array of results. Calls should "
|
||||
"be structs of the form {'methodName': string, 'params': array}. Each "
|
||||
"result will either be a single-item array containg the result value, or "
|
||||
"a struct of the form {'faultCode': int, 'faultString': string}. This "
|
||||
"is useful when you need to make lots of small calls without lots of "
|
||||
"round trips.";
|
||||
|
||||
static xmlrpc_value *
|
||||
call_one_method(xmlrpc_env *env, xmlrpc_registry *registry,
|
||||
xmlrpc_value *method_info) {
|
||||
|
||||
xmlrpc_value *result_val, *result;
|
||||
char *method_name;
|
||||
xmlrpc_value *param_array;
|
||||
|
||||
/* Error-handling preconditions. */
|
||||
result = result_val = NULL;
|
||||
|
||||
/* Extract our method name and parameters. */
|
||||
xmlrpc_parse_value(env, method_info, "{s:s,s:A,*}",
|
||||
"methodName", &method_name,
|
||||
"params", ¶m_array);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Watch out for a deep recursion attack. */
|
||||
if (strcmp(method_name, "system.multicall") == 0)
|
||||
XMLRPC_FAIL(env, XMLRPC_REQUEST_REFUSED_ERROR,
|
||||
"Recursive system.multicall strictly forbidden");
|
||||
|
||||
/* Perform the call. */
|
||||
dispatch_call(env, registry, method_name, param_array, &result_val);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Build our one-item result array. */
|
||||
result = xmlrpc_build_value(env, "(V)", result_val);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
cleanup:
|
||||
if (result_val)
|
||||
xmlrpc_DECREF(result_val);
|
||||
if (env->fault_occurred) {
|
||||
if (result)
|
||||
xmlrpc_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static xmlrpc_value *
|
||||
system_multicall(xmlrpc_env *env,
|
||||
xmlrpc_value *param_array,
|
||||
void *user_data) {
|
||||
|
||||
xmlrpc_registry *registry;
|
||||
xmlrpc_value *methlist, *methinfo, *results, *result;
|
||||
size_t size, i;
|
||||
xmlrpc_env env2;
|
||||
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
XMLRPC_ASSERT_VALUE_OK(param_array);
|
||||
XMLRPC_ASSERT_PTR_OK(user_data);
|
||||
|
||||
/* Error-handling preconditions. */
|
||||
results = result = NULL;
|
||||
xmlrpc_env_init(&env2);
|
||||
|
||||
/* Turn our arguments into something more useful. */
|
||||
registry = (xmlrpc_registry*) user_data;
|
||||
xmlrpc_parse_value(env, param_array, "(A)", &methlist);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Create an empty result list. */
|
||||
results = xmlrpc_build_value(env, "()");
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Loop over our input list, calling each method in turn. */
|
||||
size = xmlrpc_array_size(env, methlist);
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
for (i = 0; i < size; i++) {
|
||||
methinfo = xmlrpc_array_get_item(env, methlist, i);
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
|
||||
/* Call our method. */
|
||||
xmlrpc_env_clean(&env2);
|
||||
xmlrpc_env_init(&env2);
|
||||
result = call_one_method(&env2, registry, methinfo);
|
||||
|
||||
/* Turn any fault into a structure. */
|
||||
if (env2.fault_occurred) {
|
||||
XMLRPC_ASSERT(result == NULL);
|
||||
result =
|
||||
xmlrpc_build_value(env, "{s:i,s:s}",
|
||||
"faultCode", (xmlrpc_int32) env2.fault_code,
|
||||
"faultString", env2.fault_string);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
}
|
||||
|
||||
/* Append this method result to our master array. */
|
||||
xmlrpc_array_append_item(env, results, result);
|
||||
xmlrpc_DECREF(result);
|
||||
result = NULL;
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
xmlrpc_env_clean(&env2);
|
||||
if (result)
|
||||
xmlrpc_DECREF(result);
|
||||
if (env->fault_occurred) {
|
||||
if (results)
|
||||
xmlrpc_DECREF(results);
|
||||
return NULL;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
** system.listMethods
|
||||
**=========================================================================
|
||||
** List all available methods by name.
|
||||
*/
|
||||
|
||||
static char *listMethods_help =
|
||||
"Return an array of all available XML-RPC methods on this server.";
|
||||
|
||||
static xmlrpc_value *
|
||||
system_listMethods(xmlrpc_env *env,
|
||||
xmlrpc_value *param_array,
|
||||
void *user_data) {
|
||||
|
||||
xmlrpc_registry *registry;
|
||||
xmlrpc_value *method_names, *method_name, *method_info;
|
||||
size_t size, i;
|
||||
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
XMLRPC_ASSERT_VALUE_OK(param_array);
|
||||
XMLRPC_ASSERT_PTR_OK(user_data);
|
||||
|
||||
/* Error-handling preconditions. */
|
||||
method_names = NULL;
|
||||
|
||||
/* Turn our arguments into something more useful. */
|
||||
registry = (xmlrpc_registry*) user_data;
|
||||
xmlrpc_parse_value(env, param_array, "()");
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Make sure we're allowed to introspect. */
|
||||
if (!registry->_introspection_enabled)
|
||||
XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
|
||||
"Introspection disabled for security reasons");
|
||||
|
||||
/* Iterate over all the methods in the registry, adding their names
|
||||
** to a list. */
|
||||
method_names = xmlrpc_build_value(env, "()");
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
size = xmlrpc_struct_size(env, registry->_methods);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
for (i = 0; i < size; i++) {
|
||||
xmlrpc_struct_get_key_and_value(env, registry->_methods, i,
|
||||
&method_name, &method_info);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
xmlrpc_array_append_item(env, method_names, method_name);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (env->fault_occurred) {
|
||||
if (method_names)
|
||||
xmlrpc_DECREF(method_names);
|
||||
return NULL;
|
||||
}
|
||||
return method_names;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
** system.methodHelp
|
||||
**=========================================================================
|
||||
** Get the help string for a particular method.
|
||||
*/
|
||||
|
||||
static char *methodHelp_help =
|
||||
"Given the name of a method, return a help string.";
|
||||
|
||||
static xmlrpc_value *
|
||||
system_methodHelp(xmlrpc_env *env,
|
||||
xmlrpc_value *param_array,
|
||||
void *user_data) {
|
||||
|
||||
xmlrpc_registry *registry;
|
||||
char *method_name;
|
||||
xmlrpc_value *ignored1, *ignored2, *ignored3, *help;
|
||||
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
XMLRPC_ASSERT_VALUE_OK(param_array);
|
||||
XMLRPC_ASSERT_PTR_OK(user_data);
|
||||
|
||||
/* Turn our arguments into something more useful. */
|
||||
registry = (xmlrpc_registry*) user_data;
|
||||
xmlrpc_parse_value(env, param_array, "(s)", &method_name);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Make sure we're allowed to introspect. */
|
||||
if (!registry->_introspection_enabled)
|
||||
XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
|
||||
"Introspection disabled for security reasons");
|
||||
|
||||
/* Get our documentation string. */
|
||||
xmlrpc_parse_value(env, registry->_methods, "{s:(VVVV*),*}",
|
||||
method_name, &ignored1, &ignored2, &ignored3, &help);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
cleanup:
|
||||
if (env->fault_occurred)
|
||||
return NULL;
|
||||
xmlrpc_INCREF(help);
|
||||
return help;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
** system.methodSignature
|
||||
**=========================================================================
|
||||
** Return an array of arrays describing possible signatures for this
|
||||
** method.
|
||||
**
|
||||
** XXX - This is the ugliest function in the entire library.
|
||||
*/
|
||||
|
||||
static char *methodSignature_help =
|
||||
"Given the name of a method, return an array of legal signatures. "
|
||||
"Each signature is an array of strings. The first item of each signature "
|
||||
"is the return type, and any others items are parameter types.";
|
||||
|
||||
static char *bad_sig_str =
|
||||
"Application has incorrect method signature information";
|
||||
|
||||
#define BAD_SIG(env) \
|
||||
XMLRPC_FAIL((env), XMLRPC_INTERNAL_ERROR, bad_sig_str);
|
||||
|
||||
static xmlrpc_value *
|
||||
system_methodSignature(xmlrpc_env *env,
|
||||
xmlrpc_value *param_array,
|
||||
void *user_data) {
|
||||
|
||||
xmlrpc_registry *registry;
|
||||
char *method_name;
|
||||
xmlrpc_value *ignored1, *ignored2, *ignored3;
|
||||
xmlrpc_value *item, *current, *result;
|
||||
int at_sig_start;
|
||||
char *sig, *code;
|
||||
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
XMLRPC_ASSERT_VALUE_OK(param_array);
|
||||
XMLRPC_ASSERT_PTR_OK(user_data);
|
||||
|
||||
/* Error-handling preconditions. */
|
||||
item = current = result = NULL;
|
||||
|
||||
/* Turn our arguments into something more useful. */
|
||||
registry = (xmlrpc_registry*) user_data;
|
||||
xmlrpc_parse_value(env, param_array, "(s)", &method_name);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Make sure we're allowed to introspect. */
|
||||
if (!registry->_introspection_enabled)
|
||||
XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
|
||||
"Introspection disabled for security reasons");
|
||||
|
||||
/* Get our signature string. */
|
||||
xmlrpc_parse_value(env, registry->_methods, "{s:(VVsV*),*}",
|
||||
method_name, &ignored1, &ignored2, &sig, &ignored3);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
if (sig[0] == '?' && sig[1] == '\0') {
|
||||
/* No signature supplied. */
|
||||
result = xmlrpc_build_value(env, "s", "undef");
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
} else {
|
||||
/* Build an array of arrays. */
|
||||
current = xmlrpc_build_value(env, "()");
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
result = xmlrpc_build_value(env, "(V)", current);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
at_sig_start = 1;
|
||||
|
||||
do {
|
||||
next_loop:
|
||||
|
||||
/* Process the current code. */
|
||||
switch (*(sig++)) {
|
||||
case 'i': code = "int"; break;
|
||||
case 'b': code = "boolean"; break;
|
||||
case 'd': code = "double"; break;
|
||||
case 's': code = "string"; break;
|
||||
case '8': code = "dateTime.iso8601"; break;
|
||||
case '6': code = "base64"; break;
|
||||
case 'S': code = "struct"; break;
|
||||
case 'A': code = "array"; break;
|
||||
|
||||
case ',':
|
||||
/* Start a new signature array. */
|
||||
if (at_sig_start)
|
||||
BAD_SIG(env);
|
||||
xmlrpc_DECREF(current);
|
||||
current = xmlrpc_build_value(env, "()");
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
xmlrpc_array_append_item(env, result, current);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
at_sig_start = 1;
|
||||
goto next_loop;
|
||||
|
||||
default:
|
||||
BAD_SIG(env);
|
||||
}
|
||||
|
||||
/* Append the appropriate string to our current signature. */
|
||||
item = xmlrpc_build_value(env, "s", code);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
xmlrpc_array_append_item(env, current, item);
|
||||
xmlrpc_DECREF(item);
|
||||
item = NULL;
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Advance to the next code, and skip over ':' if necessary. */
|
||||
if (at_sig_start) {
|
||||
if (*sig != ':')
|
||||
BAD_SIG(env);
|
||||
sig++;
|
||||
at_sig_start = 0;
|
||||
}
|
||||
|
||||
} while (*sig != '\0');
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (item)
|
||||
xmlrpc_DECREF(item);
|
||||
if (current)
|
||||
xmlrpc_DECREF(current);
|
||||
if (env->fault_occurred) {
|
||||
if (result)
|
||||
xmlrpc_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
** install_system_methods
|
||||
**=========================================================================
|
||||
** Install the standard methods under system.*.
|
||||
** This particular function is highly experimental, and may disappear
|
||||
** without warning.
|
||||
*/
|
||||
|
||||
static void
|
||||
install_system_methods(xmlrpc_env *env, xmlrpc_registry *registry) {
|
||||
|
||||
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
|
||||
"system.listMethods",
|
||||
&system_listMethods, registry,
|
||||
"A:", listMethods_help);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
|
||||
"system.methodSignature",
|
||||
&system_methodSignature, registry,
|
||||
"A:s", methodSignature_help);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
|
||||
"system.methodHelp",
|
||||
&system_methodHelp, registry,
|
||||
"s:s", methodHelp_help);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
xmlrpc_registry_add_method_w_doc(env, registry, NULL,
|
||||
"system.multicall",
|
||||
&system_multicall, registry,
|
||||
"A:A", multicall_help);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
cleanup:
|
||||
return;
|
||||
}
|
||||
@@ -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; /* < */
|
||||
else if (str[i] == '>')
|
||||
needed += 4; /* > */
|
||||
else if (str[i] == '&')
|
||||
needed += 5; /* & */
|
||||
else
|
||||
needed++;
|
||||
}
|
||||
|
||||
/* Allocate our memory block. */
|
||||
retval = XMLRPC_TYPED_MEM_BLOCK_NEW(char, env, needed);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
/* Copy over the newly-allocated data. */
|
||||
out = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, retval);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (str[i] == '<') {
|
||||
*out++ = '&';
|
||||
*out++ = 'l';
|
||||
*out++ = 't';
|
||||
*out++ = ';';
|
||||
} else if (str[i] == '>') {
|
||||
*out++ = '&';
|
||||
*out++ = 'g';
|
||||
*out++ = 't';
|
||||
*out++ = ';';
|
||||
} else if (str[i] == '&') {
|
||||
*out++ = '&';
|
||||
*out++ = 'a';
|
||||
*out++ = 'm';
|
||||
*out++ = 'p';
|
||||
*out++ = ';';
|
||||
} else {
|
||||
*out++ = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (env->fault_occurred) {
|
||||
if (retval)
|
||||
XMLRPC_TYPED_MEM_BLOCK_FREE(char, retval);
|
||||
retval = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static xmlrpc_mem_block*
|
||||
escape_block (xmlrpc_env *env,
|
||||
xmlrpc_mem_block *block) {
|
||||
|
||||
XMLRPC_ASSERT_ENV_OK(env);
|
||||
XMLRPC_ASSERT(block != NULL);
|
||||
|
||||
return escape_string(env,
|
||||
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, block),
|
||||
XMLRPC_TYPED_MEM_BLOCK_SIZE(char, block));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
** xmlrpc_serialize_string_data
|
||||
**=========================================================================
|
||||
** Escape and print the contents of a string.
|
||||
*/
|
||||
|
||||
static void
|
||||
xmlrpc_serialize_string_data(xmlrpc_env *env,
|
||||
xmlrpc_mem_block *output,
|
||||
xmlrpc_value *string) {
|
||||
|
||||
xmlrpc_mem_block *escaped;
|
||||
char *contents;
|
||||
size_t size;
|
||||
|
||||
/* Since this routine can only be called internally, we only need
|
||||
** an assertion here, not a runtime type check.
|
||||
** XXX - Temporarily disabled because we're using this code to
|
||||
** print <dateTime.iso8601> values as well. */
|
||||
/* XMLRPC_ASSERT(string->_type == XMLRPC_TYPE_STRING); */
|
||||
|
||||
/* Escape any '&' and '<' characters in the string. */
|
||||
escaped = escape_block(env, &string->_block);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, escaped);
|
||||
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, escaped) - 1;
|
||||
|
||||
/* Print the string. */
|
||||
XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, contents, size);
|
||||
XMLRPC_FAIL_IF_FAULT(env);
|
||||
|
||||
cleanup:
|
||||
if (escaped)
|
||||
XMLRPC_TYPED_MEM_BLOCK_FREE(char, escaped);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 (); */
|
||||
}
|
||||
@@ -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 */
|
||||
Reference in new issue
Block a user