• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 #include "XmlRpcServerConnection.h"
3 
4 #include "XmlRpcSocket.h"
5 #include "XmlRpc.h"
6 #ifndef MAKEDEPEND
7 # include <stdio.h>
8 # include <stdlib.h>
9 #endif
10 
11 using namespace XmlRpc;
12 
13 // Static data
14 const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
15 const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
16 const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
17 const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
18 const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
19 
20 const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
21 const std::string XmlRpcServerConnection::METHODNAME = "methodName";
22 const std::string XmlRpcServerConnection::PARAMS = "params";
23 
24 const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
25 const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
26 
27 
28 
29 // The server delegates handling client requests to a serverConnection object.
XmlRpcServerConnection(int fd,XmlRpcServer * server,bool deleteOnClose)30 XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) :
31   XmlRpcSource(fd, deleteOnClose)
32 {
33   XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
34   _server = server;
35   _connectionState = READ_HEADER;
36   _keepAlive = true;
37 }
38 
39 
~XmlRpcServerConnection()40 XmlRpcServerConnection::~XmlRpcServerConnection()
41 {
42   XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
43   _server->removeConnection(this);
44 }
45 
46 
47 // Handle input on the server socket by accepting the connection
48 // and reading the rpc request. Return true to continue to monitor
49 // the socket for events, false to remove it from the dispatcher.
50 unsigned
handleEvent(unsigned)51 XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
52 {
53   if (_connectionState == READ_HEADER)
54     if ( ! readHeader()) return 0;
55 
56   if (_connectionState == READ_REQUEST)
57     if ( ! readRequest()) return 0;
58 
59   if (_connectionState == WRITE_RESPONSE)
60     if ( ! writeResponse()) return 0;
61 
62   return (_connectionState == WRITE_RESPONSE)
63         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
64 }
65 
66 
67 bool
readHeader()68 XmlRpcServerConnection::readHeader()
69 {
70   // Read available data
71   bool eof;
72   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) {
73     // Its only an error if we already have read some data
74     if (_header.length() > 0)
75       XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
76     return false;
77   }
78 
79   XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
80   char *hp = (char*)_header.c_str();  // Start of header
81   char *ep = hp + _header.length();   // End of string
82   char *bp = 0;                       // Start of body
83   char *lp = 0;                       // Start of content-length value
84   char *kp = 0;                       // Start of connection value
85 
86   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
87 	if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
88 	  lp = cp + 16;
89 	else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
90 	  kp = cp + 12;
91 	else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
92 	  bp = cp + 4;
93 	else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
94 	  bp = cp + 2;
95   }
96 
97   // If we haven't gotten the entire header yet, return (keep reading)
98   if (bp == 0) {
99     // EOF in the middle of a request is an error, otherwise its ok
100     if (eof) {
101       XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
102       if (_header.length() > 0)
103         XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
104       return false;   // Either way we close the connection
105     }
106 
107     return true;  // Keep reading
108   }
109 
110   // Decode content length
111   if (lp == 0) {
112     XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
113     return false;   // We could try to figure it out by parsing as we read, but for now...
114   }
115 
116   _contentLength = atoi(lp);
117   if (_contentLength <= 0) {
118     XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
119     return false;
120   }
121 
122   XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
123 
124   // Otherwise copy non-header data to request buffer and set state to read request.
125   _request = bp;
126 
127   // Parse out any interesting bits from the header (HTTP version, connection)
128   _keepAlive = true;
129   if (_header.find("HTTP/1.0") != std::string::npos) {
130     if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
131       _keepAlive = false;           // Default for HTTP 1.0 is to close the connection
132   } else {
133     if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
134       _keepAlive = false;
135   }
136   XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
137 
138 
139   _header = "";
140   _connectionState = READ_REQUEST;
141   return true;    // Continue monitoring this source
142 }
143 
144 bool
readRequest()145 XmlRpcServerConnection::readRequest()
146 {
147   // If we dont have the entire request yet, read available data
148   if (int(_request.length()) < _contentLength) {
149     bool eof;
150     if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) {
151       XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
152       return false;
153     }
154 
155     // If we haven't gotten the entire request yet, return (keep reading)
156     if (int(_request.length()) < _contentLength) {
157       if (eof) {
158         XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
159         return false;   // Either way we close the connection
160       }
161       return true;
162     }
163   }
164 
165   // Otherwise, parse and dispatch the request
166   XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
167   //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
168 
169   _connectionState = WRITE_RESPONSE;
170 
171   return true;    // Continue monitoring this source
172 }
173 
174 
175 bool
writeResponse()176 XmlRpcServerConnection::writeResponse()
177 {
178   if (_response.length() == 0) {
179     executeRequest();
180     _bytesWritten = 0;
181     if (_response.length() == 0) {
182       XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
183       return false;
184     }
185   }
186 
187   // Try to write the response
188   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) {
189     XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
190     return false;
191   }
192   XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
193 
194   // Prepare to read the next request
195   if (_bytesWritten == int(_response.length())) {
196     _header = "";
197     _request = "";
198     _response = "";
199     _connectionState = READ_HEADER;
200   }
201 
202   return _keepAlive;    // Continue monitoring this source if true
203 }
204 
205 // Run the method, generate _response string
206 void
executeRequest()207 XmlRpcServerConnection::executeRequest()
208 {
209   XmlRpcValue params, resultValue;
210   std::string methodName = parseRequest(params);
211   XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'",
212                     methodName.c_str());
213 
214   try {
215 
216     if ( ! executeMethod(methodName, params, resultValue) &&
217          ! executeMulticall(methodName, params, resultValue))
218       generateFaultResponse(methodName + ": unknown method name");
219     else
220       generateResponse(resultValue.toXml());
221 
222   } catch (const XmlRpcException& fault) {
223     XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
224                     fault.getMessage().c_str());
225     generateFaultResponse(fault.getMessage(), fault.getCode());
226   }
227 }
228 
229 // Parse the method name and the argument values from the request.
230 std::string
parseRequest(XmlRpcValue & params)231 XmlRpcServerConnection::parseRequest(XmlRpcValue& params)
232 {
233   int offset = 0;   // Number of chars parsed from the request
234 
235   XmlRpcUtil::log(5, "XmlRpcServerConnection::parseRequest:\n%s\n", _request.c_str());
236 
237   std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset);
238 
239   if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset))
240   {
241     int nArgs = 0;
242     while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) {
243       params[nArgs++] = XmlRpcValue(_request, &offset);
244       (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset);
245     }
246 
247     (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset);
248   }
249 
250   return methodName;
251 }
252 
253 // Execute a named method with the specified params.
254 bool
executeMethod(const std::string & methodName,XmlRpcValue & params,XmlRpcValue & result)255 XmlRpcServerConnection::executeMethod(const std::string& methodName,
256                                       XmlRpcValue& params, XmlRpcValue& result)
257 {
258   XmlRpcServerMethod* method = _server->findMethod(methodName);
259 
260   if ( ! method) return false;
261 
262   method->execute(params, result);
263 
264   // Ensure a valid result value
265   if ( ! result.valid())
266       result = std::string();
267 
268   return true;
269 }
270 
271 // Execute multiple calls and return the results in an array.
272 bool
executeMulticall(const std::string & methodName,XmlRpcValue & params,XmlRpcValue & result)273 XmlRpcServerConnection::executeMulticall(const std::string& methodName,
274                                          XmlRpcValue& params, XmlRpcValue& result)
275 {
276   if (methodName != SYSTEM_MULTICALL) return false;
277 
278   // There ought to be 1 parameter, an array of structs
279   if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
280     throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)");
281 
282   int nc = params[0].size();
283   result.setSize(nc);
284 
285   for (int i=0; i<nc; ++i) {
286 
287     if ( ! params[0][i].hasMember(METHODNAME) ||
288          ! params[0][i].hasMember(PARAMS)) {
289       result[i][FAULTCODE] = -1;
290       result[i][FAULTSTRING] = SYSTEM_MULTICALL +
291               ": Invalid argument (expected a struct with members methodName and params)";
292       continue;
293     }
294 
295     const std::string& methodName = params[0][i][METHODNAME];
296     XmlRpcValue& methodParams = params[0][i][PARAMS];
297 
298     XmlRpcValue resultValue;
299     resultValue.setSize(1);
300     try {
301       if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
302            ! executeMulticall(methodName, params, resultValue[0]))
303       {
304         result[i][FAULTCODE] = -1;
305         result[i][FAULTSTRING] = methodName + ": unknown method name";
306       }
307       else
308         result[i] = resultValue;
309 
310     } catch (const XmlRpcException& fault) {
311         result[i][FAULTCODE] = fault.getCode();
312         result[i][FAULTSTRING] = fault.getMessage();
313     }
314   }
315 
316   return true;
317 }
318 
319 
320 // Create a response from results xml
321 void
generateResponse(std::string const & resultXml)322 XmlRpcServerConnection::generateResponse(std::string const& resultXml)
323 {
324   const char RESPONSE_1[] =
325     "<?xml version=\"1.0\"?>\r\n"
326     "<methodResponse><params><param>\r\n\t";
327   const char RESPONSE_2[] =
328     "\r\n</param></params></methodResponse>\r\n";
329 
330   std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
331   std::string header = generateHeader(body);
332 
333   _response = header + body;
334   XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str());
335 }
336 
337 // Prepend http headers
338 std::string
generateHeader(std::string const & body)339 XmlRpcServerConnection::generateHeader(std::string const& body)
340 {
341   std::string header =
342     "HTTP/1.1 200 OK\r\n"
343     "Server: ";
344   header += XMLRPC_VERSION;
345   header += "\r\n"
346     "Content-Type: text/xml\r\n"
347     "Content-length: ";
348 
349   char buffLen[40];
350   sprintf(buffLen,"%d\r\n\r\n", (int)body.size());
351 
352   return header + buffLen;
353 }
354 
355 
356 void
generateFaultResponse(std::string const & errorMsg,int errorCode)357 XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode)
358 {
359   const char RESPONSE_1[] =
360     "<?xml version=\"1.0\"?>\r\n"
361     "<methodResponse><fault>\r\n\t";
362   const char RESPONSE_2[] =
363     "\r\n</fault></methodResponse>\r\n";
364 
365   XmlRpcValue faultStruct;
366   faultStruct[FAULTCODE] = errorCode;
367   faultStruct[FAULTSTRING] = errorMsg;
368   std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
369   std::string header = generateHeader(body);
370 
371   _response = header + body;
372 }
373 
374