summaryrefslogtreecommitdiffstats
path: root/Utilities/cmxmlrpc/xmlrpc_client.c
diff options
context:
space:
mode:
authorAndy Cedilnik <andy.cedilnik@kitware.com>2005-02-22 18:08:27 (GMT)
committerAndy Cedilnik <andy.cedilnik@kitware.com>2005-02-22 18:08:27 (GMT)
commitb9b4ea0f7bbf0313d8aa5e354ee56f912969763c (patch)
treeba5e857cda91949be14f508e6df490e1e2176f40 /Utilities/cmxmlrpc/xmlrpc_client.c
parentbfcb4b693763bb11f841094d2ca9852a64e5d33b (diff)
downloadCMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.zip
CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.gz
CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.bz2
ENH: Initial import
Diffstat (limited to 'Utilities/cmxmlrpc/xmlrpc_client.c')
-rw-r--r--Utilities/cmxmlrpc/xmlrpc_client.c981
1 files changed, 981 insertions, 0 deletions
diff --git a/Utilities/cmxmlrpc/xmlrpc_client.c b/Utilities/cmxmlrpc/xmlrpc_client.c
new file mode 100644
index 0000000..3603a21
--- /dev/null
+++ b/Utilities/cmxmlrpc/xmlrpc_client.c
@@ -0,0 +1,981 @@
+/* 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;
+#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;
+ xmlrpc_value * retval;
+ 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, &paramArrayP, &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;
+
+ /* Error-handling preconditions. */
+ holder = NULL;
+
+ 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. */
+ server = NULL;
+ 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. */
+ server = NULL;
+ 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, &paramArrayP, &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, &paramArrayP, &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;
+ xmlrpc_value * responseP;
+
+ 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. */
+ raw_token = NULL;
+ token = NULL;
+ token_data = auth_type = auth_header = NULL;
+
+ 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);
+ }
+}
+