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
11using namespace XmlRpc;
12
13// Static data
14const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
15const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
16const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
17const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
18const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
19
20const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
21const std::string XmlRpcServerConnection::METHODNAME = "methodName";
22const std::string XmlRpcServerConnection::PARAMS = "params";
23
24const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
25const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
26
27
28
29// The server delegates handling client requests to a serverConnection object.
30XmlRpcServerConnection::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
40XmlRpcServerConnection::~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.
50unsigned
51XmlRpcServerConnection::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
67bool
68XmlRpcServerConnection::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
144bool
145XmlRpcServerConnection::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
175bool
176XmlRpcServerConnection::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
206void
207XmlRpcServerConnection::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.
230std::string
231XmlRpcServerConnection::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.
254bool
255XmlRpcServerConnection::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.
272bool
273XmlRpcServerConnection::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
321void
322XmlRpcServerConnection::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
338std::string
339XmlRpcServerConnection::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
356void
357XmlRpcServerConnection::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