1 /*=============================================================================
3 ===============================================================================
4 This is the C++ XML-RPC client library for Xmlrpc-c.
6 Note that unlike most of Xmlprc-c's C++ API, this is _not_ based on the
7 C client library. This code is independent of the C client library, and
8 is based directly on the client XML transport libraries (with a little
9 help from internal C utility libraries).
10 =============================================================================*/
17 #include "xmlrpc-c/girerr.hpp"
20 #include "xmlrpc-c/girmem.hpp"
21 using girmem::autoObjectPtr;
22 using girmem::autoObject;
23 #include "env_wrap.hpp"
24 #include "xmlrpc-c/base.h"
25 #include "xmlrpc-c/client.h"
26 #include "xmlrpc-c/transport.h"
27 #include "xmlrpc-c/base.hpp"
28 #include "xmlrpc-c/xml.hpp"
29 #include "xmlrpc-c/client.hpp"
30 #include "transport_config.h"
33 using namespace xmlrpc_c;
39 throwIfError(env_wrap const& env) {
41 if (env.env_c.fault_occurred)
42 throw(error(env.env_c.fault_string));
47 class memblockStringWrapper {
50 memblockStringWrapper(string const value) {
54 this->memblockP = XMLRPC_MEMBLOCK_NEW(char, &env.env_c, 0);
57 XMLRPC_MEMBLOCK_APPEND(char, &env.env_c, this->memblockP,
58 value.c_str(), value.size());
62 memblockStringWrapper(xmlrpc_mem_block * const memblockP) :
63 memblockP(memblockP) {};
65 ~memblockStringWrapper() {
66 XMLRPC_MEMBLOCK_FREE(char, this->memblockP);
69 xmlrpc_mem_block * memblockP;
76 carriageParm::carriageParm() {}
80 carriageParm::~carriageParm() {}
84 carriageParmPtr::carriageParmPtr() {
85 // Base class constructor will construct pointer that points to nothing
90 carriageParmPtr::carriageParmPtr(
91 carriageParm * const carriageParmP) {
92 this->point(carriageParmP);
98 carriageParmPtr::operator->() const {
100 autoObject * const p(this->objectP);
101 return dynamic_cast<carriageParm *>(p);
107 carriageParmPtr::get() const {
108 return dynamic_cast<carriageParm *>(objectP);
113 carriageParm_http0::carriageParm_http0() :
114 c_serverInfoP(NULL) {}
118 carriageParm_http0::carriageParm_http0(string const serverUrl) {
119 this->c_serverInfoP = NULL;
121 this->instantiate(serverUrl);
126 carriageParm_http0::~carriageParm_http0() {
128 if (this->c_serverInfoP)
129 xmlrpc_server_info_free(this->c_serverInfoP);
135 carriageParm_http0::instantiate(string const serverUrl) {
138 throw(error("object already instantiated"));
142 this->c_serverInfoP =
143 xmlrpc_server_info_new(&env.env_c, serverUrl.c_str());
150 carriageParm_http0::setBasicAuth(string const username,
151 string const password) {
154 throw(error("object not instantiated"));
158 xmlrpc_server_info_set_basic_auth(
159 &env.env_c, this->c_serverInfoP, username.c_str(), password.c_str());
165 carriageParm_http0Ptr::carriageParm_http0Ptr() {
166 // Base class constructor will construct pointer that points to nothing
171 carriageParm_http0Ptr::carriageParm_http0Ptr(
172 carriageParm_http0 * const carriageParmP) {
173 this->point(carriageParmP);
179 carriageParm_http0Ptr::operator->() const {
181 autoObject * const p(this->objectP);
182 return dynamic_cast<carriageParm_http0 *>(p);
187 xmlTransaction::xmlTransaction() {}
192 xmlTransaction::finish(string const& responseXml) const {
194 xml::trace("XML-RPC RESPONSE", responseXml);
200 xmlTransaction::finishErr(error const&) const {
206 xmlTransactionPtr::xmlTransactionPtr() {}
211 xmlTransactionPtr::operator->() const {
212 autoObject * const p(this->objectP);
213 return dynamic_cast<xmlTransaction *>(p);
219 /*----------------------------------------------------------------------------
220 This contains information needed to conduct a transaction. You
221 construct it as you start the transaction and destroy it after the
222 work is done. You need this only for an asynchronous one, because
223 where the user starts and finishes the RPC in the same
224 libxmlrpc_client call, you can just keep this information in
225 various stack variables, and it's faster and easier to understand
228 The C transport is designed to take a xmlrpc_call_info argument for
229 similar stuff needed by the the C client object. But it's really
230 opaque to the transport, so we just let xmlTranCtl masquerade as
231 xmlprc_call_info in our call to the C transport.
232 -----------------------------------------------------------------------------*/
233 xmlTranCtl(xmlTransactionPtr const& xmlTranP,
234 string const& callXml) :
240 this->callXmlP = XMLRPC_MEMBLOCK_NEW(char, &env.env_c, 0);
243 XMLRPC_MEMBLOCK_APPEND(char, &env.env_c, this->callXmlP,
244 callXml.c_str(), callXml.size());
249 XMLRPC_MEMBLOCK_FREE(char, this->callXmlP);
252 xmlTransactionPtr const xmlTranP;
253 // The transaction we're controlling. Most notable use of this is
254 // that this object we inform when the transaction is done. This
255 // is where the response XML and other transaction results go.
257 xmlrpc_mem_block * callXmlP;
258 // The XML of the call. This is what the transport transports.
263 clientXmlTransport::~clientXmlTransport() {}
268 clientXmlTransport::start(carriageParm * const carriageParmP,
269 string const& callXml,
270 xmlTransactionPtr const& xmlTranP) {
274 this->call(carriageParmP, callXml, &responseXml);
276 xmlTranP->finish(responseXml);
282 clientXmlTransport::finishAsync(xmlrpc_c::timeout const timeout) {
283 if (timeout.finite == timeout.finite)
284 throw(error("This class does not have finishAsync()"));
290 clientXmlTransport::asyncComplete(
291 struct xmlrpc_call_info * const callInfoP,
292 xmlrpc_mem_block * const responseXmlMP,
293 xmlrpc_env const transportEnv) {
295 xmlTranCtl * const xmlTranCtlP = reinterpret_cast<xmlTranCtl *>(callInfoP);
298 if (transportEnv.fault_occurred) {
299 xmlTranCtlP->xmlTranP->finishErr(error(transportEnv.fault_string));
301 string const responseXml(
302 XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlMP),
303 XMLRPC_MEMBLOCK_SIZE(char, responseXmlMP));
304 xmlTranCtlP->xmlTranP->finish(responseXml);
307 /* We can't throw an error back to C code, and the async_complete
308 interface does not provide for failure, so we define ->finish()
309 as not being capable of throwing an error.
315 /* Ordinarily, *xmlTranCtlP is the last reference to
316 xmlTranCtlP->xmlTranP, so that will get destroyed too. But
317 ->finish() could conceivably create a new reference to
318 xmlTranCtlP->xmlTranP, and then it would keep living.
324 clientXmlTransportPtr::clientXmlTransportPtr() {
325 // Base class constructor will construct pointer that points to nothing
330 clientXmlTransportPtr::clientXmlTransportPtr(
331 clientXmlTransport * const transportP) {
332 this->point(transportP);
338 clientXmlTransportPtr::get() const {
339 return dynamic_cast<clientXmlTransport *>(objectP);
345 clientXmlTransportPtr::operator->() const {
347 autoObject * const p(this->objectP);
348 return dynamic_cast<clientXmlTransport *>(p);
353 clientXmlTransport_http::~clientXmlTransport_http() {}
358 clientXmlTransport_http::call(
359 carriageParm * const carriageParmP,
360 string const& callXml,
361 string * const responseXmlP) {
363 carriageParm_http0 * const carriageParmHttpP =
364 dynamic_cast<carriageParm_http0 *>(carriageParmP);
366 if (carriageParmHttpP == NULL)
367 throw(error("HTTP client XML transport called with carriage "
368 "parameter object not of class carriageParm_http"));
370 memblockStringWrapper callXmlM(callXml);
372 xmlrpc_mem_block * responseXmlMP;
376 this->c_transportOpsP->call(&env.env_c,
378 carriageParmHttpP->c_serverInfoP,
384 memblockStringWrapper responseHolder(responseXmlMP);
385 // Makes responseXmlMP get freed at end of scope
387 *responseXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlMP),
388 XMLRPC_MEMBLOCK_SIZE(char, responseXmlMP));
394 clientXmlTransport_http::start(
395 carriageParm * const carriageParmP,
396 string const& callXml,
397 xmlTransactionPtr const& xmlTranP) {
401 carriageParm_http0 * const carriageParmHttpP =
402 dynamic_cast<carriageParm_http0 *>(carriageParmP);
404 if (carriageParmHttpP == NULL)
405 throw(error("HTTP client XML transport called with carriage "
406 "parameter object not of type carriageParm_http"));
408 xmlTranCtl * const tranCtlP(new xmlTranCtl(xmlTranP, callXml));
411 this->c_transportOpsP->send_request(
414 carriageParmHttpP->c_serverInfoP,
416 &this->asyncComplete,
417 reinterpret_cast<xmlrpc_call_info *>(tranCtlP));
429 clientXmlTransport_http::finishAsync(xmlrpc_c::timeout const timeout) {
431 xmlrpc_timeoutType const c_timeoutType(
432 timeout.finite ? timeout_yes : timeout_no);
433 xmlrpc_timeout const c_timeout(timeout.duration);
435 this->c_transportOpsP->finish_asynch(
436 this->c_transportP, c_timeoutType, c_timeout);
441 #if MUST_BUILD_CURL_CLIENT
448 bool const haveLibwww(
449 #if MUST_BUILD_LIBWWW_CLIENT
456 bool const haveWininet(
457 #if MUST_BUILD_WININET_CLIENT
467 clientXmlTransport_http::availableTypes() {
469 vector<string> retval;
472 retval.push_back("curl");
475 retval.push_back("libwww");
478 retval.push_back("wininet");
485 clientXmlTransportPtr
486 clientXmlTransport_http::create() {
487 /*----------------------------------------------------------------------------
488 Make an HTTP Client XML transport of any kind (Caller doesn't care).
490 Caller can find out what kind he got by trying dynamic casts.
492 Caller can use a carriageParm_http0 with the transport.
493 -----------------------------------------------------------------------------*/
495 return clientXmlTransportPtr(new clientXmlTransport_curl());
497 return clientXmlTransportPtr(new clientXmlTransport_libwww());
498 else if (haveWininet)
499 return clientXmlTransportPtr(new clientXmlTransport_wininet());
501 throwf("This XML-RPC client library contains no HTTP XML transports");
506 clientTransaction::clientTransaction() {}
510 clientTransactionPtr::clientTransactionPtr() {}
514 clientTransactionPtr::~clientTransactionPtr() {}
519 clientTransactionPtr::operator->() const {
520 autoObject * const p(this->objectP);
521 return dynamic_cast<clientTransaction *>(p);
531 client::start(carriageParm * const carriageParmP,
532 string const& methodName,
533 paramList const& paramList,
534 clientTransactionPtr const& tranP) {
535 /*----------------------------------------------------------------------------
536 Start an RPC, wait for it to complete, and finish it.
538 Usually, a derived class overrides this with something that does
539 not wait for the RPC to complete, but rather arranges for something
540 to finish the RPC later when the RPC does complete.
541 -----------------------------------------------------------------------------*/
544 this->call(carriageParmP, methodName, paramList, &outcome);
546 tranP->finish(outcome);
551 clientPtr::clientPtr() {
552 // Base class constructor will construct pointer that points to nothing
557 clientPtr::clientPtr(
558 client * const clientP) {
559 this->point(clientP);
565 clientPtr::operator->() const {
567 autoObject * const p(this->objectP);
568 return dynamic_cast<client *>(p);
574 clientPtr::get() const {
575 return dynamic_cast<client *>(objectP);
580 client_xml::client_xml(clientXmlTransport * const transportP) :
581 transportP(transportP) {}
585 client_xml::client_xml(clientXmlTransportPtr const transportPtr) {
587 this->transportPtr = transportPtr;
588 this->transportP = transportPtr.get();
594 client_xml::call(carriageParm * const carriageParmP,
595 string const& methodName,
596 paramList const& paramList,
597 rpcOutcome * const outcomeP) {
602 xml::generateCall(methodName, paramList, &callXml);
604 xml::trace("XML-RPC CALL", callXml);
607 this->transportP->call(carriageParmP, callXml, &responseXml);
608 } catch (error const& error) {
609 throwf("Unable to transport XML to server and "
610 "get XML response back. %s", error.what());
612 xml::trace("XML-RPC RESPONSE", responseXml);
615 xml::parseResponse(responseXml, outcomeP);
616 } catch (error const& error) {
617 throwf("Response XML from server is not valid XML-RPC response. %s",
625 client_xml::start(carriageParm * const carriageParmP,
626 string const& methodName,
627 paramList const& paramList,
628 clientTransactionPtr const& tranP) {
632 xml::generateCall(methodName, paramList, &callXml);
634 xml::trace("XML-RPC CALL", callXml);
636 xmlTransaction_clientPtr const xmlTranP(tranP);
638 this->transportP->start(carriageParmP, callXml, xmlTranP);
644 client_xml::finishAsync(xmlrpc_c::timeout const timeout) {
646 transportP->finishAsync(timeout);
651 serverAccessor::serverAccessor(clientPtr const clientP,
652 carriageParmPtr const carriageParmP) :
654 clientP(clientP), carriageParmP(carriageParmP) {};
659 serverAccessor::call(std::string const& methodName,
660 xmlrpc_c::paramList const& paramList,
661 xmlrpc_c::rpcOutcome * const outcomeP) const {
663 this->clientP->call(this->carriageParmP.get(),
671 serverAccessorPtr::serverAccessorPtr() {
672 // Base class constructor will construct pointer that points to nothing
677 serverAccessorPtr::serverAccessorPtr(
678 serverAccessor * const serverAccessorParmP) {
679 this->point(serverAccessorParmP);
685 serverAccessorPtr::operator->() const {
687 autoObject * const p(this->objectP);
688 return dynamic_cast<serverAccessor *>(p);
694 serverAccessorPtr::get() const {
695 return dynamic_cast<serverAccessor *>(objectP);
700 connection::connection(client * const clientP,
701 carriageParm * const carriageParmP) :
702 clientP(clientP), carriageParmP(carriageParmP) {}
706 connection::~connection() {}
710 rpc::rpc(string const methodName,
711 xmlrpc_c::paramList const& paramList) {
713 this->state = STATE_UNFINISHED;
714 this->methodName = methodName;
715 this->paramList = paramList;
722 if (this->state == STATE_ERROR)
723 delete(this->errorP);
729 rpc::call(client * const clientP,
730 carriageParm * const carriageParmP) {
732 if (this->state != STATE_UNFINISHED)
733 throw(error("Attempt to execute an RPC that has already been "
736 clientP->call(carriageParmP,
741 this->state = outcome.succeeded() ? STATE_SUCCEEDED : STATE_FAILED;
747 rpc::call(connection const& connection) {
749 this->call(connection.clientP, connection.carriageParmP);
756 rpc::start(client * const clientP,
757 carriageParm * const carriageParmP) {
759 if (this->state != STATE_UNFINISHED)
760 throw(error("Attempt to execute an RPC that has already been "
763 clientP->start(carriageParmP,
772 rpc::start(xmlrpc_c::connection const& connection) {
774 this->start(connection.clientP, connection.carriageParmP);
780 rpc::finish(rpcOutcome const& outcome) {
782 this->state = outcome.succeeded() ? STATE_SUCCEEDED : STATE_FAILED;
784 this->outcome = outcome;
786 this->notifyComplete();
792 rpc::finishErr(error const& error) {
794 this->state = STATE_ERROR;
795 this->errorP = new girerr::error(error);
796 this->notifyComplete();
802 rpc::notifyComplete() {
803 /*----------------------------------------------------------------------------
804 Anyone who does RPCs asynchronously and doesn't use polling will
805 want to make his own class derived from 'rpc' and override this
806 with a notifyFinish() that does something.
808 Typically, notifyFinish() will queue the RPC so some other thread
809 will deal with the fact that the RPC is finished.
812 In the absence of the aforementioned queueing, the RPC becomes
813 unreferenced as soon as our Caller releases his reference, so the
814 RPC gets destroyed when we return.
815 -----------------------------------------------------------------------------*/
822 rpc::getResult() const {
824 switch (this->state) {
825 case STATE_UNFINISHED:
826 throw(error("Attempt to get result of RPC that is not finished."));
829 throw(*this->errorP);
832 throw(error("RPC response indicates failure. " +
833 this->outcome.getFault().getDescription()));
835 case STATE_SUCCEEDED: {
840 return this->outcome.getResult();
847 rpc::getFault() const {
849 switch (this->state) {
850 case STATE_UNFINISHED:
851 throw(error("Attempt to get fault from RPC that is not finished"));
854 throw(*this->errorP);
856 case STATE_SUCCEEDED:
857 throw(error("Attempt to get fault from an RPC that succeeded"));
864 return this->outcome.getFault();
870 rpc::isFinished() const {
871 return (this->state != STATE_UNFINISHED);
877 rpc::isSuccessful() const {
878 return (this->state == STATE_SUCCEEDED);
887 rpcPtr::rpcPtr(rpc * const rpcP) {
893 rpcPtr::rpcPtr(string const methodName,
894 xmlrpc_c::paramList const& paramList) {
896 this->point(new rpc(methodName, paramList));
902 rpcPtr::operator->() const {
904 autoObject * const p(this->objectP);
905 return dynamic_cast<rpc *>(p);
910 xmlTransaction_client::xmlTransaction_client(
911 clientTransactionPtr const& tranP) :
917 xmlTransaction_client::finish(string const& responseXml) const {
919 xml::trace("XML-RPC RESPONSE", responseXml);
924 xml::parseResponse(responseXml, &outcome);
926 this->tranP->finish(outcome);
927 } catch (error const& error) {
928 this->tranP->finishErr(error);
935 xmlTransaction_client::finishErr(error const& error) const {
937 this->tranP->finishErr(error);
942 xmlTransaction_clientPtr::xmlTransaction_clientPtr() {}
946 xmlTransaction_clientPtr::xmlTransaction_clientPtr(
947 clientTransactionPtr const& tranP) {
949 this->point(new xmlTransaction_client(tranP));
954 xmlTransaction_client *
955 xmlTransaction_clientPtr::operator->() const {
956 autoObject * const p(this->objectP);
957 return dynamic_cast<xmlTransaction_client *>(p);