| /* 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; |
| else if (strcmp(transportName, "libcurl") == 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), |
| (unsigned int)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), |
| (unsigned int)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 = 0; |
| 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; |
| xmlrpc_value * responseP = 0; |
| |
| 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; |
| 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); |
| } |
| } |
| |