initial load of upstream version 1.06.32
[xmlrpc-c] / src / cpp / client.cpp
1 /*=============================================================================
2                                 client.cpp
3 ===============================================================================
4   This is the C++ XML-RPC client library for Xmlrpc-c.
5
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 =============================================================================*/
11
12 #include <stdlib.h>
13 #include <cassert>
14 #include <string>
15 #include <vector>
16
17 #include "xmlrpc-c/girerr.hpp"
18 using girerr::error;
19 using girerr::throwf;
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"
31
32 using namespace std;
33 using namespace xmlrpc_c;
34
35
36 namespace {
37
38 void
39 throwIfError(env_wrap const& env) {
40
41     if (env.env_c.fault_occurred)
42         throw(error(env.env_c.fault_string));
43 }
44
45
46
47 class memblockStringWrapper {
48
49 public:    
50     memblockStringWrapper(string const value) {
51
52         env_wrap env;
53
54         this->memblockP = XMLRPC_MEMBLOCK_NEW(char, &env.env_c, 0);
55         throwIfError(env);
56
57         XMLRPC_MEMBLOCK_APPEND(char, &env.env_c, this->memblockP,
58                                value.c_str(), value.size());
59         throwIfError(env);
60     }
61     
62     memblockStringWrapper(xmlrpc_mem_block * const memblockP) :
63         memblockP(memblockP) {};
64
65     ~memblockStringWrapper() {
66         XMLRPC_MEMBLOCK_FREE(char, this->memblockP);
67     }
68
69     xmlrpc_mem_block * memblockP;
70 };
71
72 } // namespace
73
74 namespace xmlrpc_c {
75
76 carriageParm::carriageParm() {}
77
78
79
80 carriageParm::~carriageParm() {}
81
82
83
84 carriageParmPtr::carriageParmPtr() {
85     // Base class constructor will construct pointer that points to nothing
86 }
87
88
89
90 carriageParmPtr::carriageParmPtr(
91     carriageParm * const carriageParmP) {
92     this->point(carriageParmP);
93 }
94
95
96
97 carriageParm *
98 carriageParmPtr::operator->() const {
99
100     autoObject * const p(this->objectP);
101     return dynamic_cast<carriageParm *>(p);
102 }
103
104
105
106 carriageParm *
107 carriageParmPtr::get() const {
108     return dynamic_cast<carriageParm *>(objectP);
109 }
110
111
112
113 carriageParm_http0::carriageParm_http0() :
114     c_serverInfoP(NULL) {}
115
116
117
118 carriageParm_http0::carriageParm_http0(string const serverUrl) {
119     this->c_serverInfoP = NULL;
120
121     this->instantiate(serverUrl);
122 }
123
124
125
126 carriageParm_http0::~carriageParm_http0() {
127
128     if (this->c_serverInfoP)
129         xmlrpc_server_info_free(this->c_serverInfoP);
130 }
131
132
133
134 void
135 carriageParm_http0::instantiate(string const serverUrl) {
136
137     if (c_serverInfoP)
138         throw(error("object already instantiated"));
139     
140     env_wrap env;
141
142     this->c_serverInfoP =
143         xmlrpc_server_info_new(&env.env_c, serverUrl.c_str());
144     throwIfError(env);
145 }
146
147
148
149 void
150 carriageParm_http0::setBasicAuth(string const username,
151                                  string const password) {
152
153     if (!c_serverInfoP)
154         throw(error("object not instantiated"));
155     
156     env_wrap env;
157
158     xmlrpc_server_info_set_basic_auth(
159         &env.env_c, this->c_serverInfoP, username.c_str(), password.c_str());
160     throwIfError(env);
161 }
162
163
164
165 carriageParm_http0Ptr::carriageParm_http0Ptr() {
166     // Base class constructor will construct pointer that points to nothing
167 }
168
169
170
171 carriageParm_http0Ptr::carriageParm_http0Ptr(
172     carriageParm_http0 * const carriageParmP) {
173     this->point(carriageParmP);
174 }
175
176
177
178 carriageParm_http0 *
179 carriageParm_http0Ptr::operator->() const {
180
181     autoObject * const p(this->objectP);
182     return dynamic_cast<carriageParm_http0 *>(p);
183 }
184
185
186
187 xmlTransaction::xmlTransaction() {}
188
189
190
191 void
192 xmlTransaction::finish(string const& responseXml) const {
193
194     xml::trace("XML-RPC RESPONSE", responseXml);
195 }
196
197
198
199 void
200 xmlTransaction::finishErr(error const&) const {
201
202 }
203
204
205
206 xmlTransactionPtr::xmlTransactionPtr() {}
207
208
209
210 xmlTransaction *
211 xmlTransactionPtr::operator->() const {
212     autoObject * const p(this->objectP);
213     return dynamic_cast<xmlTransaction *>(p);
214 }
215
216
217
218 struct xmlTranCtl {
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
226    that way.
227
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) :
235
236         xmlTranP(xmlTranP) {
237
238         env_wrap env;
239
240         this->callXmlP = XMLRPC_MEMBLOCK_NEW(char, &env.env_c, 0);
241         throwIfError(env);
242
243         XMLRPC_MEMBLOCK_APPEND(char, &env.env_c, this->callXmlP,
244                                callXml.c_str(), callXml.size());
245         throwIfError(env);
246     }
247     
248     ~xmlTranCtl() {
249         XMLRPC_MEMBLOCK_FREE(char, this->callXmlP);
250     }
251
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.
256
257     xmlrpc_mem_block * callXmlP;
258         // The XML of the call.  This is what the transport transports.
259 };
260
261
262
263 clientXmlTransport::~clientXmlTransport() {}
264
265
266
267 void
268 clientXmlTransport::start(carriageParm *    const  carriageParmP,
269                           string            const& callXml,
270                           xmlTransactionPtr const& xmlTranP) {
271     
272     string responseXml;
273
274     this->call(carriageParmP, callXml, &responseXml);
275
276     xmlTranP->finish(responseXml);
277 }
278
279
280
281 void
282 clientXmlTransport::finishAsync(xmlrpc_c::timeout const timeout) {
283     if (timeout.finite == timeout.finite)
284         throw(error("This class does not have finishAsync()"));
285 }
286
287
288
289 void
290 clientXmlTransport::asyncComplete(
291     struct xmlrpc_call_info * const callInfoP,
292     xmlrpc_mem_block *        const responseXmlMP,
293     xmlrpc_env                const transportEnv) {
294
295     xmlTranCtl * const xmlTranCtlP = reinterpret_cast<xmlTranCtl *>(callInfoP);
296
297     try {
298         if (transportEnv.fault_occurred) {
299             xmlTranCtlP->xmlTranP->finishErr(error(transportEnv.fault_string));
300         } else {
301             string const responseXml(
302                 XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlMP),
303                 XMLRPC_MEMBLOCK_SIZE(char, responseXmlMP));
304             xmlTranCtlP->xmlTranP->finish(responseXml);
305         }
306     } catch(error) {
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.
310         */
311         assert(false);
312     }
313     delete(xmlTranCtlP);
314
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.
319     */
320 }
321
322
323
324 clientXmlTransportPtr::clientXmlTransportPtr() {
325     // Base class constructor will construct pointer that points to nothing
326 }
327
328
329
330 clientXmlTransportPtr::clientXmlTransportPtr(
331     clientXmlTransport * const transportP) {
332     this->point(transportP);
333 }
334
335
336
337 clientXmlTransport *
338 clientXmlTransportPtr::get() const {
339     return dynamic_cast<clientXmlTransport *>(objectP);
340 }
341
342
343
344 clientXmlTransport *
345 clientXmlTransportPtr::operator->() const {
346
347     autoObject * const p(this->objectP);
348     return dynamic_cast<clientXmlTransport *>(p);
349 }
350
351
352
353 clientXmlTransport_http::~clientXmlTransport_http() {}
354
355
356
357 void
358 clientXmlTransport_http::call(
359     carriageParm * const  carriageParmP,
360     string         const& callXml,
361     string *       const  responseXmlP) {
362
363     carriageParm_http0 * const carriageParmHttpP =
364         dynamic_cast<carriageParm_http0 *>(carriageParmP);
365
366     if (carriageParmHttpP == NULL)
367         throw(error("HTTP client XML transport called with carriage "
368                     "parameter object not of class carriageParm_http"));
369
370     memblockStringWrapper callXmlM(callXml);
371
372     xmlrpc_mem_block * responseXmlMP;
373
374     env_wrap env;
375
376     this->c_transportOpsP->call(&env.env_c,
377                                 this->c_transportP,
378                                 carriageParmHttpP->c_serverInfoP,
379                                 callXmlM.memblockP,
380                                 &responseXmlMP);
381
382     throwIfError(env);
383
384     memblockStringWrapper responseHolder(responseXmlMP);
385         // Makes responseXmlMP get freed at end of scope
386     
387     *responseXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, responseXmlMP),
388                            XMLRPC_MEMBLOCK_SIZE(char, responseXmlMP));
389 }
390
391
392
393 void
394 clientXmlTransport_http::start(
395     carriageParm *    const  carriageParmP,
396     string            const& callXml,
397     xmlTransactionPtr const& xmlTranP) {
398
399     env_wrap env;
400
401     carriageParm_http0 * const carriageParmHttpP =
402         dynamic_cast<carriageParm_http0 *>(carriageParmP);
403
404     if (carriageParmHttpP == NULL)
405         throw(error("HTTP client XML transport called with carriage "
406                     "parameter object not of type carriageParm_http"));
407
408     xmlTranCtl * const tranCtlP(new xmlTranCtl(xmlTranP, callXml));
409
410     try {
411         this->c_transportOpsP->send_request(
412             &env.env_c,
413             this->c_transportP,
414             carriageParmHttpP->c_serverInfoP,
415             tranCtlP->callXmlP,
416             &this->asyncComplete,
417             reinterpret_cast<xmlrpc_call_info *>(tranCtlP));
418
419         throwIfError(env);
420     } catch (...) {
421         delete tranCtlP;
422         throw;
423     }
424 }
425
426
427
428 void
429 clientXmlTransport_http::finishAsync(xmlrpc_c::timeout const timeout) {
430
431     xmlrpc_timeoutType const c_timeoutType(
432         timeout.finite ? timeout_yes : timeout_no);
433     xmlrpc_timeout const c_timeout(timeout.duration);
434
435     this->c_transportOpsP->finish_asynch(
436         this->c_transportP, c_timeoutType, c_timeout);
437 }
438
439
440 bool const haveCurl(
441 #if MUST_BUILD_CURL_CLIENT
442 true
443 #else
444 false
445 #endif
446 );
447
448 bool const haveLibwww(
449 #if MUST_BUILD_LIBWWW_CLIENT
450 true
451 #else
452 false
453 #endif
454 );
455
456 bool const haveWininet(
457 #if MUST_BUILD_WININET_CLIENT
458 true
459 #else
460 false
461 #endif
462 );
463
464
465
466 vector<string>
467 clientXmlTransport_http::availableTypes() {
468
469     vector<string> retval;
470
471     if (haveCurl)
472         retval.push_back("curl");
473
474     if (haveLibwww)
475         retval.push_back("libwww");
476
477     if (haveWininet)
478         retval.push_back("wininet");
479
480     return retval;
481 }
482
483
484
485 clientXmlTransportPtr
486 clientXmlTransport_http::create() {
487 /*----------------------------------------------------------------------------
488   Make an HTTP Client XML transport of any kind (Caller doesn't care).
489
490   Caller can find out what kind he got by trying dynamic casts.
491
492   Caller can use a carriageParm_http0 with the transport.
493 -----------------------------------------------------------------------------*/
494     if (haveCurl)
495         return clientXmlTransportPtr(new clientXmlTransport_curl());
496     else if (haveLibwww)
497         return clientXmlTransportPtr(new clientXmlTransport_libwww());
498     else if (haveWininet)
499         return clientXmlTransportPtr(new clientXmlTransport_wininet());
500     else
501         throwf("This XML-RPC client library contains no HTTP XML transports");
502 }
503
504
505
506 clientTransaction::clientTransaction() {}
507
508
509
510 clientTransactionPtr::clientTransactionPtr() {}
511
512
513
514 clientTransactionPtr::~clientTransactionPtr() {}
515
516
517
518 clientTransaction *
519 clientTransactionPtr::operator->() const {
520     autoObject * const p(this->objectP);
521     return dynamic_cast<clientTransaction *>(p);
522 }
523
524
525
526 client::~client() {}
527
528
529
530 void
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.
537
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 -----------------------------------------------------------------------------*/
542     rpcOutcome outcome;
543
544     this->call(carriageParmP, methodName, paramList, &outcome);
545
546     tranP->finish(outcome);
547 }
548
549
550
551 clientPtr::clientPtr() {
552     // Base class constructor will construct pointer that points to nothing
553 }
554
555
556
557 clientPtr::clientPtr(
558     client * const clientP) {
559     this->point(clientP);
560 }
561
562
563
564 client *
565 clientPtr::operator->() const {
566
567     autoObject * const p(this->objectP);
568     return dynamic_cast<client *>(p);
569 }
570
571
572
573 client *
574 clientPtr::get() const {
575     return dynamic_cast<client *>(objectP);
576 }
577
578
579
580 client_xml::client_xml(clientXmlTransport * const transportP) :
581     transportP(transportP) {}
582
583
584
585 client_xml::client_xml(clientXmlTransportPtr const transportPtr) {
586
587     this->transportPtr = transportPtr;
588     this->transportP   = transportPtr.get();
589 }
590      
591
592
593 void
594 client_xml::call(carriageParm * const  carriageParmP,
595                  string         const& methodName,
596                  paramList      const& paramList,
597                  rpcOutcome *   const  outcomeP) {
598
599     string callXml;
600     string responseXml;
601
602     xml::generateCall(methodName, paramList, &callXml);
603     
604     xml::trace("XML-RPC CALL", callXml);
605
606     try {
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());
611     }
612     xml::trace("XML-RPC RESPONSE", responseXml);
613         
614     try {
615         xml::parseResponse(responseXml, outcomeP);
616     } catch (error const& error) {
617         throwf("Response XML from server is not valid XML-RPC response.  %s",
618                error.what());
619     }
620 }
621  
622
623
624 void
625 client_xml::start(carriageParm *       const  carriageParmP,
626                   string               const& methodName,
627                   paramList            const& paramList,
628                   clientTransactionPtr const& tranP) {
629
630     string callXml;
631
632     xml::generateCall(methodName, paramList, &callXml);
633     
634     xml::trace("XML-RPC CALL", callXml);
635
636     xmlTransaction_clientPtr const xmlTranP(tranP);
637
638     this->transportP->start(carriageParmP, callXml, xmlTranP);
639 }
640  
641
642
643 void
644 client_xml::finishAsync(xmlrpc_c::timeout const timeout) {
645
646     transportP->finishAsync(timeout);
647 }
648
649
650
651 serverAccessor::serverAccessor(clientPtr       const clientP,
652                                carriageParmPtr const carriageParmP) :
653
654     clientP(clientP), carriageParmP(carriageParmP) {};
655
656
657
658 void
659 serverAccessor::call(std::string            const& methodName,
660                      xmlrpc_c::paramList    const& paramList,
661                      xmlrpc_c::rpcOutcome * const  outcomeP) const {
662
663     this->clientP->call(this->carriageParmP.get(),
664                         methodName,
665                         paramList,
666                         outcomeP);
667 }
668
669
670
671 serverAccessorPtr::serverAccessorPtr() {
672     // Base class constructor will construct pointer that points to nothing
673 }
674
675
676
677 serverAccessorPtr::serverAccessorPtr(
678     serverAccessor * const serverAccessorParmP) {
679     this->point(serverAccessorParmP);
680 }
681
682
683
684 serverAccessor *
685 serverAccessorPtr::operator->() const {
686
687     autoObject * const p(this->objectP);
688     return dynamic_cast<serverAccessor *>(p);
689 }
690
691
692
693 serverAccessor *
694 serverAccessorPtr::get() const {
695     return dynamic_cast<serverAccessor *>(objectP);
696 }
697
698
699
700 connection::connection(client *       const clientP,
701                        carriageParm * const carriageParmP) :
702     clientP(clientP), carriageParmP(carriageParmP) {}
703
704
705
706 connection::~connection() {}
707
708
709
710 rpc::rpc(string              const  methodName,
711          xmlrpc_c::paramList const& paramList) {
712     
713     this->state      = STATE_UNFINISHED;
714     this->methodName = methodName;
715     this->paramList  = paramList;
716 }
717
718
719
720 rpc::~rpc() {
721
722     if (this->state == STATE_ERROR)
723         delete(this->errorP);
724 }
725
726
727
728 void
729 rpc::call(client       * const clientP,
730           carriageParm * const carriageParmP) {
731
732     if (this->state != STATE_UNFINISHED)
733         throw(error("Attempt to execute an RPC that has already been "
734                     "executed"));
735
736     clientP->call(carriageParmP,
737                   this->methodName,
738                   this->paramList,
739                   &this->outcome);
740
741     this->state = outcome.succeeded() ? STATE_SUCCEEDED : STATE_FAILED;
742 }
743
744
745
746 void
747 rpc::call(connection const& connection) {
748
749     this->call(connection.clientP, connection.carriageParmP);
750
751 }
752
753
754  
755 void
756 rpc::start(client       * const clientP,
757            carriageParm * const carriageParmP) {
758     
759     if (this->state != STATE_UNFINISHED)
760         throw(error("Attempt to execute an RPC that has already been "
761                     "executed"));
762
763     clientP->start(carriageParmP,
764                    this->methodName,
765                    this->paramList,
766                    rpcPtr(this));
767 }
768
769
770  
771 void
772 rpc::start(xmlrpc_c::connection const& connection) {
773     
774     this->start(connection.clientP, connection.carriageParmP);
775 }
776
777
778
779 void
780 rpc::finish(rpcOutcome const& outcome) {
781
782     this->state = outcome.succeeded() ? STATE_SUCCEEDED : STATE_FAILED;
783
784     this->outcome = outcome;
785
786     this->notifyComplete();
787 }
788
789
790
791 void
792 rpc::finishErr(error const& error) {
793
794     this->state = STATE_ERROR;
795     this->errorP = new girerr::error(error);
796     this->notifyComplete();
797 }
798
799
800
801 void
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.
807
808    Typically, notifyFinish() will queue the RPC so some other thread
809    will deal with the fact that the RPC is finished.
810
811
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 -----------------------------------------------------------------------------*/
816
817 }
818
819     
820
821 value
822 rpc::getResult() const {
823
824     switch (this->state) {
825     case STATE_UNFINISHED:
826         throw(error("Attempt to get result of RPC that is not finished."));
827         break;
828     case STATE_ERROR:
829         throw(*this->errorP);
830         break;
831     case STATE_FAILED:
832         throw(error("RPC response indicates failure.  " +
833                     this->outcome.getFault().getDescription()));
834         break;
835     case STATE_SUCCEEDED: {
836         // All normal
837     }
838     }
839
840     return this->outcome.getResult();
841 }
842
843
844
845
846 fault
847 rpc::getFault() const {
848
849     switch (this->state) {
850     case STATE_UNFINISHED:
851         throw(error("Attempt to get fault from RPC that is not finished"));
852         break;
853     case STATE_ERROR:
854         throw(*this->errorP);
855         break;
856     case STATE_SUCCEEDED:
857         throw(error("Attempt to get fault from an RPC that succeeded"));
858         break;
859     case STATE_FAILED: {
860         // All normal
861     }
862     }
863
864     return this->outcome.getFault();
865 }
866
867
868
869 bool
870 rpc::isFinished() const {
871     return (this->state != STATE_UNFINISHED);
872 }
873
874
875
876 bool
877 rpc::isSuccessful() const {
878     return (this->state == STATE_SUCCEEDED);
879 }
880
881
882
883 rpcPtr::rpcPtr() {}
884
885
886
887 rpcPtr::rpcPtr(rpc * const rpcP) {
888     this->point(rpcP);
889 }
890
891
892
893 rpcPtr::rpcPtr(string              const  methodName,
894                xmlrpc_c::paramList const& paramList) {
895
896     this->point(new rpc(methodName, paramList));
897 }
898
899
900
901 rpc *
902 rpcPtr::operator->() const {
903
904     autoObject * const p(this->objectP);
905     return dynamic_cast<rpc *>(p);
906 }
907
908
909
910 xmlTransaction_client::xmlTransaction_client(
911     clientTransactionPtr const& tranP) :
912     tranP(tranP) {}
913
914
915
916 void
917 xmlTransaction_client::finish(string const& responseXml) const {
918
919     xml::trace("XML-RPC RESPONSE", responseXml);
920
921     try {
922         rpcOutcome outcome;
923     
924         xml::parseResponse(responseXml, &outcome);
925
926         this->tranP->finish(outcome);
927     } catch (error const& error) {
928         this->tranP->finishErr(error);
929     }
930 }
931
932
933
934 void
935 xmlTransaction_client::finishErr(error const& error) const {
936
937     this->tranP->finishErr(error);
938 }
939
940
941
942 xmlTransaction_clientPtr::xmlTransaction_clientPtr() {}
943
944
945
946 xmlTransaction_clientPtr::xmlTransaction_clientPtr(
947     clientTransactionPtr const& tranP) {
948
949     this->point(new xmlTransaction_client(tranP));
950 }
951
952
953
954 xmlTransaction_client *
955 xmlTransaction_clientPtr::operator->() const {
956     autoObject * const p(this->objectP);
957     return dynamic_cast<xmlTransaction_client *>(p);
958 }
959
960
961
962 } // namespace