llxmlrpctransaction.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /**
  2. * @file llxmlrpctransaction.cpp
  3. * @brief LLXMLRPCTransaction and related class implementations
  4. *
  5. * $LicenseInfo:firstyear=2006&license=viewergpl$
  6. *
  7. * Copyright (c) 2006-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include <memory>
  34. #include "llxmlrpctransaction.h"
  35. #include "llcorebufferarray.h"
  36. #include "llcorehttphandler.h"
  37. #include "llcorehttprequest.h"
  38. #include "llcorehttpresponse.h"
  39. #include "llhttpconstants.h"
  40. #include "llsdutil.h" // For ll_pretty_print_sd()
  41. #include "llxmlnode.h"
  42. ///////////////////////////////////////////////////////////////////////////////
  43. // LLXMLRPCTransaction::Handler sub-class
  44. ///////////////////////////////////////////////////////////////////////////////
  45. class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler
  46. {
  47. protected:
  48. LOG_CLASS(LLXMLRPCTransaction::Handler);
  49. public:
  50. typedef std::shared_ptr<LLXMLRPCTransaction::Handler> ptr_t;
  51. Handler(LLCore::HttpRequest::ptr_t& request,
  52. LLXMLRPCTransaction::Impl* impl)
  53. : mImpl(impl),
  54. mRequest(request)
  55. {
  56. }
  57. virtual ~Handler() = default;
  58. virtual void onCompleted(LLCore::HttpHandle handle,
  59. LLCore::HttpResponse* responsep);
  60. private:
  61. LLXMLRPCTransaction::Impl* mImpl;
  62. LLCore::HttpRequest::ptr_t mRequest;
  63. };
  64. ///////////////////////////////////////////////////////////////////////////////
  65. // LLXMLRPCTransaction::Impl sub-class
  66. ///////////////////////////////////////////////////////////////////////////////
  67. class LLXMLRPCTransaction::Impl
  68. {
  69. protected:
  70. LOG_CLASS(LLXMLRPCTransaction::Impl);
  71. public:
  72. typedef LLXMLRPCTransaction::EStatus EStatus;
  73. Impl(const std::string& uri, const std::string& method,
  74. const LLSD& params);
  75. bool process();
  76. void setStatus(EStatus code, const std::string& message = "",
  77. const std::string& uri = "");
  78. void setHttpStatus(const LLCore::HttpStatus& status);
  79. private:
  80. bool parseResponse(LLXMLNodePtr root);
  81. bool parseValue(LLSD& target, LLXMLNodePtr source);
  82. public:
  83. LLCore::HttpRequest::ptr_t mHttpRequest;
  84. LLCore::HttpHandle mPostH;
  85. Handler::ptr_t mHandler;
  86. EStatus mStatus;
  87. CURLcode mCurlCode;
  88. std::string mStatusMessage;
  89. std::string mStatusURI;
  90. std::string mURI;
  91. std::string mResponseText;
  92. LLSD mResponseData;
  93. bool mHasResponse;
  94. bool mResponseParsed;
  95. static std::string sSupportURL;
  96. static std::string sWebsiteURL;
  97. static std::string sServerIsDownMsg;
  98. static std::string sNotResolvingMsg;
  99. static std::string sNotVerifiedMsg;
  100. static std::string sConnectErrorMsg;
  101. static bool sVerifyCert;
  102. };
  103. ///////////////////////////////////////////////////////////////////////////////
  104. // LLXMLRPCTransaction::Handler sub-class method
  105. ///////////////////////////////////////////////////////////////////////////////
  106. void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle,
  107. LLCore::HttpResponse* responsep)
  108. {
  109. if (!responsep || !mImpl) return; // Paranioa
  110. LLCore::HttpStatus status = responsep->getStatus();
  111. if (!status)
  112. {
  113. if (status.toULong() != CURLE_SSL_PEER_CERTIFICATE &&
  114. status.toULong() != CURLE_SSL_CACERT)
  115. {
  116. // If we have a curl error that has not already been handled (a non
  117. // cert error), then generate the error message as appropriate
  118. mImpl->setHttpStatus(status);
  119. llwarns << "Error " << status.toHex() << ": " << status.toString()
  120. << " - Request URI: " << mImpl->mURI << llendl;
  121. }
  122. return;
  123. }
  124. // The contents of a buffer array are potentially non-contiguous, so we
  125. // will need to copy them into an contiguous block of memory for XMLRPC.
  126. LLCore::BufferArray* bodyp = responsep->getBody();
  127. if (!bodyp) // This *does* happen !
  128. {
  129. mImpl->mResponseText.clear();
  130. mImpl->mResponseData.clear();
  131. llwarns << "Empty response; request URI: " << mImpl->mURI << llendl;
  132. return;
  133. }
  134. size_t body_size = bodyp->size();
  135. mImpl->mResponseText.resize(body_size, '\0');
  136. bodyp->read(0, mImpl->mResponseText.data(), body_size);
  137. LL_DEBUGS("XmlRpc") << "XMLRPC response body: " << mImpl->mResponseText
  138. << LL_ENDL;
  139. // We do not do the parsing in the HTTP coroutine, since it could exhaust
  140. // the coroutine stack in extreme cases. Instead, we flag the data buffer
  141. // as ready, and let mImpl decode it in its process() method, on the main
  142. // coroutine. HB
  143. mImpl->mHasResponse = true;
  144. mImpl->setStatus(LLXMLRPCTransaction::StatusComplete);
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////
  147. // LLXMLRPCTransaction::Impl sub-class methods
  148. ///////////////////////////////////////////////////////////////////////////////
  149. // Static members
  150. std::string LLXMLRPCTransaction::Impl::sSupportURL;
  151. std::string LLXMLRPCTransaction::Impl::sWebsiteURL;
  152. std::string LLXMLRPCTransaction::Impl::sServerIsDownMsg;
  153. std::string LLXMLRPCTransaction::Impl::sNotResolvingMsg;
  154. std::string LLXMLRPCTransaction::Impl::sNotVerifiedMsg;
  155. std::string LLXMLRPCTransaction::Impl::sConnectErrorMsg;
  156. bool LLXMLRPCTransaction::Impl::sVerifyCert = true;
  157. LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
  158. const std::string& method,
  159. const LLSD& params)
  160. : mStatus(LLXMLRPCTransaction::StatusNotStarted),
  161. mURI(uri),
  162. mHasResponse(false),
  163. mResponseParsed(false)
  164. {
  165. if (!mHttpRequest)
  166. {
  167. mHttpRequest = DEFAULT_HTTP_REQUEST;
  168. }
  169. // LLRefCounted starts with a 1 ref, so do not add a ref in the smart
  170. // pointer
  171. LLCore::HttpOptions::ptr_t options = DEFAULT_HTTP_OPTIONS;
  172. // Be a little impatient about establishing connections.
  173. options->setTimeout(40L);
  174. options->setSSLVerifyPeer(sVerifyCert);
  175. options->setSSLVerifyHost(sVerifyCert);
  176. options->setDNSCacheTimeout(40);
  177. options->setRetries(3);
  178. // LLRefCounted starts with a 1 ref, so do not add a ref in the smart
  179. // pointer
  180. LLCore::HttpHeaders::ptr_t headers = DEFAULT_HTTP_HEADERS;
  181. headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML);
  182. LLCore::BufferArray::ptr_t body =
  183. LLCore::BufferArray::ptr_t(new LLCore::BufferArray());
  184. std::string request = "<?xml version=\"1.0\"?><methodCall><methodName>" +
  185. method + "</methodName><params><param>" +
  186. params.asXMLRPCValue() +
  187. "</param></params></methodCall>";
  188. body->append(request.c_str(), request.size());
  189. #if LL_DEBUG
  190. // Note: this shows password and MFA hashes, so only enabled for debug
  191. // builds. HB
  192. LL_DEBUGS("XmlRpc") << "XMLRPC request body:\n" << request << LL_ENDL;
  193. #endif
  194. mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler(mHttpRequest,
  195. this));
  196. mPostH = mHttpRequest->requestPost(LLCore::HttpRequest::DEFAULT_POLICY_ID,
  197. mURI, body.get(), options, headers,
  198. mHandler);
  199. }
  200. bool LLXMLRPCTransaction::Impl::parseResponse(LLXMLNodePtr root)
  201. {
  202. // We have already checked in LLXMLNode::parseBuffer() that root contains
  203. // exactly one child.
  204. if (!root->hasName("methodResponse"))
  205. {
  206. llwarns << "Invalid root element in XML response; request URI: "
  207. << mURI << llendl;
  208. return false;
  209. }
  210. LLXMLNodePtr first = root->getFirstChild();
  211. LLXMLNodePtr second = first->getFirstChild();
  212. if (first && !first->getNextSibling() && second &&
  213. !second->getNextSibling())
  214. {
  215. if (first->hasName("fault"))
  216. {
  217. LLSD fault;
  218. if (parseValue(fault, second) && fault.isMap() &&
  219. fault.has("faultCode") && fault.has("faultString"))
  220. {
  221. llwarns << "Request failed. faultCode: '"
  222. << fault.get("faultCode").asString()
  223. << "', faultString: '"
  224. << fault.get("faultString").asString()
  225. << "', request URI: " << mURI << llendl;
  226. return false;
  227. }
  228. }
  229. else if (first->hasName("params") &&
  230. second->hasName("param") && !second->getNextSibling())
  231. {
  232. LLXMLNodePtr third = second->getFirstChild();
  233. if (third && !third->getNextSibling() &&
  234. parseValue(mResponseData, third))
  235. {
  236. return true;
  237. }
  238. }
  239. }
  240. llwarns << "Invalid response format; request URI: " << mURI << llendl;
  241. return false;
  242. }
  243. bool LLXMLRPCTransaction::Impl::parseValue(LLSD& target, LLXMLNodePtr src)
  244. {
  245. bool success = src->fromXMLRPCValue(target);
  246. LL_DEBUGS("XmlRpc") << "XMLTreeNode parsing "
  247. << (success ? "succeeded" : "failed") << LL_ENDL;
  248. return success;
  249. }
  250. bool LLXMLRPCTransaction::Impl::process()
  251. {
  252. if (!mPostH || !mHttpRequest)
  253. {
  254. llwarns << "Transaction failed." << llendl;
  255. return true;
  256. }
  257. // Parse the response when we have one and it has not yet been parsed. HB
  258. if (mHasResponse && !mResponseParsed)
  259. {
  260. LLXMLNodePtr root;
  261. if (!LLXMLNode::parseBuffer(mResponseText.data(), mResponseText.size(),
  262. root, NULL))
  263. {
  264. llwarns << "XML response parsing failed; request URI: " << mURI
  265. << llendl;
  266. }
  267. else if (parseResponse(root))
  268. {
  269. LL_DEBUGS("XmlRpc") << "Parsed response LLSD:\n"
  270. << ll_pretty_print_sd(mResponseData)
  271. << LL_ENDL;
  272. }
  273. else
  274. {
  275. llwarns << "XMLRPC response parsing failed; request URI: " << mURI
  276. << llendl;
  277. }
  278. mResponseParsed = true;
  279. }
  280. switch (mStatus)
  281. {
  282. case LLXMLRPCTransaction::StatusComplete:
  283. case LLXMLRPCTransaction::StatusCURLError:
  284. case LLXMLRPCTransaction::StatusXMLRPCError:
  285. case LLXMLRPCTransaction::StatusOtherError:
  286. {
  287. return true;
  288. }
  289. case LLXMLRPCTransaction::StatusNotStarted:
  290. {
  291. setStatus(LLXMLRPCTransaction::StatusStarted);
  292. }
  293. default:
  294. break;
  295. }
  296. LLCore::HttpStatus status = mHttpRequest->update(0);
  297. if (!status)
  298. {
  299. llwarns << "Error (1): " << status.toString() << llendl;
  300. return false;
  301. }
  302. status = mHttpRequest->getStatus();
  303. if (!status)
  304. {
  305. llwarns << "Error (2): " << status.toString() << llendl;
  306. }
  307. return false;
  308. }
  309. void LLXMLRPCTransaction::Impl::setStatus(EStatus status,
  310. const std::string& message,
  311. const std::string& uri)
  312. {
  313. mStatus = status;
  314. mStatusMessage = message;
  315. mStatusURI = uri;
  316. if (mStatusMessage.empty())
  317. {
  318. switch (mStatus)
  319. {
  320. case StatusNotStarted:
  321. mStatusMessage = "(not started)";
  322. break;
  323. case StatusStarted:
  324. mStatusMessage = "(waiting for server response)";
  325. break;
  326. case StatusDownloading:
  327. mStatusMessage = "(reading server response)";
  328. break;
  329. case StatusComplete:
  330. mStatusMessage = "(done)";
  331. break;
  332. default:
  333. {
  334. // Usually this means that there is a problem with the login
  335. // server, not with the client. Direct user to status page.
  336. mStatusMessage = sServerIsDownMsg;
  337. mStatusURI = sWebsiteURL;
  338. }
  339. }
  340. }
  341. }
  342. void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus& status)
  343. {
  344. CURLcode code = (CURLcode)status.toULong();
  345. std::string message;
  346. switch (code)
  347. {
  348. case CURLE_COULDNT_RESOLVE_HOST:
  349. message = sNotResolvingMsg;
  350. break;
  351. case CURLE_SSL_CACERT:
  352. // Note: CURLE_SSL_CACERT and CURLE_SSL_CACERT may expand to the same
  353. // value in recent curl versions (seen with curl v7.68).
  354. #if CURLE_SSL_CACERT != CURLE_SSL_PEER_CERTIFICATE
  355. case CURLE_SSL_PEER_CERTIFICATE:
  356. #endif
  357. message = sNotVerifiedMsg;
  358. break;
  359. case CURLE_SSL_CONNECT_ERROR:
  360. message = sConnectErrorMsg;
  361. break;
  362. default:
  363. break;
  364. }
  365. mCurlCode = code;
  366. setStatus(StatusCURLError, message, sSupportURL);
  367. }
  368. ///////////////////////////////////////////////////////////////////////////////
  369. // LLXMLRPCTransaction class proper
  370. ///////////////////////////////////////////////////////////////////////////////
  371. LLXMLRPCTransaction::LLXMLRPCTransaction(const std::string& uri,
  372. const std::string& method,
  373. const LLSD& params)
  374. : impl(*new Impl(uri, method, params))
  375. {
  376. }
  377. LLXMLRPCTransaction::~LLXMLRPCTransaction()
  378. {
  379. delete &impl;
  380. }
  381. bool LLXMLRPCTransaction::process()
  382. {
  383. return impl.process();
  384. }
  385. LLXMLRPCTransaction::EStatus LLXMLRPCTransaction::status(S32* curl_code)
  386. {
  387. if (curl_code)
  388. {
  389. *curl_code = impl.mStatus == StatusCURLError ? impl.mCurlCode
  390. : CURLE_OK;
  391. }
  392. return impl.mStatus;
  393. }
  394. std::string LLXMLRPCTransaction::statusMessage()
  395. {
  396. return impl.mStatusMessage;
  397. }
  398. std::string LLXMLRPCTransaction::statusURI()
  399. {
  400. return impl.mStatusURI;
  401. }
  402. const LLSD& LLXMLRPCTransaction::response()
  403. {
  404. return impl.mResponseData;
  405. }
  406. //static
  407. void LLXMLRPCTransaction::setSupportURL(const std::string& url)
  408. {
  409. Impl::sSupportURL = url;
  410. }
  411. //static
  412. void LLXMLRPCTransaction::setWebsiteURL(const std::string& url)
  413. {
  414. Impl::sWebsiteURL = url;
  415. }
  416. //static
  417. void LLXMLRPCTransaction::setVerifyCert(bool verify)
  418. {
  419. Impl::sVerifyCert = verify;
  420. }
  421. //static
  422. void LLXMLRPCTransaction::setMessages(const std::string& server_down,
  423. const std::string& not_resolving,
  424. const std::string& not_verified,
  425. const std::string& connect_error)
  426. {
  427. Impl::sServerIsDownMsg = server_down;
  428. Impl::sNotResolvingMsg = not_resolving;
  429. Impl::sNotVerifiedMsg = not_verified;
  430. Impl::sConnectErrorMsg = connect_error;
  431. }