1 
2 #include "XmlRpcClient.h"
3 
4 #include "XmlRpcSocket.h"
5 #include "XmlRpc.h"
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 
10 
11 using namespace XmlRpc;
12 
13 // Static data
14 const char XmlRpcClient::REQUEST_BEGIN[] =
15   "<?xml version=\"1.0\"?>\r\n"
16   "<methodCall><methodName>";
17 const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
18 const char XmlRpcClient::PARAMS_TAG[] = "<params>";
19 const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
20 const char XmlRpcClient::PARAM_TAG[] = "<param>";
21 const char XmlRpcClient::PARAM_ETAG[] =  "</param>";
22 const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
23 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
24 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
25 
26 
27 
XmlRpcClient(const char * host,int port,const char * uri)28 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
29 {
30   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
31 
32   _host = host;
33   _port = port;
34   if (uri)
35     _uri = uri;
36   else
37     _uri = "/RPC2";
38   _connectionState = NO_CONNECTION;
39   _executing = false;
40   _eof = false;
41 
42   // Default to keeping the connection open until an explicit close is done
43   setKeepOpen();
44 }
45 
46 
~XmlRpcClient()47 XmlRpcClient::~XmlRpcClient()
48 {
49 }
50 
51 // Close the owned fd
52 void
close()53 XmlRpcClient::close()
54 {
55   XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
56   _connectionState = NO_CONNECTION;
57   _disp.exit();
58   _disp.removeSource(this);
59   XmlRpcSource::close();
60 }
61 
62 
63 // Clear the referenced flag even if exceptions or errors occur.
64 struct ClearFlagOnExit {
ClearFlagOnExitClearFlagOnExit65   ClearFlagOnExit(bool& flag) : _flag(flag) {}
~ClearFlagOnExitClearFlagOnExit66   ~ClearFlagOnExit() { _flag = false; }
67   bool& _flag;
68 };
69 
70 // Execute the named procedure on the remote server.
71 // Params should be an array of the arguments for the method.
72 // Returns true if the request was sent and a result received (although the result
73 // might be a fault).
74 bool
execute(const char * method,XmlRpcValue const & params,XmlRpcValue & result)75 XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
76 {
77   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
78 
79   // This is not a thread-safe operation, if you want to do multithreading, use separate
80   // clients for each thread. If you want to protect yourself from multiple threads
81   // accessing the same client, replace this code with a real mutex.
82   if (_executing)
83     return false;
84 
85   _executing = true;
86   ClearFlagOnExit cf(_executing);
87 
88   _sendAttempts = 0;
89   _isFault = false;
90 
91   if ( ! setupConnection())
92     return false;
93 
94   if ( ! generateRequest(method, params))
95     return false;
96 
97   result.clear();
98   double msTime = -1.0;   // Process until exit is called
99   _disp.work(msTime);
100 
101   if (_connectionState != IDLE || ! parseResponse(result))
102     return false;
103 
104   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
105   _response = "";
106   return true;
107 }
108 
109 // XmlRpcSource interface implementation
110 // Handle server responses. Called by the event dispatcher during execute.
111 unsigned
handleEvent(unsigned eventType)112 XmlRpcClient::handleEvent(unsigned eventType)
113 {
114   if (eventType == XmlRpcDispatch::Exception)
115   {
116     if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
117       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
118                        XmlRpcSocket::getErrorMsg().c_str());
119     else
120       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
121                         _connectionState, XmlRpcSocket::getErrorMsg().c_str());
122     return 0;
123   }
124 
125   if (_connectionState == WRITE_REQUEST)
126     if ( ! writeRequest()) return 0;
127 
128   if (_connectionState == READ_HEADER)
129     if ( ! readHeader()) return 0;
130 
131   if (_connectionState == READ_RESPONSE)
132     if ( ! readResponse()) return 0;
133 
134   // This should probably always ask for Exception events too
135   return (_connectionState == WRITE_REQUEST)
136         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
137 }
138 
139 
140 // Create the socket connection to the server if necessary
141 bool
setupConnection()142 XmlRpcClient::setupConnection()
143 {
144   // If an error occurred last time through, or if the server closed the connection, close our end
145   if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
146     close();
147 
148   _eof = false;
149   if (_connectionState == NO_CONNECTION)
150     if (! doConnect())
151       return false;
152 
153   // Prepare to write the request
154   _connectionState = WRITE_REQUEST;
155   _bytesWritten = 0;
156 
157   // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
158   _disp.removeSource(this);       // Make sure nothing is left over
159   _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
160 
161   return true;
162 }
163 
164 
165 // Connect to the xmlrpc server
166 bool
doConnect()167 XmlRpcClient::doConnect()
168 {
169   int fd = XmlRpcSocket::socket();
170   if (fd < 0)
171   {
172     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
173     return false;
174   }
175 
176   XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
177   this->setfd(fd);
178 
179   // Don't block on connect/reads/writes
180   if ( ! XmlRpcSocket::setNonBlocking(fd))
181   {
182     this->close();
183     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
184     return false;
185   }
186 
187   if ( ! XmlRpcSocket::connect(fd, _host, _port))
188   {
189     this->close();
190     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
191     return false;
192   }
193 
194   return true;
195 }
196 
197 // Encode the request to call the specified method with the specified parameters into xml
198 bool
generateRequest(const char * methodName,XmlRpcValue const & params)199 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
200 {
201   std::string body = REQUEST_BEGIN;
202   body += methodName;
203   body += REQUEST_END_METHODNAME;
204 
205   // If params is an array, each element is a separate parameter
206   if (params.valid()) {
207     body += PARAMS_TAG;
208     if (params.getType() == XmlRpcValue::TypeArray)
209     {
210       for (int i=0; i<params.size(); ++i) {
211         body += PARAM_TAG;
212         body += params[i].toXml();
213         body += PARAM_ETAG;
214       }
215     }
216     else
217     {
218       body += PARAM_TAG;
219       body += params.toXml();
220       body += PARAM_ETAG;
221     }
222 
223     body += PARAMS_ETAG;
224   }
225   body += REQUEST_END;
226 
227   std::string header = generateHeader(body);
228   XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
229                   header.length(), body.length());
230 
231   _request = header + body;
232   return true;
233 }
234 
235 // Prepend http headers
236 std::string
generateHeader(std::string const & body)237 XmlRpcClient::generateHeader(std::string const& body)
238 {
239   std::string header =
240     "POST " + _uri + " HTTP/1.1\r\n"
241     "User-Agent: ";
242   header += XMLRPC_VERSION;
243   header += "\r\nHost: ";
244   header += _host;
245 
246   char buff[40];
247   sprintf(buff,":%d\r\n", _port);
248 
249   header += buff;
250   header += "Content-Type: text/xml\r\nContent-length: ";
251 
252   sprintf(buff,"%d\r\n\r\n", body.size());
253 
254   return header + buff;
255 }
256 
257 bool
writeRequest()258 XmlRpcClient::writeRequest()
259 {
260   if (_bytesWritten == 0)
261     XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
262 
263   // Try to write the request
264   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
265     XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
266     return false;
267   }
268 
269   XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
270 
271   // Wait for the result
272   if (_bytesWritten == int(_request.length())) {
273     _header = "";
274     _response = "";
275     _connectionState = READ_HEADER;
276   }
277   return true;
278 }
279 
280 
281 // Read the header from the response
282 bool
readHeader()283 XmlRpcClient::readHeader()
284 {
285   // Read available data
286   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
287        (_eof && _header.length() == 0)) {
288 
289     // If we haven't read any data yet and this is a keep-alive connection, the server may
290     // have timed out, so we try one more time.
291     if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
292       XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
293       XmlRpcSource::close();
294       _connectionState = NO_CONNECTION;
295       _eof = false;
296       return setupConnection();
297     }
298 
299     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
300                       XmlRpcSocket::getErrorMsg().c_str(), getfd());
301     return false;
302   }
303 
304   XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
305 
306   char *hp = (char*)_header.c_str();  // Start of header
307   char *ep = hp + _header.length();   // End of string
308   char *bp = 0;                       // Start of body
309   char *lp = 0;                       // Start of content-length value
310 
311   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
312     if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
313       lp = cp + 16;
314     else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
315       bp = cp + 4;
316     else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
317       bp = cp + 2;
318   }
319 
320   // If we haven't gotten the entire header yet, return (keep reading)
321   if (bp == 0) {
322     if (_eof)          // EOF in the middle of a response is an error
323     {
324       XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
325       return false;   // Close the connection
326     }
327 
328     return true;  // Keep reading
329   }
330 
331   // Decode content length
332   if (lp == 0) {
333     XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
334     return false;   // We could try to figure it out by parsing as we read, but for now...
335   }
336 
337   _contentLength = atoi(lp);
338   if (_contentLength <= 0) {
339     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
340     return false;
341   }
342 
343   XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
344 
345   // Otherwise copy non-header data to response buffer and set state to read response.
346   _response = bp;
347   _header = "";   // should parse out any interesting bits from the header (connection, etc)...
348   _connectionState = READ_RESPONSE;
349   return true;    // Continue monitoring this source
350 }
351 
352 
353 bool
readResponse()354 XmlRpcClient::readResponse()
355 {
356   // If we dont have the entire response yet, read available data
357   if (int(_response.length()) < _contentLength) {
358     if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
359       XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
360       return false;
361     }
362 
363     // If we haven't gotten the entire _response yet, return (keep reading)
364     if (int(_response.length()) < _contentLength) {
365       if (_eof) {
366         XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
367         return false;
368       }
369       return true;
370     }
371   }
372 
373   // Otherwise, parse and return the result
374   XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
375   XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
376 
377   _connectionState = IDLE;
378 
379   return false;    // Stop monitoring this source (causes return from work)
380 }
381 
382 
383 // Convert the response xml into a result value
384 bool
parseResponse(XmlRpcValue & result)385 XmlRpcClient::parseResponse(XmlRpcValue& result)
386 {
387   // Parse response xml into result
388   int offset = 0;
389   if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
390     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
391     return false;
392   }
393 
394   // Expect either <params><param>... or <fault>...
395   if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
396        XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
397       (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
398   {
399     if ( ! result.fromXml(_response, &offset)) {
400       XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
401       _response = "";
402       return false;
403     }
404   } else {
405     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
406     _response = "";
407     return false;
408   }
409 
410   _response = "";
411   return result.valid();
412 }
413 
414