ENH: make this work for older versions of OSX
[cmake.git] / Utilities / cmxmlrpc / xmlrpc_client.c
bloba5ad65ceaf992ab6c3d57fc0894685f4a7e54203
1 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 ** 1. Redistributions of source code must retain the above copyright
7 ** notice, this list of conditions and the following disclaimer.
8 ** 2. Redistributions in binary form must reproduce the above copyright
9 ** notice, this list of conditions and the following disclaimer in the
10 ** documentation and/or other materials provided with the distribution.
11 ** 3. The name of the author may not be used to endorse or promote products
12 ** derived from this software without specific prior written permission.
13 **
14 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 ** SUCH DAMAGE. */
26 #include "xmlrpc_config.h"
28 #undef PACKAGE
29 #undef VERSION
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <assert.h>
36 #include <errno.h>
38 #include "bool.h"
39 #include "mallocvar.h"
40 #include "xmlrpc.h"
41 #include "xmlrpc_int.h"
42 #include "xmlrpc_client.h"
43 #include "xmlrpc_client_int.h"
44 /* transport_config.h defines XMLRPC_DEFAULT_TRANSPORT,
45 MUST_BUILD_WININET_CLIENT, MUST_BUILD_CURL_CLIENT,
46 MUST_BUILD_LIBWWW_CLIENT
48 #include "transport_config.h"
50 #if MUST_BUILD_WININET_CLIENT
51 #include "xmlrpc_wininet_transport.h"
52 #endif
53 #if MUST_BUILD_CURL_CLIENT
54 #include "xmlrpc_curl_transport.h"
55 #endif
56 #if MUST_BUILD_LIBWWW_CLIENT
57 #include "xmlrpc_libwww_transport.h"
58 #endif
60 struct xmlrpc_client {
61 /*----------------------------------------------------------------------------
62 This represents a client object.
63 -----------------------------------------------------------------------------*/
64 struct clientTransport * transportP;
69 typedef struct call_info
71 /* These fields are used when performing asynchronous calls.
72 ** The _asynch_data_holder contains server_url, method_name and
73 ** param_array, so it's the only thing we need to free. */
74 xmlrpc_value *_asynch_data_holder;
75 char *server_url;
76 char *method_name;
77 xmlrpc_value *param_array;
78 xmlrpc_response_handler callback;
79 void *user_data;
81 /* The serialized XML data passed to this call. We keep this around
82 ** for use by our source_anchor field. */
83 xmlrpc_mem_block *serialized_xml;
84 } call_info;
86 static bool clientInitialized = FALSE;
88 /*=========================================================================
89 ** Initialization and Shutdown
90 **=========================================================================
93 static struct clientTransportOps clientTransportOps;
95 static struct xmlrpc_client client;
96 /* Some day, we need to make this dynamically allocated, so there can
97 be more than one client per program and just generally to provide
98 a cleaner interface.
101 extern void
102 xmlrpc_client_init(int const flags,
103 const char * const appname,
104 const char * const appversion) {
106 struct xmlrpc_clientparms clientparms;
108 /* As our interface does not allow for failure, we just fail silently ! */
110 xmlrpc_env env;
111 xmlrpc_env_init(&env);
113 clientparms.transport = XMLRPC_DEFAULT_TRANSPORT;
115 xmlrpc_client_init2(&env, flags,
116 appname, appversion,
117 &clientparms, XMLRPC_CPSIZE(transport));
119 xmlrpc_env_clean(&env);
124 const char *
125 xmlrpc_client_get_default_transport(xmlrpc_env * const env ATTR_UNUSED) {
127 return XMLRPC_DEFAULT_TRANSPORT;
132 static void
133 setupTransport(xmlrpc_env * const envP,
134 const char * const transportName) {
136 if (FALSE) {
138 #if MUST_BUILD_WININET_CLIENT
139 else if (strcmp(transportName, "wininet") == 0)
140 clientTransportOps = xmlrpc_wininet_transport_ops;
141 #endif
142 #if MUST_BUILD_CURL_CLIENT
143 else if (strcmp(transportName, "curl") == 0)
144 clientTransportOps = xmlrpc_curl_transport_ops;
145 else if (strcmp(transportName, "libcurl") == 0)
146 clientTransportOps = xmlrpc_curl_transport_ops;
147 #endif
148 #if MUST_BUILD_LIBWWW_CLIENT
149 else if (strcmp(transportName, "libwww") == 0)
150 clientTransportOps = xmlrpc_libwww_transport_ops;
151 #endif
152 else
153 xmlrpc_env_set_fault_formatted(
154 envP, XMLRPC_INTERNAL_ERROR,
155 "Unrecognized XML transport name '%s'", transportName);
160 void
161 xmlrpc_client_init2(xmlrpc_env * const envP,
162 int const flags,
163 const char * const appname,
164 const char * const appversion,
165 struct xmlrpc_clientparms * const clientparmsP,
166 unsigned int const parm_size) {
168 if (clientInitialized)
169 xmlrpc_env_set_fault_formatted(
170 envP, XMLRPC_INTERNAL_ERROR,
171 "Xmlrpc-c client instance has already been initialized "
172 "(need to call xmlrpc_client_cleanup() before you can "
173 "reinitialize).");
174 else {
175 const char * transportName;
177 if (parm_size < XMLRPC_CPSIZE(transport) ||
178 clientparmsP->transport == NULL) {
179 /* He didn't specify a transport. Use the default */
180 transportName = xmlrpc_client_get_default_transport(envP);
181 } else
182 transportName = clientparmsP->transport;
184 if (!envP->fault_occurred) {
185 setupTransport(envP, transportName);
186 if (!envP->fault_occurred) {
187 clientTransportOps.create(envP, flags, appname, appversion,
188 &client.transportP);
189 if (!envP->fault_occurred)
190 clientInitialized = TRUE;
198 void
199 xmlrpc_client_cleanup() {
201 XMLRPC_ASSERT(clientInitialized);
203 clientTransportOps.destroy(client.transportP);
205 clientInitialized = FALSE;
210 static void
211 call_info_free(call_info * const callInfoP) {
213 /* Assume the worst.. That only parts of the call_info are valid. */
215 XMLRPC_ASSERT_PTR_OK(callInfoP);
217 /* If this has been allocated, we're responsible for destroying it. */
218 if (callInfoP->_asynch_data_holder)
219 xmlrpc_DECREF(callInfoP->_asynch_data_holder);
221 /* Now we can blow away the XML data. */
222 if (callInfoP->serialized_xml)
223 xmlrpc_mem_block_free(callInfoP->serialized_xml);
225 free(callInfoP);
230 static void
231 call_info_new(xmlrpc_env * const envP,
232 xmlrpc_server_info * const server,
233 const char * const method_name,
234 xmlrpc_value * const argP,
235 call_info ** const callInfoPP) {
236 /*----------------------------------------------------------------------------
237 Create a call_info object. A call_info object represents an XML-RPC
238 call.
239 -----------------------------------------------------------------------------*/
240 call_info * callInfoP;
242 XMLRPC_ASSERT_PTR_OK(argP);
243 XMLRPC_ASSERT_PTR_OK(callInfoPP);
245 if (method_name == NULL)
246 xmlrpc_env_set_fault_formatted(
247 envP, XMLRPC_INTERNAL_ERROR,
248 "method name argument is NULL pointer");
249 else if (server == NULL)
250 xmlrpc_env_set_fault_formatted(
251 envP, XMLRPC_INTERNAL_ERROR,
252 "server info argument is NULL pointer");
253 else {
254 MALLOCVAR(callInfoP);
255 if (callInfoP == NULL)
256 xmlrpc_env_set_fault_formatted(
257 envP, XMLRPC_INTERNAL_ERROR,
258 "Couldn't allocate memory for xmlrpc_call_info");
259 else {
260 xmlrpc_mem_block * callXmlP;
262 /* Clear contents. */
263 memset(callInfoP, 0, sizeof(*callInfoP));
265 /* Make the XML for our call */
266 callXmlP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
267 if (!envP->fault_occurred) {
268 xmlrpc_serialize_call(envP, callXmlP, method_name, argP);
269 if (!envP->fault_occurred) {
270 xmlrpc_traceXml("XML-RPC CALL",
271 XMLRPC_MEMBLOCK_CONTENTS(char, callXmlP),
272 (unsigned int)XMLRPC_MEMBLOCK_SIZE(char, callXmlP));
274 callInfoP->serialized_xml = callXmlP;
276 *callInfoPP = callInfoP;
278 if (envP->fault_occurred)
279 XMLRPC_MEMBLOCK_FREE(char, callXmlP);
281 if (envP->fault_occurred)
282 free(callInfoP);
289 static void
290 clientCallServerParams(xmlrpc_env * const envP,
291 struct clientTransport * const transportP,
292 xmlrpc_server_info * const serverP,
293 const char * const methodName,
294 xmlrpc_value * const paramArrayP,
295 xmlrpc_value ** const resultPP) {
297 call_info * callInfoP;
299 if (!clientInitialized)
300 xmlrpc_env_set_fault_formatted(
301 envP, XMLRPC_INTERNAL_ERROR,
302 "Xmlrpc-c client instance has not been initialized "
303 "(need to call xmlrpc_client_init2()).");
304 else {
305 call_info_new(envP, serverP, methodName, paramArrayP, &callInfoP);
306 if (!envP->fault_occurred) {
307 xmlrpc_mem_block * respXmlP;
309 clientTransportOps.call(envP, transportP, serverP,
310 callInfoP->serialized_xml, callInfoP,
311 &respXmlP);
312 if (!envP->fault_occurred) {
313 xmlrpc_traceXml("XML-RPC RESPONSE",
314 XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
315 (unsigned int)XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
317 *resultPP = xmlrpc_parse_response(
318 envP,
319 XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
320 XMLRPC_MEMBLOCK_SIZE(char, respXmlP));
321 XMLRPC_MEMBLOCK_FREE(char, respXmlP);
323 call_info_free(callInfoP);
330 xmlrpc_value *
331 xmlrpc_client_call_params(xmlrpc_env * const envP,
332 const char * const serverUrl,
333 const char * const methodName,
334 xmlrpc_value * const paramArrayP) {
336 xmlrpc_value *retval;
338 XMLRPC_ASSERT_ENV_OK(envP);
339 XMLRPC_ASSERT_PTR_OK(serverUrl);
341 if (!clientInitialized)
342 xmlrpc_env_set_fault_formatted(
343 envP, XMLRPC_INTERNAL_ERROR,
344 "Xmlrpc-c client instance has not been initialized "
345 "(need to call xmlrpc_client_init2()).");
346 else {
347 xmlrpc_server_info * serverP;
349 /* Build a server info object and make our call. */
350 serverP = xmlrpc_server_info_new(envP, serverUrl);
351 if (!envP->fault_occurred) {
352 clientCallServerParams(envP, client.transportP, serverP,
353 methodName, paramArrayP,
354 &retval);
356 xmlrpc_server_info_free(serverP);
360 if (!envP->fault_occurred)
361 XMLRPC_ASSERT_VALUE_OK(retval);
363 return retval;
368 static xmlrpc_value *
369 xmlrpc_client_call_va(xmlrpc_env * const envP,
370 const char * const server_url,
371 const char * const method_name,
372 const char * const format,
373 va_list args) {
375 xmlrpc_value * argP;
376 xmlrpc_value * retval = 0;
377 xmlrpc_env argenv;
378 const char * suffix;
380 XMLRPC_ASSERT_ENV_OK(envP);
381 XMLRPC_ASSERT_PTR_OK(format);
383 /* Build our argument value. */
384 xmlrpc_env_init(&argenv);
385 xmlrpc_build_value_va(&argenv, format, args, &argP, &suffix);
386 if (argenv.fault_occurred) {
387 xmlrpc_env_set_fault_formatted(
388 envP, argenv.fault_code, "Invalid RPC arguments. "
389 "The format argument must indicate a single array, and the "
390 "following arguments must correspond to that format argument. "
391 "The failure is: %s",
392 argenv.fault_string);
393 xmlrpc_env_clean(&argenv);
394 } else {
395 XMLRPC_ASSERT_VALUE_OK(argP);
397 if (*suffix != '\0')
398 xmlrpc_env_set_fault_formatted(
399 envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
400 "specifier: '%s'. There must be exactly one arument.",
401 suffix);
402 else {
403 /* Perform the actual XML-RPC call. */
404 retval = xmlrpc_client_call_params(
405 envP, server_url, method_name, argP);
406 if (!envP->fault_occurred)
407 XMLRPC_ASSERT_VALUE_OK(retval);
409 xmlrpc_DECREF(argP);
411 return retval;
416 xmlrpc_value *
417 xmlrpc_client_call(xmlrpc_env * const envP,
418 const char * const server_url,
419 const char * const method_name,
420 const char * const format,
421 ...) {
423 xmlrpc_value * result;
424 va_list args;
426 va_start(args, format);
427 result = xmlrpc_client_call_va(envP, server_url,
428 method_name, format, args);
429 va_end(args);
431 return result;
436 xmlrpc_value *
437 xmlrpc_client_call_server(xmlrpc_env * const envP,
438 xmlrpc_server_info * const serverP,
439 const char * const methodName,
440 const char * const format,
441 ...) {
443 va_list args;
444 xmlrpc_value * paramArrayP;
445 xmlrpc_value * retval;
446 const char * suffix;
448 XMLRPC_ASSERT_ENV_OK(envP);
449 XMLRPC_ASSERT_PTR_OK(format);
451 /* Build our argument */
452 va_start(args, format);
453 xmlrpc_build_value_va(envP, format, args, &paramArrayP, &suffix);
454 va_end(args);
456 if (!envP->fault_occurred) {
457 if (*suffix != '\0')
458 xmlrpc_env_set_fault_formatted(
459 envP, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
460 "specifier: '%s'. There must be exactly one arument.",
461 suffix);
462 else
463 clientCallServerParams(envP, client.transportP, serverP,
464 methodName, paramArrayP,
465 &retval);
467 xmlrpc_DECREF(paramArrayP);
469 return retval;
473 void
474 xmlrpc_client_event_loop_finish_asynch(void) {
475 XMLRPC_ASSERT(clientInitialized);
476 clientTransportOps.finish_asynch(client.transportP, timeout_no, 0);
481 void
482 xmlrpc_client_event_loop_finish_asynch_timeout(timeout_t const timeout) {
483 XMLRPC_ASSERT(clientInitialized);
484 clientTransportOps.finish_asynch(client.transportP, timeout_yes, timeout);
489 static void
490 call_info_set_asynch_data(xmlrpc_env * const env,
491 call_info * const info,
492 const char * const server_url,
493 const char * const method_name,
494 xmlrpc_value * const argP,
495 xmlrpc_response_handler callback,
496 void * const user_data) {
498 xmlrpc_value *holder;
500 XMLRPC_ASSERT_ENV_OK(env);
501 XMLRPC_ASSERT_PTR_OK(info);
502 XMLRPC_ASSERT(info->_asynch_data_holder == NULL);
503 XMLRPC_ASSERT_PTR_OK(server_url);
504 XMLRPC_ASSERT_PTR_OK(method_name);
505 XMLRPC_ASSERT_VALUE_OK(argP);
507 /* Install our callback and user_data.
508 ** (We're not responsible for destroying the user_data.) */
509 info->callback = callback;
510 info->user_data = user_data;
512 /* Build an XML-RPC data structure to hold our other data. This makes
513 ** copies of server_url and method_name, and increments the reference
514 ** to the argument *argP. */
515 holder = xmlrpc_build_value(env, "(ssV)",
516 server_url, method_name, argP);
517 XMLRPC_FAIL_IF_FAULT(env);
519 /* Parse the newly-allocated structure into our public member variables.
520 ** This doesn't make any new references, so we can dispose of the whole
521 ** thing by DECREF'ing the one master reference. Nifty, huh? */
522 xmlrpc_parse_value(env, holder, "(ssV)",
523 &info->server_url,
524 &info->method_name,
525 &info->param_array);
526 XMLRPC_FAIL_IF_FAULT(env);
528 /* Hand over ownership of the holder to the call_info struct. */
529 info->_asynch_data_holder = holder;
530 holder = NULL;
532 cleanup:
533 if (env->fault_occurred) {
534 if (holder)
535 xmlrpc_DECREF(holder);
539 /*=========================================================================
540 ** xmlrpc_server_info
541 **=========================================================================
544 xmlrpc_server_info *
545 xmlrpc_server_info_new (xmlrpc_env * const env,
546 const char * const server_url) {
548 xmlrpc_server_info *server;
549 char *url_copy;
551 /* Error-handling preconditions. */
552 url_copy = NULL;
554 XMLRPC_ASSERT_ENV_OK(env);
555 XMLRPC_ASSERT_PTR_OK(server_url);
557 /* Allocate our memory blocks. */
558 server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
559 XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
560 "Couldn't allocate memory for xmlrpc_server_info");
561 memset(server, 0, sizeof(xmlrpc_server_info));
562 url_copy = (char*) malloc(strlen(server_url) + 1);
563 XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
564 "Couldn't allocate memory for server URL");
566 /* Build our object. */
567 strcpy(url_copy, server_url);
568 server->_server_url = url_copy;
569 server->_http_basic_auth = NULL;
571 cleanup:
572 if (env->fault_occurred) {
573 if (url_copy)
574 free(url_copy);
575 if (server)
576 free(server);
577 return NULL;
579 return server;
582 xmlrpc_server_info * xmlrpc_server_info_copy(xmlrpc_env *env,
583 xmlrpc_server_info *aserver)
585 xmlrpc_server_info *server;
586 char *url_copy, *auth_copy;
588 XMLRPC_ASSERT_ENV_OK(env);
589 XMLRPC_ASSERT_PTR_OK(aserver);
591 /* Error-handling preconditions. */
592 url_copy = NULL;
593 auth_copy = NULL;
595 /* Allocate our memory blocks. */
596 server = (xmlrpc_server_info*) malloc(sizeof(xmlrpc_server_info));
597 XMLRPC_FAIL_IF_NULL(server, env, XMLRPC_INTERNAL_ERROR,
598 "Couldn't allocate memory for xmlrpc_server_info");
599 url_copy = (char*) malloc(strlen(aserver->_server_url) + 1);
600 XMLRPC_FAIL_IF_NULL(url_copy, env, XMLRPC_INTERNAL_ERROR,
601 "Couldn't allocate memory for server URL");
602 auth_copy = (char*) malloc(strlen(aserver->_http_basic_auth) + 1);
603 XMLRPC_FAIL_IF_NULL(auth_copy, env, XMLRPC_INTERNAL_ERROR,
604 "Couldn't allocate memory for authentication info");
606 /* Build our object. */
607 strcpy(url_copy, aserver->_server_url);
608 server->_server_url = url_copy;
609 strcpy(auth_copy, aserver->_http_basic_auth);
610 server->_http_basic_auth = auth_copy;
612 cleanup:
613 if (env->fault_occurred) {
614 if (url_copy)
615 free(url_copy);
616 if (auth_copy)
617 free(auth_copy);
618 if (server)
619 free(server);
620 return NULL;
622 return server;
626 void xmlrpc_server_info_free (xmlrpc_server_info *server)
628 XMLRPC_ASSERT_PTR_OK(server);
629 XMLRPC_ASSERT(server->_server_url != XMLRPC_BAD_POINTER);
631 if (server->_http_basic_auth)
632 free(server->_http_basic_auth);
633 free(server->_server_url);
634 server->_server_url = XMLRPC_BAD_POINTER;
635 free(server);
638 /*=========================================================================
639 ** xmlrpc_client_call_asynch
640 **=========================================================================
643 void
644 xmlrpc_client_call_asynch(const char * const serverUrl,
645 const char * const methodName,
646 xmlrpc_response_handler callback,
647 void * const userData,
648 const char * const format,
649 ...) {
651 xmlrpc_env env;
652 va_list args;
653 xmlrpc_value * paramArrayP;
654 const char * suffix;
656 xmlrpc_env_init(&env);
658 XMLRPC_ASSERT_PTR_OK(serverUrl);
659 XMLRPC_ASSERT_PTR_OK(format);
661 /* Build our argument array. */
662 va_start(args, format);
663 xmlrpc_build_value_va(&env, format, args, &paramArrayP, &suffix);
664 va_end(args);
665 if (env.fault_occurred) {
666 /* Unfortunately, we have no way to return an error and the
667 regular callback for a failed RPC is designed to have the
668 parameter array passed to it. This was probably an oversight
669 of the original asynch design, but now we have to be as
670 backward compatible as possible, so we do this:
672 (*callback)(serverUrl, methodName, NULL, userData, &env, NULL);
673 } else {
674 if (*suffix != '\0')
675 xmlrpc_env_set_fault_formatted(
676 &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
677 "specifier: '%s'. There must be exactly one arument.",
678 suffix);
679 else {
680 xmlrpc_server_info * serverP;
681 serverP = xmlrpc_server_info_new(&env, serverUrl);
682 if (!env.fault_occurred) {
683 xmlrpc_client_call_server_asynch_params(
684 serverP, methodName, callback, userData,
685 paramArrayP);
687 xmlrpc_server_info_free(serverP);
689 if (env.fault_occurred)
690 (*callback)(serverUrl, methodName, paramArrayP, userData,
691 &env, NULL);
692 xmlrpc_DECREF(paramArrayP);
695 xmlrpc_env_clean(&env);
700 void
701 xmlrpc_client_call_asynch_params(const char * const serverUrl,
702 const char * const methodName,
703 xmlrpc_response_handler callback,
704 void * const userData,
705 xmlrpc_value * const paramArrayP) {
707 xmlrpc_env env;
708 xmlrpc_server_info *serverP;
710 xmlrpc_env_init(&env);
712 XMLRPC_ASSERT_PTR_OK(serverUrl);
714 serverP = xmlrpc_server_info_new(&env, serverUrl);
715 if (!env.fault_occurred) {
716 xmlrpc_client_call_server_asynch_params(
717 serverP, methodName, callback, userData, paramArrayP);
719 xmlrpc_server_info_free(serverP);
722 if (env.fault_occurred)
723 /* We have no way to return failure; we report the failure
724 as it happened after we successfully started the RPC.
726 (*callback)(serverUrl, methodName, paramArrayP, userData,
727 &env, NULL);
729 xmlrpc_env_clean(&env);
734 void
735 xmlrpc_client_call_server_asynch(xmlrpc_server_info * const serverP,
736 const char * const methodName,
737 xmlrpc_response_handler callback,
738 void * const userData,
739 const char * const format,
740 ...) {
742 xmlrpc_env env;
743 va_list args;
744 xmlrpc_value * paramArrayP;
745 const char * suffix;
747 xmlrpc_env_init(&env);
749 XMLRPC_ASSERT_PTR_OK(format);
751 /* Build our parameter array. */
752 va_start(args, format);
753 xmlrpc_build_value_va(&env, format, args, &paramArrayP, &suffix);
754 va_end(args);
755 if (env.fault_occurred) {
756 /* Unfortunately, we have no way to return an error and the
757 regular callback for a failed RPC is designed to have the
758 parameter array passed to it. This was probably an oversight
759 of the original asynch design, but now we have to be as
760 backward compatible as possible, so we do this:
762 (*callback)(serverP->_server_url, methodName, NULL, userData,
763 &env, NULL);
764 } else {
765 if (*suffix != '\0')
766 xmlrpc_env_set_fault_formatted(
767 &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
768 "specifier: '%s'. There must be exactly one arument.",
769 suffix);
770 else {
771 xmlrpc_client_call_server_asynch_params(
772 serverP, methodName, callback, userData, paramArrayP);
774 xmlrpc_DECREF(paramArrayP);
777 if (env.fault_occurred)
778 (*callback)(serverP->_server_url, methodName, paramArrayP, userData,
779 &env, NULL);
781 xmlrpc_env_clean(&env);
786 static void
787 asynchComplete(call_info * const callInfoP,
788 xmlrpc_mem_block * const responseXmlP,
789 xmlrpc_env const transportEnv) {
790 /*----------------------------------------------------------------------------
791 Complete an asynchronous XML-RPC call request.
793 This includes calling the user's RPC completion routine.
795 'transportEnv' describes the an error that the transport
796 encountered in processing the call. If the transport successfully
797 sent the call to the server and processed the response but the
798 server failed the call, 'transportEnv' indicates no error, and the
799 response in *callInfoP might very well indicate that the server
800 failed the request.
801 -----------------------------------------------------------------------------*/
802 xmlrpc_env env;
803 xmlrpc_value * responseP = 0;
805 xmlrpc_env_init(&env);
807 if (transportEnv.fault_occurred)
808 xmlrpc_env_set_fault_formatted(
809 &env, transportEnv.fault_code,
810 "Client transport failed to execute the RPC. %s",
811 transportEnv.fault_string);
813 if (!env.fault_occurred)
814 responseP = xmlrpc_parse_response(
815 &env,
816 XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlP),
817 XMLRPC_MEMBLOCK_SIZE(char, responseXmlP));
819 /* Call the user's callback function with the result */
820 (*callInfoP->callback)(callInfoP->server_url,
821 callInfoP->method_name,
822 callInfoP->param_array,
823 callInfoP->user_data, &env, responseP);
825 if (!env.fault_occurred)
826 xmlrpc_DECREF(responseP);
828 call_info_free(callInfoP);
830 xmlrpc_env_clean(&env);
835 static void
836 sendRequest(xmlrpc_env * const envP,
837 struct clientTransport * const transportP,
838 xmlrpc_server_info * const serverP,
839 const char * const methodName,
840 xmlrpc_response_handler responseHandler,
841 void * const userData,
842 xmlrpc_value * const argP) {
844 call_info * callInfoP;
846 call_info_new(envP, serverP, methodName, argP, &callInfoP);
847 if (!envP->fault_occurred) {
848 call_info_set_asynch_data(envP, callInfoP,
849 serverP->_server_url, methodName,
850 argP, responseHandler, userData);
851 if (!envP->fault_occurred)
852 clientTransportOps.send_request(
853 envP, transportP, serverP, callInfoP->serialized_xml,
854 &asynchComplete, callInfoP);
856 if (envP->fault_occurred)
857 call_info_free(callInfoP);
858 else {
859 /* asynchComplete() will free *callInfoP */
862 if (envP->fault_occurred) {
863 /* Transport did not start the call. Report the call complete
864 (with error) now.
866 (*responseHandler)(serverP->_server_url, methodName, argP, userData,
867 envP, NULL);
868 } else {
869 /* The transport will call *responseHandler() when it has completed
870 the call
877 void
878 xmlrpc_client_call_server_asynch_params(
879 xmlrpc_server_info * const serverP,
880 const char * const methodName,
881 xmlrpc_response_handler responseHandler,
882 void * const userData,
883 xmlrpc_value * const argP) {
884 xmlrpc_env env;
886 xmlrpc_env_init(&env);
888 XMLRPC_ASSERT_PTR_OK(serverP);
889 XMLRPC_ASSERT_PTR_OK(methodName);
890 XMLRPC_ASSERT_PTR_OK(responseHandler);
891 XMLRPC_ASSERT_VALUE_OK(argP);
893 if (!clientInitialized)
894 xmlrpc_env_set_fault_formatted(
895 &env, XMLRPC_INTERNAL_ERROR,
896 "Xmlrpc-c client instance has not been initialized "
897 "(need to call xmlrpc_client_init2()).");
898 else
899 sendRequest(&env, client.transportP, serverP,
900 methodName, responseHandler, userData,
901 argP);
903 xmlrpc_env_clean(&env);
908 void
909 xmlrpc_server_info_set_basic_auth(xmlrpc_env * const envP,
910 xmlrpc_server_info * const serverP,
911 const char * const username,
912 const char * const password) {
914 size_t username_len, password_len, raw_token_len;
915 char *raw_token;
916 xmlrpc_mem_block *token;
917 char *token_data, *auth_type, *auth_header;
918 size_t token_len, auth_type_len, auth_header_len;
920 /* Error-handling preconditions. */
921 token = NULL;
922 auth_header = NULL;
924 XMLRPC_ASSERT_ENV_OK(envP);
925 XMLRPC_ASSERT_PTR_OK(serverP);
926 XMLRPC_ASSERT_PTR_OK(username);
927 XMLRPC_ASSERT_PTR_OK(password);
929 /* Calculate some lengths. */
930 username_len = strlen(username);
931 password_len = strlen(password);
932 raw_token_len = username_len + password_len + 1;
934 /* Build a raw token of the form 'username:password'. */
935 raw_token = (char*) malloc(raw_token_len + 1);
936 XMLRPC_FAIL_IF_NULL(raw_token, envP, XMLRPC_INTERNAL_ERROR,
937 "Couldn't allocate memory for auth token");
938 strcpy(raw_token, username);
939 raw_token[username_len] = ':';
940 strcpy(&raw_token[username_len + 1], password);
942 /* Encode our raw token using Base64. */
943 token = xmlrpc_base64_encode_without_newlines(envP,
944 (unsigned char*) raw_token,
945 raw_token_len);
946 XMLRPC_FAIL_IF_FAULT(envP);
947 token_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, token);
948 token_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, token);
950 /* Build our actual header value. (I hate string processing in C.) */
951 auth_type = "Basic ";
952 auth_type_len = strlen(auth_type);
953 auth_header_len = auth_type_len + token_len;
954 auth_header = (char*) malloc(auth_header_len + 1);
955 XMLRPC_FAIL_IF_NULL(auth_header, envP, XMLRPC_INTERNAL_ERROR,
956 "Couldn't allocate memory for auth header");
957 memcpy(auth_header, auth_type, auth_type_len);
958 memcpy(&auth_header[auth_type_len], token_data, token_len);
959 auth_header[auth_header_len] = '\0';
961 /* Clean up any pre-existing authentication information, and install
962 ** the new value. */
963 if (serverP->_http_basic_auth)
964 free(serverP->_http_basic_auth);
965 serverP->_http_basic_auth = auth_header;
967 cleanup:
968 if (raw_token)
969 free(raw_token);
970 if (token)
971 xmlrpc_mem_block_free(token);
972 if (envP->fault_occurred) {
973 if (auth_header)
974 free(auth_header);