PageRenderTime 38ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/newview/llxmlrpclistener.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 537 lines | 375 code | 34 blank | 128 comment | 47 complexity | 10dbbf001130c14a78a9c81de75ae9f6 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llxmlrpclistener.cpp
  3. * @author Nat Goodspeed
  4. * @date 2009-03-18
  5. * @brief Implementation for llxmlrpclistener.
  6. *
  7. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. // Precompiled header
  29. #include "llviewerprecompiledheaders.h"
  30. // associated header
  31. #include "llxmlrpclistener.h"
  32. // STL headers
  33. #include <map>
  34. #include <set>
  35. // std headers
  36. // external library headers
  37. #include <boost/scoped_ptr.hpp>
  38. #include <boost/range.hpp> // boost::begin(), boost::end()
  39. #include <xmlrpc-epi/xmlrpc.h>
  40. #include "curl/curl.h"
  41. // other Linden headers
  42. #include "llerror.h"
  43. #include "stringize.h"
  44. #include "llxmlrpctransaction.h"
  45. #include "llsecapi.h"
  46. #if LL_WINDOWS
  47. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  48. #endif
  49. template <typename STATUS>
  50. class StatusMapperBase
  51. {
  52. typedef std::map<STATUS, std::string> MapType;
  53. public:
  54. StatusMapperBase(const std::string& desc):
  55. mDesc(desc)
  56. {}
  57. std::string lookup(STATUS status) const
  58. {
  59. typename MapType::const_iterator found = mMap.find(status);
  60. if (found != mMap.end())
  61. {
  62. return found->second;
  63. }
  64. return STRINGIZE("<unknown " << mDesc << " " << status << ">");
  65. }
  66. protected:
  67. std::string mDesc;
  68. MapType mMap;
  69. };
  70. class StatusMapper: public StatusMapperBase<LLXMLRPCTransaction::EStatus>
  71. {
  72. public:
  73. StatusMapper(): StatusMapperBase<LLXMLRPCTransaction::EStatus>("Status")
  74. {
  75. mMap[LLXMLRPCTransaction::StatusNotStarted] = "NotStarted";
  76. mMap[LLXMLRPCTransaction::StatusStarted] = "Started";
  77. mMap[LLXMLRPCTransaction::StatusDownloading] = "Downloading";
  78. mMap[LLXMLRPCTransaction::StatusComplete] = "Complete";
  79. mMap[LLXMLRPCTransaction::StatusCURLError] = "CURLError";
  80. mMap[LLXMLRPCTransaction::StatusXMLRPCError] = "XMLRPCError";
  81. mMap[LLXMLRPCTransaction::StatusOtherError] = "OtherError";
  82. }
  83. };
  84. static const StatusMapper sStatusMapper;
  85. class CURLcodeMapper: public StatusMapperBase<CURLcode>
  86. {
  87. public:
  88. CURLcodeMapper(): StatusMapperBase<CURLcode>("CURLcode")
  89. {
  90. // from curl.h
  91. // skip the "CURLE_" prefix for each of these strings
  92. #define def(sym) (mMap[sym] = #sym + 6)
  93. def(CURLE_OK);
  94. def(CURLE_UNSUPPORTED_PROTOCOL); /* 1 */
  95. def(CURLE_FAILED_INIT); /* 2 */
  96. def(CURLE_URL_MALFORMAT); /* 3 */
  97. def(CURLE_URL_MALFORMAT_USER); /* 4 - NOT USED */
  98. def(CURLE_COULDNT_RESOLVE_PROXY); /* 5 */
  99. def(CURLE_COULDNT_RESOLVE_HOST); /* 6 */
  100. def(CURLE_COULDNT_CONNECT); /* 7 */
  101. def(CURLE_FTP_WEIRD_SERVER_REPLY); /* 8 */
  102. def(CURLE_FTP_ACCESS_DENIED); /* 9 a service was denied by the FTP server
  103. due to lack of access - when login fails
  104. this is not returned. */
  105. def(CURLE_FTP_USER_PASSWORD_INCORRECT); /* 10 - NOT USED */
  106. def(CURLE_FTP_WEIRD_PASS_REPLY); /* 11 */
  107. def(CURLE_FTP_WEIRD_USER_REPLY); /* 12 */
  108. def(CURLE_FTP_WEIRD_PASV_REPLY); /* 13 */
  109. def(CURLE_FTP_WEIRD_227_FORMAT); /* 14 */
  110. def(CURLE_FTP_CANT_GET_HOST); /* 15 */
  111. def(CURLE_FTP_CANT_RECONNECT); /* 16 */
  112. def(CURLE_FTP_COULDNT_SET_BINARY); /* 17 */
  113. def(CURLE_PARTIAL_FILE); /* 18 */
  114. def(CURLE_FTP_COULDNT_RETR_FILE); /* 19 */
  115. def(CURLE_FTP_WRITE_ERROR); /* 20 */
  116. def(CURLE_FTP_QUOTE_ERROR); /* 21 */
  117. def(CURLE_HTTP_RETURNED_ERROR); /* 22 */
  118. def(CURLE_WRITE_ERROR); /* 23 */
  119. def(CURLE_MALFORMAT_USER); /* 24 - NOT USED */
  120. def(CURLE_UPLOAD_FAILED); /* 25 - failed upload "command" */
  121. def(CURLE_READ_ERROR); /* 26 - could open/read from file */
  122. def(CURLE_OUT_OF_MEMORY); /* 27 */
  123. /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
  124. instead of a memory allocation error if CURL_DOES_CONVERSIONS
  125. is defined
  126. */
  127. def(CURLE_OPERATION_TIMEOUTED); /* 28 - the timeout time was reached */
  128. def(CURLE_FTP_COULDNT_SET_ASCII); /* 29 - TYPE A failed */
  129. def(CURLE_FTP_PORT_FAILED); /* 30 - FTP PORT operation failed */
  130. def(CURLE_FTP_COULDNT_USE_REST); /* 31 - the REST command failed */
  131. def(CURLE_FTP_COULDNT_GET_SIZE); /* 32 - the SIZE command failed */
  132. def(CURLE_HTTP_RANGE_ERROR); /* 33 - RANGE "command" didn't work */
  133. def(CURLE_HTTP_POST_ERROR); /* 34 */
  134. def(CURLE_SSL_CONNECT_ERROR); /* 35 - wrong when connecting with SSL */
  135. def(CURLE_BAD_DOWNLOAD_RESUME); /* 36 - couldn't resume download */
  136. def(CURLE_FILE_COULDNT_READ_FILE); /* 37 */
  137. def(CURLE_LDAP_CANNOT_BIND); /* 38 */
  138. def(CURLE_LDAP_SEARCH_FAILED); /* 39 */
  139. def(CURLE_LIBRARY_NOT_FOUND); /* 40 */
  140. def(CURLE_FUNCTION_NOT_FOUND); /* 41 */
  141. def(CURLE_ABORTED_BY_CALLBACK); /* 42 */
  142. def(CURLE_BAD_FUNCTION_ARGUMENT); /* 43 */
  143. def(CURLE_BAD_CALLING_ORDER); /* 44 - NOT USED */
  144. def(CURLE_INTERFACE_FAILED); /* 45 - CURLOPT_INTERFACE failed */
  145. def(CURLE_BAD_PASSWORD_ENTERED); /* 46 - NOT USED */
  146. def(CURLE_TOO_MANY_REDIRECTS ); /* 47 - catch endless re-direct loops */
  147. def(CURLE_UNKNOWN_TELNET_OPTION); /* 48 - User specified an unknown option */
  148. def(CURLE_TELNET_OPTION_SYNTAX ); /* 49 - Malformed telnet option */
  149. def(CURLE_OBSOLETE); /* 50 - NOT USED */
  150. def(CURLE_SSL_PEER_CERTIFICATE); /* 51 - peer's certificate wasn't ok */
  151. def(CURLE_GOT_NOTHING); /* 52 - when this is a specific error */
  152. def(CURLE_SSL_ENGINE_NOTFOUND); /* 53 - SSL crypto engine not found */
  153. def(CURLE_SSL_ENGINE_SETFAILED); /* 54 - can not set SSL crypto engine as
  154. default */
  155. def(CURLE_SEND_ERROR); /* 55 - failed sending network data */
  156. def(CURLE_RECV_ERROR); /* 56 - failure in receiving network data */
  157. def(CURLE_SHARE_IN_USE); /* 57 - share is in use */
  158. def(CURLE_SSL_CERTPROBLEM); /* 58 - problem with the local certificate */
  159. def(CURLE_SSL_CIPHER); /* 59 - couldn't use specified cipher */
  160. def(CURLE_SSL_CACERT); /* 60 - problem with the CA cert (path?) */
  161. def(CURLE_BAD_CONTENT_ENCODING); /* 61 - Unrecognized transfer encoding */
  162. def(CURLE_LDAP_INVALID_URL); /* 62 - Invalid LDAP URL */
  163. def(CURLE_FILESIZE_EXCEEDED); /* 63 - Maximum file size exceeded */
  164. def(CURLE_FTP_SSL_FAILED); /* 64 - Requested FTP SSL level failed */
  165. def(CURLE_SEND_FAIL_REWIND); /* 65 - Sending the data requires a rewind
  166. that failed */
  167. def(CURLE_SSL_ENGINE_INITFAILED); /* 66 - failed to initialise ENGINE */
  168. def(CURLE_LOGIN_DENIED); /* 67 - user); password or similar was not
  169. accepted and we failed to login */
  170. def(CURLE_TFTP_NOTFOUND); /* 68 - file not found on server */
  171. def(CURLE_TFTP_PERM); /* 69 - permission problem on server */
  172. def(CURLE_TFTP_DISKFULL); /* 70 - out of disk space on server */
  173. def(CURLE_TFTP_ILLEGAL); /* 71 - Illegal TFTP operation */
  174. def(CURLE_TFTP_UNKNOWNID); /* 72 - Unknown transfer ID */
  175. def(CURLE_TFTP_EXISTS); /* 73 - File already exists */
  176. def(CURLE_TFTP_NOSUCHUSER); /* 74 - No such user */
  177. def(CURLE_CONV_FAILED); /* 75 - conversion failed */
  178. def(CURLE_CONV_REQD); /* 76 - caller must register conversion
  179. callbacks using curl_easy_setopt options
  180. CURLOPT_CONV_FROM_NETWORK_FUNCTION);
  181. CURLOPT_CONV_TO_NETWORK_FUNCTION); and
  182. CURLOPT_CONV_FROM_UTF8_FUNCTION */
  183. def(CURLE_SSL_CACERT_BADFILE); /* 77 - could not load CACERT file); missing
  184. or wrong format */
  185. def(CURLE_REMOTE_FILE_NOT_FOUND); /* 78 - remote file not found */
  186. def(CURLE_SSH); /* 79 - error from the SSH layer); somewhat
  187. generic so the error message will be of
  188. interest when this has happened */
  189. def(CURLE_SSL_SHUTDOWN_FAILED); /* 80 - Failed to shut down the SSL
  190. connection */
  191. #undef def
  192. }
  193. };
  194. static const CURLcodeMapper sCURLcodeMapper;
  195. LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname):
  196. mBoundListener(LLEventPumps::instance().
  197. obtain(pumpname).
  198. listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1)))
  199. {
  200. }
  201. /**
  202. * Capture an outstanding LLXMLRPCTransaction and poll it periodically until
  203. * done.
  204. *
  205. * The sequence is:
  206. * # Instantiate Poller, which instantiates, populates and initiates an
  207. * LLXMLRPCTransaction. Poller self-registers on the LLEventPump named
  208. * "mainloop".
  209. * # "mainloop" is conventionally pumped once per frame. On each such call,
  210. * Poller checks its LLXMLRPCTransaction for completion.
  211. * # When the LLXMLRPCTransaction completes, Poller collects results (if any)
  212. * and sends notification.
  213. * # The tricky part: Poller frees itself (and thus its LLXMLRPCTransaction)
  214. * when done. The only external reference to it is the connection to the
  215. * "mainloop" LLEventPump.
  216. */
  217. class Poller
  218. {
  219. public:
  220. /// Validate the passed request for required fields, then use it to
  221. /// populate an XMLRPC_REQUEST and an associated LLXMLRPCTransaction. Send
  222. /// the request.
  223. Poller(const LLSD& command):
  224. mReqID(command),
  225. mUri(command["uri"]),
  226. mMethod(command["method"]),
  227. mReplyPump(command["reply"])
  228. {
  229. // LL_ERRS if any of these are missing
  230. const char* required[] = { "uri", "method", "reply" };
  231. // optional: "options" (array of string)
  232. // Validate the request
  233. std::set<std::string> missing;
  234. for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri)
  235. {
  236. // If the command does not contain this required entry, add it to 'missing'.
  237. if (! command.has(*ri))
  238. {
  239. missing.insert(*ri);
  240. }
  241. }
  242. if (! missing.empty())
  243. {
  244. LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: ";
  245. const char* separator = "";
  246. for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end());
  247. mi != mend; ++mi)
  248. {
  249. LL_CONT << separator << *mi;
  250. separator = ", ";
  251. }
  252. LL_CONT << LL_ENDL;
  253. }
  254. // Build the XMLRPC request.
  255. XMLRPC_REQUEST request = XMLRPC_RequestNew();
  256. XMLRPC_RequestSetMethodName(request, mMethod.c_str());
  257. XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
  258. XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
  259. LLSD params(command["params"]);
  260. if (params.isMap())
  261. {
  262. for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap());
  263. pi != pend; ++pi)
  264. {
  265. std::string name(pi->first);
  266. LLSD param(pi->second);
  267. if (param.isString())
  268. {
  269. XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0);
  270. }
  271. else if (param.isInteger() || param.isBoolean())
  272. {
  273. XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger());
  274. }
  275. else if (param.isReal())
  276. {
  277. XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal());
  278. }
  279. else
  280. {
  281. LL_ERRS("LLXMLRPCListener") << mMethod << " request param "
  282. << name << " has unknown type: " << param << LL_ENDL;
  283. }
  284. }
  285. }
  286. LLSD options(command["options"]);
  287. if (options.isArray())
  288. {
  289. XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array);
  290. for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray());
  291. oi != oend; ++oi)
  292. {
  293. XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0);
  294. }
  295. XMLRPC_AddValueToVector(xparams, xoptions);
  296. }
  297. XMLRPC_RequestSetData(request, xparams);
  298. mTransaction.reset(new LLXMLRPCTransaction(mUri, request));
  299. mPreviousStatus = mTransaction->status(NULL);
  300. // Free the XMLRPC_REQUEST object and the attached data values.
  301. XMLRPC_RequestFree(request, 1);
  302. // Now ensure that we get regular callbacks to poll for completion.
  303. mBoundListener =
  304. LLEventPumps::instance().
  305. obtain("mainloop").
  306. listen(LLEventPump::inventName(), boost::bind(&Poller::poll, this, _1));
  307. LL_INFOS("LLXMLRPCListener") << mMethod << " request sent to " << mUri << LL_ENDL;
  308. }
  309. /// called by "mainloop" LLEventPump
  310. bool poll(const LLSD&)
  311. {
  312. bool done = mTransaction->process();
  313. CURLcode curlcode;
  314. LLXMLRPCTransaction::EStatus status;
  315. {
  316. // LLXMLRPCTransaction::status() is defined to accept int* rather
  317. // than CURLcode*. I don't feel the urge to fix the signature, but
  318. // we want a CURLcode rather than an int. So fetch it as a local
  319. // int, but then assign to a CURLcode for the remainder of this
  320. // method.
  321. int curlint;
  322. status = mTransaction->status(&curlint);
  323. curlcode = CURLcode(curlint);
  324. }
  325. LLSD data(mReqID.makeResponse());
  326. data["status"] = sStatusMapper.lookup(status);
  327. data["errorcode"] = sCURLcodeMapper.lookup(curlcode);
  328. data["error"] = "";
  329. data["transfer_rate"] = 0.0;
  330. LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump));
  331. if (! done)
  332. {
  333. // Not done yet, carry on.
  334. if (status == LLXMLRPCTransaction::StatusDownloading
  335. && status != mPreviousStatus)
  336. {
  337. // If a response has been received, send the
  338. // 'downloading' status if it hasn't been sent.
  339. replyPump.post(data);
  340. }
  341. mPreviousStatus = status;
  342. return false;
  343. }
  344. // Here the transaction is complete. Check status.
  345. data["error"] = mTransaction->statusMessage();
  346. data["transfer_rate"] = mTransaction->transferRate();
  347. LL_INFOS("LLXMLRPCListener") << mMethod << " result from " << mUri << ": status "
  348. << data["status"].asString() << ", errorcode "
  349. << data["errorcode"].asString()
  350. << " (" << data["error"].asString() << ")"
  351. << LL_ENDL;
  352. switch (curlcode)
  353. {
  354. case CURLE_SSL_PEER_CERTIFICATE:
  355. case CURLE_SSL_CACERT:
  356. {
  357. LLPointer<LLCertificate> error_cert(mTransaction->getErrorCert());
  358. if(error_cert)
  359. {
  360. data["certificate"] = error_cert->getPem();
  361. }
  362. break;
  363. }
  364. default:
  365. break;
  366. }
  367. // values of 'curlcode':
  368. // CURLE_COULDNT_RESOLVE_HOST,
  369. // CURLE_SSL_PEER_CERTIFICATE,
  370. // CURLE_SSL_CACERT,
  371. // CURLE_SSL_CONNECT_ERROR.
  372. // Given 'message', need we care?
  373. if (status == LLXMLRPCTransaction::StatusComplete)
  374. {
  375. // Success! Parse data.
  376. std::string status_string(data["status"]);
  377. data["responses"] = parseResponse(status_string);
  378. data["status"] = status_string;
  379. }
  380. // whether successful or not, send reply on requested LLEventPump
  381. replyPump.post(data);
  382. // Because mTransaction is a boost::scoped_ptr, deleting this object
  383. // frees our LLXMLRPCTransaction object.
  384. // Because mBoundListener is an LLTempBoundListener, deleting this
  385. // object disconnects it from "mainloop".
  386. // *** MUST BE LAST ***
  387. delete this;
  388. return false;
  389. }
  390. private:
  391. /// Derived from LLUserAuth::parseResponse() and parseOptionInto()
  392. LLSD parseResponse(std::string& status_string)
  393. {
  394. // Extract every member into data["responses"] (a map of string
  395. // values).
  396. XMLRPC_REQUEST response = mTransaction->response();
  397. if (! response)
  398. {
  399. LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL;
  400. return LLSD();
  401. }
  402. XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
  403. if (! param)
  404. {
  405. LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL;
  406. return LLSD();
  407. }
  408. // Now, parse everything
  409. return parseValues(status_string, "", param);
  410. }
  411. /**
  412. * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
  413. * @param key_pfx Used to describe a given key in log messages. At top
  414. * level, pass "". When parsing an options array, pass the top-level key
  415. * name of the array plus the index of the array entry; to this we'll
  416. * append the subkey of interest.
  417. * @param param XMLRPC_VALUE iterator. At top level, pass
  418. * XMLRPC_RequestGetData(XMLRPC_REQUEST).
  419. */
  420. LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
  421. {
  422. LLSD responses;
  423. for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
  424. current = XMLRPC_VectorNext(param))
  425. {
  426. std::string key(XMLRPC_GetValueID(current));
  427. LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
  428. XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current);
  429. if (xmlrpc_type_string == type)
  430. {
  431. LLSD::String val(XMLRPC_GetValueString(current));
  432. LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
  433. responses.insert(key, val);
  434. }
  435. else if (xmlrpc_type_int == type)
  436. {
  437. LLSD::Integer val(XMLRPC_GetValueInt(current));
  438. LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
  439. responses.insert(key, val);
  440. }
  441. else if (xmlrpc_type_double == type)
  442. {
  443. LLSD::Real val(XMLRPC_GetValueDouble(current));
  444. LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
  445. responses.insert(key, val);
  446. }
  447. else if (xmlrpc_type_array == type)
  448. {
  449. // We expect this to be an array of submaps. Walk the array,
  450. // recursively parsing each submap and collecting them.
  451. LLSD array;
  452. int i = 0; // for descriptive purposes
  453. for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row;
  454. row = XMLRPC_VectorNext(current), ++i)
  455. {
  456. // Recursive call. For the lower-level key_pfx, if 'key'
  457. // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
  458. // nested call, a subkey "bar" will then be logged as
  459. // "foo[0]:bar", and so forth.
  460. // Parse the scalar subkey/value pairs from this array
  461. // entry into a temp submap. Collect such submaps in 'array'.
  462. array.append(parseValues(status_string,
  463. STRINGIZE(key_pfx << key << '[' << i << "]:"),
  464. row));
  465. }
  466. // Having collected an 'array' of 'submap's, insert that whole
  467. // 'array' as the value of this 'key'.
  468. responses.insert(key, array);
  469. }
  470. else if (xmlrpc_type_struct == type)
  471. {
  472. LLSD submap = parseValues(status_string,
  473. STRINGIZE(key_pfx << key << ':'),
  474. current);
  475. responses.insert(key, submap);
  476. }
  477. else
  478. {
  479. // whoops - unrecognized type
  480. LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key "
  481. << key_pfx << key << LL_ENDL;
  482. responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>'));
  483. status_string = "BadType";
  484. }
  485. }
  486. return responses;
  487. }
  488. const LLReqID mReqID;
  489. const std::string mUri;
  490. const std::string mMethod;
  491. const std::string mReplyPump;
  492. LLTempBoundListener mBoundListener;
  493. boost::scoped_ptr<LLXMLRPCTransaction> mTransaction;
  494. LLXMLRPCTransaction::EStatus mPreviousStatus; // To detect state changes.
  495. };
  496. bool LLXMLRPCListener::process(const LLSD& command)
  497. {
  498. // Allocate a new heap Poller, but do not save a pointer to it. Poller
  499. // will check its own status and free itself on completion of the request.
  500. (new Poller(command));
  501. // conventional event listener return
  502. return false;
  503. }