p(buffer); // To make it go away when we leave
++
++ size_t count;
++
++ count = fread(buffer, sizeof(buffer[0]), length, fileP);
++ if (count < length)
++ throwf("Expected %lu bytes, received %lu",
++ (unsigned long) length, (unsigned long) count);
++
++ return string(buffer, length);
++}
++
++
++
++static void
++writeNormalHttpResp(FILE * const fileP,
++ bool const sendCookie,
++ string const& authCookie,
++ string const& httpBody) {
++
++ setModeBinary(fileP);
++
++ // HTTP headers
++
++ fprintf(fileP, "Status: 200 OK\n");
++
++ if (sendCookie)
++ fprintf(fileP, "Set-Cookie: auth=%s\n", authCookie.c_str());
++
++ fprintf(fileP, "Content-type: text/xml; charset=\"utf-8\"\n");
++ fprintf(fileP, "Content-length: %u\n", (unsigned)httpBody.size());
++ fprintf(fileP, "\n");
++
++ // HTTP body
++
++ fwrite(httpBody.c_str(), sizeof(char), httpBody.size(), fileP);
++}
++
++
++
++void
++processCall2(const registry * const registryP,
++ FILE * const callFileP,
++ unsigned int const callSize,
++ bool const sendCookie,
++ string const& authCookie,
++ FILE * const respFileP) {
++
++ if (callSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
++ throw(xmlrpc_c::fault(string("XML-RPC call is too large"),
++ fault::CODE_LIMIT_EXCEEDED));
++ else {
++ string const callXml(getHttpBody(callFileP, callSize));
++
++ string responseXml;
++
++ try {
++ registryP->processCall(callXml, &responseXml);
++ } catch (exception const& e) {
++ throw(httpError(500, e.what()));
++ }
++
++ writeNormalHttpResp(respFileP, sendCookie, authCookie, responseXml);
++ }
++}
++
++
++
++
++static void
++sendHttpErrorResp(FILE * const fileP,
++ httpError const& e) {
++
++ setModeBinary(fileP);
++
++ // HTTP headers
++
++ fprintf(fileP, "Status: %d %s\n", e.code, e.msg.c_str());
++ fprintf(fileP, "Content-type: text/html\n");
++ fprintf(fileP, "\n");
++
++ // HTTP body: HTML error message
++
++ fprintf(fileP, "%d %s\n", e.code, e.msg.c_str());
++ fprintf(fileP, "%d %s
\n", e.code, e.msg.c_str());
++ fprintf(fileP, "The Xmlrpc-c CGI server was unable to process "
++ "your request. It could not process it even enough to generate "
++ "an XML-RPC fault response.
\n");
++}
++
++
++
++void
++serverCgi_impl::tryToProcessCall() {
++
++ httpInfo httpInfo;
++
++ if (httpInfo.requestMethod != string("POST"))
++ throw(httpError(405, "Method must be POST"));
++
++ if (!httpInfo.contentTypePresent)
++ throw(httpError(400, "Must have content-type header"));
++
++ if (httpInfo.contentType != string("text/xml"))
++ throw(httpError(400, string("ContentType must be 'text/xml', not '") +
++ httpInfo.contentType + string("'")));
++
++ if (!httpInfo.contentLengthPresent)
++ throw(httpError(411, "Content-length required"));
++
++ processCall2(this->registryP, stdin, httpInfo.contentLength,
++ httpInfo.authCookiePresent, httpInfo.authCookie, stdout);
++}
++
++
++
++void
++serverCgi::processCall() {
++/*----------------------------------------------------------------------------
++ 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.
++-----------------------------------------------------------------------------*/
++ try {
++ this->implP->tryToProcessCall();
++ } catch (httpError const e) {
++ sendHttpErrorResp(stdout, e);
++ }
++}
++
++
++
++} // namespace
diff --git a/libs/xmlrpc-c/src/cpp/server_pstream.cpp b/libs/xmlrpc-c/src/cpp/server_pstream.cpp
index 86f2cc9..a1b19f8 100644
--- a/libs/xmlrpc-c/src/cpp/server_pstream.cpp
@@ -52488,6 +55749,376 @@ index 86f2cc9..a1b19f8 100644
+
+
} // namespace
+diff --git a/libs/xmlrpc-c/src/cpp/server_pstream_conn.cpp b/libs/xmlrpc-c/src/cpp/server_pstream_conn.cpp
+new file mode 100644
+index 0000000..3598164
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/server_pstream_conn.cpp
+@@ -0,0 +1,364 @@
++/*=============================================================================
++ server_pstream
++===============================================================================
++
++ RPC server based on a very simple byte stream and XML-RPC XML
++ (But this is not an XML-RPC server because it doesn't use HTTP).
++
++ The protocol we use is the "packet socket" protocol, which
++ is an Xmlrpc-c invention. It is an almost trivial representation of
++ a sequence of packets on a byte stream.
++
++ You can create a pstream server from any file descriptor from which
++ you can read and write a bidirectional character stream. Typically,
++ it's a TCP socket. Such a server talks to one client its entire life.
++
++ Some day, we'll also have a version that you create from a "listening"
++ socket, which can talk to multiple clients serially (a client connects,
++ does some RPCs, and disconnects).
++
++ By Bryan Henderson 07.05.12.
++
++ Contributed to the public domain by its author.
++=============================================================================*/
++
++#include
++
++#include "xmlrpc-c/girerr.hpp"
++using girerr::throwf;
++#include "xmlrpc-c/packetsocket.hpp"
++
++#include "xmlrpc-c/server_pstream.hpp"
++
++using namespace std;
++
++namespace xmlrpc_c {
++
++
++struct serverPstreamConn::constrOpt_impl {
++
++ constrOpt_impl();
++
++ struct value {
++ xmlrpc_c::registryPtr registryPtr;
++ const xmlrpc_c::registry * registryP;
++ XMLRPC_SOCKET socketFd;
++ } value;
++ struct {
++ bool registryPtr;
++ bool registryP;
++ bool socketFd;
++ } present;
++};
++
++
++
++serverPstreamConn::constrOpt_impl::constrOpt_impl() {
++
++ this->present.socketFd = false;
++ this->present.registryP = false;
++ this->present.registryPtr = false;
++}
++
++
++
++serverPstreamConn::constrOpt::constrOpt() {
++
++ this->implP = new constrOpt_impl();
++}
++
++
++
++serverPstreamConn::constrOpt::~constrOpt() {
++
++ delete(this->implP);
++}
++
++
++
++#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
++serverPstreamConn::constrOpt & \
++serverPstreamConn::constrOpt::OPTION_NAME(TYPE const& arg) { \
++ this->implP->value.OPTION_NAME = arg; \
++ this->implP->present.OPTION_NAME = true; \
++ return *this; \
++}
++
++DEFINE_OPTION_SETTER(socketFd, XMLRPC_SOCKET);
++DEFINE_OPTION_SETTER(registryP, const registry *);
++DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
++
++#undef DEFINE_OPTION_SETTER
++
++
++
++struct serverPstreamConn_impl {
++
++ serverPstreamConn_impl(serverPstreamConn::constrOpt_impl const& opt);
++
++ ~serverPstreamConn_impl();
++
++ void
++ establishRegistry(serverPstreamConn::constrOpt_impl const& opt);
++
++ void
++ establishPacketSocket(serverPstreamConn::constrOpt_impl const& opt);
++
++ void
++ processRecdPacket(packetPtr const callPacketP,
++ callInfo * const callInfoP);
++
++ // 'registryP' is what we actually use; 'registryHolder' just holds a
++ // reference to 'registryP' so the registry doesn't disappear while
++ // this server exists. But note that if the creator doesn't supply
++ // a registryPtr, 'registryHolder' is just a placeholder variable and
++ // the creator is responsible for making sure the registry doesn't
++ // go anywhere while the server exists.
++
++ registryPtr registryHolder;
++ const registry * registryP;
++
++ packetSocket * packetSocketP;
++ // The packet socket over which we received RPCs.
++ // This is permanently connected to our fixed client.
++};
++
++
++
++serverPstreamConn_impl::serverPstreamConn_impl(
++ serverPstreamConn::constrOpt_impl const& opt) {
++
++ this->establishRegistry(opt);
++
++ this->establishPacketSocket(opt);
++}
++
++
++
++serverPstreamConn_impl::~serverPstreamConn_impl() {
++
++ delete(this->packetSocketP);
++}
++
++
++
++void
++serverPstreamConn_impl::establishRegistry(
++ serverPstreamConn::constrOpt_impl const& opt) {
++
++ if (!opt.present.registryP && !opt.present.registryPtr)
++ throwf("You must specify the 'registryP' or 'registryPtr' option");
++ else if (opt.present.registryP && opt.present.registryPtr)
++ throwf("You may not specify both the 'registryP' and "
++ "the 'registryPtr' options");
++ else {
++ if (opt.present.registryP)
++ this->registryP = opt.value.registryP;
++ else {
++ this->registryHolder = opt.value.registryPtr;
++ this->registryP = opt.value.registryPtr.get();
++ }
++ }
++}
++
++
++
++void
++serverPstreamConn_impl::establishPacketSocket(
++ serverPstreamConn::constrOpt_impl const& opt) {
++
++ if (!opt.present.socketFd)
++ throwf("You must provide a 'socketFd' constructor option.");
++
++ auto_ptr packetSocketAP;
++
++ try {
++ auto_ptr p(new packetSocket(opt.value.socketFd));
++ packetSocketAP = p;
++ } catch (exception const& e) {
++ throwf("Unable to create packet socket out of file descriptor %d. %s",
++ opt.value.socketFd, e.what());
++ }
++ this->packetSocketP = packetSocketAP.get();
++ packetSocketAP.release();
++}
++
++
++
++serverPstreamConn::serverPstreamConn(constrOpt const& opt) {
++
++ this->implP = new serverPstreamConn_impl(*opt.implP);
++}
++
++
++
++serverPstreamConn::~serverPstreamConn() {
++
++ delete(this->implP);
++}
++
++
++
++static void
++processCall(const registry * const registryP,
++ packetPtr const& callPacketP,
++ callInfo * const callInfoP,
++ packetPtr * const responsePacketPP) {
++
++ string const callXml(reinterpret_cast(callPacketP->getBytes()),
++ callPacketP->getLength());
++
++ string responseXml;
++
++ registryP->processCall(callXml, callInfoP, &responseXml);
++
++ *responsePacketPP = packetPtr(new packet(responseXml.c_str(),
++ responseXml.length()));
++}
++
++
++
++void
++serverPstreamConn_impl::processRecdPacket(packetPtr const callPacketP,
++ callInfo * const callInfoP) {
++
++ packetPtr responsePacketP;
++ try {
++ processCall(this->registryP, callPacketP, callInfoP, &responsePacketP);
++ } catch (exception const& e) {
++ throwf("Error executing received packet as an XML-RPC RPC. %s",
++ e.what());
++ }
++ try {
++ this->packetSocketP->writeWait(responsePacketP);
++ } catch (exception const& e) {
++ throwf("Failed to write the response to the packet socket. %s",
++ e.what());
++ }
++}
++
++
++
++void
++serverPstreamConn::runOnce(callInfo * const callInfoP,
++ volatile const int * const interruptP,
++ bool * const eofP) {
++/*----------------------------------------------------------------------------
++ Get and execute one RPC from the client.
++
++ Unless *interruptP gets set nonzero first.
++-----------------------------------------------------------------------------*/
++ bool gotPacket;
++ packetPtr callPacketP;
++
++ try {
++ this->implP->packetSocketP->readWait(interruptP, eofP, &gotPacket,
++ &callPacketP);
++ } catch (exception const& e) {
++ throwf("Error reading a packet from the packet socket. %s",
++ e.what());
++ }
++ if (gotPacket)
++ this->implP->processRecdPacket(callPacketP, callInfoP);
++}
++
++
++
++void
++serverPstreamConn::runOnce(volatile const int * const interruptP,
++ bool * const eofP) {
++
++ this->runOnce(NULL, interruptP, eofP);
++}
++
++
++
++void
++serverPstreamConn::runOnce(bool * const eofP) {
++/*----------------------------------------------------------------------------
++ Get and execute one RPC from the client.
++-----------------------------------------------------------------------------*/
++ int const interrupt(0); // Never interrupt
++
++ this->runOnce(&interrupt, eofP);
++}
++
++
++
++void
++serverPstreamConn::runOnceNoWait(callInfo * const callInfoP,
++ bool * const eofP,
++ bool * const didOneP) {
++/*----------------------------------------------------------------------------
++ Get and execute one RPC from the client, unless none has been
++ received yet. Return as *didOneP whether or not one has been
++ received. Unless didOneP is NULL.
++-----------------------------------------------------------------------------*/
++ bool gotPacket;
++ packetPtr callPacketP;
++
++ try {
++ this->implP->packetSocketP->read(eofP, &gotPacket, &callPacketP);
++ } catch (exception const& e) {
++ throwf("Error reading a packet from the packet socket. %s",
++ e.what());
++ }
++ if (gotPacket)
++ this->implP->processRecdPacket(callPacketP, callInfoP);
++
++ if (didOneP)
++ *didOneP = gotPacket;
++}
++
++
++
++void
++serverPstreamConn::runOnceNoWait(bool * const eofP,
++ bool * const didOneP) {
++
++ this->runOnceNoWait(NULL, eofP, didOneP);
++}
++
++
++
++void
++serverPstreamConn::runOnceNoWait(bool * const eofP) {
++/*----------------------------------------------------------------------------
++ Get and execute one RPC from the client, unless none has been
++ received yet.
++-----------------------------------------------------------------------------*/
++ this->runOnceNoWait(eofP, NULL);
++}
++
++
++
++void
++serverPstreamConn::run(callInfo * const callInfoP,
++ volatile const int * const interruptP) {
++
++ for (bool clientHasDisconnected = false;
++ !clientHasDisconnected && !*interruptP;)
++ this->runOnce(callInfoP, interruptP, &clientHasDisconnected);
++}
++
++
++
++void
++serverPstreamConn::run(volatile const int * const interruptP) {
++
++ this->run(NULL, interruptP);
++}
++
++
++
++void
++serverPstreamConn::run() {
++
++ int const interrupt(0); // Never interrupt
++
++ this->run(&interrupt);
++}
++
++
++
++} // namespace
diff --git a/libs/xmlrpc-c/src/cpp/test/Makefile b/libs/xmlrpc-c/src/cpp/test/Makefile
index ff6750f..ccde8f5 100644
--- a/libs/xmlrpc-c/src/cpp/test/Makefile
@@ -52562,6 +56193,81 @@ index ff6750f..ccde8f5 100644
-include Makefile.depend
+include depend.mk
+diff --git a/libs/xmlrpc-c/src/cpp/test/base64.cpp b/libs/xmlrpc-c/src/cpp/test/base64.cpp
+new file mode 100644
+index 0000000..17e4e78
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/test/base64.cpp
+@@ -0,0 +1,54 @@
++#include
++#include
++#include
++
++#include "xmlrpc-c/girerr.hpp"
++using girerr::error;
++#include "xmlrpc-c/base64.hpp"
++
++#include "tools.hpp"
++
++#include "base64.hpp"
++
++using namespace xmlrpc_c;
++using namespace std;
++
++
++
++string
++base64TestSuite::suiteName() {
++ return "base64TestSuite";
++}
++
++
++
++void
++base64TestSuite::runtests(unsigned int const) {
++
++ unsigned char const bytes0Data[] = "This is a test";
++
++ vector bytes0(&bytes0Data[0],
++ &bytes0Data[sizeof(bytes0Data)]);
++
++ string const base64_0("VGhpcyBpcyBhIHRlc3QA");
++
++ string const expectedBase64_0(base64_0 + "\r\n");
++
++ TEST(base64FromBytes(bytes0) == expectedBase64_0);
++
++ TEST(bytesFromBase64(base64_0) == bytes0);
++
++ unsigned char const bytes1Data[] = {0x80, 0xff};
++
++ vector bytes1(&bytes1Data[0],
++ &bytes1Data[sizeof(bytes1Data)]);
++
++ string const base64_1("gP8=");
++
++ string const expectedBase64_1(base64_1 + "\r\n");
++
++ TEST(base64FromBytes(bytes1) == expectedBase64_1);
++
++ TEST(bytesFromBase64(base64_1) == bytes1);
++
++}
+diff --git a/libs/xmlrpc-c/src/cpp/test/base64.hpp b/libs/xmlrpc-c/src/cpp/test/base64.hpp
+new file mode 100644
+index 0000000..72ee02d
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/test/base64.hpp
+@@ -0,0 +1,9 @@
++#include "tools.hpp"
++
++class base64TestSuite : public testSuite {
++
++public:
++ virtual std::string suiteName();
++ virtual void runtests(unsigned int const indentation);
++};
++
diff --git a/libs/xmlrpc-c/src/cpp/test/registry.cpp b/libs/xmlrpc-c/src/cpp/test/registry.cpp
index 6ca265a..c7e6e42 100644
--- a/libs/xmlrpc-c/src/cpp/test/registry.cpp
@@ -54539,6 +58245,601 @@ index 6eadf8d..f8a2b3a 100644
string
clientTestSuite::suiteName() {
return "clientTestSuite";
+diff --git a/libs/xmlrpc-c/src/cpp/test/value.cpp b/libs/xmlrpc-c/src/cpp/test/value.cpp
+new file mode 100644
+index 0000000..f8d8e3f
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/test/value.cpp
+@@ -0,0 +1,453 @@
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include "xmlrpc-c/girerr.hpp"
++using girerr::error;
++#include "transport_config.h"
++#include "xmlrpc-c/base.hpp"
++#include "xmlrpc-c/oldcppwrapper.hpp"
++#include "xmlrpc-c/registry.hpp"
++#include "c_util.h"
++
++#include "tools.hpp"
++
++#include "value.hpp"
++
++using namespace xmlrpc_c;
++using namespace std;
++
++
++
++namespace {
++
++class intTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "intTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ value_int int1(7);
++ TEST(static_cast(int1) == 7);
++ value_int int2(-7);
++ TEST(static_cast(int2) == -7);
++ value val1(int1);
++ TEST(val1.type() == value::TYPE_INT);
++ value_int int3(val1);
++ TEST(static_cast(int3) == 7);
++ try {
++ value_int int4(value_double(3.7));
++ TEST_FAILED("invalid cast double-int suceeded");
++ } catch (error) {}
++
++ value const int1x(toValue(7));
++ TEST(int1x.type() == value::TYPE_INT);
++ TEST(static_cast(value_int(int1x)) == 7);
++
++ int test1x;
++ fromValue(test1x, int1x);
++ TEST(test1x == 7);
++ }
++};
++
++
++
++class doubleTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "doubleTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ value_double double1(3.14);
++ TEST(static_cast(double1) == 3.14);
++ value val1(double1);
++ TEST(val1.type() == value::TYPE_DOUBLE);
++ value_double double2(val1);
++ TEST(static_cast(double2) == 3.14);
++ try {
++ value_double double4(value_int(4));
++ TEST_FAILED("invalid cast int-double suceeded");
++ } catch (error) {}
++
++ value const double1x(toValue(3.14));
++ TEST(double1x.type() == value::TYPE_DOUBLE);
++ TEST(static_cast(value_double(double1x)) == 3.14);
++
++ double test1x;
++ fromValue(test1x, double1x);
++ TEST(test1x == 3.14);
++ }
++};
++
++
++
++class booleanTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "booleanTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ value_boolean boolean1(true);
++ TEST(static_cast(boolean1) == true);
++ value_boolean boolean2(false);
++ TEST(static_cast(boolean2) == false);
++ value val1(boolean1);
++ TEST(val1.type() == value::TYPE_BOOLEAN);
++ value_boolean boolean3(val1);
++ TEST(static_cast(boolean3) == true);
++ try {
++ value_boolean boolean4(value_int(4));
++ TEST_FAILED("invalid cast int-boolean suceeded");
++ } catch (error) {}
++
++ value const boolean1x(toValue(true));
++ TEST(boolean1x.type() == value::TYPE_BOOLEAN);
++ TEST(static_cast(value_boolean(boolean1x)) == true);
++
++ bool test1x;
++ fromValue(test1x, boolean1x);
++ TEST(test1x == true);
++ }
++};
++
++
++
++#if XMLRPC_HAVE_TIMEVAL
++
++static struct timeval
++makeTv(time_t const secs,
++ unsigned int const usecs) {
++
++ struct timeval retval;
++
++ retval.tv_sec = secs;
++ retval.tv_usec = usecs;
++
++ return retval;
++}
++
++static bool
++tvIsEqual(struct timeval const comparand,
++ struct timeval const comparator) {
++ return
++ comparand.tv_sec == comparator.tv_sec &&
++ comparand.tv_usec == comparator.tv_usec;
++}
++#endif
++
++
++
++#if XMLRPC_HAVE_TIMESPEC
++
++static struct timespec
++makeTs(time_t const secs,
++ unsigned int const usecs) {
++
++ struct timespec retval;
++
++ retval.tv_sec = secs;
++ retval.tv_nsec = usecs * 1000;
++
++ return retval;
++}
++
++static bool
++tsIsEqual(struct timespec const comparand,
++ struct timespec const comparator) {
++ return
++ comparand.tv_sec == comparator.tv_sec &&
++ comparand.tv_nsec == comparator.tv_nsec;
++}
++#endif
++
++
++
++class datetimeTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "datetimeTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ time_t const testTime(900684535);
++ value_datetime datetime1("19980717T14:08:55");
++ TEST(static_cast(datetime1) == testTime);
++ value_datetime datetime2(testTime);
++ TEST(static_cast(datetime2) == testTime);
++ value val1(datetime1);
++ TEST(val1.type() == value::TYPE_DATETIME);
++ value_datetime datetime3(val1);
++ TEST(static_cast(datetime3) == testTime);
++#if XMLRPC_HAVE_TIMEVAL
++ struct timeval const testTimeTv(makeTv(testTime, 0));
++ value_datetime datetime4(testTimeTv);
++ TEST(static_cast(datetime4) == testTime);
++ TEST(tvIsEqual(static_cast(datetime4), testTimeTv));
++#endif
++#if XMLRPC_HAVE_TIMESPEC
++ struct timespec const testTimeTs(makeTs(testTime, 0));
++ value_datetime datetime5(testTimeTs);
++ TEST(static_cast(datetime5) == testTime);
++ TEST(tsIsEqual(static_cast(datetime5), testTimeTs));
++#endif
++ try {
++ value_datetime datetime4(value_int(4));
++ TEST_FAILED("invalid cast int-datetime suceeded");
++ } catch (error) {}
++ }
++};
++
++
++
++class stringTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "stringTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ value_string string1("hello world");
++ TEST(static_cast(string1) == "hello world");
++ value_string string2("embedded\0null");
++ TEST(static_cast(string2) == "embedded\0null");
++ value val1(string1);
++ TEST(val1.type() == value::TYPE_STRING);
++ value_string string3(val1);
++ TEST(static_cast(string3) == "hello world");
++ try {
++ value_string string4(value_int(4));
++ TEST_FAILED("invalid cast int-string succeeded");
++ } catch (error) {}
++ value_string string5("hello world", value_string::nlCode_all);
++ TEST(static_cast(string5) == "hello world");
++ value_string string6("hello\nthere\rworld\r\n\n",
++ value_string::nlCode_all);
++ TEST(static_cast(string6) == "hello\nthere\nworld\n\n");
++ TEST(string6.crlfValue() == "hello\r\nthere\r\nworld\r\n\r\n");
++ value_string string7("hello\nthere\rworld\r\n\n",
++ value_string::nlCode_lf);
++ TEST(static_cast(string7) == "hello\nthere\rworld\r\n\n");
++
++ value const string1x(toValue("hello world"));
++ TEST(string1x.type() == value::TYPE_STRING);
++ TEST(static_cast(value_string(string1x)) == "hello world");
++
++ string test1x;
++ fromValue(test1x, string1x);
++ TEST(test1x == "hello world");
++
++ value const string2x(toValue(string("hello world")));
++ TEST(string2x.type() == value::TYPE_STRING);
++ TEST(static_cast(value_string(string2x)) == "hello world");
++ }
++};
++
++
++
++class bytestringTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "bytestringTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ unsigned char bytestringArray[] = {0x10, 0x11, 0x12, 0x13, 0x14};
++ cbytestring
++ bytestringData(&bytestringArray[0], &bytestringArray[4]);
++ value_bytestring bytestring1(bytestringData);
++
++ cbytestring const dataReadBack1(bytestring1.vectorUcharValue());
++ TEST(dataReadBack1 == bytestringData);
++ value val1(bytestring1);
++ TEST(val1.type() == value::TYPE_BYTESTRING);
++ value_bytestring bytestring2(val1);
++ vector const dataReadBack2(
++ bytestring2.vectorUcharValue());
++ TEST(dataReadBack2 == bytestringData);
++ try {
++ value_bytestring bytestring4(value_int(4));
++ TEST_FAILED("invalid cast int-bytestring suceeded");
++ } catch (error) {}
++
++ value const bytestring1x(toValue(bytestringData));
++ TEST(bytestring1x.type() == value::TYPE_BYTESTRING);
++ vector const dataReadBack1x(
++ value_bytestring(bytestring1x).vectorUcharValue());
++ TEST(dataReadBack1x == bytestringData);
++
++ vector test1x;
++ fromValue(test1x, bytestring1x);
++ TEST(test1x == bytestringData);
++
++ }
++};
++
++
++
++class nilTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "nilTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ value_nil nil1;
++ value val1(nil1);
++ TEST(val1.type() == value::TYPE_NIL);
++ value_nil nil2(val1);
++ try {
++ value_nil nil4(value_int(4));
++ TEST_FAILED("invalid cast int-nil suceeded");
++ } catch (error) {}
++ }
++};
++
++
++
++class i8TestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "i8TestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ value_i8 int1(7);
++ TEST(static_cast(int1) == 7);
++ value_i8 int2(-7);
++ TEST(static_cast(int2) == -7);
++ value_i8 int5(1ull << 40);
++ TEST(static_cast(int5) == (1ull << 40));
++ value val1(int1);
++ TEST(val1.type() == value::TYPE_I8);
++ value_i8 int3(val1);
++ TEST(static_cast(int3) == 7);
++ try {
++ value_i8 int4(value_double(3.7));
++ TEST_FAILED("invalid cast double-i8 suceeded");
++ } catch (error) {}
++ }
++};
++
++
++
++class structTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "structTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ cstruct structData;
++ pair member("the_integer", value_int(9));
++ structData.insert(member);
++
++ value_struct struct1(structData);
++
++ map dataReadBack(struct1);
++
++ TEST(static_cast(value_int(dataReadBack["the_integer"])) == 9);
++
++ value val1(struct1);
++ TEST(val1.type() == value::TYPE_STRUCT);
++ value_struct struct2(val1);
++ try {
++ value_struct struct4(value_int(4));
++ TEST_FAILED("invalid cast int-struct suceeded");
++ } catch (error) {}
++
++ map structDatax;
++ structDatax["one"] = 1;
++ structDatax["two"] = 2;
++
++ value const struct5(toValue(structDatax));
++ TEST(struct5.type() == value::TYPE_STRUCT);
++ map dataReadBackx;
++ dataReadBackx = value_struct(struct5);
++
++ TEST(static_cast(value_int(dataReadBackx["two"])) == 2);
++
++ map test5x;
++ fromValue(test5x, struct5);
++ TEST(test5x["two"] == 2);
++ }
++};
++
++
++
++class arrayTestSuite : public testSuite {
++public:
++ virtual string suiteName() {
++ return "arrayTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++ carray arrayData;
++ arrayData.push_back(value_int(7));
++ arrayData.push_back(value_double(2.78));
++ arrayData.push_back(value_string("hello world"));
++ value_array array1(arrayData);
++
++ TEST(array1.size() == 3);
++ vector dataReadBack1(array1.vectorValueValue());
++ TEST(dataReadBack1[0].type() == value::TYPE_INT);
++ TEST(static_cast(value_int(dataReadBack1[0])) == 7);
++ TEST(dataReadBack1[1].type() == value::TYPE_DOUBLE);
++ TEST(static_cast(value_double(dataReadBack1[1])) == 2.78);
++ TEST(dataReadBack1[2].type() == value::TYPE_STRING);
++ TEST(static_cast(value_string(dataReadBack1[2])) ==
++ "hello world");
++
++ value val1(array1);
++ TEST(val1.type() == value::TYPE_ARRAY);
++ value_array array2(val1);
++ TEST(array2.size() == 3);
++ try {
++ value_array array4(value_int(4));
++ TEST_FAILED("invalid cast int-array suceeded");
++ } catch (error) {}
++
++ int const arrayDatax[] = {7, 4};
++
++ value const array5(
++ arrayValueArray(arrayDatax, ARRAY_SIZE(arrayDatax)));
++ TEST(array5.type() == value::TYPE_ARRAY);
++ TEST(value_array(array5).size() == 2);
++ vector dataReadBackx(value_array(array5).vectorValueValue());
++
++ TEST(dataReadBackx.size() == 2);
++ TEST(static_cast(value_int(dataReadBackx[0])) == 7);
++ vector test5x;
++ fromValue(test5x, array5);
++ TEST(test5x[1] == 4);
++
++ vector arrayDataVec;
++ arrayDataVec.push_back("hello world");
++ value const array6(toValue(arrayDataVec));
++ TEST(array6.type() == value::TYPE_ARRAY);
++ TEST(value_array(array6).size() == 1);
++ }
++};
++
++
++} // unnamed namespace
++
++
++string
++valueTestSuite::suiteName() {
++ return "valueTestSuite";
++}
++
++
++
++void
++valueTestSuite::runtests(unsigned int const indentation) {
++
++ intTestSuite().run(indentation+1);
++ doubleTestSuite().run(indentation+1);
++ booleanTestSuite().run(indentation+1);
++ datetimeTestSuite().run(indentation+1);
++ stringTestSuite().run(indentation+1);
++ bytestringTestSuite().run(indentation+1);
++ nilTestSuite().run(indentation+1);
++ i8TestSuite().run(indentation+1);
++ structTestSuite().run(indentation+1);
++ arrayTestSuite().run(indentation+1);
++}
+diff --git a/libs/xmlrpc-c/src/cpp/test/value.hpp b/libs/xmlrpc-c/src/cpp/test/value.hpp
+new file mode 100644
+index 0000000..65d0d90
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/test/value.hpp
+@@ -0,0 +1,9 @@
++#include "tools.hpp"
++
++class valueTestSuite : public testSuite {
++
++public:
++ virtual std::string suiteName();
++ virtual void runtests(unsigned int const indentation);
++};
++
+diff --git a/libs/xmlrpc-c/src/cpp/test/xml.cpp b/libs/xmlrpc-c/src/cpp/test/xml.cpp
+new file mode 100644
+index 0000000..0a73e24
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/test/xml.cpp
+@@ -0,0 +1,100 @@
++/*=============================================================================
++ xml
++===============================================================================
++ Test the XML generator and parser C++ facilities of XML-RPC for C/C++.
++
++=============================================================================*/
++
++#include
++
++#include "xmlrpc-c/girerr.hpp"
++using girerr::error;
++using girerr::throwf;
++#include "xmlrpc-c/base.hpp"
++#include "xmlrpc-c/xml.hpp"
++
++#include "tools.hpp"
++#include "xml.hpp"
++
++using namespace xmlrpc_c;
++using namespace std;
++
++
++namespace {
++
++class callTestSuite : public testSuite {
++
++public:
++ virtual string suiteName() {
++ return "callTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++
++ string callXml;
++
++ string const methodName0("myMethod");
++ paramList const paramList0;
++
++ xml::generateCall(methodName0, paramList(), &callXml);
++
++ string methodName;
++ paramList paramList;
++
++ xml::parseCall(callXml, &methodName, ¶mList);
++
++ TEST(methodName == methodName0);
++ TEST(paramList.size() == paramList0.size());
++ }
++};
++
++
++
++class responseTestSuite : public testSuite {
++
++public:
++ virtual string suiteName() {
++ return "responseTestSuite";
++ }
++ virtual void runtests(unsigned int const) {
++
++ string respXml;
++
++ rpcOutcome outcome0(value_int(7));
++
++ xml::generateResponse(outcome0, &respXml);
++
++ rpcOutcome outcome;
++
++ xml::parseResponse(respXml, &outcome);
++
++ TEST((int)value_int(outcome.getResult()) ==
++ (int)value_int(outcome0.getResult()));
++
++ value result;
++
++ xml::parseSuccessfulResponse(respXml, &result);
++
++ TEST((int)value_int(result) == (int)value_int(outcome0.getResult()));
++ }
++};
++
++
++
++} // unnamed namespace
++
++
++
++string
++xmlTestSuite::suiteName() {
++ return "XMLTestSuite";
++}
++
++
++
++void
++xmlTestSuite::runtests(unsigned int const indentation) {
++
++ callTestSuite().run(indentation+1);
++
++ responseTestSuite().run(indentation+1);
++}
+diff --git a/libs/xmlrpc-c/src/cpp/test/xml.hpp b/libs/xmlrpc-c/src/cpp/test/xml.hpp
+new file mode 100644
+index 0000000..3946f46
+--- /dev/null
++++ b/libs/xmlrpc-c/src/cpp/test/xml.hpp
+@@ -0,0 +1,9 @@
++#include "tools.hpp"
++
++class xmlTestSuite : public testSuite {
++
++public:
++ virtual std::string suiteName();
++ virtual void runtests(unsigned int const indentation);
++};
++
diff --git a/libs/xmlrpc-c/src/cpp/value.cpp b/libs/xmlrpc-c/src/cpp/value.cpp
index 6dcba93..3af785c 100644
--- a/libs/xmlrpc-c/src/cpp/value.cpp
@@ -55280,6 +59581,1478 @@ index 1968d40..f96728b 100644
bufferConcat(formattedP, digitChar(digitValue));
+diff --git a/libs/xmlrpc-c/src/json.c b/libs/xmlrpc-c/src/json.c
+new file mode 100644
+index 0000000..e422687
+--- /dev/null
++++ b/libs/xmlrpc-c/src/json.c
+@@ -0,0 +1,1466 @@
++/*=============================================================================
++ json.c
++===============================================================================
++
++ Bo Lorentsen (bl@lue.dk) had the idea to do XML-RPC values in JSON
++ and wrote the original version of this code in February and March
++ 2010.
++
++ Bryan Henderson restructured the code and improved diagnostic information
++ (made it tell you where the JSON is screwed up) before its first release
++ in XML-RPC for C and C++ in Release 1.22.
++
++ JSON: RFC-4627
++=============================================================================*/
++
++#include "xmlrpc_config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include "xmlrpc-c/json.h"
++#include "xmlrpc-c/util.h"
++#include "xmlrpc-c/base_int.h"
++#include "xmlrpc-c/string_int.h"
++#include "xmlrpc-c/string_number.h"
++
++
++
++/*=============================================================================
++ Tokenizer for the json parser
++=============================================================================*/
++enum ttype {
++ typeNone,
++ typeOpenBrace,
++ typeCloseBrace,
++ typeOpenBracket,
++ typeCloseBracket,
++ typeColon,
++ typeComma,
++ typeString,
++ typeInteger,
++ typeFloat,
++ typeNull,
++ typeUndefined,
++ typeTrue,
++ typeFalse,
++ typeEof,
++} ;
++
++static const char *
++tokTypeName(enum ttype const type) {
++
++ switch (type) {
++ case typeNone: return "None";
++ case typeOpenBrace: return "Open brace";
++ case typeCloseBrace: return "Close brace";
++ case typeOpenBracket: return "Open bracket";
++ case typeCloseBracket: return "Close bracket";
++ case typeColon: return "Colon";
++ case typeComma: return "Comma";
++ case typeString: return "String";
++ case typeInteger: return "Integer";
++ case typeFloat: return "Float";
++ case typeNull: return "Null";
++ case typeUndefined: return "Undefined";
++ case typeTrue: return "True";
++ case typeFalse: return "False";
++ case typeEof: return "Eof";
++ default: return "???";
++ }
++}
++
++
++
++typedef struct {
++ const char * original;
++ size_t size;
++ const char * begin;
++ const char * end;
++ enum ttype type;
++} Tokenizer;
++
++
++
++static void
++initializeTokenizer(Tokenizer * const tokP,
++ const char * const str) {
++
++ tokP->original = str;
++ tokP->end = str; /* end of the "previous" token */
++ tokP->type = typeNone;
++}
++
++
++
++static void
++terminateTokenizer(Tokenizer * const tokP ATTR_UNUSED ) {
++
++}
++
++
++
++struct docPosition {
++ /* A position in the document, as meaningful to the user */
++ unsigned int lineNum; /* First line is 1 */
++ unsigned int colNum; /* First column is 1 */
++};
++
++
++
++static struct docPosition
++currentDocumentPosition(Tokenizer * const tokP) {
++/*----------------------------------------------------------------------------
++ Return the document position (line & column) of the start of the current
++ token
++-----------------------------------------------------------------------------*/
++ struct docPosition retval;
++
++ unsigned int curLine;
++ unsigned int curCol;
++ const char * cursor;
++
++ curLine = 0;
++ curCol = 0;
++
++ for (cursor = tokP->original; cursor < tokP->begin; ++cursor) {
++ ++curCol;
++
++ if (*cursor == '\n') {
++ ++curLine;
++ curCol = 0;
++ }
++ }
++ retval.lineNum = curLine + 1;
++ retval.colNum = curCol + 1;
++
++ return retval;
++}
++
++
++
++static void
++setParseErr(xmlrpc_env * const envP,
++ Tokenizer * const tokP,
++ const char * const format,
++ ...) {
++
++ struct docPosition const pos = currentDocumentPosition(tokP);
++
++ va_list args;
++ const char * msg;
++
++ XMLRPC_ASSERT(envP != NULL);
++ XMLRPC_ASSERT(format != NULL);
++
++ va_start(args, format);
++
++ xmlrpc_vasprintf(&msg, format, args);
++
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "JSON parse error at Line %u, Column %u: %s",
++ pos.lineNum, pos.colNum, msg);
++
++ xmlrpc_strfree(msg);
++
++ va_end(args);
++}
++
++
++
++static void
++finishStringToken(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ ++tokP->end;
++
++ while (*tokP->end != '"' && *tokP->end != '\0' && !envP->fault_occurred) {
++ if (*tokP->end == '\\') {
++ ++tokP->end;
++ switch (*tokP->end) {
++ case '"':
++ case '\\':
++ case '/':
++ case 'b':
++ case 'f':
++ case 'n':
++ case 'r':
++ case 't':
++ ++tokP->end;
++ break;
++ case 'u': {
++ const char * cur;
++
++ ++tokP->end;
++
++ cur = tokP->end;
++
++ while (isxdigit(*cur) && cur - tokP->end < 4)
++ ++cur;
++
++ if (cur - tokP->end < 4)
++ setParseErr(envP, tokP,
++ "hex unicode must contain 4 digits. "
++ "There are only %u here", cur - tokP->end);
++ else
++ tokP->end = cur;
++ } break;
++
++ default:
++ setParseErr(envP, tokP, "unknown escape character "
++ "after backslash: '%c'", *tokP->end);
++ }
++ } else
++ ++tokP->end;
++ }
++ if (!envP->fault_occurred) {
++ if (*tokP->end == '\0')
++ setParseErr(envP, tokP, "JSON document ends in the middle "
++ "of a backslash escape sequence");
++ else {
++ ++tokP->end;
++ tokP->size = (tokP->end - tokP->begin) - 1;
++ }
++ }
++}
++
++
++
++static bool
++isInteger(const char * const token,
++ unsigned int const tokSize) {
++
++ if (tokSize < 1)
++ return false;
++ else {
++ unsigned int i;
++
++ i = 0;
++
++ if (token[0] == '-')
++ ++i;
++
++ while (i < tokSize) {
++ if (!isdigit(token[i]))
++ return false;
++ ++i;
++ }
++ return true;
++ }
++}
++
++
++
++static bool
++isFloat(const char * const token,
++ unsigned int const tokSize) {
++
++ unsigned int i;
++ bool seenPeriod;
++ bool seenDigit;
++
++ seenPeriod = false;
++ seenDigit = false;
++ i = 0;
++
++ if (tokSize >= 1 && token[0] == '-')
++ ++i;
++
++ while (i < tokSize) {
++ char const c = token[i];
++
++ if (c == 'e')
++ return isInteger(&token[i], tokSize - i);
++ else if (c == '.') {
++ if (seenPeriod) {
++ /* It's a second period */
++ return false;
++ } else {
++ seenPeriod = true;
++ }
++ } else if (isdigit(c))
++ seenDigit = true;
++ else
++ return false;
++ ++i;
++ }
++ if (seenDigit)
++ return true;
++ else
++ return false;
++}
++
++
++
++static bool
++isWordChar(char const candidate) {
++/*----------------------------------------------------------------------------
++ Return true iff 'candidate' is a character that can be in a "word" token.
++ A word token is a multi-character token that is either a JSON keyword or a
++ number.
++-----------------------------------------------------------------------------*/
++ return (isalnum(candidate) || candidate == '.' || candidate == '-');
++}
++
++
++
++static void
++finishAlphanumericWordToken(Tokenizer * const tokP) {
++
++ ++tokP->end;
++
++ while (isWordChar(*tokP->end))
++ ++tokP->end;
++
++ tokP->size = tokP->end - tokP->begin;
++}
++
++
++
++static void
++finishDelimiterToken(Tokenizer * const tokP) {
++
++ ++tokP->end;
++ tokP->size = tokP->end - tokP->begin;
++}
++
++
++
++static bool
++atComment(Tokenizer * const tokP) {
++
++ return (*tokP->begin == '/' && *(tokP->begin + 1) == '/');
++}
++
++
++
++static void
++advancePastWhiteSpace(Tokenizer * const tokP) {
++
++ while (isspace(*tokP->begin))
++ ++tokP->begin;
++}
++
++
++
++static void
++advancePastComments(Tokenizer * const tokP) {
++/*----------------------------------------------------------------------------
++ Advance the pointer over any comments.
++-----------------------------------------------------------------------------*/
++ while (atComment(tokP)) {
++ /* A comment ends at a newline or end of document */
++ while (*tokP->begin != '\n' && *tokP->begin != '\0')
++ ++tokP->begin;
++ }
++}
++
++
++
++static void
++advanceToNextToken(Tokenizer * const tokP) {
++/*----------------------------------------------------------------------------
++ Advance the pointer over any white space and comments to the next
++ token, or end of document, whichever comes first.
++-----------------------------------------------------------------------------*/
++
++ while (*tokP->begin != '\0' &&
++ (isspace(*tokP->begin) || atComment(tokP))) {
++
++ advancePastWhiteSpace(tokP);
++
++ advancePastComments(tokP);
++ }
++}
++
++
++
++static void
++getToken(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ /* The token starts where the last one left off */
++ tokP->begin = tokP->end;
++
++ advanceToNextToken(tokP);
++
++ if (*tokP->begin == '\0') {
++ /* End of document */
++ tokP->end = tokP->begin;
++ tokP->type = typeEof;
++ tokP->size = tokP->end - tokP->begin;
++ } else {
++ tokP->end = tokP->begin; /* initial value */
++
++ if (*tokP->begin == '{') {
++ finishDelimiterToken(tokP);
++ tokP->type = typeOpenBrace;
++ } else if (*tokP->begin == '}') {
++ finishDelimiterToken(tokP);
++ tokP->type = typeCloseBrace;
++ } else if (*tokP->begin == '[') {
++ finishDelimiterToken(tokP);
++ tokP->type = typeOpenBracket;
++ } else if (*tokP->begin == ']') {
++ finishDelimiterToken(tokP);
++ tokP->type = typeCloseBracket;
++ } else if (*tokP->begin == ':') {
++ finishDelimiterToken(tokP);
++ tokP->type = typeColon;
++ } else if (*tokP->begin == ',') {
++ finishDelimiterToken(tokP);
++ tokP->type = typeComma;
++ } else if (*tokP->begin == '"') {
++ finishStringToken(envP, tokP);
++
++ if (!envP->fault_occurred)
++ tokP->type = typeString;
++ } else {
++ if (isWordChar(*tokP->begin)) {
++ finishAlphanumericWordToken(tokP);
++
++ if (isInteger(tokP->begin, tokP->size))
++ tokP->type = typeInteger;
++ else if (isFloat(tokP->begin, tokP->size))
++ tokP->type = typeFloat;
++ else if (xmlrpc_strneq(tokP->begin, "null", tokP->size))
++ tokP->type = typeNull;
++ else if (xmlrpc_strneq(tokP->begin, "undefined", tokP->size))
++ tokP->type = typeUndefined;
++ else if(xmlrpc_strneq(tokP->begin, "false", tokP->size))
++ tokP->type = typeFalse;
++ else if(xmlrpc_strneq(tokP->begin, "true", tokP->size))
++ tokP->type = typeTrue;
++ else
++ setParseErr(envP, tokP, "Invalid word token -- "
++ "Not a valid integer, floating point "
++ "number, 'null', 'true', or 'false'");
++ } else {
++ setParseErr(envP, tokP,
++ "Not a valid token -- starts with '%c'; "
++ "a valid token starts with "
++ "one of []{}:,\"-. or digit or letter",
++ *tokP->begin);
++ }
++ }
++ }
++}
++
++
++
++/*===========================================================================*/
++
++
++
++static int
++utf8Decode(uint32_t const c,
++ char * const out) {
++/*---------------------------------------------------------------------------
++ convert a unicode char to a utf8 char
++---------------------------------------------------------------------------*/
++ if (c <= 0x7F) { /* 0XXX XXXX one byte */
++ out[0] = (char) c;
++ return 1;
++ } else if (c <= 0x7FF) { /* 110X XXXX two bytes */
++ out[0] = (char)( 0xC0 | (c >> 6) );
++ out[1] = (char)( 0x80 | (c & 0x3F) );
++ return 2;
++ } else if (c <= 0xFFFF) { /* 1110 XXXX three bytes */
++ out[0] = (char) (0xE0 | (c >> 12));
++ out[1] = (char) (0x80 | ((c >> 6) & 0x3F));
++ out[2] = (char) (0x80 | (c & 0x3F));
++ return 3;
++ } else if (c <= 0x1FFFFF) { /* 1111 0XXX four bytes */
++ out[0] = (char) (0xF0 | (c >> 18));
++ out[1] = (char) (0x80 | ((c >> 12) & 0x3F));
++ out[2] = (char) (0x80 | ((c >> 6) & 0x3F));
++ out[3] = (char) (0x80 | (c & 0x3F));
++ return 4;
++ } else
++ return 0;
++}
++
++
++
++static void
++getBackslashSequence(xmlrpc_env * const envP,
++ const char * const cur,
++ xmlrpc_mem_block * const memBlockP,
++ unsigned int * const nBytesConsumedP) {
++
++ char buffer[5];
++ unsigned int tsize;
++
++ switch (*cur) {
++ case '"':
++ buffer[0] = '"';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case '/':
++ buffer[0] = '/';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case '\\':
++ buffer[0] = '\\';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case 'b':
++ buffer[0] = '\b';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case 'f':
++ buffer[0] = '\f';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case 'n':
++ buffer[0] = '\n';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case 'r':
++ buffer[0] = '\r';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case 't':
++ buffer[0] = '\t';
++ tsize = 1;
++ *nBytesConsumedP = 1;
++ break;
++ case 'u': {
++ long digit;
++ strncpy(buffer, cur + 1, 4);
++ digit = strtol(buffer, NULL, 16);
++ tsize = utf8Decode(digit, buffer);
++ *nBytesConsumedP = 5; /* uXXXX */
++ break;
++ }
++ default:
++ xmlrpc_faultf(envP, "Invalid character after backslash "
++ "escape: '%c'", *cur);
++ *nBytesConsumedP = 0; /* quiet compiler warning */
++ tsize = 0; /* quiet compiler warning */
++ }
++ if (!envP->fault_occurred)
++ XMLRPC_MEMBLOCK_APPEND(char, envP, memBlockP, buffer, tsize );
++}
++
++
++
++static void
++unescapeString(xmlrpc_env * const envP,
++ const char * const begin,
++ const char * const end,
++ xmlrpc_mem_block * const memBlockP) {
++
++ XMLRPC_MEMBLOCK_INIT(char, envP, memBlockP, 0);
++
++ if (!envP->fault_occurred) {
++ const char * cur;
++ const char * last;
++
++ cur = begin;
++ last = cur;
++
++ while (cur != end && !envP->fault_occurred) {
++ if (*cur == '\\') {
++ if (cur != last) {
++ XMLRPC_MEMBLOCK_APPEND(
++ char, envP, memBlockP, last, cur - last );
++ if (!envP->fault_occurred)
++ last = cur;
++ }
++ if (!envP->fault_occurred) {
++ unsigned int nBytesConsumed;
++
++ cur += 1; /* consume slash */
++
++ getBackslashSequence(envP, cur, memBlockP,
++ &nBytesConsumed);
++
++ if (!envP->fault_occurred) {
++ cur += nBytesConsumed;
++ last = cur;
++ }
++ }
++ } else
++ ++cur;
++ }
++ if (!envP->fault_occurred) {
++ if (cur != last) {
++ XMLRPC_MEMBLOCK_APPEND(char, envP,
++ memBlockP, last, cur - last );
++ }
++ }
++ if (!envP->fault_occurred) {
++ /* Append terminating NUL */
++ XMLRPC_MEMBLOCK_APPEND(char, envP, memBlockP, "", 1);
++ }
++ if (envP->fault_occurred)
++ XMLRPC_MEMBLOCK_CLEAN(char, memBlockP);
++ }
++}
++
++
++
++static xmlrpc_value *
++makeUtf8String(xmlrpc_env * const envP,
++ const char * const begin,
++ const char * const end) {
++/*----------------------------------------------------------------------------
++ Copy a json string directly into a string value, and convert any json
++ escaping (\uXXXX) to something acceptable to the internal string handling.
++
++ Try to do this in as few chunks as possible !
++-----------------------------------------------------------------------------*/
++ xmlrpc_value * valP;
++
++ xmlrpc_createXmlrpcValue(envP, &valP);
++
++ if (!envP->fault_occurred) {
++ valP->_type = XMLRPC_TYPE_STRING;
++ valP->_wcs_block = NULL;
++
++ if (!envP->fault_occurred)
++ unescapeString(envP, begin, end, &valP->_block);
++
++ if (envP->fault_occurred)
++ xmlrpc_DECREF(valP);
++ }
++ return valP;
++}
++
++
++
++static xmlrpc_value *
++stringTokenValue(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ xmlrpc_env env;
++ xmlrpc_value * valP;
++
++ xmlrpc_env_init(&env);
++
++ assert(tokP->end >= tokP->begin + 2);
++ assert(*tokP->begin == '"');
++ assert(*(tokP->end-1) == '"');
++
++ valP = makeUtf8String(&env, tokP->begin + 1, tokP->end - 1);
++
++ if (env.fault_occurred) {
++ setParseErr(envP, tokP, "Error in string token: %s",
++ env.fault_string);
++ }
++ xmlrpc_env_clean(&env);
++
++ return valP;
++}
++
++
++
++static xmlrpc_value *
++integerTokenValue(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ xmlrpc_env env;
++ char valueString[tokP->size + 1];
++ xmlrpc_int64 value;
++ xmlrpc_value * valP;
++
++ xmlrpc_env_init(&env);
++
++ memcpy(valueString, tokP->begin, tokP->size);
++ valueString[tokP->size] = '\0';
++
++ xmlrpc_parse_int64(&env, valueString, &value);
++
++ if (env.fault_occurred)
++ setParseErr(envP, tokP, "Error in integer token value '%s': %s",
++ tokP->begin, env.fault_string);
++ else
++ valP = xmlrpc_i8_new(envP, value);
++
++ xmlrpc_env_clean(&env);
++
++ return valP;
++}
++
++
++
++/* Forward declarations for recursion: */
++
++static xmlrpc_value *
++parseValue(xmlrpc_env * const envP,
++ Tokenizer * const tokP);
++
++static xmlrpc_value *
++parseList(xmlrpc_env * const envP,
++ Tokenizer * const tokP);
++
++static xmlrpc_value *
++parseObject(xmlrpc_env * const envP,
++ Tokenizer * const tokP);
++
++
++
++static void
++parseListElement(xmlrpc_env * const envP,
++ Tokenizer * const tokP,
++ xmlrpc_value * const listArrayP,
++ bool * const endOfListP) {
++
++ xmlrpc_value * itemP;
++
++ itemP = parseValue(envP, tokP);
++
++ if (!envP->fault_occurred) {
++ xmlrpc_array_append_item(envP, listArrayP, itemP);
++
++ if (!envP->fault_occurred) {
++ getToken(envP, tokP);
++ if (!envP->fault_occurred) {
++ if (tokP->type == typeComma) {
++ *endOfListP = false;
++ } else if (tokP->type == typeCloseBracket)
++ *endOfListP = true;
++ else
++ setParseErr(envP, tokP,
++ "Need comma or close bracket "
++ "after array item. Instead we have %s",
++ tokTypeName(tokP->type));
++ }
++ }
++ xmlrpc_DECREF(itemP);
++ }
++}
++
++
++
++static xmlrpc_value *
++parseList(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ xmlrpc_value * retval;
++
++ XMLRPC_ASSERT_ENV_OK(envP);
++
++ retval = xmlrpc_array_new(envP);
++
++ if (!envP->fault_occurred) {
++ bool endOfList;
++ for (endOfList = false; !endOfList && !envP->fault_occurred; ) {
++ getToken(envP,tokP);
++
++ if (!envP->fault_occurred) {
++ if (tokP->type == typeEof)
++ endOfList = true;
++ else if (tokP->type == typeCloseBracket)
++ endOfList = true;
++ else
++ parseListElement(envP, tokP, retval, &endOfList);
++ }
++ }
++ if (envP->fault_occurred)
++ xmlrpc_DECREF(retval);
++ }
++ return retval;
++}
++
++
++
++static void
++parseObjectMemberValue(xmlrpc_env * const envP,
++ Tokenizer * const tokP,
++ xmlrpc_value * const keyP,
++ xmlrpc_value * const objectP) {
++
++ xmlrpc_value * valP;
++
++ getToken(envP,tokP);
++
++ if (!envP->fault_occurred) {
++ valP = parseValue(envP, tokP);
++
++ if (!envP->fault_occurred) {
++ xmlrpc_struct_set_value_v(envP, objectP, keyP, valP);
++
++ xmlrpc_DECREF(valP);
++ }
++ }
++}
++
++
++
++static void
++parseObjectMember(xmlrpc_env * const envP,
++ Tokenizer * const tokP,
++ xmlrpc_value * const objectP) {
++
++ xmlrpc_env env;
++ xmlrpc_value * keyP;
++
++ xmlrpc_env_init(&env);
++
++ /* The current token is the string which is the member name: */
++ assert(tokP->type = typeString);
++ assert(tokP->end >= tokP->begin + 2);
++ assert(*tokP->begin == '"');
++ assert(*(tokP->end-1) == '"');
++
++ keyP = makeUtf8String(&env, tokP->begin + 1, tokP->end - 1);
++
++ if (env.fault_occurred)
++ setParseErr(envP, tokP, "Error in what is supposed to be "
++ "the key of a member of an object: %s",
++ env.fault_string);
++ else {
++ getToken(envP, tokP);
++
++ if (!envP->fault_occurred) {
++ if (tokP->type == typeColon)
++ parseObjectMemberValue(envP, tokP, keyP, objectP);
++ else
++ setParseErr(envP, tokP,
++ "Need a colon after member key "
++ "in object. Instead we have %s",
++ tokTypeName(tokP->type));
++ }
++ xmlrpc_DECREF(keyP);
++ }
++ xmlrpc_env_clean(&env);
++}
++
++
++
++static xmlrpc_value *
++parseObject(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ xmlrpc_value * retval;
++
++ XMLRPC_ASSERT_ENV_OK(envP);
++
++ retval = xmlrpc_struct_new(envP);
++
++ if (!envP->fault_occurred) {
++ bool objectDone;
++
++ objectDone = false;
++ while (!objectDone && !envP->fault_occurred) {
++ getToken(envP, tokP);
++
++ if (!envP->fault_occurred) {
++ if (tokP->type == typeCloseBrace) {
++ objectDone = true;
++ } else if (tokP->type == typeString) {
++ parseObjectMember(envP, tokP, retval);
++
++ if (!envP->fault_occurred) {
++ getToken(envP, tokP);
++
++ if (!envP->fault_occurred) {
++ if (tokP->type == typeComma) {
++ /* member separator; keep going */
++ } else if (tokP->type == typeCloseBrace) {
++ /* No more members in this object */
++ objectDone = true;
++ } else
++ setParseErr(
++ envP, tokP,
++ "Need a comma or close brace after object "
++ "member. Instead we have %s",
++ tokTypeName(tokP->type));
++ }
++ }
++ } else {
++ setParseErr(envP, tokP,
++ "Need a string (i.e. starting with "
++ "a quotation mark) as member key "
++ "in object, or closing brace to end the "
++ "object. Instead we have %s",
++ tokTypeName(tokP->type));
++ }
++ }
++ }
++ if (envP->fault_occurred)
++ xmlrpc_DECREF(retval);
++ }
++ return retval;
++}
++
++
++
++
++
++static xmlrpc_value *
++parseValue(xmlrpc_env * const envP,
++ Tokenizer * const tokP) {
++
++ xmlrpc_value * retval;
++
++ XMLRPC_ASSERT_ENV_OK(envP);
++
++ switch (tokP->type) {
++
++ case typeOpenBracket:
++ retval = parseList(envP, tokP);
++ break;
++
++ case typeOpenBrace:
++ retval = parseObject(envP, tokP);
++ break;
++
++ case typeNull:
++ retval = xmlrpc_nil_new(envP);
++ break;
++
++ case typeUndefined:
++ retval = xmlrpc_nil_new(envP);
++ break;
++
++ case typeFalse:
++ retval = xmlrpc_bool_new(envP, (xmlrpc_bool)false);
++ break;
++
++ case typeTrue:
++ retval = xmlrpc_bool_new(envP, (xmlrpc_bool)true);
++ break;
++
++ case typeInteger:
++ retval = integerTokenValue(envP, tokP);
++ break;
++
++ case typeFloat:
++ retval = xmlrpc_double_new(envP, strtod(tokP->begin, NULL));
++ break;
++
++ case typeString:
++ retval = stringTokenValue(envP, tokP);
++ break;
++
++ default:
++ retval = NULL;
++ setParseErr(envP, tokP, "Invalid token "
++ "where a value is supposed to begin: %s. "
++ "Should be an open bracket, open brace, "
++ "'null', 'false', 'true', a number, or a string",
++ tokTypeName(tokP->type));
++ }
++ return retval;
++}
++
++
++
++xmlrpc_value *
++xmlrpc_parse_json(xmlrpc_env * const envP,
++ const char * const str) {
++
++ xmlrpc_value * retval = retval;
++ Tokenizer tok;
++
++ XMLRPC_ASSERT_ENV_OK(envP);
++
++ initializeTokenizer(&tok, str);
++
++ getToken(envP, &tok);
++
++ if (!envP->fault_occurred) {
++ retval = parseValue(envP, &tok);
++
++ if (!envP->fault_occurred) {
++ getToken(envP, &tok);
++
++ if (!envP->fault_occurred) {
++ if (tok.type != typeEof)
++ setParseErr(envP, &tok, "There is junk after the end of "
++ "the JSON value, to wit a %s token",
++ tokTypeName(tok.type));
++ }
++ if (envP->fault_occurred)
++ xmlrpc_DECREF(retval);
++ }
++ }
++
++ terminateTokenizer(&tok);
++
++ return retval;
++}
++
++
++
++/*============================================================================
++ Serialize value to JSON
++============================================================================*/
++
++/* Borrowed from xmlrpc_serialize */
++
++static void
++formatOut(xmlrpc_env * const envP,
++ xmlrpc_mem_block * const outputP,
++ const char * const formatString, ... ) {
++
++ va_list args;
++ char buffer[1024];
++ int rc;
++
++ XMLRPC_ASSERT_ENV_OK(envP);
++
++ va_start(args, formatString);
++
++ rc = XMLRPC_VSNPRINTF(buffer, sizeof(buffer), formatString, args);
++
++ /* Old vsnprintf() (and Windows) fails with return value -1 if the full
++ string doesn't fit in the buffer. New vsnprintf() puts whatever will
++ fit in the buffer, and returns the length of the full string
++ regardless. For us, this truncation is a failure.
++ */
++
++ if (rc < 0)
++ xmlrpc_faultf(envP, "formatOut() overflowed internal buffer");
++ else {
++ unsigned int const formattedLen = rc;
++
++ if (formattedLen + 1 >= (sizeof(buffer)))
++ xmlrpc_faultf(envP, "formatOut() overflowed internal buffer");
++ else
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outputP, buffer, formattedLen);
++ }
++ va_end(args);
++}
++
++
++
++static void
++indent(xmlrpc_env * const envP,
++ unsigned int const level,
++ xmlrpc_mem_block * const outP) {
++
++ unsigned int i;
++
++ for (i = 0; i < level * 2 && !envP->fault_occurred; ++i)
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, " ", 1);
++}
++
++
++
++/* Forward declaration for recursion */
++
++static void
++serializeValue(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ unsigned int const level,
++ xmlrpc_mem_block * const outP);
++
++
++
++static void
++appendEscapeSeq(xmlrpc_env * const envP,
++ xmlrpc_mem_block * const outP,
++ unsigned char const c) {
++/*----------------------------------------------------------------------------
++ Append to *outP the escaped representation of 'c'.
++
++ This is e.g. "\t" for tab, or "\u001C" for something exotic.
++-----------------------------------------------------------------------------*/
++ unsigned int size;
++ char buffer[6];
++ char slashChar;
++ /* Character that goes after the backslash, including 'u' for \uHHHH */
++
++ switch (c) {
++ case '"' : slashChar = '"'; break; /* U+0022 */
++ case '\\': slashChar = '\\'; break; /* U+005C */
++ case '\b': slashChar = 'b'; break; /* U+0008 */
++ case '\f': slashChar = 'f'; break; /* U+000C */
++ case '\n': slashChar = 'n'; break; /* U+000A */
++ case '\r': slashChar = 'r'; break; /* U+000D */
++ case '\t': slashChar = 't'; break; /* U+0009 */
++ default:
++ slashChar = 'u';
++ };
++
++ buffer[0] = '\\';
++ buffer[1] = slashChar;
++
++ if (slashChar == 'u') {
++ sprintf(&buffer[2], "%04x", c);
++ size = 6; /* \u1234 */
++ } else
++ size = 2;
++
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, buffer, size);
++}
++
++
++
++static void
++makeJsonString(xmlrpc_env * const envP,
++ const char * const value,
++ size_t const length,
++ xmlrpc_mem_block * const outP) {
++/*----------------------------------------------------------------------------
++ Create a JSON representation of a string, appended to *outP.
++-----------------------------------------------------------------------------*/
++ const char * const begin = &value[0];
++ const char * const end = begin + length;
++
++ const char * cur;
++ const char * last;
++
++ last = cur = begin;
++
++ while (cur != end && !envP->fault_occurred) {
++ unsigned char const c = *cur;
++
++ if (c < 0x1F || c == '"' || c == '\\') {
++ /* This characters needs to be escaped. Put a backslash escape
++ sequence in the output for this character, after copying all
++ the characters before it to the output.
++ */
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, last, cur - last);
++
++ if (!envP->fault_occurred) {
++ appendEscapeSeq(envP, outP, c);
++
++ ++cur;
++ last = cur;
++ }
++ } else
++ ++cur;
++ }
++
++ /* Copy all characters since the last escaped character to the output */
++ if (cur != last)
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, last, cur - last);
++
++ if (envP->fault_occurred)
++ XMLRPC_MEMBLOCK_CLEAN(char, outP);
++}
++
++
++
++static void
++makeJsonStringFromXmlRpc(xmlrpc_env * const envP,
++ const xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++/*----------------------------------------------------------------------------
++ Convert a string XML-RPC value to JSON, appended to *outP.
++-----------------------------------------------------------------------------*/
++ const char * value;
++ size_t length;
++
++ xmlrpc_read_string_lp(envP, valP, &length, &value);
++ if (!envP->fault_occurred) {
++ makeJsonString(envP, value, length, outP);
++
++ xmlrpc_strfree(value);
++ }
++}
++
++
++
++static void
++serializeInt(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ xmlrpc_int value;
++
++ xmlrpc_read_int(envP, valP, &value);
++
++ formatOut(envP, outP, "%d", value);
++}
++
++
++
++static void
++serializeI8(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ xmlrpc_int64 value;
++
++ xmlrpc_read_i8(envP, valP, &value);
++
++ formatOut(envP, outP, "%" XMLRPC_PRId64, value);
++}
++
++
++
++static void
++serializeBool(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ xmlrpc_bool value;
++ xmlrpc_read_bool(envP, valP, &value);
++
++ formatOut(envP, outP, "%s", value ? "true" : "false");
++}
++
++
++
++static void
++serializeDouble(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ xmlrpc_double value;
++ xmlrpc_read_double(envP, valP, &value);
++
++ formatOut(envP, outP, "%e", value);
++}
++
++
++
++static void
++serializeDatetime(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ /* ISO 8601 time string as JSON does not have a datetime type */
++
++ formatOut(envP, outP, "\"%u%02u%02uT%02u:%02u:%02u\"",
++ valP->_value.dt.Y,
++ valP->_value.dt.M,
++ valP->_value.dt.D,
++ valP->_value.dt.h,
++ valP->_value.dt.m,
++ valP->_value.dt.s);
++}
++
++
++
++static void
++serializeString(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ formatOut(envP, outP, "\"");
++
++ makeJsonStringFromXmlRpc(envP, valP, outP);
++
++ formatOut(envP, outP, "\"");
++}
++
++
++
++static void
++serializeBitstring(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++/*----------------------------------------------------------------------------
++ Append to *outP a JSON string whose value is the bit string *valP in
++ base64 ASCII.
++-----------------------------------------------------------------------------*/
++ const unsigned char * bytes;
++ size_t size;
++
++ xmlrpc_read_base64(envP, valP, &size, &bytes);
++
++ if (!envP->fault_occurred) {
++ xmlrpc_mem_block * const base64P =
++ xmlrpc_base64_encode(envP, bytes, size);
++
++ if (!envP->fault_occurred) {
++
++ formatOut(envP, outP, "\"");
++
++ XMLRPC_MEMBLOCK_APPEND(
++ char, envP, outP,
++ XMLRPC_MEMBLOCK_CONTENTS(char, base64P),
++ XMLRPC_MEMBLOCK_SIZE(char, base64P));
++
++ if (!envP->fault_occurred)
++ formatOut(envP, outP, "\"");
++
++ XMLRPC_MEMBLOCK_FREE(char, base64P);
++ }
++ free((unsigned char*)bytes);
++ }
++}
++
++
++
++static void
++serializeArray(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ unsigned int const level,
++ xmlrpc_mem_block * const outP) {
++
++ unsigned int const size = xmlrpc_array_size(envP, valP);
++
++ if (!envP->fault_occurred) {
++ unsigned int i;
++
++ formatOut(envP, outP, "[\n");
++
++ for (i = 0; i < size && !envP->fault_occurred; ++i) {
++ xmlrpc_value * const itemP =
++ xmlrpc_array_get_item(envP, valP, i);
++
++ if (!envP->fault_occurred) {
++ if (!envP->fault_occurred) {
++ serializeValue(envP, itemP, level + 1, outP);
++
++ if (i < size - 1)
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, ",\n", 2);
++ }
++ }
++ }
++ if (!envP->fault_occurred) {
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, "\n", 1);
++ indent(envP, level, outP);
++ if (!envP->fault_occurred) {
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, "]", 1);
++ }
++ }
++ }
++}
++
++
++
++static void
++serializeStructMember(xmlrpc_env * const envP,
++ xmlrpc_value * const memberKeyP,
++ xmlrpc_value * const memberValueP,
++ unsigned int const level,
++ xmlrpc_mem_block * const outP) {
++
++ serializeValue(envP, memberKeyP, level, outP);
++
++ if (!envP->fault_occurred) {
++ formatOut(envP, outP, ":");
++
++ if (!envP->fault_occurred)
++ serializeValue(envP, memberValueP, level, outP);
++ }
++}
++
++
++
++static void
++serializeStruct(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ unsigned int const level,
++ xmlrpc_mem_block * const outP) {
++
++ if (!envP->fault_occurred) {
++ formatOut(envP, outP, "{\n");
++ if (!envP->fault_occurred) {
++ unsigned int const size = xmlrpc_struct_size(envP, valP);
++
++ if (!envP->fault_occurred) {
++ unsigned int i;
++ for (i = 0; i < size && !envP->fault_occurred; ++i) {
++ xmlrpc_value * memberKeyP;
++ xmlrpc_value * memberValueP;
++
++ xmlrpc_struct_get_key_and_value(envP, valP, i,
++ &memberKeyP,
++ &memberValueP);
++ if (!envP->fault_occurred) {
++ serializeStructMember(envP, memberKeyP, memberValueP,
++ level + 1, outP);
++
++ if (!envP->fault_occurred && i < size - 1)
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, ",\n", 2);
++ }
++ }
++ if (!envP->fault_occurred) {
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, "\n", 1);
++ indent(envP, level, outP);
++ XMLRPC_MEMBLOCK_APPEND(char, envP, outP, "}", 1);
++ }
++ }
++ }
++ }
++}
++
++
++
++static void
++serializeValue(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ unsigned int const level,
++ xmlrpc_mem_block * const outP) {
++
++ XMLRPC_ASSERT_ENV_OK(envP);
++
++ indent(envP, level, outP);
++
++ switch (xmlrpc_value_type(valP)) {
++ case XMLRPC_TYPE_INT:
++ serializeInt(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_I8:
++ serializeI8(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_BOOL:
++ serializeBool(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_DOUBLE:
++ serializeDouble(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_DATETIME:
++ serializeDatetime(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_STRING:
++ serializeString(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_BASE64:
++ serializeBitstring(envP, valP, outP);
++ break;
++
++ case XMLRPC_TYPE_ARRAY:
++ serializeArray(envP, valP, level, outP);
++ break;
++
++ case XMLRPC_TYPE_STRUCT:
++ serializeStruct(envP, valP, level, outP);
++ break;
++
++ case XMLRPC_TYPE_C_PTR:
++ xmlrpc_faultf(envP, "Tried to serialize a C pointer value.");
++ break;
++
++ case XMLRPC_TYPE_NIL:
++ formatOut(envP, outP, "null");
++ break;
++
++ case XMLRPC_TYPE_DEAD:
++ xmlrpc_faultf(envP, "Tried to serialize a dead value.");
++ break;
++
++ default:
++ xmlrpc_faultf(envP, "Invalid xmlrpc_value type: 0x%x",
++ xmlrpc_value_type(valP));
++ }
++}
++
++
++
++void
++xmlrpc_serialize_json(xmlrpc_env * const envP,
++ xmlrpc_value * const valP,
++ xmlrpc_mem_block * const outP) {
++
++ serializeValue(envP, valP, 0, outP);
++}
diff --git a/libs/xmlrpc-c/src/method.c b/libs/xmlrpc-c/src/method.c
index cf58b26..467bbbe 100644
--- a/libs/xmlrpc-c/src/method.c
@@ -55379,6 +61152,496 @@ index 2ceca75..f1d56cb 100644
xmlrpc_methodInfo ** const methodPP);
void
+diff --git a/libs/xmlrpc-c/src/parse_datetime.c b/libs/xmlrpc-c/src/parse_datetime.c
+new file mode 100644
+index 0000000..a6299d7
+--- /dev/null
++++ b/libs/xmlrpc-c/src/parse_datetime.c
+@@ -0,0 +1,466 @@
++#include "xmlrpc_config.h"
++
++#include
++#include
++#include
++#include
++
++#if HAVE_REGEX
++#include /* Missing from regex.h in GNU libc */
++#include
++#endif
++
++#include "bool.h"
++
++#include "xmlrpc-c/base.h"
++#include "xmlrpc-c/base_int.h"
++#include "xmlrpc-c/util.h"
++
++#include "parse_datetime.h"
++
++
++
++#if HAVE_REGEX
++
++static unsigned int
++digitStringValue(const char * const string,
++ regmatch_t const match) {
++/*----------------------------------------------------------------------------
++ Return the numerical value of the decimal whole number substring of
++ 'string' identified by 'match'. E.g. if 'string' is 'abc34d' and
++ 'match' says start at 3 and end at 5, we return 34.
++-----------------------------------------------------------------------------*/
++ unsigned int i;
++ unsigned int accum;
++
++ assert(match.rm_so >= 0);
++ assert(match.rm_eo >= 0);
++
++ for (i = match.rm_so, accum = 0; i < (unsigned)match.rm_eo; ++i) {
++ accum *= 10;
++ assert(isdigit(string[i]));
++ accum += string[i] - '0';
++ }
++ return accum;
++}
++#endif /* HAVE_REGEX */
++
++
++
++#if HAVE_REGEX
++
++static unsigned int
++digitStringMillionths(const char * const string,
++ regmatch_t const match) {
++/*----------------------------------------------------------------------------
++ Return the number of millionths represented by the digits after the
++ decimal point in a decimal string, where thse digits are the substring
++ of 'string' identified by 'match'. E.g. if the substring is
++ 34, we return 340,000.
++-----------------------------------------------------------------------------*/
++ unsigned int i;
++ unsigned int accum;
++
++ assert(match.rm_so >= 0);
++ assert(match.rm_eo >= 0);
++
++ for (i = match.rm_so, accum = 0; i < (unsigned)match.rm_so+6; ++i) {
++ accum *= 10;
++ if (i < (unsigned)match.rm_eo) {
++ assert(isdigit(string[i]));
++ accum += string[i] - '0';
++ }
++ }
++ return accum;
++}
++#endif /* HAVE_REGEX */
++
++
++#if HAVE_REGEX
++
++static void
++subParseDtRegex_standard(regmatch_t * const matches,
++ const char * const datetimeString,
++ xmlrpc_datetime * const dtP) {
++
++ dtP->Y = digitStringValue(datetimeString, matches[1]);
++ dtP->M = digitStringValue(datetimeString, matches[2]);
++ dtP->D = digitStringValue(datetimeString, matches[3]);
++ dtP->h = digitStringValue(datetimeString, matches[4]);
++ dtP->m = digitStringValue(datetimeString, matches[5]);
++ dtP->s = digitStringValue(datetimeString, matches[6]);
++
++ if (matches[7].rm_so == -1)
++ dtP->u = 0;
++ else
++ dtP->u = digitStringMillionths(datetimeString, matches[7]);
++}
++
++
++
++static void
++subParseDtRegex_standardtzd(regmatch_t * const matches,
++ const char * const datetimeString,
++ xmlrpc_datetime * const dtP) {
++
++ dtP->Y = digitStringValue(datetimeString, matches[1]);
++ dtP->M = digitStringValue(datetimeString, matches[2]);
++ dtP->D = digitStringValue(datetimeString, matches[3]);
++ dtP->h = digitStringValue(datetimeString, matches[4]);
++ dtP->m = digitStringValue(datetimeString, matches[5]);
++ dtP->s = digitStringValue(datetimeString, matches[6]);
++}
++
++#endif /* HAVE_REGEX */
++
++
++#if HAVE_REGEX
++
++typedef void (*regparsefunc_t)(regmatch_t * const matches,
++ const char * const datetimeString,
++ xmlrpc_datetime * const dtP);
++
++
++struct regexParser {
++ const char * const regex;
++ regparsefunc_t func;
++};
++
++static const struct regexParser iso8601Regex[]
++
++ /* Each entry of this table is instructions for recognizing and parsing
++ some form of a "dateTime.iso8601" XML element.
++
++ (Note that we recognize far more than just the XML-RPC standard
++ dateTime.iso8601).
++ */
++
++ = {
++ {
++ /* Examples:
++ YYYYMMDD[T]HHMMSS
++ YYYY-MM-DD[T]HH:MM:SS
++ YYYY-MM-DD[T]HH:MM:SS.ssss
++ */
++
++ "^([0-9]{4})\\-?([0-9]{2})\\-?([0-9]{2})T"
++ "([0-9]{2}):?([0-9]{2}):?([0-9]{2})\\.?([0-9]+)?$",
++ subParseDtRegex_standard
++ },
++
++ {
++ /* Examples:
++ YYYYMMDD[T]HHMMSS[Z]
++ YYYYMMDD[T]HHMMSS[+-]hh
++ YYYYMMDD[T]HHMMSS[+-]hhmm
++ */
++
++ "^([0-9]{4})\\-?([0-9]{2})\\-?([0-9]{2})T"
++ "([0-9]{2}):?([0-9]{2}):?([0-9]{2})[Z\\+\\-]([0-9]{2,4})?$",
++ subParseDtRegex_standardtzd
++ },
++ { NULL, NULL }
++ };
++#endif /* HAVE_REGEX */
++
++
++
++#if HAVE_REGEX
++static void
++parseDtRegex(xmlrpc_env * const envP,
++ const char * const datetimeString,
++ xmlrpc_datetime * const dtP) {
++
++ unsigned int i;
++ const struct regexParser * parserP;
++ /* The parser that matches 'datetimeString'. Null if no match yet
++ found.
++ */
++ regmatch_t matches[1024];
++
++ for (i = 0, parserP = NULL; iso8601Regex[i].regex && !parserP; ++i) {
++ const struct regexParser * const thisParserP = &iso8601Regex[i];
++
++ regex_t re;
++ int status;
++
++ status = regcomp(&re, thisParserP->regex, REG_ICASE | REG_EXTENDED);
++
++ /* Our regex is valid, so it must have compiled: */
++ assert(status == 0);
++ {
++ int status;
++
++ status = regexec(&re, datetimeString, ARRAY_SIZE(matches),
++ matches, 0);
++
++ if (status == 0) {
++ assert(matches[0].rm_so != -1); /* Match of whole regex */
++
++ parserP = thisParserP;
++ }
++ regfree(&re);
++ }
++ }
++
++ if (parserP) {
++ parserP->func(matches, datetimeString, dtP);
++ } else {
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "value '%s' is not of any form we recognize "
++ "for a element",
++ datetimeString);
++ }
++
++}
++#endif /* HAVE_REGEX */
++
++
++
++static __inline__ void
++parseDtNoRegex(xmlrpc_env * const envP,
++ const char * const datetimeString,
++ xmlrpc_datetime * const dtP) {
++
++ unsigned int const dtStrlen = strlen(datetimeString);
++
++ char year[4+1];
++ char month[2+1];
++ char day[2+1];
++ char hour[2+1];
++ char minute[2+1];
++ char second[2+1];
++
++ if (dtStrlen < 17 || dtStrlen == 18 || dtStrlen > 24)
++ xmlrpc_faultf(envP, "could not parse date, size incompatible: '%d'",
++ dtStrlen);
++ else {
++ year[0] = datetimeString[ 0];
++ year[1] = datetimeString[ 1];
++ year[2] = datetimeString[ 2];
++ year[3] = datetimeString[ 3];
++ year[4] = '\0';
++
++ month[0] = datetimeString[ 4];
++ month[1] = datetimeString[ 5];
++ month[2] = '\0';
++
++ day[0] = datetimeString[ 6];
++ day[1] = datetimeString[ 7];
++ day[2] = '\0';
++
++ assert(datetimeString[ 8] == 'T');
++
++ hour[0] = datetimeString[ 9];
++ hour[1] = datetimeString[10];
++ hour[2] = '\0';
++
++ assert(datetimeString[11] == ':');
++
++ minute[0] = datetimeString[12];
++ minute[1] = datetimeString[13];
++ minute[2] = '\0';
++
++ assert(datetimeString[14] == ':');
++
++ second[0] = datetimeString[15];
++ second[1] = datetimeString[16];
++ second[2] = '\0';
++
++ if (dtStrlen > 17) {
++ unsigned int const pad = 24 - dtStrlen;
++ unsigned int i;
++
++ dtP->u = atoi(&datetimeString[18]);
++ for (i = 0; i < pad; ++i)
++ dtP->u *= 10;
++ } else
++ dtP->u = 0;
++
++ dtP->Y = atoi(year);
++ dtP->M = atoi(month);
++ dtP->D = atoi(day);
++ dtP->h = atoi(hour);
++ dtP->m = atoi(minute);
++ dtP->s = atoi(second);
++ }
++}
++
++
++
++static void
++validateFirst17(xmlrpc_env * const envP,
++ const char * const dt) {
++/*----------------------------------------------------------------------------
++ Assuming 'dt' is at least 17 characters long, validate that the first
++ 17 characters are a valid XML-RPC datetime, e.g.
++ "20080628T16:35:02"
++-----------------------------------------------------------------------------*/
++ unsigned int i;
++
++ for (i = 0; i < 8 && !envP->fault_occurred; ++i)
++ if (!isdigit(dt[i]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[i]);
++
++ if (dt[8] != 'T')
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "9th character is '%c', not 'T'",
++ dt[8]);
++ if (!isdigit(dt[9]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[9]);
++ if (!isdigit(dt[10]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[10]);
++ if (dt[11] != ':')
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a colon: '%c'", dt[11]);
++ if (!isdigit(dt[12]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[12]);
++ if (!isdigit(dt[13]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[13]);
++ if (dt[14] != ':')
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a colon: '%c'", dt[14]);
++ if (!isdigit(dt[15]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[15]);
++ if (!isdigit(dt[16]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Not a digit: '%c'", dt[16]);
++}
++
++
++
++static void
++validateFractionalSeconds(xmlrpc_env * const envP,
++ const char * const dt) {
++/*----------------------------------------------------------------------------
++ Validate the fractional seconds part of the XML-RPC datetime string
++ 'dt', if any. That's the decimal point and everything following
++ it.
++-----------------------------------------------------------------------------*/
++ if (strlen(dt) > 17) {
++ if (dt[17] != '.') {
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "'%c' where only a period is valid", dt[17]);
++ } else {
++ if (dt[18] == '\0')
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR, "Nothing after decimal point");
++ else {
++ unsigned int i;
++ for (i = 18; dt[i] != '\0' && !envP->fault_occurred; ++i) {
++ if (!isdigit(dt[i]))
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Non-digit in fractional seconds: '%c'", dt[i]);
++ }
++ }
++ }
++ }
++}
++
++
++
++static __inline__ void
++validateFormatNoRegex(xmlrpc_env * const envP,
++ const char * const dt) {
++
++ if (strlen(dt) < 17)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Invalid length of %u of datetime. "
++ "Must be at least 17 characters",
++ (unsigned)strlen(dt));
++ else {
++ validateFirst17(envP, dt);
++
++ validateFractionalSeconds(envP, dt);
++ }
++}
++
++
++
++static void
++validateXmlrpcDatetimeSome(xmlrpc_env * const envP,
++ xmlrpc_datetime const dt) {
++/*----------------------------------------------------------------------------
++ Type xmlrpc_datetime is defined such that it can represent a nonexistent
++ datetime such as February 30.
++
++ Validate that 'dt' doesn't have glaring invalidities such as Hour 25.
++ We leave the possibility of more subtle invalidity such as February 30.
++-----------------------------------------------------------------------------*/
++
++ if (dt.M < 1 || dt.M > 12)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Month of year value %u is not in the range 1-12", dt.M);
++ else if (dt.D < 1 || dt.D > 31)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Day of month value %u is not in the range 1-31", dt.D);
++ else if (dt.h > 23)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Hour of day value %u is not in the range 0-23", dt.h);
++ else if (dt.m > 59)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Minute of hour value %u is not in the range 0-59", dt.m);
++ else if (dt.s > 59)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Second of minute value %u is not in the range 0-59", dt.s);
++ else if (dt.u > 999999)
++ xmlrpc_env_set_fault_formatted(
++ envP, XMLRPC_PARSE_ERROR,
++ "Microsecond of second value %u is not in the range 0-1M", dt.u);
++}
++
++
++
++void
++xmlrpc_parseDatetime(xmlrpc_env * const envP,
++ const char * const datetimeString,
++ xmlrpc_value ** const valuePP) {
++/*----------------------------------------------------------------------------
++ Parse the content of a XML-RPC XML element, e.g.
++ "20000301T00:00:00".
++
++ 'str' is that content.
++
++ 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.
++
++ Note that Xmlrpc-c recognizes various extensions of the XML-RPC
++ element type.
++
++ 'str' may not be valid XML-RPC (with extensions). In that case we fail
++ with fault code XMLRPC_PARSE_ERROR.
++-----------------------------------------------------------------------------*/
++ xmlrpc_datetime dt;
++
++#if HAVE_REGEX
++ parseDtRegex(envP, datetimeString, &dt);
++#else
++ /* Note: validation is not as strong without regex */
++ validateFormatNoRegex(envP, datetimeString);
++ if (!envP->fault_occurred)
++ parseDtNoRegex(envP, datetimeString, &dt);
++#endif
++
++ if (!envP->fault_occurred) {
++ validateXmlrpcDatetimeSome(envP, dt);
++
++ if (!envP->fault_occurred)
++ *valuePP = xmlrpc_datetime_new(envP, dt);
++ }
++}
+diff --git a/libs/xmlrpc-c/src/parse_datetime.h b/libs/xmlrpc-c/src/parse_datetime.h
+new file mode 100644
+index 0000000..85207b7
+--- /dev/null
++++ b/libs/xmlrpc-c/src/parse_datetime.h
+@@ -0,0 +1,12 @@
++#ifndef PARSE_DATETIME_H_INCLUDED
++#define PARSE_DATETIME_H_INCLUDED
++
++#include "xmlrpc-c/util.h"
++#include "xmlrpc-c/base.h"
++
++void
++xmlrpc_parseDatetime(xmlrpc_env * const envP,
++ const char * const str,
++ xmlrpc_value ** const valuePP);
++
++#endif
diff --git a/libs/xmlrpc-c/src/parse_value.c b/libs/xmlrpc-c/src/parse_value.c
index a00b2a1..b40cf47 100644
--- a/libs/xmlrpc-c/src/parse_value.c
@@ -57328,6 +63591,163 @@ index 70a8371..0000000
- } while (0)
-
-;
+diff --git a/libs/xmlrpc-c/src/test/testtool.c b/libs/xmlrpc-c/src/test/testtool.c
+new file mode 100644
+index 0000000..85d2e8c
+--- /dev/null
++++ b/libs/xmlrpc-c/src/test/testtool.c
+@@ -0,0 +1,65 @@
++#include
++#include
++
++#include "xmlrpc_config.h"
++#include "xmlrpc-c/util.h"
++#include "xmlrpc-c/util_int.h"
++
++#include "testtool.h"
++ /* Defines global variable, 'total_tests', 'total_failures' */
++
++
++/* This is a good place to set a breakpoint. */
++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);
++
++ /* We abort rather than exit so one can tell from a dump or debug session
++ who called us.
++ */
++ abort();
++}
++
++
++
++void
++test_fault(xmlrpc_env * const envP,
++ int const expectedCode,
++ const char * const fileName,
++ unsigned int const lineNumber) {
++
++ ++total_tests;
++
++ if (!envP->fault_occurred)
++ test_failure(fileName, lineNumber, "no fault occurred", "");
++ else if (envP->fault_code != expectedCode)
++ test_failure(fileName, lineNumber, "wrong fault occurred",
++ envP->fault_string);
++ else
++ printf(".");
++
++ xmlrpc_env_clean(envP);
++ xmlrpc_env_init(envP);
++}
++
++
++
++void
++test_null_string(const char * const string,
++ const char * const fileName,
++ unsigned int const lineNumber) {
++
++ ++total_tests;
++
++ if (string != NULL)
++ test_failure(fileName, lineNumber, "string not null", string);
++ else
++ printf(".");
++}
++
++
+diff --git a/libs/xmlrpc-c/src/test/testtool.h b/libs/xmlrpc-c/src/test/testtool.h
+new file mode 100644
+index 0000000..8aeeebc
+--- /dev/null
++++ b/libs/xmlrpc-c/src/test/testtool.h
+@@ -0,0 +1,80 @@
++#ifndef TESTTOOL_H_INCLUDED
++#define TESTTOOL_H_INCLUDED
++
++#include
++#include
++#include
++
++#include "xmlrpc-c/util.h"
++#include "xmlrpc-c/util_int.h"
++
++extern int total_tests;
++extern int total_failures;
++
++
++void
++test_failure(const char * const file,
++ unsigned int const line,
++ const char * const label,
++ const char * const statement);
++
++void
++test_fault(xmlrpc_env * const envP,
++ int const expectedCode,
++ const char * const fileName,
++ unsigned int const lineNumber);
++
++void
++test_null_string(const char * const string,
++ const char * const fileName,
++ unsigned int const lineNumber);
++
++#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_EPSILON 1E-5
++
++#define FORCENONZERO(x) (MAX(fabs(x), TEST_EPSILON))
++
++#define FLOATEQUAL(comparand, comparator) \
++ ((fabs((comparand)-(comparator)))/FORCENONZERO(comparand) < TEST_EPSILON)
++#define TESTFLOATEQUAL(comparand, comparator) \
++ TEST(FLOATEQUAL(comparand, comparator))
++
++#define TEST_FAULT(envP, code) \
++ do { test_fault(envP, code, __FILE__, __LINE__); } while(0)
++
++;
++
++#define TEST_NULL_STRING(string) \
++ do { test_null_string(string, __FILE__, __LINE__); } while(0)
++
++;
++
++#define TEST_ERROR(reason) \
++do { \
++ printf("Unable to test at %s/%u. %s", __FILE__, __LINE__, reason); \
++ abort(); \
++ } while (0)
++
++;
++
++#endif
diff --git a/libs/xmlrpc-c/src/test/value.c b/libs/xmlrpc-c/src/test/value.c
index bda938c..6ed6c47 100644
--- a/libs/xmlrpc-c/src/test/value.c
@@ -57684,6 +64104,449 @@ index bda938c..6ed6c47 100644
/* First value of wrong type */
xmlrpc_decompose_value(&env, testStructP, "{s:b,s:i,*}",
+diff --git a/libs/xmlrpc-c/src/test/value_datetime.c b/libs/xmlrpc-c/src/test/value_datetime.c
+new file mode 100644
+index 0000000..6671713
+--- /dev/null
++++ b/libs/xmlrpc-c/src/test/value_datetime.c
+@@ -0,0 +1,424 @@
++#define _XOPEN_SOURCE 600 /* Make sure strdup() is in */
++
++#include
++#include
++#include
++#include
++
++#include "casprintf.h"
++#include "girstring.h"
++
++#include "xmlrpc_config.h"
++
++#include "xmlrpc-c/base.h"
++#include "xmlrpc-c/string_int.h"
++
++#include "testtool.h"
++
++#include "value_datetime.h"
++
++
++
++static const char *
++truncateFracSec(const char * const datestring) {
++/*----------------------------------------------------------------------------
++ Return 'datestring', but with any fractional seconds chopped off.
++ E.g. if 'datestring' is "20000301T00:00:00.654321",
++ we return "20000301T00:00:00".
++-----------------------------------------------------------------------------*/
++ char * buffer;
++ unsigned int i;
++
++ buffer = strdup(datestring);
++
++ for (i = 0; i < strlen(buffer); ++i) {
++ if (buffer[i] == '.')
++ buffer[i] = '\0';
++ }
++ return buffer;
++}
++
++
++
++#if XMLRPC_HAVE_TIMEVAL
++
++static struct timeval
++makeTv(time_t const secs,
++ unsigned int const usecs) {
++
++ struct timeval retval;
++
++ retval.tv_sec = secs;
++ retval.tv_usec = usecs;
++
++ return retval;
++}
++
++static bool
++tvIsEqual(struct timeval const comparand,
++ struct timeval const comparator) {
++ return
++ comparand.tv_sec == comparator.tv_sec &&
++ comparand.tv_usec == comparator.tv_usec;
++}
++#endif
++
++
++
++#if XMLRPC_HAVE_TIMESPEC
++
++static struct timespec
++makeTs(time_t const secs,
++ unsigned int const usecs) {
++
++ struct timespec retval;
++
++ retval.tv_sec = secs;
++ retval.tv_nsec = usecs * 1000;
++
++ return retval;
++}
++
++static bool
++tsIsEqual(struct timespec const comparand,
++ struct timespec const comparator) {
++ return
++ comparand.tv_sec == comparator.tv_sec &&
++ comparand.tv_nsec == comparator.tv_nsec;
++}
++#endif
++
++
++
++static void
++test_value_datetime_varytime(const char * const datestring,
++ time_t const datetime,
++ unsigned int const usec) {
++
++ xmlrpc_value * v;
++ xmlrpc_env env;
++ const char * readBackString;
++ time_t readBackDt;
++ unsigned int readBackUsec;
++ const char * datestringSec;
++#if XMLRPC_HAVE_TIMEVAL
++ struct timeval const dtTimeval = makeTv(datetime, usec);
++ struct timeval readBackTv;
++#endif
++#if XMLRPC_HAVE_TIMESPEC
++ struct timespec const dtTimespec = makeTs(datetime, usec);
++ struct timespec readBackTs;
++#endif
++
++ datestringSec = truncateFracSec(datestring);
++
++ xmlrpc_env_init(&env);
++
++ /* Test xmlrpc_datetime_new_str and time read functions*/
++ v = xmlrpc_datetime_new_str(&env, datestring);
++ TEST_NO_FAULT(&env);
++ TEST(XMLRPC_TYPE_DATETIME == xmlrpc_value_type(v));
++
++ xmlrpc_read_datetime_sec(&env, v, &readBackDt);
++ TEST_NO_FAULT(&env);
++ TEST(readBackDt == datetime);
++
++ xmlrpc_read_datetime_usec(&env, v, &readBackDt, &readBackUsec);
++ TEST_NO_FAULT(&env);
++ TEST(readBackDt == datetime);
++ TEST(readBackUsec == usec);
++
++#if XMLRPC_HAVE_TIMEVAL
++ xmlrpc_read_datetime_timeval(&env, v, &readBackTv);
++ TEST_NO_FAULT(&env);
++ TEST(tvIsEqual(dtTimeval, readBackTv));
++#endif
++
++#if XMLRPC_HAVE_TIMESPEC
++ xmlrpc_read_datetime_timespec(&env, v, &readBackTs);
++ TEST_NO_FAULT(&env);
++ TEST(tsIsEqual(dtTimespec, readBackTs));
++#endif
++
++ xmlrpc_DECREF(v);
++
++ /* Test xmlrpc_datetime_new_sec */
++ v = xmlrpc_datetime_new_sec(&env, datetime);
++ TEST_NO_FAULT(&env);
++ TEST(XMLRPC_TYPE_DATETIME == xmlrpc_value_type(v));
++
++ xmlrpc_read_datetime_str(&env, v, &readBackString);
++ TEST_NO_FAULT(&env);
++ TEST(streq(readBackString, datestringSec));
++ strfree(readBackString);
++
++ xmlrpc_DECREF(v);
++
++ /* Test xmlrpc_datetime_new_usec */
++ v = xmlrpc_datetime_new_usec(&env, datetime, usec);
++ TEST_NO_FAULT(&env);
++ TEST(XMLRPC_TYPE_DATETIME == xmlrpc_value_type(v));
++
++ xmlrpc_read_datetime_str(&env, v, &readBackString);
++ TEST_NO_FAULT(&env);
++ TEST(streq(readBackString, datestring));
++ strfree(readBackString);
++
++ xmlrpc_DECREF(v);
++
++#if XMLRPC_HAVE_TIMEVAL
++ /* Test xmlrpc_datetime_new_timeval */
++ v = xmlrpc_datetime_new_timeval(&env, dtTimeval);
++ TEST_NO_FAULT(&env);
++ TEST(XMLRPC_TYPE_DATETIME == xmlrpc_value_type(v));
++
++ xmlrpc_read_datetime_str(&env, v, &readBackString);
++ TEST_NO_FAULT(&env);
++ TEST(streq(readBackString, datestring));
++ strfree(readBackString);
++
++ xmlrpc_DECREF(v);
++#endif
++
++#if XMLRPC_HAVE_TIMESPEC
++ /* Test xmlrpc_datetime_new_timespec */
++ v = xmlrpc_datetime_new_timespec(&env, dtTimespec);
++ TEST_NO_FAULT(&env);
++ TEST(XMLRPC_TYPE_DATETIME == xmlrpc_value_type(v));
++
++ xmlrpc_read_datetime_str(&env, v, &readBackString);
++ TEST_NO_FAULT(&env);
++ TEST(streq(readBackString, datestring));
++ strfree(readBackString);
++
++ xmlrpc_DECREF(v);
++#endif
++
++ xmlrpc_env_clean(&env);
++ strfree(datestringSec);
++}
++
++
++
++static void
++test_value_datetime_not_unix(const char * const datestring) {
++
++ xmlrpc_value * v;
++ xmlrpc_env env;
++ time_t dt;
++
++ xmlrpc_env_init(&env);
++
++ v = xmlrpc_datetime_new_str(&env, datestring);
++ TEST_NO_FAULT(&env);
++
++ xmlrpc_read_datetime_sec(&env, v, &dt);
++ TEST_FAULT(&env, XMLRPC_INTERNAL_ERROR);
++
++ xmlrpc_DECREF(v);
++
++ xmlrpc_env_clean(&env);
++}
++
++
++
++static void
++test_value_datetime_str_invalid1(const char * const datestring) {
++
++ /* Ideally, xmlrpc_datetime_new_str() would fail on these, but
++ the code doesn't implement that today. However,
++ xmlrpc_read_datetime_sec() does catch many cases, so we
++ use that.
++
++ Note that xmlrpc_read_datetime_sec() doesn't catch them all.
++ Sometimes it just returns garbage, e.g. returns July 1 for
++ June 31.
++ */
++
++ xmlrpc_value * v;
++ xmlrpc_env env;
++ time_t dt;
++
++ xmlrpc_env_init(&env);
++
++ v = xmlrpc_datetime_new_str(&env, datestring);
++ TEST_NO_FAULT(&env);
++
++ xmlrpc_read_datetime_sec(&env, v, &dt);
++ TEST_FAULT(&env, XMLRPC_PARSE_ERROR);
++
++ xmlrpc_DECREF(v);
++
++ xmlrpc_env_clean(&env);
++}
++
++
++
++static void
++test_value_datetime_str_invalid2(const char * const datestring) {
++
++ xmlrpc_value * v;
++ xmlrpc_env env;
++
++ xmlrpc_env_init(&env);
++
++ v = xmlrpc_datetime_new_str(&env, datestring);
++ TEST_FAULT(&env, XMLRPC_INTERNAL_ERROR);
++
++ xmlrpc_env_clean(&env);
++}
++
++
++
++static void
++test_build_decomp_datetime(void) {
++
++ const char * datestring = "19980717T14:08:55";
++ time_t const datetime = 900684535;
++
++ xmlrpc_env env;
++ xmlrpc_value * v;
++ time_t dt;
++ const char * ds;
++
++ xmlrpc_env_init(&env);
++
++ v = xmlrpc_build_value(&env, "t", datetime);
++ TEST_NO_FAULT(&env);
++ TEST(v != NULL);
++ TEST(xmlrpc_value_type(v) == XMLRPC_TYPE_DATETIME);
++
++ dt = 0;
++ xmlrpc_read_datetime_sec(&env, v, &dt);
++ TEST(dt == datetime);
++
++ dt = 0;
++ xmlrpc_decompose_value(&env, v, "t", &dt);
++ xmlrpc_DECREF(v);
++ TEST_NO_FAULT(&env);
++ TEST(dt == datetime);
++
++ v = xmlrpc_int_new(&env, 9);
++ TEST_NO_FAULT(&env);
++ xmlrpc_decompose_value(&env, v, "t", &dt);
++ TEST_FAULT(&env, XMLRPC_TYPE_ERROR);
++ xmlrpc_env_clean(&env);
++ xmlrpc_env_init(&env);
++ xmlrpc_decompose_value(&env, v, "8", &ds);
++ TEST_FAULT(&env, XMLRPC_TYPE_ERROR);
++ xmlrpc_env_clean(&env);
++ xmlrpc_env_init(&env);
++ xmlrpc_DECREF(v);
++
++ v = xmlrpc_build_value(&env, "8", datestring);
++ TEST_NO_FAULT(&env);
++ TEST(v != NULL);
++ TEST(xmlrpc_value_type(v) == XMLRPC_TYPE_DATETIME);
++ xmlrpc_decompose_value(&env, v, "8", &ds);
++ xmlrpc_DECREF(v);
++ TEST_NO_FAULT(&env);
++ TEST(streq(ds, datestring));
++ strfree(ds);
++
++ xmlrpc_env_clean(&env);
++}
++
++
++
++
++static void
++test_value_datetime_basic(void) {
++
++ xmlrpc_value * v;
++ xmlrpc_env env;
++ xmlrpc_datetime dt;
++ xmlrpc_datetime readBackDt;
++
++ xmlrpc_env_init(&env);
++
++ dt.Y = 2001;
++ dt.M = 12;
++ dt.D = 25;
++ dt.h = 1;
++ dt.m = 2;
++ dt.s = 3;
++ dt.u = 4;
++
++ v = xmlrpc_datetime_new(&env, dt);
++
++ xmlrpc_read_datetime(&env, v, &readBackDt);
++ TEST_NO_FAULT(&env);
++ TEST(readBackDt.Y = dt.Y);
++ TEST(readBackDt.M = dt.M);
++ TEST(readBackDt.D = dt.D);
++ TEST(readBackDt.h = dt.h);
++ TEST(readBackDt.m = dt.m);
++ TEST(readBackDt.s = dt.s);
++ TEST(readBackDt.u = dt.u);
++
++ xmlrpc_env_clean(&env);
++}
++
++
++
++void
++test_value_datetime(void) {
++
++ const char * datestring = "19980717T14:08:55";
++ time_t const datetime = 900684535;
++
++ xmlrpc_env env;
++
++ printf("\n Running datetime value tests");
++
++ xmlrpc_env_init(&env);
++
++ TEST(streq(xmlrpc_type_name(XMLRPC_TYPE_DATETIME), "DATETIME"));
++
++ test_value_datetime_basic();
++
++ /* Valid datetime, generated from XML-RPC string, time_t, and
++ time_t + microseconds
++ */
++
++ test_value_datetime_varytime(datestring, datetime, 0);
++
++ /* test microseconds */
++ test_value_datetime_varytime("20000301T00:00:00.654321",
++ 951868800, 654321);
++ test_value_datetime_varytime("20040229T23:59:59.123000",
++ 1078099199, 123000);
++ test_value_datetime_varytime("20000229T23:59:59.000123",
++ 951868799, 123);
++
++ /* Leap years */
++ test_value_datetime_varytime("20000229T23:59:59", 951868799, 0);
++ test_value_datetime_varytime("20000301T00:00:00", 951868800, 0);
++ test_value_datetime_varytime("20010228T23:59:59", 983404799, 0);
++ test_value_datetime_varytime("20010301T00:00:00", 983404800, 0);
++ test_value_datetime_varytime("20040229T23:59:59", 1078099199, 0);
++ test_value_datetime_varytime("20040301T00:00:00", 1078099200, 0);
++
++ /* Datetimes that can't be represented as time_t */
++ test_value_datetime_not_unix("19691231T23:59:59");
++
++ /* Invalid datetimes */
++ /* Note that the code today does a pretty weak job of validating datetimes,
++ so we test only the validation that we know is implemented.
++ */
++ test_value_datetime_str_invalid1("19700101T25:00:00");
++ test_value_datetime_str_invalid1("19700101T10:61:01");
++ test_value_datetime_str_invalid1("19700101T10:59:61");
++ test_value_datetime_str_invalid1("19700001T10:00:00");
++ test_value_datetime_str_invalid1("19701301T10:00:00");
++ test_value_datetime_str_invalid1("19700132T10:00:00");
++ test_value_datetime_str_invalid2("19700132T10:00:00.");
++ test_value_datetime_str_invalid2("19700132T10:00:00,123");
++
++ test_build_decomp_datetime();
++
++ xmlrpc_env_clean(&env);
++
++ printf("\n");
++ printf(" datetime value tests done.\n");
++}
+diff --git a/libs/xmlrpc-c/src/test/value_datetime.h b/libs/xmlrpc-c/src/test/value_datetime.h
+new file mode 100644
+index 0000000..5a26f60
+--- /dev/null
++++ b/libs/xmlrpc-c/src/test/value_datetime.h
+@@ -0,0 +1,7 @@
++#ifndef VALUE_DATETIME_H_INCLUDED
++#define VALUE_DATETIME_H_INCLUDED
++
++void
++test_value_datetime(void);
++
++#endif
diff --git a/libs/xmlrpc-c/src/test/xml_data.h b/libs/xmlrpc-c/src/test/xml_data.h
index 4b34238..89b5e49 100644
--- a/libs/xmlrpc-c/src/test/xml_data.h
@@ -64732,6 +71595,13 @@ index 390cc7b..0000000
-}
-
-#endif /* HAVE_UNICODE_WCHAR */
+diff --git a/libs/xmlrpc-c/srcdir.mk.in b/libs/xmlrpc-c/srcdir.mk.in
+new file mode 100644
+index 0000000..d389df1
+--- /dev/null
++++ b/libs/xmlrpc-c/srcdir.mk.in
+@@ -0,0 +1 @@
++SRCDIR=@abs_srcdir@
diff --git a/libs/xmlrpc-c/tools/.cvsignore b/libs/xmlrpc-c/tools/.cvsignore
deleted file mode 100644
index f3c7a7c..0000000
@@ -64982,6 +71852,9 @@ index dbe374f..0ad3907 100644
.PHONY: clean
clean: clean-common
+diff --git a/libs/xmlrpc-c/tools/xml-rpc-api2cpp/Makefile.depend b/libs/xmlrpc-c/tools/xml-rpc-api2cpp/Makefile.depend
+deleted file mode 100644
+index e69de29..0000000
diff --git a/libs/xmlrpc-c/tools/xml-rpc-api2cpp/README b/libs/xmlrpc-c/tools/xml-rpc-api2cpp/README
deleted file mode 100644
index 4dff2a3..0000000
@@ -65246,6 +72119,9 @@ index c1f3b34..a8b3372 100644
.PHONY: dep
dep: dep-common
+diff --git a/libs/xmlrpc-c/tools/xmlrpc/Makefile.depend b/libs/xmlrpc-c/tools/xmlrpc/Makefile.depend
+deleted file mode 100644
+index e69de29..0000000
diff --git a/libs/xmlrpc-c/tools/xmlrpc/xmlrpc.c b/libs/xmlrpc-c/tools/xmlrpc/xmlrpc.c
index c768b7b..06da3e5 100644
--- a/libs/xmlrpc-c/tools/xmlrpc/xmlrpc.c
@@ -65563,6 +72439,9 @@ index 9941eb1..d9eefcb 100644
.PHONY: dep
dep: dep-common
+diff --git a/libs/xmlrpc-c/tools/xmlrpc_transport/Makefile.depend b/libs/xmlrpc-c/tools/xmlrpc_transport/Makefile.depend
+deleted file mode 100644
+index e69de29..0000000
diff --git a/libs/xmlrpc-c/tools/xmlrpc_transport/xmlrpc_transport b/libs/xmlrpc-c/tools/xmlrpc_transport/xmlrpc_transport
deleted file mode 100755
index e002ec0..0000000
@@ -65747,6 +72626,51 @@ index 5791af6..0000000
- endif
-endif
-
+diff --git a/libs/xmlrpc-c/transport_config.mk b/libs/xmlrpc-c/transport_config.mk
+new file mode 100644
+index 0000000..5791af6
+--- /dev/null
++++ b/libs/xmlrpc-c/transport_config.mk
+@@ -0,0 +1,39 @@
++# -*-makefile-*- <-- an Emacs control
++
++
++# The make variables herein come from config.mk, which is included
++# by the make file that includes us.
++
++transport_config.h: $(BLDDIR)/config.mk
++ rm -f $@
++ echo '/* This file was generated by a make rule */' >>$@
++ifeq ($(MUST_BUILD_WININET_CLIENT),yes)
++ echo '#define MUST_BUILD_WININET_CLIENT 1' >>$@
++else
++ echo '#define MUST_BUILD_WININET_CLIENT 0' >>$@
++endif
++ifeq ($(MUST_BUILD_CURL_CLIENT),yes)
++ echo '#define MUST_BUILD_CURL_CLIENT 1' >>$@
++else
++ echo '#define MUST_BUILD_CURL_CLIENT 0' >>$@
++endif
++ifeq ($(MUST_BUILD_LIBWWW_CLIENT),yes)
++ echo '#define MUST_BUILD_LIBWWW_CLIENT 1' >>$@
++else
++ echo '#define MUST_BUILD_LIBWWW_CLIENT 0' >>$@
++endif
++ echo "static const char * const XMLRPC_DEFAULT_TRANSPORT =" >>$@
++ifeq ($(MUST_BUILD_LIBWWW_CLIENT),yes)
++ echo '"libwww";' >>$@
++else
++ ifeq ($(MUST_BUILD_CURL_CLIENT),yes)
++ echo '"curl";' >>$@
++ else
++ ifeq ($(MUST_BUILD_WININET_CLIENT),yes)
++ echo '"wininet";' >>$@
++ else
++ @echo 'ERROR: no client XML transport configured'; rm $@; false
++ endif
++ endif
++endif
++
diff --git a/libs/xmlrpc-c/unix-common.make b/libs/xmlrpc-c/unix-common.make
deleted file mode 100644
index b684c70..0000000
@@ -65832,6 +72756,94 @@ index b684c70..0000000
- cd $(DESTDIR)$(LIBINST_DIR); \
- rm -f $(<:%.$(MAJ).$(MIN)=%); \
- $(LN_S) $(<:%.$(MIN)=%) $(<:%.$(MAJ).$(MIN)=%)
+diff --git a/libs/xmlrpc-c/unix-common.mk b/libs/xmlrpc-c/unix-common.mk
+new file mode 100644
+index 0000000..8f5016c
+--- /dev/null
++++ b/libs/xmlrpc-c/unix-common.mk
+@@ -0,0 +1,82 @@
++# -*-makefile-*- <-- an Emacs control
++
++# The including make file must define these make variables:
++#
++# SHARED_LIBS_TO_BUILD: List of the shared libraries that need to be
++# built -- just the basic library names. E.g. "libfoo libbar"
++#
++# SHARED_LIBS_TO_INSTALL: List of the shared libraries that need to be
++# installed -- just the basic library names. E.g. "libfoo libbar"
++#
++# SHLIB_SUFFIX: Shared library filename suffix, e.g. "so".
++#
++# MAJ: Library major version number, e.g. "3" in file name "libfoo.3.1"
++#
++# MIN: Library minor version number, e.g. "1" in file name "libfoo.3.1"
++#
++# LDFLAGS_SHLIB: linker (Ld) flags needed to link object files together into
++# a shared library. May use $(SONAME) for the soname of the library.
++# Include -lc if appropriate.
++#
++# LADD: Additional linker flags (normally set on the make command line).
++#
++# INSTALL_DATA: beginning of shell command to install a library file.
++#
++# DESTDIR: main installation directory
++#
++# LIBINST_DIR: directory in which to install libraries, relative to DESTDIR.
++#
++# LN_S: beginning of shell command to make symbolic link (e.g. "ln -s").
++#
++# CXXLD: beginning of shell command to link, e.g. "g++".
++
++# This make file defines these make variables that the including make file
++# can use:
++#
++# SHLIB_CMD: a command to build a shared library for C linkage
++# You can use this in a rule to build a shared library
++# SHLIBPP_CMD: Same, but for C++ linkage
++
++# Including make file must contain a rule to build each library file
++# (e.g. libfoo.3.1)
++
++# This make file provides these rules:
++#
++# install-shared-libraries: install all shared libraries and the necessary
++# symbolic links.
++
++# SONAME is to be referenced by $(LDFLAGS_SHLIB) in the rule to make
++# a shared library (in common.mk). I.e. $@ is the name of the shared
++# library file.
++
++# SONAME is the name of the library file being built, with the minor
++# version number cut off. E.g. if we're building libfoo.so.1.2, SONAME
++# is libfoo.so.1 .
++SONAME = $(@:%.$(MIN)=%)
++
++SHLIB_CMD = $(CCLD) $(LADD) $(LDFLAGS_SHLIB) -o $@ $^
++
++SHLIB_LE_TARGETS = $(call shliblefn, $(SHARED_LIBS_TO_BUILD))
++
++$(SHLIB_LE_TARGETS):%:%.$(MAJ).$(MIN)
++ rm -f $@
++ $(LN_S) $< $@
++
++.PHONY: $(SHLIB_INSTALL_TARGETS)
++.PHONY: install-shared-libraries
++
++SHLIB_INSTALL_TARGETS = $(SHARED_LIBS_TO_INSTALL:%=%/install)
++
++#SHLIB_INSTALL_TARGETS is like "libfoo/install libbar/install"
++
++install-shared-libraries: $(SHLIB_INSTALL_TARGETS)
++
++$(SHLIB_INSTALL_TARGETS):%/install:%.$(SHLIB_SUFFIX).$(MAJ).$(MIN)
++# $< is a library file name, e.g. libfoo.so.3.1 .
++ $(INSTALL_SHLIB) $< $(DESTDIR)$(LIBINST_DIR)/$<
++ cd $(DESTDIR)$(LIBINST_DIR); \
++ rm -f $(<:%.$(MIN)=%); \
++ $(LN_S) $< $(<:%.$(MIN)=%)
++ cd $(DESTDIR)$(LIBINST_DIR); \
++ rm -f $(<:%.$(MAJ).$(MIN)=%); \
++ $(LN_S) $(<:%.$(MIN)=%) $(<:%.$(MAJ).$(MIN)=%)
diff --git a/libs/xmlrpc-c/version.h b/libs/xmlrpc-c/version.h
deleted file mode 100644
index 18e70f2..0000000
@@ -65846,6 +72858,23 @@ index 18e70f2..0000000
-#define XMLRPC_VERSION_MINOR 14
-#define XMLRPC_VERSION_POINT 99
-#endif
+diff --git a/libs/xmlrpc-c/version.mk b/libs/xmlrpc-c/version.mk
+new file mode 100644
+index 0000000..b59310a
+--- /dev/null
++++ b/libs/xmlrpc-c/version.mk
+@@ -0,0 +1,11 @@
++###############################################################################
++# This is not only a make file inclusion, but a source file for the program
++# 'mkvers' in the Windows build system. The latter is very particular about
++# the format of this file. Do not change white space, add comment lines, or
++# anything!
++#
++#
++###############################################################################
++XMLRPC_MAJOR_RELEASE = 1
++XMLRPC_MINOR_RELEASE = 26
++XMLRPC_POINT_RELEASE = 0
diff --git a/libs/xmlrpc-c/xmlrpc-c-config.in b/libs/xmlrpc-c/xmlrpc-c-config.in
deleted file mode 100644
index 2095b5f..0000000
@@ -68316,332 +75345,98 @@ index b4890d5..cfc0276 100644
+
+
diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj
-index a57eaf0..f95e975 100644
+index a57eaf0..3471e07 100644
--- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj
+++ b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.2010.vcxproj
-@@ -1,167 +1,155 @@
--
--
--
--
-- Debug
-- Win32
--
--
-- Debug
-- x64
--
--
-- Release
-- Win32
--
--
-- Release
-- x64
--
--
--
-- mod_xml_rpc
-- {CBEC7225-0C21-4DA8-978E-1F158F8AD950}
-- mod_xml_rpc
-- Win32Proj
--
--
--
-- DynamicLibrary
-- MultiByte
--
--
-- DynamicLibrary
-- MultiByte
--
--
-- DynamicLibrary
-- MultiByte
--
--
-- DynamicLibrary
-- MultiByte
--
--
--
--
--
--
--
+@@ -47,22 +47,18 @@
+
+
+
-
--
--
--
--
+
+
+
+
-
--
--
--
--
+
+
+
+
-
--
--
--
--
+
+
+
+
-
--
--
--
-- <_ProjectFileVersion>10.0.30319.1
--
--
--
+
+
+
+@@ -70,7 +66,7 @@
+
+
+
- %(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\abyss\src;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\util\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\Windows;%(AdditionalIncludeDirectories)
-- ABYSS_WIN32;%(PreprocessorDefinitions)
--
--
--
--
-- ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibD;%(AdditionalLibraryDirectories)
-- false
--
--
--
--
--
--
-- X64
--
--
++ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
+ ABYSS_WIN32;%(PreprocessorDefinitions)
+
+
+@@ -87,7 +83,7 @@
+ X64
+
+
- %(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\abyss\src;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\util\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\Windows;%(AdditionalIncludeDirectories)
-- ABYSS_WIN32;%(PreprocessorDefinitions)
--
--
--
--
-- %(AdditionalLibraryDirectories)
-- false
--
--
-- MachineX64
--
--
--
--
++ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
+ ABYSS_WIN32;%(PreprocessorDefinitions)
+
+
+@@ -102,7 +98,7 @@
+
+
+
- %(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\abyss\src;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\util\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\Windows;%(AdditionalIncludeDirectories)
-- ABYSS_WIN32;%(PreprocessorDefinitions)
--
--
--
--
-- ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibR;%(AdditionalLibraryDirectories)
-- false
--
--
--
--
--
--
-- X64
--
--
++ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
+ ABYSS_WIN32;%(PreprocessorDefinitions)
+
+
+@@ -119,7 +115,7 @@
+ X64
+
+
- %(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\abyss\src;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\lib\util\include;%(RootDir)%(Directory)..\..\..\..\libs\xmlrpc-c\Windows;%(AdditionalIncludeDirectories)
-- ABYSS_WIN32;%(PreprocessorDefinitions)
--
--
--
--
-- %(AdditionalLibraryDirectories)
-- false
--
--
-- MachineX64
--
--
--
--
--
--
++ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
+ ABYSS_WIN32;%(PreprocessorDefinitions)
+
+
+@@ -136,25 +132,17 @@
+
+
+
-
- {87ee9da4-de1e-4448-8324-183c98dca588}
- false
-
-
-- {d2396dd7-7d38-473a-abb7-6f96d65ae1b9}
++
+ {d2396dd7-7d38-473a-abb7-6f96d65ae1b9}
- false
--
+
-
-- {0d108721-eae8-4baf-8102-d8960ec93647}
++
+ {0d108721-eae8-4baf-8102-d8960ec93647}
- false
--
+
-
-- {cee544a9-0303-44c2-8ece-efa7d7bcbbba}
++
+ {cee544a9-0303-44c2-8ece-efa7d7bcbbba}
- false
--
+
-
-- {b535402e-38d2-4d54-8360-423acbd17192}
++
+ {b535402e-38d2-4d54-8360-423acbd17192}
- false
--
--
-- {202d7a4e-760d-4d0e-afa1-d7459ced30ff}
-- false
--
--
--
--
--
-+
-+
-+
-+
-+ Debug
-+ Win32
-+
-+
-+ Debug
-+ x64
-+
-+
-+ Release
-+ Win32
-+
-+
-+ Release
-+ x64
-+
-+
-+
-+ mod_xml_rpc
-+ {CBEC7225-0C21-4DA8-978E-1F158F8AD950}
-+ mod_xml_rpc
-+ Win32Proj
-+
-+
-+
-+ DynamicLibrary
-+ MultiByte
-+
-+
-+ DynamicLibrary
-+ MultiByte
-+
-+
-+ DynamicLibrary
-+ MultiByte
-+
-+
-+ DynamicLibrary
-+ MultiByte
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+
-+ <_ProjectFileVersion>10.0.30319.1
-+
-+
-+
-+ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
-+ ABYSS_WIN32;%(PreprocessorDefinitions)
-+
-+
-+
-+
-+ ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibD;%(AdditionalLibraryDirectories)
-+ false
-+
-+
-+
-+
-+
-+
-+ X64
-+
-+
-+ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
-+ ABYSS_WIN32;%(PreprocessorDefinitions)
-+
-+
-+
-+
-+ %(AdditionalLibraryDirectories)
-+ false
-+
-+
-+ MachineX64
-+
-+
-+
-+
-+ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
-+ ABYSS_WIN32;%(PreprocessorDefinitions)
-+
-+
-+
-+
-+ ..\..\..\..\libs\xmlrpc\lib;..\..\..\..\libs\xmlrpc\lib\abyss\src\$(OutDir);..\..\..\..\libs\apr-util\xml\expat\lib\LibR;%(AdditionalLibraryDirectories)
-+ false
-+
-+
-+
-+
-+
-+
-+ X64
-+
-+
-+ $(SolutionDir)libs\xmlrpc-c\include;$(SolutionDir)libs\xmlrpc-c\lib\abyss\src;$(SolutionDir)libs\xmlrpc-c\lib\util\include;%(AdditionalIncludeDirectories)
-+ ABYSS_WIN32;%(PreprocessorDefinitions)
-+
-+
-+
-+
-+ %(AdditionalLibraryDirectories)
-+ false
-+
-+
-+ MachineX64
-+
-+
-+
-+
-+
-+
-+
-+ {d2396dd7-7d38-473a-abb7-6f96d65ae1b9}
-+
-+
-+ {0d108721-eae8-4baf-8102-d8960ec93647}
-+
-+
-+ {cee544a9-0303-44c2-8ece-efa7d7bcbbba}
-+
-+
-+ {b535402e-38d2-4d54-8360-423acbd17192}
-+
-+
-+ {202d7a4e-760d-4d0e-afa1-d7459ced30ff}
-+ false
-+
-+
-+
-+
-+
-
-\ No newline at end of file
+
+
+ {202d7a4e-760d-4d0e-afa1-d7459ced30ff}
diff --git a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c b/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c
index 9b20546..4489cfc 100644
--- a/src/mod/xml_int/mod_xml_rpc/mod_xml_rpc.c
@@ -69521,3 +76316,46 @@ index f441567..2e74780 100644
SWITCH_DECLARE(switch_xml_t) switch_xml_insert(switch_xml_t xml, switch_xml_t dest, switch_size_t off)
{
switch_xml_t cur, prev, head;
+diff --git a/w32/xmlrpc.props b/w32/xmlrpc.props
+new file mode 100644
+index 0000000..ac0fc55
+--- /dev/null
++++ b/w32/xmlrpc.props
+@@ -0,0 +1,15 @@
++
++
++
++
++
++ $(SolutionDir)libs\xmlrpc-c\
++
++
++
++ $(XMLRPCDir);$(XMLRPCDir)include;$(XMLRPCDir)lib\util\include;%(AdditionalIncludeDirectories)
++
++
++
++
++
+\ No newline at end of file
+diff --git a/w32/xmlrpc.vsprops b/w32/xmlrpc.vsprops
+new file mode 100644
+index 0000000..eae9700
+--- /dev/null
++++ b/w32/xmlrpc.vsprops
+@@ -0,0 +1,15 @@
++
++
++
++
++