2005-02-22 21:08:27 +03:00
|
|
|
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
** SUCH DAMAGE. */
|
|
|
|
|
|
|
|
#include "xmlrpc_config.h"
|
|
|
|
|
|
|
|
#undef PACKAGE
|
|
|
|
#undef VERSION
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "bool.h"
|
|
|
|
#include "mallocvar.h"
|
|
|
|
#include "xmlrpc.h"
|
|
|
|
#include "xmlrpc_int.h"
|
|
|
|
#include "xmlrpc_client.h"
|
|
|
|
#include "xmlrpc_client_int.h"
|
|
|
|
/* transport_config.h defines XMLRPC_DEFAULT_TRANSPORT,
|
|
|
|
MUST_BUILD_WININET_CLIENT, MUST_BUILD_CURL_CLIENT,
|
|
|
|
MUST_BUILD_LIBWWW_CLIENT
|
|
|
|
*/
|
|
|
|
#include "transport_config.h"
|
|
|
|
|
|
|
|
#if MUST_BUILD_WININET_CLIENT
|
|
|
|
#include "xmlrpc_wininet_transport.h"
|
|
|
|
#endif
|
|
|
|
#if MUST_BUILD_CURL_CLIENT
|
|
|
|
#include "xmlrpc_curl_transport.h"
|
|
|
|
#endif
|
|
|
|
#if MUST_BUILD_LIBWWW_CLIENT
|
|
|
|
#include "xmlrpc_libwww_transport.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct xmlrpc_client {
|
|
|
|
/*----------------------------------------------------------------------------
|
|
|
|
This represents a client object.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
struct clientTransport * transportP;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct call_info
|
|
|
|
{
|
|
|
|
/* These fields are used when performing asynchronous calls.
|
|
|
|
** The _asynch_data_holder contains server_url, method_name and
|
|
|
|
** param_array, so it's the only thing we need to free. */
|
|
|
|
xmlrpc_value *_asynch_data_holder;
|
|
|
|
char *server_url;
|
|
|
|
char *method_name;
|
|
|
|
xmlrpc_value *param_array;
|
|
|
|
xmlrpc_response_handler callback;
|
|
|
|
void *user_data;
|
|
|
|
|
|
|
|
/* The serialized XML data passed to this call. We keep this around
|
|
|
|
** for use by our source_anchor field. */
|
|
|
|
xmlrpc_mem_block *serialized_xml;
|
|
|
|
} call_info;
|
|
|
|
|
|
|
|
static bool clientInitialized = FALSE;
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
|
|
** Initialization and Shutdown
|
|
|
|
**=========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct clientTransportOps clientTransportOps;
|
|
|
|
|
|
|
|
static struct xmlrpc_client client;
|
|
|
|
/* Some day, we need to make this dynamically allocated, so there can
|
|
|
|
be more than one client per program and just generally to provide
|
|
|
|
a cleaner interface.
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern void
|
|
|
|
xmlrpc_client_init(int const flags,
|
|
|
|
const char * const appname,
|
|
|
|
const char * const appversion) {
|
|
|
|
|
|
|
|
struct xmlrpc_clientparms clientparms;
|
|
|
|
|
|
|
|
/* As our interface does not allow for failure, we just fail silently ! */
|
|
|
|
|
|
|
|
xmlrpc_env env;
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
|
|
|
|
clientparms.transport = XMLRPC_DEFAULT_TRANSPORT;
|
|
|
|
|
|
|
|
xmlrpc_client_init2(&env, flags,
|
|
|
|
appname, appversion,
|
|
|
|
&clientparms, XMLRPC_CPSIZE(transport));
|
|
|
|
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
xmlrpc_client_get_default_transport(xmlrpc_env * const env ATTR_UNUSED) {
|
|
|
|
|
|
|
|
return XMLRPC_DEFAULT_TRANSPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
setupTransport(xmlrpc_env * const envP,
|
|
|
|
const char * const transportName) {
|
|
|
|
|
|
|
|
if (FALSE) {
|
|
|
|
}
|
|
|
|
#if MUST_BUILD_WININET_CLIENT
|
|
|
|
else if (strcmp(transportName, "wininet") == 0)
|
|
|
|
clientTransportOps = xmlrpc_wininet_transport_ops;
|
|
|
|
#endif
|
|
|
|
#if MUST_BUILD_CURL_CLIENT
|
|
|
|
else if (strcmp(transportName, "curl") == 0)
|
|
|
|
clientTransportOps = xmlrpc_curl_transport_ops;
|
2005-03-01 00:26:20 +03:00
|
|
|
else if (strcmp(transportName, "libcurl") == 0)
|
|
|
|
clientTransportOps = xmlrpc_curl_transport_ops;
|
2005-02-22 21:08:27 +03:00
|
|
|
#endif
|
|
|
|
#if MUST_BUILD_LIBWWW_CLIENT
|
|
|
|
else if (strcmp(transportName, "libwww") == 0)
|
|
|
|
clientTransportOps = xmlrpc_libwww_transport_ops;
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Unrecognized XML transport name '%s'", transportName);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_init2(xmlrpc_env * const envP,
|
|
|
|
int const flags,
|
|
|
|
const char * const appname,
|
|
|
|
const char * const appversion,
|
|
|
|
struct xmlrpc_clientparms * const clientparmsP,
|
|
|
|
unsigned int const parm_size) {
|
|
|
|
|
|
|
|
if (clientInitialized)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Xmlrpc-c client instance has already been initialized "
|
|
|
|
"(need to call xmlrpc_client_cleanup() before you can "
|
|
|
|
"reinitialize).");
|
|
|
|
else {
|
|
|
|
const char * transportName;
|
|
|
|
|
|
|
|
if (parm_size < XMLRPC_CPSIZE(transport) ||
|
|
|
|
clientparmsP->transport == NULL) {
|
|
|
|
/* He didn't specify a transport. Use the default */
|
|
|
|
transportName = xmlrpc_client_get_default_transport(envP);
|
|
|
|
} else
|
|
|
|
transportName = clientparmsP->transport;
|
|
|
|
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
setupTransport(envP, transportName);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
clientTransportOps.create(envP, flags, appname, appversion,
|
|
|
|
&client.transportP);
|
|
|
|
if (!envP->fault_occurred)
|
|
|
|
clientInitialized = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_cleanup() {
|
|
|
|
|
|
|
|
XMLRPC_ASSERT(clientInitialized);
|
|
|
|
|
|
|
|
clientTransportOps.destroy(client.transportP);
|
|
|
|
|
|
|
|
clientInitialized = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
call_info_free(call_info * const callInfoP) {
|
|
|
|
|
|
|
|
/* Assume the worst.. That only parts of the call_info are valid. */
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_PTR_OK(callInfoP);
|
|
|
|
|
|
|
|
/* If this has been allocated, we're responsible for destroying it. */
|
|
|
|
if (callInfoP->_asynch_data_holder)
|
|
|
|
xmlrpc_DECREF(callInfoP->_asynch_data_holder);
|
|
|
|
|
|
|
|
/* Now we can blow away the XML data. */
|
|
|
|
if (callInfoP->serialized_xml)
|
|
|
|
xmlrpc_mem_block_free(callInfoP->serialized_xml);
|
|
|
|
|
|
|
|
free(callInfoP);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
call_info_new(xmlrpc_env * const envP,
|
|
|
|
xmlrpc_server_info * const server,
|
|
|
|
const char * const method_name,
|
|
|
|
xmlrpc_value * const argP,
|
|
|
|
call_info ** const callInfoPP) {
|
|
|
|
/*----------------------------------------------------------------------------
|
|
|
|
Create a call_info object. A call_info object represents an XML-RPC
|
|
|
|
call.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
call_info * callInfoP;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_PTR_OK(argP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(callInfoPP);
|
|
|
|
|
|
|
|
if (method_name == NULL)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"method name argument is NULL pointer");
|
|
|
|
else if (server == NULL)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"server info argument is NULL pointer");
|
|
|
|
else {
|
|
|
|
MALLOCVAR(callInfoP);
|
|
|
|
if (callInfoP == NULL)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for xmlrpc_call_info");
|
|
|
|
else {
|
|
|
|
xmlrpc_mem_block * callXmlP;
|
|
|
|
|
|
|
|
/* Clear contents. */
|
|
|
|
memset(callInfoP, 0, sizeof(*callInfoP));
|
|
|
|
|
|
|
|
/* Make the XML for our call */
|
|
|
|
callXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
xmlrpc_serialize_call(envP, callXmlP, method_name, argP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
xmlrpc_traceXml("XML-RPC CALL",
|
|
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP),
|
|
|
|
XMLRPC_MEMBLOCK_SIZE(char, callXmlP));
|
|
|
|
|
|
|
|
callInfoP->serialized_xml = callXmlP;
|
|
|
|
|
|
|
|
*callInfoPP = callInfoP;
|
|
|
|
}
|
|
|
|
if (envP->fault_occurred)
|
|
|
|
XMLRPC_MEMBLOCK_FREE(char, callXmlP);
|
|
|
|
}
|
|
|
|
if (envP->fault_occurred)
|
|
|
|
free(callInfoP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
clientCallServerParams(xmlrpc_env * const envP,
|
|
|
|
struct clientTransport * const transportP,
|
|
|
|
xmlrpc_server_info * const serverP,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_value * const paramArrayP,
|
|
|
|
xmlrpc_value ** const resultPP) {
|
|
|
|
|
|
|
|
call_info * callInfoP;
|
|
|
|
|
|
|
|
if (!clientInitialized)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Xmlrpc-c client instance has not been initialized "
|
|
|
|
"(need to call xmlrpc_client_init2()).");
|
|
|
|
else {
|
|
|
|
call_info_new(envP, serverP, methodName, paramArrayP, &callInfoP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
xmlrpc_mem_block * respXmlP;
|
|
|
|
|
|
|
|
clientTransportOps.call(envP, transportP, serverP,
|
|
|
|
callInfoP->serialized_xml, callInfoP,
|
|
|
|
&respXmlP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
xmlrpc_traceXml("XML-RPC RESPONSE",
|
|
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
|
|
|
|
XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
|
|
|
|
|
|
|
|
*resultPP = xmlrpc_parse_response(
|
|
|
|
envP,
|
|
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
|
|
|
|
XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
|
|
|
|
XMLRPC_MEMBLOCK_FREE(char, respXmlP);
|
|
|
|
}
|
|
|
|
call_info_free(callInfoP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xmlrpc_value *
|
|
|
|
xmlrpc_client_call_params(xmlrpc_env * const envP,
|
|
|
|
const char * const serverUrl,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_value * const paramArrayP) {
|
|
|
|
|
|
|
|
xmlrpc_value *retval;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
|
|
|
|
|
|
if (!clientInitialized)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Xmlrpc-c client instance has not been initialized "
|
|
|
|
"(need to call xmlrpc_client_init2()).");
|
|
|
|
else {
|
|
|
|
xmlrpc_server_info * serverP;
|
|
|
|
|
|
|
|
/* Build a server info object and make our call. */
|
|
|
|
serverP = xmlrpc_server_info_new(envP, serverUrl);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
clientCallServerParams(envP, client.transportP, serverP,
|
|
|
|
methodName, paramArrayP,
|
|
|
|
&retval);
|
|
|
|
|
|
|
|
xmlrpc_server_info_free(serverP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!envP->fault_occurred)
|
|
|
|
XMLRPC_ASSERT_VALUE_OK(retval);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static xmlrpc_value *
|
|
|
|
xmlrpc_client_call_va(xmlrpc_env * const envP,
|
|
|
|
const char * const server_url,
|
|
|
|
const char * const method_name,
|
|
|
|
const char * const format,
|
|
|
|
va_list args) {
|
|
|
|
|
|
|
|
xmlrpc_value * argP;
|
2005-03-02 19:04:32 +03:00
|
|
|
xmlrpc_value * retval = 0;
|
2005-02-22 21:08:27 +03:00
|
|
|
xmlrpc_env argenv;
|
|
|
|
const char * suffix;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
|
|
|
|
/* Build our argument value. */
|
|
|
|
xmlrpc_env_init(&argenv);
|
|
|
|
xmlrpc_build_value_va(&argenv, format, args, &argP, &suffix);
|
|
|
|
if (argenv.fault_occurred) {
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, argenv.fault_code, "Invalid RPC arguments. "
|
|
|
|
"The format argument must indicate a single array, and the "
|
|
|
|
"following arguments must correspond to that format argument. "
|
|
|
|
"The failure is: %s",
|
|
|
|
argenv.fault_string);
|
|
|
|
xmlrpc_env_clean(&argenv);
|
|
|
|
} else {
|
|
|
|
XMLRPC_ASSERT_VALUE_OK(argP);
|
|
|
|
|
|
|
|
if (*suffix != '\0')
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
|
|
"specifier: '%s'. There must be exactly one arument.",
|
|
|
|
suffix);
|
|
|
|
else {
|
|
|
|
/* Perform the actual XML-RPC call. */
|
|
|
|
retval = xmlrpc_client_call_params(
|
|
|
|
envP, server_url, method_name, argP);
|
|
|
|
if (!envP->fault_occurred)
|
|
|
|
XMLRPC_ASSERT_VALUE_OK(retval);
|
|
|
|
}
|
|
|
|
xmlrpc_DECREF(argP);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xmlrpc_value *
|
|
|
|
xmlrpc_client_call(xmlrpc_env * const envP,
|
|
|
|
const char * const server_url,
|
|
|
|
const char * const method_name,
|
|
|
|
const char * const format,
|
|
|
|
...) {
|
|
|
|
|
|
|
|
xmlrpc_value * result;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
result = xmlrpc_client_call_va(envP, server_url,
|
|
|
|
method_name, format, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xmlrpc_value *
|
|
|
|
xmlrpc_client_call_server(xmlrpc_env * const envP,
|
|
|
|
xmlrpc_server_info * const serverP,
|
|
|
|
const char * const methodName,
|
|
|
|
const char * const format,
|
|
|
|
...) {
|
|
|
|
|
|
|
|
va_list args;
|
|
|
|
xmlrpc_value * paramArrayP;
|
|
|
|
xmlrpc_value * retval;
|
|
|
|
const char * suffix;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
|
|
|
|
/* Build our argument */
|
|
|
|
va_start(args, format);
|
|
|
|
xmlrpc_build_value_va(envP, format, args, ¶mArrayP, &suffix);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
if (*suffix != '\0')
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
|
|
"specifier: '%s'. There must be exactly one arument.",
|
|
|
|
suffix);
|
|
|
|
else
|
|
|
|
clientCallServerParams(envP, client.transportP, serverP,
|
|
|
|
methodName, paramArrayP,
|
|
|
|
&retval);
|
|
|
|
|
|
|
|
xmlrpc_DECREF(paramArrayP);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_event_loop_finish_asynch(void) {
|
|
|
|
XMLRPC_ASSERT(clientInitialized);
|
|
|
|
clientTransportOps.finish_asynch(client.transportP, timeout_no, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_event_loop_finish_asynch_timeout(timeout_t const timeout) {
|
|
|
|
XMLRPC_ASSERT(clientInitialized);
|
|
|
|
clientTransportOps.finish_asynch(client.transportP, timeout_yes, timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
call_info_set_asynch_data(xmlrpc_env * const env,
|
|
|
|
call_info * const info,
|
|
|
|
const char * const server_url,
|
|
|
|
const char * const method_name,
|
|
|
|
xmlrpc_value * const argP,
|
|
|
|
xmlrpc_response_handler callback,
|
|
|
|
void * const user_data) {
|
|
|
|
|
|
|
|
xmlrpc_value *holder;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(env);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(info);
|
|
|
|
XMLRPC_ASSERT(info->_asynch_data_holder == NULL);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(server_url);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(method_name);
|
|
|
|
XMLRPC_ASSERT_VALUE_OK(argP);
|
|
|
|
|
|
|
|
/* Install our callback and user_data.
|
|
|
|
** (We're not responsible for destroying the user_data.) */
|
|
|
|
info->callback = callback;
|
|
|
|
info->user_data = user_data;
|
|
|
|
|
|
|
|
/* Build an XML-RPC data structure to hold our other data. This makes
|
|
|
|
** copies of server_url and method_name, and increments the reference
|
|
|
|
** to the argument *argP. */
|
|
|
|
holder = xmlrpc_build_value(env, "(ssV)",
|
|
|
|
server_url, method_name, argP);
|
|
|
|
XMLRPC_FAIL_IF_FAULT(env);
|
|
|
|
|
|
|
|
/* Parse the newly-allocated structure into our public member variables.
|
|
|
|
** This doesn't make any new references, so we can dispose of the whole
|
|
|
|
** thing by DECREF'ing the one master reference. Nifty, huh? */
|
|
|
|
xmlrpc_parse_value(env, holder, "(ssV)",
|
|
|
|
&info->server_url,
|
|
|
|
&info->method_name,
|
|
|
|
&info->param_array);
|
|
|
|
XMLRPC_FAIL_IF_FAULT(env);
|
|
|
|
|
|
|
|
/* Hand over ownership of the holder to the call_info struct. */
|
|
|
|
info->_asynch_data_holder = holder;
|
|
|
|
holder = NULL;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (env->fault_occurred) {
|
|
|
|
if (holder)
|
|
|
|
xmlrpc_DECREF(holder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
|
|
** xmlrpc_server_info
|
|
|
|
**=========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
xmlrpc_server_info *
|
|
|
|
xmlrpc_server_info_new (xmlrpc_env * const env,
|
|
|
|
const char * const server_url) {
|
|
|
|
|
|
|
|
xmlrpc_server_info *server;
|
|
|
|
char *url_copy;
|
|
|
|
|
|
|
|
/* Error-handling preconditions. */
|
|
|
|
url_copy = NULL;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(env);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(server_url);
|
|
|
|
|
|
|
|
/* Allocate our memory blocks. */
|
|
|
|
server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
|
|
|
|
XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for xmlrpc_server_info");
|
|
|
|
memset(server, 0, sizeof(xmlrpc_server_info));
|
|
|
|
url_copy = (char*) malloc(strlen(server_url) + 1);
|
|
|
|
XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for server URL");
|
|
|
|
|
|
|
|
/* Build our object. */
|
|
|
|
strcpy(url_copy, server_url);
|
|
|
|
server->_server_url = url_copy;
|
|
|
|
server->_http_basic_auth = NULL;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (env->fault_occurred) {
|
|
|
|
if (url_copy)
|
|
|
|
free(url_copy);
|
|
|
|
if (server)
|
|
|
|
free(server);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlrpc_server_info * xmlrpc_server_info_copy(xmlrpc_env *env,
|
|
|
|
xmlrpc_server_info *aserver)
|
|
|
|
{
|
|
|
|
xmlrpc_server_info *server;
|
|
|
|
char *url_copy, *auth_copy;
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(env);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(aserver);
|
|
|
|
|
|
|
|
/* Error-handling preconditions. */
|
|
|
|
url_copy = NULL;
|
|
|
|
auth_copy = NULL;
|
|
|
|
|
|
|
|
/* Allocate our memory blocks. */
|
|
|
|
server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
|
|
|
|
XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for xmlrpc_server_info");
|
|
|
|
url_copy = (char*) malloc(strlen(aserver->_server_url) + 1);
|
|
|
|
XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for server URL");
|
|
|
|
auth_copy = (char*) malloc(strlen(aserver->_http_basic_auth) + 1);
|
|
|
|
XMLRPC_FAIL_IF_NULL(auth_copy, env, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for authentication info");
|
|
|
|
|
|
|
|
/* Build our object. */
|
|
|
|
strcpy(url_copy, aserver->_server_url);
|
|
|
|
server->_server_url = url_copy;
|
|
|
|
strcpy(auth_copy, aserver->_http_basic_auth);
|
|
|
|
server->_http_basic_auth = auth_copy;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (env->fault_occurred) {
|
|
|
|
if (url_copy)
|
|
|
|
free(url_copy);
|
|
|
|
if (auth_copy)
|
|
|
|
free(auth_copy);
|
|
|
|
if (server)
|
|
|
|
free(server);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return server;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void xmlrpc_server_info_free (xmlrpc_server_info *server)
|
|
|
|
{
|
|
|
|
XMLRPC_ASSERT_PTR_OK(server);
|
|
|
|
XMLRPC_ASSERT(server->_server_url != XMLRPC_BAD_POINTER);
|
|
|
|
|
|
|
|
if (server->_http_basic_auth)
|
|
|
|
free(server->_http_basic_auth);
|
|
|
|
free(server->_server_url);
|
|
|
|
server->_server_url = XMLRPC_BAD_POINTER;
|
|
|
|
free(server);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*=========================================================================
|
|
|
|
** xmlrpc_client_call_asynch
|
|
|
|
**=========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_call_asynch(const char * const serverUrl,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_response_handler callback,
|
|
|
|
void * const userData,
|
|
|
|
const char * const format,
|
|
|
|
...) {
|
|
|
|
|
|
|
|
xmlrpc_env env;
|
|
|
|
va_list args;
|
|
|
|
xmlrpc_value * paramArrayP;
|
|
|
|
const char * suffix;
|
|
|
|
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
|
|
|
|
/* Build our argument array. */
|
|
|
|
va_start(args, format);
|
|
|
|
xmlrpc_build_value_va(&env, format, args, ¶mArrayP, &suffix);
|
|
|
|
va_end(args);
|
|
|
|
if (env.fault_occurred) {
|
|
|
|
/* Unfortunately, we have no way to return an error and the
|
|
|
|
regular callback for a failed RPC is designed to have the
|
|
|
|
parameter array passed to it. This was probably an oversight
|
|
|
|
of the original asynch design, but now we have to be as
|
|
|
|
backward compatible as possible, so we do this:
|
|
|
|
*/
|
|
|
|
(*callback)(serverUrl, methodName, NULL, userData, &env, NULL);
|
|
|
|
} else {
|
|
|
|
if (*suffix != '\0')
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
&env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
|
|
"specifier: '%s'. There must be exactly one arument.",
|
|
|
|
suffix);
|
|
|
|
else {
|
|
|
|
xmlrpc_server_info * serverP;
|
|
|
|
serverP = xmlrpc_server_info_new(&env, serverUrl);
|
|
|
|
if (!env.fault_occurred) {
|
|
|
|
xmlrpc_client_call_server_asynch_params(
|
|
|
|
serverP, methodName, callback, userData,
|
|
|
|
paramArrayP);
|
|
|
|
}
|
|
|
|
xmlrpc_server_info_free(serverP);
|
|
|
|
}
|
|
|
|
if (env.fault_occurred)
|
|
|
|
(*callback)(serverUrl, methodName, paramArrayP, userData,
|
|
|
|
&env, NULL);
|
|
|
|
xmlrpc_DECREF(paramArrayP);
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_call_asynch_params(const char * const serverUrl,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_response_handler callback,
|
|
|
|
void * const userData,
|
|
|
|
xmlrpc_value * const paramArrayP) {
|
|
|
|
|
|
|
|
xmlrpc_env env;
|
|
|
|
xmlrpc_server_info *serverP;
|
|
|
|
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_PTR_OK(serverUrl);
|
|
|
|
|
|
|
|
serverP = xmlrpc_server_info_new(&env, serverUrl);
|
|
|
|
if (!env.fault_occurred) {
|
|
|
|
xmlrpc_client_call_server_asynch_params(
|
|
|
|
serverP, methodName, callback, userData, paramArrayP);
|
|
|
|
|
|
|
|
xmlrpc_server_info_free(serverP);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (env.fault_occurred)
|
|
|
|
/* We have no way to return failure; we report the failure
|
|
|
|
as it happened after we successfully started the RPC.
|
|
|
|
*/
|
|
|
|
(*callback)(serverUrl, methodName, paramArrayP, userData,
|
|
|
|
&env, NULL);
|
|
|
|
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_call_server_asynch(xmlrpc_server_info * const serverP,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_response_handler callback,
|
|
|
|
void * const userData,
|
|
|
|
const char * const format,
|
|
|
|
...) {
|
|
|
|
|
|
|
|
xmlrpc_env env;
|
|
|
|
va_list args;
|
|
|
|
xmlrpc_value * paramArrayP;
|
|
|
|
const char * suffix;
|
|
|
|
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_PTR_OK(format);
|
|
|
|
|
|
|
|
/* Build our parameter array. */
|
|
|
|
va_start(args, format);
|
|
|
|
xmlrpc_build_value_va(&env, format, args, ¶mArrayP, &suffix);
|
|
|
|
va_end(args);
|
|
|
|
if (env.fault_occurred) {
|
|
|
|
/* Unfortunately, we have no way to return an error and the
|
|
|
|
regular callback for a failed RPC is designed to have the
|
|
|
|
parameter array passed to it. This was probably an oversight
|
|
|
|
of the original asynch design, but now we have to be as
|
|
|
|
backward compatible as possible, so we do this:
|
|
|
|
*/
|
|
|
|
(*callback)(serverP->_server_url, methodName, NULL, userData,
|
|
|
|
&env, NULL);
|
|
|
|
} else {
|
|
|
|
if (*suffix != '\0')
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
&env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
|
|
|
|
"specifier: '%s'. There must be exactly one arument.",
|
|
|
|
suffix);
|
|
|
|
else {
|
|
|
|
xmlrpc_client_call_server_asynch_params(
|
|
|
|
serverP, methodName, callback, userData, paramArrayP);
|
|
|
|
}
|
|
|
|
xmlrpc_DECREF(paramArrayP);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (env.fault_occurred)
|
|
|
|
(*callback)(serverP->_server_url, methodName, paramArrayP, userData,
|
|
|
|
&env, NULL);
|
|
|
|
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
asynchComplete(call_info * const callInfoP,
|
|
|
|
xmlrpc_mem_block * const responseXmlP,
|
|
|
|
xmlrpc_env const transportEnv) {
|
|
|
|
/*----------------------------------------------------------------------------
|
|
|
|
Complete an asynchronous XML-RPC call request.
|
|
|
|
|
|
|
|
This includes calling the user's RPC completion routine.
|
|
|
|
|
|
|
|
'transportEnv' describes the an error that the transport
|
|
|
|
encountered in processing the call. If the transport successfully
|
|
|
|
sent the call to the server and processed the response but the
|
|
|
|
server failed the call, 'transportEnv' indicates no error, and the
|
|
|
|
response in *callInfoP might very well indicate that the server
|
|
|
|
failed the request.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
xmlrpc_env env;
|
2005-03-02 19:04:32 +03:00
|
|
|
xmlrpc_value * responseP = 0;
|
2005-02-22 21:08:27 +03:00
|
|
|
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
|
|
|
|
if (transportEnv.fault_occurred)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
&env, transportEnv.fault_code,
|
|
|
|
"Client transport failed to execute the RPC. %s",
|
|
|
|
transportEnv.fault_string);
|
|
|
|
|
|
|
|
if (!env.fault_occurred)
|
|
|
|
responseP = xmlrpc_parse_response(
|
|
|
|
&env,
|
|
|
|
XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlP),
|
|
|
|
XMLRPC_MEMBLOCK_SIZE(char, responseXmlP));
|
|
|
|
|
|
|
|
/* Call the user's callback function with the result */
|
|
|
|
(*callInfoP->callback)(callInfoP->server_url,
|
|
|
|
callInfoP->method_name,
|
|
|
|
callInfoP->param_array,
|
|
|
|
callInfoP->user_data, &env, responseP);
|
|
|
|
|
|
|
|
if (!env.fault_occurred)
|
|
|
|
xmlrpc_DECREF(responseP);
|
|
|
|
|
|
|
|
call_info_free(callInfoP);
|
|
|
|
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
sendRequest(xmlrpc_env * const envP,
|
|
|
|
struct clientTransport * const transportP,
|
|
|
|
xmlrpc_server_info * const serverP,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_response_handler responseHandler,
|
|
|
|
void * const userData,
|
|
|
|
xmlrpc_value * const argP) {
|
|
|
|
|
|
|
|
call_info * callInfoP;
|
|
|
|
|
|
|
|
call_info_new(envP, serverP, methodName, argP, &callInfoP);
|
|
|
|
if (!envP->fault_occurred) {
|
|
|
|
call_info_set_asynch_data(envP, callInfoP,
|
|
|
|
serverP->_server_url, methodName,
|
|
|
|
argP, responseHandler, userData);
|
|
|
|
if (!envP->fault_occurred)
|
|
|
|
clientTransportOps.send_request(
|
|
|
|
envP, transportP, serverP, callInfoP->serialized_xml,
|
|
|
|
&asynchComplete, callInfoP);
|
|
|
|
|
|
|
|
if (envP->fault_occurred)
|
|
|
|
call_info_free(callInfoP);
|
|
|
|
else {
|
|
|
|
/* asynchComplete() will free *callInfoP */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (envP->fault_occurred) {
|
|
|
|
/* Transport did not start the call. Report the call complete
|
|
|
|
(with error) now.
|
|
|
|
*/
|
|
|
|
(*responseHandler)(serverP->_server_url, methodName, argP, userData,
|
|
|
|
envP, NULL);
|
|
|
|
} else {
|
|
|
|
/* The transport will call *responseHandler() when it has completed
|
|
|
|
the call
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_client_call_server_asynch_params(
|
|
|
|
xmlrpc_server_info * const serverP,
|
|
|
|
const char * const methodName,
|
|
|
|
xmlrpc_response_handler responseHandler,
|
|
|
|
void * const userData,
|
|
|
|
xmlrpc_value * const argP) {
|
|
|
|
xmlrpc_env env;
|
|
|
|
|
|
|
|
xmlrpc_env_init(&env);
|
|
|
|
|
|
|
|
XMLRPC_ASSERT_PTR_OK(serverP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(methodName);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(responseHandler);
|
|
|
|
XMLRPC_ASSERT_VALUE_OK(argP);
|
|
|
|
|
|
|
|
if (!clientInitialized)
|
|
|
|
xmlrpc_env_set_fault_formatted(
|
|
|
|
&env, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Xmlrpc-c client instance has not been initialized "
|
|
|
|
"(need to call xmlrpc_client_init2()).");
|
|
|
|
else
|
|
|
|
sendRequest(&env, client.transportP, serverP,
|
|
|
|
methodName, responseHandler, userData,
|
|
|
|
argP);
|
|
|
|
|
|
|
|
xmlrpc_env_clean(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
xmlrpc_server_info_set_basic_auth(xmlrpc_env * const envP,
|
|
|
|
xmlrpc_server_info * const serverP,
|
|
|
|
const char * const username,
|
|
|
|
const char * const password) {
|
|
|
|
|
|
|
|
size_t username_len, password_len, raw_token_len;
|
|
|
|
char *raw_token;
|
|
|
|
xmlrpc_mem_block *token;
|
|
|
|
char *token_data, *auth_type, *auth_header;
|
|
|
|
size_t token_len, auth_type_len, auth_header_len;
|
|
|
|
|
|
|
|
/* Error-handling preconditions. */
|
|
|
|
token = NULL;
|
2005-03-03 17:25:13 +03:00
|
|
|
auth_header = NULL;
|
2005-02-22 21:08:27 +03:00
|
|
|
|
|
|
|
XMLRPC_ASSERT_ENV_OK(envP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(serverP);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(username);
|
|
|
|
XMLRPC_ASSERT_PTR_OK(password);
|
|
|
|
|
|
|
|
/* Calculate some lengths. */
|
|
|
|
username_len = strlen(username);
|
|
|
|
password_len = strlen(password);
|
|
|
|
raw_token_len = username_len + password_len + 1;
|
|
|
|
|
|
|
|
/* Build a raw token of the form 'username:password'. */
|
|
|
|
raw_token = (char*) malloc(raw_token_len + 1);
|
|
|
|
XMLRPC_FAIL_IF_NULL(raw_token, envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for auth token");
|
|
|
|
strcpy(raw_token, username);
|
|
|
|
raw_token[username_len] = ':';
|
|
|
|
strcpy(&raw_token[username_len + 1], password);
|
|
|
|
|
|
|
|
/* Encode our raw token using Base64. */
|
|
|
|
token = xmlrpc_base64_encode_without_newlines(envP,
|
|
|
|
(unsigned char*) raw_token,
|
|
|
|
raw_token_len);
|
|
|
|
XMLRPC_FAIL_IF_FAULT(envP);
|
|
|
|
token_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, token);
|
|
|
|
token_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, token);
|
|
|
|
|
|
|
|
/* Build our actual header value. (I hate string processing in C.) */
|
|
|
|
auth_type = "Basic ";
|
|
|
|
auth_type_len = strlen(auth_type);
|
|
|
|
auth_header_len = auth_type_len + token_len;
|
|
|
|
auth_header = (char*) malloc(auth_header_len + 1);
|
|
|
|
XMLRPC_FAIL_IF_NULL(auth_header, envP, XMLRPC_INTERNAL_ERROR,
|
|
|
|
"Couldn't allocate memory for auth header");
|
|
|
|
memcpy(auth_header, auth_type, auth_type_len);
|
|
|
|
memcpy(&auth_header[auth_type_len], token_data, token_len);
|
|
|
|
auth_header[auth_header_len] = '\0';
|
|
|
|
|
|
|
|
/* Clean up any pre-existing authentication information, and install
|
|
|
|
** the new value. */
|
|
|
|
if (serverP->_http_basic_auth)
|
|
|
|
free(serverP->_http_basic_auth);
|
|
|
|
serverP->_http_basic_auth = auth_header;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (raw_token)
|
|
|
|
free(raw_token);
|
|
|
|
if (token)
|
|
|
|
xmlrpc_mem_block_free(token);
|
|
|
|
if (envP->fault_occurred) {
|
|
|
|
if (auth_header)
|
|
|
|
free(auth_header);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|