1180 lines
38 KiB
C
1180 lines
38 KiB
C
/* Copyright information is at end of file */
|
|
|
|
#define _XOPEN_SOURCE 600 /* Make sure strdup() is in <string.h> */
|
|
|
|
#include "xmlrpc_config.h"
|
|
|
|
#undef PACKAGE
|
|
#undef VERSION
|
|
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include "bool.h"
|
|
#include "mallocvar.h"
|
|
|
|
#include "xmlrpc-c/base.h"
|
|
#include "xmlrpc-c/base_int.h"
|
|
#include "xmlrpc-c/string_int.h"
|
|
#include "xmlrpc-c/client.h"
|
|
#include "xmlrpc-c/client_int.h"
|
|
/* transport_config.h defines XMLRPC_DEFAULT_TRANSPORT,
|
|
MUST_BUILD_WININET_CLIENT, MUST_BUILD_CURL_CLIENT,
|
|
MUST_BUILD_LIBWWW_CLIENT
|
|
*/
|
|
#include "transport_config.h"
|
|
#include "version.h"
|
|
|
|
struct xmlrpc_client {
|
|
/*----------------------------------------------------------------------------
|
|
This represents a client object.
|
|
-----------------------------------------------------------------------------*/
|
|
bool myTransport;
|
|
/* The transport described below was created by this object;
|
|
No one else knows it exists and this object is responsible
|
|
for destroying it.
|
|
*/
|
|
struct xmlrpc_client_transport * transportP;
|
|
struct xmlrpc_client_transport_ops transportOps;
|
|
xmlrpc_dialect dialect;
|
|
xmlrpc_progress_fn * progressFn;
|
|
};
|
|
|
|
|
|
|
|
struct xmlrpc_call_info {
|
|
/* This is all the information needed to finish executing a started
|
|
RPC.
|
|
|
|
You don't need this for an RPC the user executes synchronously,
|
|
because then you can just use the storage in which the user passed
|
|
his arguments. But for asynchronous, the user will take back his
|
|
storage, and we need to keep this info in our own.
|
|
*/
|
|
|
|
void * userHandle;
|
|
/* This is a handle for this call that is meaningful to our
|
|
user.
|
|
*/
|
|
xmlrpc_progress_fn * progressFn;
|
|
struct {
|
|
/* These are arguments to pass to the completion function. It
|
|
doesn't make sense to use them for anything else. In fact, it
|
|
really doesn't make sense for them to be arguments to the
|
|
completion function, but they are historically. */
|
|
const char * serverUrl;
|
|
const char * methodName;
|
|
xmlrpc_value * paramArrayP;
|
|
} completionArgs;
|
|
xmlrpc_response_handler * completionFn;
|
|
|
|
|
|
/* The serialized XML data passed to this call. We keep this around
|
|
** for use by our source_anchor field. */
|
|
xmlrpc_mem_block *serialized_xml;
|
|
};
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Global Constant Setup/Teardown
|
|
=========================================================================*/
|
|
|
|
static void
|
|
callTransportSetup(xmlrpc_env * const envP,
|
|
xmlrpc_transport_setup setupFn) {
|
|
|
|
if (setupFn)
|
|
setupFn(envP);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
setupTransportGlobalConst(xmlrpc_env * const envP) {
|
|
|
|
#if MUST_BUILD_WININET_CLIENT
|
|
if (!envP->fault_occurred)
|
|
callTransportSetup(envP,
|
|
xmlrpc_wininet_transport_ops.setup_global_const);
|
|
#endif
|
|
#if MUST_BUILD_CURL_CLIENT
|
|
if (!envP->fault_occurred)
|
|
callTransportSetup(envP,
|
|
xmlrpc_curl_transport_ops.setup_global_const);
|
|
#endif
|
|
#if MUST_BUILD_LIBWWW_CLIENT
|
|
if (!envP->fault_occurred)
|
|
callTransportSetup(envP,
|
|
xmlrpc_libwww_transport_ops.setup_global_const);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
callTransportTeardown(xmlrpc_transport_teardown teardownFn) {
|
|
|
|
if (teardownFn)
|
|
teardownFn();
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
teardownTransportGlobalConst(void) {
|
|
|
|
#if MUST_BUILD_WININET_CLIENT
|
|
callTransportTeardown(
|
|
xmlrpc_wininet_transport_ops.teardown_global_const);
|
|
#endif
|
|
#if MUST_BUILD_CURL_CLIENT
|
|
callTransportTeardown(
|
|
xmlrpc_curl_transport_ops.teardown_global_const);
|
|
#endif
|
|
#if MUST_BUILD_LIBWWW_CLIENT
|
|
callTransportTeardown(
|
|
xmlrpc_libwww_transport_ops.teardown_global_const);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Global stuff (except the global client)
|
|
=========================================================================*/
|
|
|
|
static unsigned int constSetupCount = 0;
|
|
|
|
|
|
void
|
|
xmlrpc_client_setup_global_const(xmlrpc_env * const envP) {
|
|
/*----------------------------------------------------------------------------
|
|
Set up pseudo-constant global variables (they'd be constant, except that
|
|
the library loader doesn't set them. An explicit call from the loaded
|
|
program does).
|
|
|
|
This function is not thread-safe. The user is supposed to call it
|
|
(perhaps cascaded down from a multitude of higher level libraries)
|
|
as part of early program setup, when the program is only one thread.
|
|
-----------------------------------------------------------------------------*/
|
|
if (constSetupCount == 0)
|
|
setupTransportGlobalConst(envP);
|
|
|
|
++constSetupCount;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_teardown_global_const(void) {
|
|
/*----------------------------------------------------------------------------
|
|
Complement to xmlrpc_client_setup_global_const().
|
|
|
|
This function is not thread-safe. The user is supposed to call it
|
|
(perhaps cascaded down from a multitude of higher level libraries)
|
|
as part of final program cleanup, when the program is only one thread.
|
|
-----------------------------------------------------------------------------*/
|
|
assert(constSetupCount > 0);
|
|
|
|
--constSetupCount;
|
|
|
|
if (constSetupCount == 0)
|
|
teardownTransportGlobalConst();
|
|
}
|
|
|
|
|
|
|
|
unsigned int const xmlrpc_client_version_major = XMLRPC_VERSION_MAJOR;
|
|
unsigned int const xmlrpc_client_version_minor = XMLRPC_VERSION_MINOR;
|
|
unsigned int const xmlrpc_client_version_point = XMLRPC_VERSION_POINT;
|
|
|
|
void
|
|
xmlrpc_client_version(unsigned int * const majorP,
|
|
unsigned int * const minorP,
|
|
unsigned int * const pointP) {
|
|
|
|
*majorP = XMLRPC_VERSION_MAJOR;
|
|
*minorP = XMLRPC_VERSION_MINOR;
|
|
*pointP = XMLRPC_VERSION_POINT;
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Client Create/Destroy
|
|
=========================================================================*/
|
|
|
|
static void
|
|
getTransportOps(
|
|
xmlrpc_env * const envP,
|
|
const char * const transportName,
|
|
const struct xmlrpc_client_transport_ops ** const opsPP) {
|
|
|
|
if (false) {
|
|
}
|
|
#if MUST_BUILD_WININET_CLIENT
|
|
else if (xmlrpc_streq(transportName, "wininet"))
|
|
*opsPP = &xmlrpc_wininet_transport_ops;
|
|
#endif
|
|
#if MUST_BUILD_CURL_CLIENT
|
|
else if (xmlrpc_streq(transportName, "curl"))
|
|
*opsPP = &xmlrpc_curl_transport_ops;
|
|
#endif
|
|
#if MUST_BUILD_LIBWWW_CLIENT
|
|
else if (xmlrpc_streq(transportName, "libwww"))
|
|
*opsPP = &xmlrpc_libwww_transport_ops;
|
|
#endif
|
|
else
|
|
xmlrpc_faultf(envP, "Unrecognized XML transport name '%s'",
|
|
transportName);
|
|
}
|
|
|
|
|
|
|
|
struct xportParms {
|
|
const void * parmsP;
|
|
size_t size;
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
getTransportParmsFromClientParms(
|
|
xmlrpc_env * const envP,
|
|
const struct xmlrpc_clientparms * const clientparmsP,
|
|
unsigned int const parmSize,
|
|
struct xportParms * const xportParmsP) {
|
|
|
|
if (parmSize < XMLRPC_CPSIZE(transportparmsP) ||
|
|
clientparmsP->transportparmsP == NULL) {
|
|
|
|
xportParmsP->parmsP = NULL;
|
|
xportParmsP->size = 0;
|
|
} else {
|
|
xportParmsP->parmsP = clientparmsP->transportparmsP;
|
|
if (parmSize < XMLRPC_CPSIZE(transportparm_size))
|
|
xmlrpc_faultf(envP, "Your 'clientparms' argument contains the "
|
|
"transportparmsP member, "
|
|
"but no transportparms_size member");
|
|
else
|
|
xportParmsP->size = clientparmsP->transportparm_size;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
getTransportInfo(
|
|
xmlrpc_env * const envP,
|
|
const struct xmlrpc_clientparms * const clientparmsP,
|
|
unsigned int const parmSize,
|
|
const char ** const transportNameP,
|
|
struct xportParms * const transportParmsP,
|
|
const struct xmlrpc_client_transport_ops ** const transportOpsPP,
|
|
xmlrpc_client_transport ** const transportPP) {
|
|
|
|
const char * transportNameParm;
|
|
xmlrpc_client_transport * transportP;
|
|
const struct xmlrpc_client_transport_ops * transportOpsP;
|
|
|
|
if (parmSize < XMLRPC_CPSIZE(transport))
|
|
transportNameParm = NULL;
|
|
else
|
|
transportNameParm = clientparmsP->transport;
|
|
|
|
if (parmSize < XMLRPC_CPSIZE(transportP))
|
|
transportP = NULL;
|
|
else
|
|
transportP = clientparmsP->transportP;
|
|
|
|
if (parmSize < XMLRPC_CPSIZE(transportOpsP))
|
|
transportOpsP = NULL;
|
|
else
|
|
transportOpsP = clientparmsP->transportOpsP;
|
|
|
|
if ((transportOpsP && !transportP) || (transportP && ! transportOpsP))
|
|
xmlrpc_faultf(envP, "'transportOpsP' and 'transportP' go together. "
|
|
"You must specify both or neither");
|
|
else if (transportNameParm && transportP)
|
|
xmlrpc_faultf(envP, "You cannot specify both 'transport' and "
|
|
"'transportP' transport parameters.");
|
|
else if (transportP)
|
|
*transportNameP = NULL;
|
|
else if (transportNameParm)
|
|
*transportNameP = transportNameParm;
|
|
else
|
|
*transportNameP = xmlrpc_client_get_default_transport(envP);
|
|
|
|
*transportOpsPP = transportOpsP;
|
|
*transportPP = transportP;
|
|
|
|
if (!envP->fault_occurred) {
|
|
getTransportParmsFromClientParms(
|
|
envP, clientparmsP, parmSize, transportParmsP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
if (transportParmsP->parmsP && !transportNameParm)
|
|
xmlrpc_faultf(
|
|
envP,
|
|
"You specified transport parameters, but did not "
|
|
"specify a transport type. Parameters are specific "
|
|
"to a particular type.");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
getDialectFromClientParms(
|
|
const struct xmlrpc_clientparms * const clientparmsP,
|
|
unsigned int const parmSize,
|
|
xmlrpc_dialect * const dialectP) {
|
|
|
|
if (parmSize < XMLRPC_CPSIZE(dialect))
|
|
*dialectP = xmlrpc_dialect_i8;
|
|
else
|
|
*dialectP = clientparmsP->dialect;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
clientCreate(
|
|
xmlrpc_env * const envP,
|
|
bool const myTransport,
|
|
const struct xmlrpc_client_transport_ops * const transportOpsP,
|
|
struct xmlrpc_client_transport * const transportP,
|
|
xmlrpc_dialect const dialect,
|
|
xmlrpc_progress_fn * const progressFn,
|
|
xmlrpc_client ** const clientPP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(transportOpsP);
|
|
XMLRPC_ASSERT_PTR_OK(transportP);
|
|
XMLRPC_ASSERT_PTR_OK(clientPP);
|
|
|
|
if (constSetupCount == 0) {
|
|
xmlrpc_faultf(envP,
|
|
"You have not called "
|
|
"xmlrpc_client_setup_global_const().");
|
|
/* Impl note: We can't just call it now because it isn't
|
|
thread-safe.
|
|
*/
|
|
} else {
|
|
xmlrpc_client * clientP;
|
|
|
|
MALLOCVAR(clientP);
|
|
|
|
if (clientP == NULL)
|
|
xmlrpc_faultf(envP, "Unable to allocate memory for "
|
|
"client descriptor.");
|
|
else {
|
|
clientP->myTransport = myTransport;
|
|
clientP->transportOps = *transportOpsP;
|
|
clientP->transportP = transportP;
|
|
clientP->dialect = dialect;
|
|
clientP->progressFn = progressFn;
|
|
|
|
*clientPP = clientP;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
createTransportAndClient(
|
|
xmlrpc_env * const envP,
|
|
const char * const transportName,
|
|
const void * const transportparmsP,
|
|
size_t const transportparmSize,
|
|
int const flags,
|
|
const char * const appname,
|
|
const char * const appversion,
|
|
xmlrpc_dialect const dialect,
|
|
xmlrpc_progress_fn * const progressFn,
|
|
xmlrpc_client ** const clientPP) {
|
|
|
|
const struct xmlrpc_client_transport_ops * transportOpsP;
|
|
|
|
getTransportOps(envP, transportName, &transportOpsP);
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_client_transport * transportP;
|
|
|
|
/* The following call is not thread-safe */
|
|
transportOpsP->create(
|
|
envP, flags, appname, appversion,
|
|
transportparmsP, transportparmSize,
|
|
&transportP);
|
|
if (!envP->fault_occurred) {
|
|
bool const myTransportTrue = true;
|
|
|
|
clientCreate(envP, myTransportTrue, transportOpsP, transportP,
|
|
dialect, progressFn, clientPP);
|
|
|
|
if (envP->fault_occurred)
|
|
transportOpsP->destroy(transportP);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_create(xmlrpc_env * const envP,
|
|
int const flags,
|
|
const char * const appname,
|
|
const char * const appversion,
|
|
const struct xmlrpc_clientparms * const clientparmsP,
|
|
unsigned int const parmSize,
|
|
xmlrpc_client ** const clientPP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(clientPP);
|
|
|
|
if (constSetupCount == 0) {
|
|
xmlrpc_faultf(envP,
|
|
"You have not called "
|
|
"xmlrpc_client_setup_global_const().");
|
|
/* Impl note: We can't just call it now because it isn't
|
|
thread-safe.
|
|
*/
|
|
} else {
|
|
const char * transportName;
|
|
struct xportParms transportparms;
|
|
const struct xmlrpc_client_transport_ops * transportOpsP;
|
|
xmlrpc_client_transport * transportP;
|
|
xmlrpc_dialect dialect;
|
|
xmlrpc_progress_fn * progressFn;
|
|
|
|
getTransportInfo(envP, clientparmsP, parmSize, &transportName,
|
|
&transportparms, &transportOpsP, &transportP);
|
|
|
|
getDialectFromClientParms(clientparmsP, parmSize, &dialect);
|
|
|
|
progressFn = parmSize >= XMLRPC_CPSIZE(progressFn) ?
|
|
clientparmsP->progressFn : NULL;
|
|
|
|
if (!envP->fault_occurred) {
|
|
if (transportName)
|
|
createTransportAndClient(envP, transportName,
|
|
transportparms.parmsP,
|
|
transportparms.size,
|
|
flags, appname, appversion, dialect,
|
|
progressFn,
|
|
clientPP);
|
|
else {
|
|
bool myTransportFalse = false;
|
|
clientCreate(envP, myTransportFalse,
|
|
transportOpsP, transportP, dialect, progressFn,
|
|
clientPP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_destroy(xmlrpc_client * const clientP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
|
|
if (clientP->myTransport)
|
|
clientP->transportOps.destroy(clientP->transportP);
|
|
|
|
free(clientP);
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Call/Response Utilities
|
|
=========================================================================*/
|
|
|
|
static void
|
|
makeCallXml(xmlrpc_env * const envP,
|
|
const char * const methodName,
|
|
xmlrpc_value * const paramArrayP,
|
|
xmlrpc_dialect const dialect,
|
|
xmlrpc_mem_block ** const callXmlPP) {
|
|
|
|
XMLRPC_ASSERT_VALUE_OK(paramArrayP);
|
|
XMLRPC_ASSERT_PTR_OK(callXmlPP);
|
|
|
|
if (methodName == NULL)
|
|
xmlrpc_faultf(envP, "method name argument is NULL pointer");
|
|
else {
|
|
xmlrpc_mem_block * callXmlP;
|
|
|
|
callXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_serialize_call2(envP, callXmlP, methodName, paramArrayP,
|
|
dialect);
|
|
|
|
*callXmlPP = callXmlP;
|
|
|
|
if (envP->fault_occurred)
|
|
XMLRPC_MEMBLOCK_FREE(char, callXmlP);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Synchronous Call
|
|
=========================================================================*/
|
|
|
|
void
|
|
xmlrpc_client_transport_call2(
|
|
xmlrpc_env * const envP,
|
|
xmlrpc_client * const clientP,
|
|
const xmlrpc_server_info * const serverP,
|
|
xmlrpc_mem_block * const callXmlP,
|
|
xmlrpc_mem_block ** const respXmlPP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
XMLRPC_ASSERT_PTR_OK(serverP);
|
|
XMLRPC_ASSERT_PTR_OK(callXmlP);
|
|
XMLRPC_ASSERT_PTR_OK(respXmlPP);
|
|
|
|
clientP->transportOps.call(
|
|
envP, clientP->transportP, serverP, callXmlP,
|
|
respXmlPP);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
parseResponse(xmlrpc_env * const envP,
|
|
xmlrpc_mem_block * const respXmlP,
|
|
xmlrpc_value ** const resultPP,
|
|
int * const faultCodeP,
|
|
const char ** const faultStringP) {
|
|
|
|
xmlrpc_env respEnv;
|
|
|
|
xmlrpc_env_init(&respEnv);
|
|
|
|
xmlrpc_parse_response2(
|
|
&respEnv,
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
|
|
XMLRPC_MEMBLOCK_SIZE(char, respXmlP),
|
|
resultPP, faultCodeP, faultStringP);
|
|
|
|
if (respEnv.fault_occurred)
|
|
xmlrpc_env_set_fault_formatted(
|
|
envP, respEnv.fault_code,
|
|
"Unable to make sense of XML-RPC response from server. "
|
|
"%s. Use XMLRPC_TRACE_XML to see for yourself",
|
|
respEnv.fault_string);
|
|
|
|
xmlrpc_env_clean(&respEnv);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_call2(xmlrpc_env * const envP,
|
|
struct xmlrpc_client * const clientP,
|
|
const xmlrpc_server_info * const serverInfoP,
|
|
const char * const methodName,
|
|
xmlrpc_value * const paramArrayP,
|
|
xmlrpc_value ** const resultPP) {
|
|
|
|
xmlrpc_mem_block * callXmlP;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
XMLRPC_ASSERT_PTR_OK(serverInfoP);
|
|
XMLRPC_ASSERT_PTR_OK(paramArrayP);
|
|
|
|
makeCallXml(envP, methodName, paramArrayP, clientP->dialect, &callXmlP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_mem_block * respXmlP;
|
|
|
|
xmlrpc_traceXml("XML-RPC CALL",
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP),
|
|
XMLRPC_MEMBLOCK_SIZE(char, callXmlP));
|
|
|
|
clientP->transportOps.call(
|
|
envP, clientP->transportP, serverInfoP, callXmlP, &respXmlP);
|
|
if (!envP->fault_occurred) {
|
|
int faultCode;
|
|
const char * faultString;
|
|
|
|
xmlrpc_traceXml("XML-RPC RESPONSE",
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
|
|
XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
|
|
|
|
parseResponse(envP, respXmlP, resultPP, &faultCode, &faultString);
|
|
|
|
if (!envP->fault_occurred) {
|
|
if (faultString) {
|
|
xmlrpc_env_set_fault_formatted(
|
|
envP, faultCode,
|
|
"RPC failed at server. %s", faultString);
|
|
xmlrpc_strfree(faultString);
|
|
} else
|
|
XMLRPC_ASSERT_VALUE_OK(*resultPP);
|
|
}
|
|
XMLRPC_MEMBLOCK_FREE(char, respXmlP);
|
|
}
|
|
XMLRPC_MEMBLOCK_FREE(char, callXmlP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
computeParamArray(xmlrpc_env * const envP,
|
|
const char * const format,
|
|
va_list args,
|
|
xmlrpc_value ** const paramArrayPP) {
|
|
/*----------------------------------------------------------------------------
|
|
'format' and 'args' specify the parameter list of an RPC, in the form
|
|
of an XML-RPC array value, with one element per RPC parameter.
|
|
|
|
'format' is an XML-RPC value format string, e.g. "(ii{s:i,s:i})".
|
|
'args' is the list of substitution values for that string
|
|
(6 values in this example, 4 integers and 2 strings).
|
|
|
|
We return the XML-RPC value 'format' and 'args' represent, but throw an
|
|
error if they don't validly specify a single array.
|
|
|
|
Note that it is a common user error to specify the format string as a
|
|
single or string of argument types, instead of as an array of argument
|
|
types. E.g. "i" or "ii" instead of "(i)" and "(ii)". So we try
|
|
especially hard to give an informative message for that case.
|
|
-----------------------------------------------------------------------------*/
|
|
xmlrpc_env env;
|
|
xmlrpc_value * paramArrayP;
|
|
const char * suffix;
|
|
/* Stuff left over in format string after parameter array
|
|
specification.
|
|
*/
|
|
|
|
xmlrpc_env_init(&env);
|
|
xmlrpc_build_value_va(&env, format, args, ¶mArrayP, &suffix);
|
|
if (env.fault_occurred)
|
|
xmlrpc_env_set_fault_formatted(
|
|
envP, env.fault_code, "Invalid RPC arguments. "
|
|
"The format argument must indicate a single array (each element "
|
|
"of which is one argument to the XML-RPC call), and the "
|
|
"following arguments must correspond to that format argument. "
|
|
"The failure is: %s",
|
|
env.fault_string);
|
|
else {
|
|
XMLRPC_ASSERT_VALUE_OK(paramArrayP);
|
|
|
|
if (*suffix != '\0')
|
|
xmlrpc_faultf(envP,
|
|
"Junk after the parameter array specifier: '%s'. "
|
|
"The format string must specify exactly one value: "
|
|
"an array of RPC parameters",
|
|
suffix);
|
|
else {
|
|
if (xmlrpc_value_type(paramArrayP) != XMLRPC_TYPE_ARRAY)
|
|
xmlrpc_faultf(
|
|
envP,
|
|
"You must specify the parameter list as an "
|
|
"XML-RPC array value, "
|
|
"each element of which is a parameter of the RPC. "
|
|
"But your format string specifies an XML-RPC %s, not "
|
|
"an array",
|
|
xmlrpc_type_name(xmlrpc_value_type(paramArrayP)));
|
|
}
|
|
if (env.fault_occurred)
|
|
xmlrpc_DECREF(paramArrayP);
|
|
else
|
|
*paramArrayPP = paramArrayP;
|
|
}
|
|
xmlrpc_env_clean(&env);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_call_server2_va(xmlrpc_env * const envP,
|
|
struct xmlrpc_client * const clientP,
|
|
const xmlrpc_server_info * const serverInfoP,
|
|
const char * const methodName,
|
|
const char * const format,
|
|
va_list args,
|
|
xmlrpc_value ** const resultPP) {
|
|
|
|
/* This function exists only for use by the global client function
|
|
xmlrpc_client_call_server().
|
|
*/
|
|
|
|
xmlrpc_value * paramArrayP;
|
|
/* The XML-RPC parameter list array */
|
|
|
|
computeParamArray(envP, format, args, ¶mArrayP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_client_call2(envP, clientP,
|
|
serverInfoP, methodName, paramArrayP,
|
|
resultPP);
|
|
|
|
xmlrpc_DECREF(paramArrayP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_call2f_va(xmlrpc_env * const envP,
|
|
xmlrpc_client * const clientP,
|
|
const char * const serverUrl,
|
|
const char * const methodName,
|
|
const char * const format,
|
|
xmlrpc_value ** const resultPP,
|
|
va_list args) {
|
|
|
|
xmlrpc_value * paramArrayP;
|
|
/* The XML-RPC parameter list array */
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
XMLRPC_ASSERT_PTR_OK(methodName);
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
XMLRPC_ASSERT_PTR_OK(resultPP);
|
|
|
|
computeParamArray(envP, format, args, ¶mArrayP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_server_info * serverInfoP;
|
|
|
|
serverInfoP = xmlrpc_server_info_new(envP, serverUrl);
|
|
|
|
if (!envP->fault_occurred) {
|
|
/* Perform the actual XML-RPC call. */
|
|
xmlrpc_client_call2(envP, clientP,
|
|
serverInfoP, methodName, paramArrayP,
|
|
resultPP);
|
|
if (!envP->fault_occurred)
|
|
XMLRPC_ASSERT_VALUE_OK(*resultPP);
|
|
xmlrpc_server_info_free(serverInfoP);
|
|
}
|
|
xmlrpc_DECREF(paramArrayP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_call2f(xmlrpc_env * const envP,
|
|
xmlrpc_client * const clientP,
|
|
const char * const serverUrl,
|
|
const char * const methodName,
|
|
xmlrpc_value ** const resultPP,
|
|
const char * const format,
|
|
...) {
|
|
|
|
va_list args;
|
|
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
va_start(args, format);
|
|
xmlrpc_client_call2f_va(envP, clientP, serverUrl,
|
|
methodName, format, resultPP, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Asynchronous Call
|
|
=========================================================================*/
|
|
|
|
static void
|
|
callInfoSetCompletion(xmlrpc_env * const envP,
|
|
struct xmlrpc_call_info * const callInfoP,
|
|
const char * const serverUrl,
|
|
const char * const methodName,
|
|
xmlrpc_value * const paramArrayP,
|
|
xmlrpc_response_handler completionFn,
|
|
xmlrpc_progress_fn progressFn,
|
|
void * const userHandle) {
|
|
/*----------------------------------------------------------------------------
|
|
Set the members of callinfo structure *callInfoP that are used for
|
|
the completion and progress calls from the transport to us.
|
|
-----------------------------------------------------------------------------*/
|
|
callInfoP->completionFn = completionFn;
|
|
callInfoP->progressFn = progressFn;
|
|
callInfoP->userHandle = userHandle;
|
|
callInfoP->completionArgs.serverUrl = strdup(serverUrl);
|
|
if (callInfoP->completionArgs.serverUrl == NULL)
|
|
xmlrpc_faultf(envP, "Couldn't get memory to store server URL");
|
|
else {
|
|
callInfoP->completionArgs.methodName = strdup(methodName);
|
|
if (callInfoP->completionArgs.methodName == NULL)
|
|
xmlrpc_faultf(envP, "Couldn't get memory to store method name");
|
|
else {
|
|
callInfoP->completionArgs.paramArrayP = paramArrayP;
|
|
xmlrpc_INCREF(paramArrayP);
|
|
}
|
|
if (envP->fault_occurred)
|
|
xmlrpc_strfree(callInfoP->completionArgs.serverUrl);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
callInfoCreate(xmlrpc_env * const envP,
|
|
const char * const methodName,
|
|
xmlrpc_value * const paramArrayP,
|
|
xmlrpc_dialect const dialect,
|
|
const char * const serverUrl,
|
|
xmlrpc_response_handler completionFn,
|
|
xmlrpc_progress_fn progressFn,
|
|
void * const userHandle,
|
|
struct xmlrpc_call_info ** const callInfoPP) {
|
|
/*----------------------------------------------------------------------------
|
|
Create a call_info object. A call_info object represents an XML-RPC
|
|
call.
|
|
-----------------------------------------------------------------------------*/
|
|
struct xmlrpc_call_info * callInfoP;
|
|
|
|
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
XMLRPC_ASSERT_PTR_OK(methodName);
|
|
XMLRPC_ASSERT_VALUE_OK(paramArrayP);
|
|
XMLRPC_ASSERT_PTR_OK(callInfoPP);
|
|
|
|
MALLOCVAR(callInfoP);
|
|
if (callInfoP == NULL)
|
|
xmlrpc_faultf(envP, "Couldn't allocate memory for xmlrpc_call_info");
|
|
else {
|
|
xmlrpc_mem_block * callXmlP;
|
|
|
|
makeCallXml(envP, methodName, paramArrayP, dialect, &callXmlP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
callInfoP->serialized_xml = callXmlP;
|
|
|
|
callInfoSetCompletion(envP, callInfoP, serverUrl, methodName,
|
|
paramArrayP,
|
|
completionFn, progressFn, userHandle);
|
|
|
|
if (envP->fault_occurred)
|
|
free(callInfoP);
|
|
}
|
|
}
|
|
*callInfoPP = callInfoP;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
callInfoDestroy(struct xmlrpc_call_info * const callInfoP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(callInfoP);
|
|
|
|
if (callInfoP->completionFn) {
|
|
xmlrpc_DECREF(callInfoP->completionArgs.paramArrayP);
|
|
xmlrpc_strfree(callInfoP->completionArgs.methodName);
|
|
xmlrpc_strfree(callInfoP->completionArgs.serverUrl);
|
|
}
|
|
if (callInfoP->serialized_xml)
|
|
xmlrpc_mem_block_free(callInfoP->serialized_xml);
|
|
|
|
free(callInfoP);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_event_loop_finish(xmlrpc_client * const clientP) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
|
|
clientP->transportOps.finish_asynch(
|
|
clientP->transportP, timeout_no, 0);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_event_loop_finish_timeout(xmlrpc_client * const clientP,
|
|
xmlrpc_timeout const timeout) {
|
|
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
|
|
clientP->transportOps.finish_asynch(
|
|
clientP->transportP, timeout_yes, timeout);
|
|
}
|
|
|
|
|
|
|
|
/* Microsoft Visual C in debug mode produces code that complains about
|
|
passing an undefined value of 'resultP' to xmlrpc_parse_response2().
|
|
It's a bogus complaint, because this function knows in those cases
|
|
that the value of 'resultP' is meaningless. So we disable the check.
|
|
*/
|
|
#pragma runtime_checks("u", off)
|
|
|
|
|
|
|
|
static void
|
|
asynchComplete(struct xmlrpc_call_info * const callInfoP,
|
|
xmlrpc_mem_block * const responseXmlP,
|
|
xmlrpc_env const transportEnv) {
|
|
/*----------------------------------------------------------------------------
|
|
Complete an asynchronous XML-RPC call request.
|
|
|
|
This includes calling the user's RPC completion routine.
|
|
|
|
'transportEnv' describes an error that the transport
|
|
encountered in processing the call. If the transport successfully
|
|
sent the call to the server and processed the response but the
|
|
server failed the call, 'transportEnv' indicates no error, and the
|
|
response in *responseXmlP might very well indicate that the server
|
|
failed the request.
|
|
-----------------------------------------------------------------------------*/
|
|
xmlrpc_env env;
|
|
xmlrpc_value * resultP;
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
if (transportEnv.fault_occurred)
|
|
xmlrpc_env_set_fault_formatted(
|
|
&env, transportEnv.fault_code,
|
|
"Client transport failed to execute the RPC. %s",
|
|
transportEnv.fault_string);
|
|
|
|
if (!env.fault_occurred) {
|
|
int faultCode;
|
|
const char * faultString;
|
|
|
|
xmlrpc_parse_response2(&env,
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlP),
|
|
XMLRPC_MEMBLOCK_SIZE(char, responseXmlP),
|
|
&resultP, &faultCode, &faultString);
|
|
|
|
if (!env.fault_occurred) {
|
|
if (faultString) {
|
|
xmlrpc_env_set_fault_formatted(
|
|
&env, faultCode,
|
|
"RPC failed at server. %s", faultString);
|
|
xmlrpc_strfree(faultString);
|
|
}
|
|
}
|
|
}
|
|
/* Call the user's completion function with the RPC result */
|
|
(*callInfoP->completionFn)(callInfoP->completionArgs.serverUrl,
|
|
callInfoP->completionArgs.methodName,
|
|
callInfoP->completionArgs.paramArrayP,
|
|
callInfoP->userHandle,
|
|
&env, resultP);
|
|
|
|
if (!env.fault_occurred)
|
|
xmlrpc_DECREF(resultP);
|
|
|
|
callInfoDestroy(callInfoP);
|
|
|
|
xmlrpc_env_clean(&env);
|
|
}
|
|
|
|
|
|
|
|
#pragma runtime_checks("u", restore)
|
|
|
|
|
|
|
|
static void
|
|
progress(struct xmlrpc_call_info * const callInfoP,
|
|
struct xmlrpc_progress_data const progressData) {
|
|
|
|
/* We wouldn't have asked the transport to call our progress
|
|
function if we didn't have a user progress function to call:
|
|
*/
|
|
assert(callInfoP->progressFn);
|
|
|
|
callInfoP->progressFn(callInfoP->userHandle, progressData);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_start_rpc(xmlrpc_env * const envP,
|
|
struct xmlrpc_client * const clientP,
|
|
const xmlrpc_server_info * const serverInfoP,
|
|
const char * const methodName,
|
|
xmlrpc_value * const paramArrayP,
|
|
xmlrpc_response_handler completionFn,
|
|
void * const userHandle) {
|
|
|
|
struct xmlrpc_call_info * callInfoP;
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
XMLRPC_ASSERT_PTR_OK(serverInfoP);
|
|
XMLRPC_ASSERT_PTR_OK(methodName);
|
|
XMLRPC_ASSERT_VALUE_OK(paramArrayP);
|
|
|
|
callInfoCreate(envP, methodName, paramArrayP, clientP->dialect,
|
|
serverInfoP->serverUrl,
|
|
completionFn, clientP->progressFn, userHandle,
|
|
&callInfoP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_traceXml(
|
|
"XML-RPC CALL",
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, callInfoP->serialized_xml),
|
|
XMLRPC_MEMBLOCK_SIZE(char, callInfoP->serialized_xml));
|
|
|
|
clientP->transportOps.send_request(
|
|
envP, clientP->transportP, serverInfoP,
|
|
callInfoP->serialized_xml,
|
|
&asynchComplete, clientP->progressFn ? &progress : NULL,
|
|
callInfoP);
|
|
}
|
|
if (envP->fault_occurred)
|
|
callInfoDestroy(callInfoP);
|
|
else {
|
|
/* asynchComplete() will destroy *callInfoP */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_start_rpcf_server_va(
|
|
xmlrpc_env * const envP,
|
|
xmlrpc_client * const clientP,
|
|
const xmlrpc_server_info * const serverInfoP,
|
|
const char * const methodName,
|
|
xmlrpc_response_handler responseHandler,
|
|
void * const userHandle,
|
|
const char * const format,
|
|
va_list args) {
|
|
|
|
xmlrpc_value * paramArrayP;
|
|
/* The XML-RPC parameter list array */
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
XMLRPC_ASSERT_PTR_OK(clientP);
|
|
XMLRPC_ASSERT_PTR_OK(serverInfoP);
|
|
XMLRPC_ASSERT_PTR_OK(methodName);
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
computeParamArray(envP, format, args, ¶mArrayP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_client_start_rpc(envP, clientP,
|
|
serverInfoP, methodName, paramArrayP,
|
|
responseHandler, userHandle);
|
|
|
|
xmlrpc_DECREF(paramArrayP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_start_rpcf_va(xmlrpc_env * const envP,
|
|
xmlrpc_client * const clientP,
|
|
const char * const serverUrl,
|
|
const char * const methodName,
|
|
xmlrpc_response_handler responseHandler,
|
|
void * const userHandle,
|
|
const char * const format,
|
|
va_list args) {
|
|
|
|
xmlrpc_server_info * serverInfoP;
|
|
|
|
serverInfoP = xmlrpc_server_info_new(envP, serverUrl);
|
|
if (!envP->fault_occurred) {
|
|
xmlrpc_client_start_rpcf_server_va(
|
|
envP, clientP,
|
|
serverInfoP, methodName,
|
|
responseHandler, userHandle,
|
|
format, args);
|
|
|
|
xmlrpc_server_info_free(serverInfoP);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_start_rpcf(xmlrpc_env * const envP,
|
|
xmlrpc_client * const clientP,
|
|
const char * const serverUrl,
|
|
const char * const methodName,
|
|
xmlrpc_response_handler responseHandler,
|
|
void * const userHandle,
|
|
const char * const format,
|
|
...) {
|
|
|
|
va_list args;
|
|
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
va_start(args, format);
|
|
xmlrpc_client_start_rpcf_va(envP, clientP, serverUrl, methodName,
|
|
responseHandler,
|
|
userHandle, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
Miscellaneous
|
|
=========================================================================*/
|
|
|
|
const char *
|
|
xmlrpc_client_get_default_transport(xmlrpc_env * const envP ATTR_UNUSED) {
|
|
|
|
return XMLRPC_DEFAULT_TRANSPORT;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
xmlrpc_client_set_interrupt(xmlrpc_client * const clientP,
|
|
int * const interruptP) {
|
|
|
|
if (clientP->transportOps.set_interrupt)
|
|
clientP->transportOps.set_interrupt(clientP->transportP, interruptP);
|
|
}
|
|
|
|
|
|
|
|
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
** SUCH DAMAGE.
|
|
*/
|