00001
00007 #include "XmlRpcServer.h"
00008 #include "XmlRpcServerConnection.h"
00009 #include "XmlRpcServerMethod.h"
00010 #include "XmlRpcSocket.h"
00011 #include "XmlRpcUtil.h"
00012 #include "XmlRpcException.h"
00013 #include "XmlRpc.h"
00014
00015
00016 using namespace XmlRpc;
00017
00018
00019
00020 const char XmlRpcServer::METHODNAME_TAG[] = "<methodName>";
00021 const char XmlRpcServer::PARAMS_TAG[] = "<params>";
00022 const char XmlRpcServer::PARAMS_ETAG[] = "</params>";
00023 const char XmlRpcServer::PARAM_TAG[] = "<param>";
00024 const char XmlRpcServer::PARAM_ETAG[] = "</param>";
00025
00026 const std::string XmlRpcServer::METHODNAME = "methodName";
00027 const std::string XmlRpcServer::PARAMS = "params";
00028
00029 const std::string XmlRpcServer::FAULTCODE = "faultCode";
00030 const std::string XmlRpcServer::FAULTSTRING = "faultString";
00031
00032
00033
00034 XmlRpcServer::XmlRpcServer()
00035 {
00036 _introspectionEnabled = false;
00037 _listMethods = 0;
00038 _methodHelp = 0;
00039 }
00040
00041
00042 XmlRpcServer::~XmlRpcServer()
00043 {
00044 this->shutdown();
00045 _methods.clear();
00046 delete _listMethods;
00047 delete _methodHelp;
00048 }
00049
00050
00051
00052 void
00053 XmlRpcServer::addMethod(XmlRpcServerMethod* method)
00054 {
00055 _methods[method->getName()] = method;
00056 }
00057
00058
00059 void
00060 XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
00061 {
00062 MethodMap::iterator i = _methods.find(method->getName());
00063 if (i != _methods.end())
00064 _methods.erase(i);
00065 }
00066
00067
00068 void
00069 XmlRpcServer::removeMethod(const std::string& methodName)
00070 {
00071 MethodMap::iterator i = _methods.find(methodName);
00072 if (i != _methods.end())
00073 _methods.erase(i);
00074 }
00075
00076
00077
00078 XmlRpcServerMethod*
00079 XmlRpcServer::findMethod(const std::string& name) const
00080 {
00081 MethodMap::const_iterator i = _methods.find(name);
00082 if (i == _methods.end())
00083 return 0;
00084 return i->second;
00085 }
00086
00087
00088
00089
00090 bool
00091 XmlRpcServer::bindAndListen(int port, int backlog )
00092 {
00093 int fd = XmlRpcSocket::getSocket();
00094 if (fd < 0)
00095 {
00096 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
00097 return false;
00098 }
00099
00100 this->setfd(fd);
00101
00102
00103 if ( ! XmlRpcSocket::setNonBlocking(fd))
00104 {
00105 this->close();
00106 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00107 return false;
00108 }
00109
00110
00111 if ( ! XmlRpcSocket::setReuseAddr(fd))
00112 {
00113 this->close();
00114 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
00115 return false;
00116 }
00117
00118
00119 if ( ! XmlRpcSocket::bind(fd, port))
00120 {
00121 this->close();
00122 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
00123 return false;
00124 }
00125
00126
00127 if ( ! XmlRpcSocket::listen(fd, backlog))
00128 {
00129 this->close();
00130 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00131 return false;
00132 }
00133
00134 XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd);
00135
00136
00137 _disp.addSource(this, XmlRpcDispatch::ReadableEvent);
00138
00139 return true;
00140 }
00141
00142
00143
00144 int
00145 XmlRpcServer::getPort(void) const
00146 {
00147 return XmlRpcSocket::getPort(getfd());
00148 }
00149
00150
00151
00152
00153 void
00154 XmlRpcServer::work(double msTime)
00155 {
00156 XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
00157 _disp.work(msTime);
00158 }
00159
00160
00161
00162
00163
00164 unsigned
00165 XmlRpcServer::handleEvent(unsigned mask)
00166 {
00167 acceptConnection();
00168 return XmlRpcDispatch::ReadableEvent;
00169 }
00170
00171
00172
00173
00174 void
00175 XmlRpcServer::acceptConnection()
00176 {
00177 int s = XmlRpcSocket::accept(this->getfd());
00178 XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
00179 if (s < 0)
00180 {
00181
00182 XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
00183 }
00184 else if ( ! XmlRpcSocket::setNonBlocking(s))
00185 {
00186 XmlRpcSocket::close(s);
00187 XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00188 }
00189 else
00190 {
00191 XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
00192 XmlRpcServerConnection* c = this->createConnection(s);
00193 if (c) this->dispatchConnection(c);
00194 }
00195 }
00196
00197
00198
00199 XmlRpcServerConnection*
00200 XmlRpcServer::createConnection(int s)
00201 {
00202
00203 return new XmlRpcServerConnection(s, this, true);
00204 }
00205
00206
00207
00208 void
00209 XmlRpcServer::dispatchConnection(XmlRpcServerConnection* sc)
00210 {
00211 _disp.addSource(sc, XmlRpcDispatch::ReadableEvent);
00212 }
00213
00214
00215
00216 void
00217 XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
00218 {
00219 _disp.removeSource(sc);
00220 }
00221
00222
00223
00224 void
00225 XmlRpcServer::exit()
00226 {
00227 _disp.exit();
00228 }
00229
00230
00231
00232 void
00233 XmlRpcServer::shutdown()
00234 {
00235
00236 _disp.clear();
00237 }
00238
00239
00240
00241 static const std::string LIST_METHODS("system.listMethods");
00242 static const std::string METHOD_HELP("system.methodHelp");
00243 static const std::string MULTICALL("system.multicall");
00244
00245
00246
00247 class ListMethods : public XmlRpcServerMethod
00248 {
00249 public:
00250 ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
00251
00252 void execute(XmlRpcValue& params, XmlRpcValue& result)
00253 {
00254 _server->listMethods(result);
00255 }
00256
00257 std::string help() { return std::string("List all methods available on a server as an array of strings"); }
00258 };
00259
00260
00261
00262 class MethodHelp : public XmlRpcServerMethod
00263 {
00264 public:
00265 MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
00266
00267 void execute(XmlRpcValue& params, XmlRpcValue& result)
00268 {
00269 if (params[0].getType() != XmlRpcValue::TypeString)
00270 throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
00271
00272 XmlRpcServerMethod* m = _server->findMethod(params[0]);
00273 if ( ! m)
00274 throw XmlRpcException(METHOD_HELP + ": Unknown method name");
00275
00276 result = m->help();
00277 }
00278
00279 std::string help() { return std::string("Retrieve the help string for a named method"); }
00280 };
00281
00282
00283
00284 void
00285 XmlRpcServer::enableIntrospection(bool enabled)
00286 {
00287 if (_introspectionEnabled == enabled)
00288 return;
00289
00290 _introspectionEnabled = enabled;
00291
00292 if (enabled)
00293 {
00294 if ( ! _listMethods)
00295 {
00296 _listMethods = new ListMethods(this);
00297 _methodHelp = new MethodHelp(this);
00298 } else {
00299 addMethod(_listMethods);
00300 addMethod(_methodHelp);
00301 }
00302 }
00303 else
00304 {
00305 removeMethod(LIST_METHODS);
00306 removeMethod(METHOD_HELP);
00307 }
00308 }
00309
00310
00311 void
00312 XmlRpcServer::listMethods(XmlRpcValue& result)
00313 {
00314 int i = 0;
00315 result.setSize(_methods.size()+1);
00316 for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
00317 result[i++] = it->first;
00318
00319
00320 result[i] = MULTICALL;
00321 }
00322
00323
00324
00325
00326 std::string
00327 XmlRpcServer::executeRequest(std::string const& request)
00328 {
00329 XmlRpcValue params, resultValue;
00330 std::string methodName = parseRequest(request, params);
00331 XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: server calling method '%s'",
00332 methodName.c_str());
00333
00334 std::string response;
00335 try {
00336
00337 if ( ! executeMethod(methodName, params, resultValue) &&
00338 ! executeMulticall(methodName, params, resultValue))
00339 response = generateFaultResponse(methodName + ": unknown method name");
00340 else
00341 response = generateResponse(resultValue.toXml());
00342
00343 } catch (const XmlRpcException& fault) {
00344 XmlRpcUtil::log(2, "XmlRpcServer::executeRequest: fault %s.",
00345 fault.getMessage().c_str());
00346 response = generateFaultResponse(fault.getMessage(), fault.getCode());
00347 }
00348
00349 return response;
00350 }
00351
00352
00353 std::string
00354 XmlRpcServer::parseRequest(std::string const& request, XmlRpcValue& params)
00355 {
00356 int offset = 0;
00357
00358 std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, request, &offset);
00359
00360 if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, request, &offset))
00361 {
00362 int nArgs = 0;
00363 while (XmlRpcUtil::nextTagIs(PARAM_TAG, request, &offset)) {
00364 params[nArgs++] = XmlRpcValue(request, &offset);
00365 (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, request, &offset);
00366 }
00367
00368 (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, request, &offset);
00369 }
00370
00371 return methodName;
00372 }
00373
00374
00375 bool
00376 XmlRpcServer::executeMethod(const std::string& methodName,
00377 XmlRpcValue& params,
00378 XmlRpcValue& result)
00379 {
00380 XmlRpcServerMethod* method = findMethod(methodName);
00381
00382 if ( ! method) return false;
00383
00384 method->execute(params, result);
00385
00386
00387 if ( ! result.isValid())
00388 result = std::string();
00389
00390 return true;
00391 }
00392
00393
00394 bool
00395 XmlRpcServer::executeMulticall(const std::string& methodName,
00396 XmlRpcValue& params,
00397 XmlRpcValue& result)
00398 {
00399 if (methodName != MULTICALL) return false;
00400
00401
00402 if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
00403 throw XmlRpcException(MULTICALL + ": Invalid argument (expected an array)");
00404
00405 int nc = params[0].size();
00406 result.setSize(nc);
00407
00408 for (int i=0; i<nc; ++i) {
00409
00410 if ( ! params[0][i].hasMember(METHODNAME) ||
00411 ! params[0][i].hasMember(PARAMS)) {
00412 result[i][FAULTCODE] = -1;
00413 result[i][FAULTSTRING] = MULTICALL +
00414 ": Invalid argument (expected a struct with members methodName and params)";
00415 continue;
00416 }
00417
00418 const std::string& methodName = params[0][i][METHODNAME];
00419 XmlRpcValue& methodParams = params[0][i][PARAMS];
00420
00421 XmlRpcValue resultValue;
00422 resultValue.setSize(1);
00423 try {
00424 if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
00425 ! executeMulticall(methodName, params, resultValue[0]))
00426 {
00427 result[i][FAULTCODE] = -1;
00428 result[i][FAULTSTRING] = methodName + ": unknown method name";
00429 }
00430 else
00431 result[i] = resultValue;
00432
00433 } catch (const XmlRpcException& fault) {
00434 result[i][FAULTCODE] = fault.getCode();
00435 result[i][FAULTSTRING] = fault.getMessage();
00436 }
00437 }
00438
00439 return true;
00440 }
00441
00442
00443
00444 std::string
00445 XmlRpcServer::generateResponse(std::string const& resultXml)
00446 {
00447 const char RESPONSE_1[] =
00448 "<?xml version=\"1.0\"?>\r\n"
00449 "<methodResponse><params><param>\r\n\t";
00450 const char RESPONSE_2[] =
00451 "\r\n</param></params></methodResponse>\r\n";
00452
00453 std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
00454 std::string header = generateHeader(body);
00455 std::string response = header + body;
00456
00457 XmlRpcUtil::log(5, "XmlRpcServer::generateResponse:\n%s\n", response.c_str());
00458 return response;
00459 }
00460
00461
00462
00463 std::string
00464 XmlRpcServer::generateHeader(std::string const& body)
00465 {
00466 std::string header =
00467 "HTTP/1.1 200 OK\r\n"
00468 "Server: ";
00469 header += XMLRPC_VERSION;
00470 header += "\r\n"
00471 "Content-Type: text/xml\r\n"
00472 "Content-length: ";
00473
00474 char buffLen[40];
00475 sprintf(buffLen,"%d\r\n\r\n", body.size());
00476
00477 return header + buffLen;
00478 }
00479
00480
00481 std::string
00482 XmlRpcServer::generateFaultResponse(std::string const& errorMsg, int errorCode)
00483 {
00484 const char RESPONSE_1[] =
00485 "<?xml version=\"1.0\"?>\r\n"
00486 "<methodResponse><fault>\r\n\t";
00487 const char RESPONSE_2[] =
00488 "\r\n</fault></methodResponse>\r\n";
00489
00490 XmlRpcValue faultStruct;
00491 faultStruct[FAULTCODE] = errorCode;
00492 faultStruct[FAULTSTRING] = errorMsg;
00493 std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
00494 std::string header = generateHeader(body);
00495
00496 return header + body;
00497 }
00498