diff options
author | Andy Cedilnik <andy.cedilnik@kitware.com> | 2005-02-22 18:08:27 (GMT) |
---|---|---|
committer | Andy Cedilnik <andy.cedilnik@kitware.com> | 2005-02-22 18:08:27 (GMT) |
commit | b9b4ea0f7bbf0313d8aa5e354ee56f912969763c (patch) | |
tree | ba5e857cda91949be14f508e6df490e1e2176f40 /Utilities/cmxmlrpc/xmlrpc_client.c | |
parent | bfcb4b693763bb11f841094d2ca9852a64e5d33b (diff) | |
download | CMake-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.c | 981 |
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, ¶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; + + /* 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, ¶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; + 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); + } +} + |