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