1 
2 #include "XmlRpcClient.h"
3 
4 #include "XmlRpcSocket.h"
5 #include "XmlRpc.h"
6 
7 #include "base64.h"   // For HTTP authentication encoding
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string>
12 #include <string.h>
13 #include <strings.h>
14 
15 using namespace XmlRpc;
16 using namespace std;
17 
18 // Static data
19 const char XmlRpcClient::REQUEST_BEGIN[] =
20   "<?xml version=\"1.0\"?>\r\n"
21   "<methodCall><methodName>";
22 const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
23 const char XmlRpcClient::PARAMS_TAG[] = "<params>";
24 const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
25 const char XmlRpcClient::PARAM_TAG[] = "<param>";
26 const char XmlRpcClient::PARAM_ETAG[] =  "</param>";
27 const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
28 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
29 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
30 
XmlRpcClient(const char * host,int port,const char * uri)31 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
32 {
33   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
34 
35   _host = host;
36   _port = port;
37   if (uri && *uri)
38     _uri = uri;
39   else
40     _uri = "/RPC2";
41   _connectionState = NO_CONNECTION;
42   _executing = false;
43   _eof = false;
44   _ssl = false; _ssl_ssl = (SSL *) NULL;
45 
46   // Default to keeping the connection open until an explicit close is done
47   setKeepOpen();
48 }
XmlRpcClient(const char * host,int port,const char * uri,bool ssl)49 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri, bool ssl)
50 {
51   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
52 
53   _host = host;
54   _port = port;
55   if (uri && *uri)
56     _uri = uri;
57   else
58     _uri = "/RPC2";
59   _connectionState = NO_CONNECTION;
60   _executing = false;
61   _eof = false;
62   _ssl = ssl;
63   if (!_ssl) { _ssl_ssl = (SSL *) NULL; }
64 
65   // Default to keeping the connection open until an explicit close is done
66   setKeepOpen();
67 }
68 
69 
XmlRpcClient(const char * host,int port,const char * login,const char * password,const char * uri)70 XmlRpcClient::XmlRpcClient(const char* host, int port,
71                            const char* login, const char* password, const char* uri/*=0*/)
72 {
73   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d, login %s.", host, port, login);
74 
75   _host = host;
76   _port = port;
77   if (uri)
78     _uri = uri;
79   else
80     _uri = "/RPC2";
81 
82   _login = login;
83   _password = password;
84 
85   _connectionState = NO_CONNECTION;
86   _executing = false;
87   _eof = false;
88 
89   // Default to keeping the connection open until an explicit close is done
90   setKeepOpen();
91 }
92 
XmlRpcClient(const char * host,int port,const char * login,const char * password,const char * uri,bool ssl)93 XmlRpcClient::XmlRpcClient(const char* host, int port,
94                            const char* login, const char* password,
95                            const char* uri/*=0*/, bool ssl)
96 {
97   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d, login %s.", host, port, login);
98 
99   _host = host;
100   _port = port;
101   if (uri)
102     _uri = uri;
103   else
104     _uri = "/RPC2";
105 
106   _login = login;
107   _password = password;
108 
109   _connectionState = NO_CONNECTION;
110   _executing = false;
111   _eof = false;
112   _ssl = ssl;
113   if (!_ssl) { _ssl_ssl = (SSL *) NULL; }
114 
115   // Default to keeping the connection open until an explicit close is done
116   setKeepOpen();
117 }
118 
119 
~XmlRpcClient()120 XmlRpcClient::~XmlRpcClient()
121 {
122   XmlRpcUtil::log(1, "XmlRpcClient dtor client: host %s, port %d.", _host.c_str(), _port);
123   if (_connectionState != NO_CONNECTION) close();
124 }
125 
126 
127 // Close the owned fd
128 void
close()129 XmlRpcClient::close()
130 {
131   XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
132   _connectionState = NO_CONNECTION;
133   _disp.exit();
134   _disp.removeSource(this);
135   if (_ssl) {
136     // Pre-socket shutdown
137     XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_shutdown");
138     SSL_shutdown(_ssl_ssl);
139     XmlRpcUtil::log(4, "XmlRpcClient::close: after SSL_shutdown");
140   }
141   XmlRpcSource::close();
142   if (_ssl) {
143     // Post-socket shutdown
144     XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_free(_ssl_ssl)");
145     SSL_free(_ssl_ssl);
146     XmlRpcUtil::log(4, "XmlRpcClient::close: before SSL_CTX_free(_ssl_ctx)");
147     SSL_CTX_free(_ssl_ctx);
148     XmlRpcUtil::log(4, "XmlRpcClient::close: SSL shutdown successful!");
149   }
150 }
151 
152 
153 // Clear the referenced flag even if exceptions or errors occur.
154 struct ClearFlagOnExit {
ClearFlagOnExitClearFlagOnExit155   ClearFlagOnExit(bool& flag) : _flag(flag) {}
~ClearFlagOnExitClearFlagOnExit156   ~ClearFlagOnExit() { _flag = false; }
157   bool& _flag;
158 };
159 
160 // Execute the named procedure on the remote server.
161 // Params should be an array of the arguments for the method.
162 // Returns true if the request was sent and a result received (although the result
163 // might be a fault).
164 bool
execute(const char * method,XmlRpcValue const & params,XmlRpcValue & result)165 XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
166 {
167   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
168 
169   // This is not a thread-safe operation, if you want to do multithreading, use separate
170   // clients for each thread. If you want to protect yourself from multiple threads
171   // accessing the same client, replace this code with a real mutex.
172   if (_executing)
173     return false;
174 
175   _executing = true;
176   ClearFlagOnExit cf(_executing);
177 
178   _sendAttempts = 0;
179   _isFault = false;
180 
181   if ( ! setupConnection())
182     return false;
183 
184   if ( ! generateRequest(method, params))
185     return false;
186 
187   result.clear();
188   double msTime = -1.0;   // Process until exit is called
189   _disp.work(msTime);
190 
191   if (_connectionState != IDLE || ! parseResponse(result))
192     return false;
193 
194   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
195   _response = "";
196   return true;
197 }
198 
199 // XmlRpcSource interface implementation
200 // Handle server responses. Called by the event dispatcher during execute.
201 unsigned
handleEvent(unsigned eventType)202 XmlRpcClient::handleEvent(unsigned eventType)
203 {
204   if (eventType == XmlRpcDispatch::Exception)
205   {
206     //if (XmlRpcSocket::nonFatalError())
207     //  return (_connectionState == WRITE_REQUEST)
208     //        ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
209 
210     if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
211       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
212                        XmlRpcSocket::getErrorMsg().c_str());
213     else
214       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
215                         _connectionState, XmlRpcSocket::getErrorMsg().c_str());
216     return 0;
217   }
218 
219   if (_connectionState == WRITE_REQUEST)
220     if ( ! writeRequest()) return 0;
221 
222   if (_connectionState == READ_HEADER)
223     if ( ! readHeader()) return 0;
224 
225   if (_connectionState == READ_RESPONSE)
226     if ( ! readResponse()) return 0;
227 
228   // This should probably always ask for Exception events too
229   return (_connectionState == WRITE_REQUEST)
230         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
231 }
232 
233 
234 // Create the socket connection to the server if necessary
235 bool
setupConnection()236 XmlRpcClient::setupConnection()
237 {
238   // If an error occurred last time through, or if the server closed the connection, close our end
239   if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
240     close();
241 
242   _eof = false;
243   if (_connectionState == NO_CONNECTION)
244     if (! doConnect())
245       return false;
246 
247   // Prepare to write the request
248   _connectionState = WRITE_REQUEST;
249   _bytesWritten = 0;
250 
251   // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
252   _disp.removeSource(this);       // Make sure nothing is left over
253   _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
254 
255   return true;
256 }
257 
258 
259 // Connect to the xmlrpc server
260 bool
doConnect()261 XmlRpcClient::doConnect()
262 {
263   int fd = XmlRpcSocket::socket();
264   if (fd < 0)
265   {
266     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
267     return false;
268   }
269 
270   XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
271   this->setfd(fd);
272 
273   // Don't block on connect/reads/writes
274   if ( ! XmlRpcSocket::setNonBlocking(fd))
275   {
276     this->close();
277     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
278     return false;
279   }
280 
281   if ( ! XmlRpcSocket::connect(fd, _host, _port))
282   {
283     this->close();
284     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
285     return false;
286   }
287 
288   // Perform SSL if needed
289   if (_ssl) {
290     SSLeay_add_ssl_algorithms();
291     _ssl_meth = SSLv23_client_method();
292     SSL_load_error_strings();
293     _ssl_ctx = SSL_CTX_new (_ssl_meth);
294     _ssl_ssl = SSL_new (_ssl_ctx);
295     SSL_set_fd (_ssl_ssl, fd);
296     /* int err = */ SSL_connect (_ssl_ssl);
297   }
298 
299   return true;
300 }
301 
302 // Encode the request to call the specified method with the specified parameters into xml
303 bool
generateRequest(const char * methodName,XmlRpcValue const & params)304 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
305 {
306   std::string body = REQUEST_BEGIN;
307   body += methodName;
308   body += REQUEST_END_METHODNAME;
309 
310   // If params is an array, each element is a separate parameter
311   if (params.valid()) {
312     body += PARAMS_TAG;
313     if (params.getType() == XmlRpcValue::TypeArray)
314     {
315       for (int i=0; i<params.size(); ++i) {
316         body += PARAM_TAG;
317         body += params[i].toXml();
318         body += PARAM_ETAG;
319       }
320     }
321     else
322     {
323       body += PARAM_TAG;
324       body += params.toXml();
325       body += PARAM_ETAG;
326     }
327 
328     body += PARAMS_ETAG;
329   }
330   body += REQUEST_END;
331 
332   std::string header = generateHeader(body);
333   XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
334                   header.length(), body.length());
335 
336   _request = header + body;
337   return true;
338 }
339 
340 // Prepend http headers
341 std::string
generateHeader(std::string const & body)342 XmlRpcClient::generateHeader(std::string const& body)
343 {
344   std::string header =
345     "POST " + _uri + " HTTP/1.1\r\n"
346     "User-Agent: ";
347   header += XMLRPC_VERSION;
348   header += "\r\nHost: ";
349   header += _host;
350 
351   char buff[40];
352   sprintf(buff,":%d\r\n", _port);
353 
354   header += buff;
355 
356   if (_login.length() != 0)
357   {
358     // convert to base64
359     std::vector<char> base64data;
360     int iostatus = 0;
361     base64<char> encoder;
362     std::back_insert_iterator<std::vector<char> > ins =
363       std::back_inserter(base64data);
364 
365     std::string authBuf = _login + ":" + _password;
366 
367     encoder.put(authBuf.begin(), authBuf.end(), ins, iostatus,
368                 base64<>::crlf());
369 
370     header += "Authorization: Basic ";
371     std::string authEnc(base64data.begin(), base64data.end());
372     // handle pesky linefeed characters
373     string::size_type lf;
374     while ( (lf = authEnc.find("\r")) != string::npos ) {
375       authEnc.erase(lf, 1);
376     }
377     while ( (lf = authEnc.find("\n")) != string::npos ) {
378       authEnc.erase(lf, 1);
379     }
380     header += authEnc;
381     header += "\r\n";
382   }
383 
384   header += "Content-Type: text/xml\r\nContent-length: ";
385 
386   sprintf(buff,"%zd\r\n\r\n", body.size());
387 
388   return header + buff;
389 }
390 
391 bool
writeRequest()392 XmlRpcClient::writeRequest()
393 {
394   if (_bytesWritten == 0)
395     XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
396 
397   // Try to write the request
398   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten, _ssl_ssl)) {
399     XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
400     return false;
401   }
402 
403   XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
404 
405   // Wait for the result
406   if (_bytesWritten == int(_request.length())) {
407     _header = "";
408     _response = "";
409     _connectionState = READ_HEADER;
410   }
411   return true;
412 }
413 
414 
415 // Read the header from the response
416 bool
readHeader()417 XmlRpcClient::readHeader()
418 {
419   // Read available data
420   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof, _ssl_ssl) ||
421        (_eof && _header.length() == 0)) {
422 
423     // If we haven't read any data yet and this is a keep-alive connection, the server may
424     // have timed out, so we try one more time.
425     if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
426       XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
427       XmlRpcSource::close();
428       _connectionState = NO_CONNECTION;
429       _eof = false;
430       return setupConnection();
431     }
432 
433     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
434                       XmlRpcSocket::getErrorMsg().c_str(), getfd());
435     return false;
436   }
437 
438   XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
439 
440   char *hp = (char*)_header.c_str();  // Start of header
441   char *ep = hp + _header.length();   // End of string
442   char *bp = 0;                       // Start of body
443   char *lp = 0;                       // Start of content-length value
444 
445   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
446     if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
447       lp = cp + 16;
448     else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
449       bp = cp + 4;
450     else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
451       bp = cp + 2;
452   }
453 
454   // If we haven't gotten the entire header yet, return (keep reading)
455   if (bp == 0) {
456     if (_eof)          // EOF in the middle of a response is an error
457     {
458       XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
459       return false;   // Close the connection
460     }
461 
462     return true;  // Keep reading
463   }
464 
465   // Decode content length
466   if (lp == 0) {
467     XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
468     return false;   // We could try to figure it out by parsing as we read, but for now...
469   }
470 
471   _contentLength = atoi(lp);
472   if (_contentLength <= 0) {
473     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
474     return false;
475   }
476 
477   XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
478 
479   // Otherwise copy non-header data to response buffer and set state to read response.
480   _response = bp;
481   _header = "";   // should parse out any interesting bits from the header (connection, etc)...
482   _connectionState = READ_RESPONSE;
483   return true;    // Continue monitoring this source
484 }
485 
486 
487 bool
readResponse()488 XmlRpcClient::readResponse()
489 {
490   // If we dont have the entire response yet, read available data
491   if (int(_response.length()) < _contentLength) {
492     if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof, _ssl_ssl)) {
493       XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
494       return false;
495     }
496 
497     // If we haven't gotten the entire _response yet, return (keep reading)
498     if (int(_response.length()) < _contentLength) {
499       if (_eof) {
500         XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
501         return false;
502       }
503       return true;
504     }
505   }
506 
507   // Otherwise, parse and return the result
508   XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
509   XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
510 
511   _connectionState = IDLE;
512 
513   return false;    // Stop monitoring this source (causes return from work)
514 }
515 
516 
517 // Convert the response xml into a result value
518 bool
parseResponse(XmlRpcValue & result)519 XmlRpcClient::parseResponse(XmlRpcValue& result)
520 {
521   // Parse response xml into result
522   int offset = 0;
523   if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
524     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
525     return false;
526   }
527 
528   // Expect either <params><param>... or <fault>...
529   if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
530        XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
531       (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
532   {
533     if ( ! result.fromXml(_response, &offset)) {
534       XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
535       _response = "";
536       return false;
537     }
538   } else {
539     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
540     _response = "";
541     return false;
542   }
543 
544   _response = "";
545   return result.valid();
546 }
547 
548