123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- /**
- * @file llxmlrpctransaction.cpp
- * @brief LLXMLRPCTransaction and related class implementations
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include <memory>
- #include "llxmlrpctransaction.h"
- #include "llcorebufferarray.h"
- #include "llcorehttphandler.h"
- #include "llcorehttprequest.h"
- #include "llcorehttpresponse.h"
- #include "llhttpconstants.h"
- #include "llsdutil.h" // For ll_pretty_print_sd()
- #include "llxmlnode.h"
- ///////////////////////////////////////////////////////////////////////////////
- // LLXMLRPCTransaction::Handler sub-class
- ///////////////////////////////////////////////////////////////////////////////
- class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler
- {
- protected:
- LOG_CLASS(LLXMLRPCTransaction::Handler);
- public:
- typedef std::shared_ptr<LLXMLRPCTransaction::Handler> ptr_t;
- Handler(LLCore::HttpRequest::ptr_t& request,
- LLXMLRPCTransaction::Impl* impl)
- : mImpl(impl),
- mRequest(request)
- {
- }
- virtual ~Handler() = default;
- virtual void onCompleted(LLCore::HttpHandle handle,
- LLCore::HttpResponse* responsep);
- private:
- LLXMLRPCTransaction::Impl* mImpl;
- LLCore::HttpRequest::ptr_t mRequest;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // LLXMLRPCTransaction::Impl sub-class
- ///////////////////////////////////////////////////////////////////////////////
- class LLXMLRPCTransaction::Impl
- {
- protected:
- LOG_CLASS(LLXMLRPCTransaction::Impl);
- public:
- typedef LLXMLRPCTransaction::EStatus EStatus;
- Impl(const std::string& uri, const std::string& method,
- const LLSD& params);
- bool process();
- void setStatus(EStatus code, const std::string& message = "",
- const std::string& uri = "");
- void setHttpStatus(const LLCore::HttpStatus& status);
- private:
- bool parseResponse(LLXMLNodePtr root);
- bool parseValue(LLSD& target, LLXMLNodePtr source);
- public:
- LLCore::HttpRequest::ptr_t mHttpRequest;
- LLCore::HttpHandle mPostH;
- Handler::ptr_t mHandler;
- EStatus mStatus;
- CURLcode mCurlCode;
- std::string mStatusMessage;
- std::string mStatusURI;
- std::string mURI;
- std::string mResponseText;
- LLSD mResponseData;
- bool mHasResponse;
- bool mResponseParsed;
- static std::string sSupportURL;
- static std::string sWebsiteURL;
- static std::string sServerIsDownMsg;
- static std::string sNotResolvingMsg;
- static std::string sNotVerifiedMsg;
- static std::string sConnectErrorMsg;
- static bool sVerifyCert;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // LLXMLRPCTransaction::Handler sub-class method
- ///////////////////////////////////////////////////////////////////////////////
- void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle,
- LLCore::HttpResponse* responsep)
- {
- if (!responsep || !mImpl) return; // Paranioa
- LLCore::HttpStatus status = responsep->getStatus();
- if (!status)
- {
- if (status.toULong() != CURLE_SSL_PEER_CERTIFICATE &&
- status.toULong() != CURLE_SSL_CACERT)
- {
- // If we have a curl error that has not already been handled (a non
- // cert error), then generate the error message as appropriate
- mImpl->setHttpStatus(status);
- llwarns << "Error " << status.toHex() << ": " << status.toString()
- << " - Request URI: " << mImpl->mURI << llendl;
- }
- return;
- }
- // The contents of a buffer array are potentially non-contiguous, so we
- // will need to copy them into an contiguous block of memory for XMLRPC.
- LLCore::BufferArray* bodyp = responsep->getBody();
- if (!bodyp) // This *does* happen !
- {
- mImpl->mResponseText.clear();
- mImpl->mResponseData.clear();
- llwarns << "Empty response; request URI: " << mImpl->mURI << llendl;
- return;
- }
- size_t body_size = bodyp->size();
- mImpl->mResponseText.resize(body_size, '\0');
- bodyp->read(0, mImpl->mResponseText.data(), body_size);
- LL_DEBUGS("XmlRpc") << "XMLRPC response body: " << mImpl->mResponseText
- << LL_ENDL;
- // We do not do the parsing in the HTTP coroutine, since it could exhaust
- // the coroutine stack in extreme cases. Instead, we flag the data buffer
- // as ready, and let mImpl decode it in its process() method, on the main
- // coroutine. HB
- mImpl->mHasResponse = true;
- mImpl->setStatus(LLXMLRPCTransaction::StatusComplete);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLXMLRPCTransaction::Impl sub-class methods
- ///////////////////////////////////////////////////////////////////////////////
- // Static members
- std::string LLXMLRPCTransaction::Impl::sSupportURL;
- std::string LLXMLRPCTransaction::Impl::sWebsiteURL;
- std::string LLXMLRPCTransaction::Impl::sServerIsDownMsg;
- std::string LLXMLRPCTransaction::Impl::sNotResolvingMsg;
- std::string LLXMLRPCTransaction::Impl::sNotVerifiedMsg;
- std::string LLXMLRPCTransaction::Impl::sConnectErrorMsg;
- bool LLXMLRPCTransaction::Impl::sVerifyCert = true;
- LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
- const std::string& method,
- const LLSD& params)
- : mStatus(LLXMLRPCTransaction::StatusNotStarted),
- mURI(uri),
- mHasResponse(false),
- mResponseParsed(false)
- {
- if (!mHttpRequest)
- {
- mHttpRequest = DEFAULT_HTTP_REQUEST;
- }
- // LLRefCounted starts with a 1 ref, so do not add a ref in the smart
- // pointer
- LLCore::HttpOptions::ptr_t options = DEFAULT_HTTP_OPTIONS;
- // Be a little impatient about establishing connections.
- options->setTimeout(40L);
- options->setSSLVerifyPeer(sVerifyCert);
- options->setSSLVerifyHost(sVerifyCert);
- options->setDNSCacheTimeout(40);
- options->setRetries(3);
- // LLRefCounted starts with a 1 ref, so do not add a ref in the smart
- // pointer
- LLCore::HttpHeaders::ptr_t headers = DEFAULT_HTTP_HEADERS;
- headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML);
- LLCore::BufferArray::ptr_t body =
- LLCore::BufferArray::ptr_t(new LLCore::BufferArray());
- std::string request = "<?xml version=\"1.0\"?><methodCall><methodName>" +
- method + "</methodName><params><param>" +
- params.asXMLRPCValue() +
- "</param></params></methodCall>";
- body->append(request.c_str(), request.size());
- #if LL_DEBUG
- // Note: this shows password and MFA hashes, so only enabled for debug
- // builds. HB
- LL_DEBUGS("XmlRpc") << "XMLRPC request body:\n" << request << LL_ENDL;
- #endif
- mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler(mHttpRequest,
- this));
- mPostH = mHttpRequest->requestPost(LLCore::HttpRequest::DEFAULT_POLICY_ID,
- mURI, body.get(), options, headers,
- mHandler);
- }
- bool LLXMLRPCTransaction::Impl::parseResponse(LLXMLNodePtr root)
- {
- // We have already checked in LLXMLNode::parseBuffer() that root contains
- // exactly one child.
- if (!root->hasName("methodResponse"))
- {
- llwarns << "Invalid root element in XML response; request URI: "
- << mURI << llendl;
- return false;
- }
- LLXMLNodePtr first = root->getFirstChild();
- LLXMLNodePtr second = first->getFirstChild();
- if (first && !first->getNextSibling() && second &&
- !second->getNextSibling())
- {
- if (first->hasName("fault"))
- {
- LLSD fault;
- if (parseValue(fault, second) && fault.isMap() &&
- fault.has("faultCode") && fault.has("faultString"))
- {
- llwarns << "Request failed. faultCode: '"
- << fault.get("faultCode").asString()
- << "', faultString: '"
- << fault.get("faultString").asString()
- << "', request URI: " << mURI << llendl;
- return false;
- }
- }
- else if (first->hasName("params") &&
- second->hasName("param") && !second->getNextSibling())
- {
- LLXMLNodePtr third = second->getFirstChild();
- if (third && !third->getNextSibling() &&
- parseValue(mResponseData, third))
- {
- return true;
- }
- }
- }
- llwarns << "Invalid response format; request URI: " << mURI << llendl;
- return false;
- }
- bool LLXMLRPCTransaction::Impl::parseValue(LLSD& target, LLXMLNodePtr src)
- {
- bool success = src->fromXMLRPCValue(target);
- LL_DEBUGS("XmlRpc") << "XMLTreeNode parsing "
- << (success ? "succeeded" : "failed") << LL_ENDL;
- return success;
- }
- bool LLXMLRPCTransaction::Impl::process()
- {
- if (!mPostH || !mHttpRequest)
- {
- llwarns << "Transaction failed." << llendl;
- return true;
- }
- // Parse the response when we have one and it has not yet been parsed. HB
- if (mHasResponse && !mResponseParsed)
- {
- LLXMLNodePtr root;
- if (!LLXMLNode::parseBuffer(mResponseText.data(), mResponseText.size(),
- root, NULL))
- {
- llwarns << "XML response parsing failed; request URI: " << mURI
- << llendl;
- }
- else if (parseResponse(root))
- {
- LL_DEBUGS("XmlRpc") << "Parsed response LLSD:\n"
- << ll_pretty_print_sd(mResponseData)
- << LL_ENDL;
- }
- else
- {
- llwarns << "XMLRPC response parsing failed; request URI: " << mURI
- << llendl;
- }
- mResponseParsed = true;
- }
- switch (mStatus)
- {
- case LLXMLRPCTransaction::StatusComplete:
- case LLXMLRPCTransaction::StatusCURLError:
- case LLXMLRPCTransaction::StatusXMLRPCError:
- case LLXMLRPCTransaction::StatusOtherError:
- {
- return true;
- }
- case LLXMLRPCTransaction::StatusNotStarted:
- {
- setStatus(LLXMLRPCTransaction::StatusStarted);
- }
- default:
- break;
- }
- LLCore::HttpStatus status = mHttpRequest->update(0);
- if (!status)
- {
- llwarns << "Error (1): " << status.toString() << llendl;
- return false;
- }
- status = mHttpRequest->getStatus();
- if (!status)
- {
- llwarns << "Error (2): " << status.toString() << llendl;
- }
- return false;
- }
- void LLXMLRPCTransaction::Impl::setStatus(EStatus status,
- const std::string& message,
- const std::string& uri)
- {
- mStatus = status;
- mStatusMessage = message;
- mStatusURI = uri;
- if (mStatusMessage.empty())
- {
- switch (mStatus)
- {
- case StatusNotStarted:
- mStatusMessage = "(not started)";
- break;
- case StatusStarted:
- mStatusMessage = "(waiting for server response)";
- break;
- case StatusDownloading:
- mStatusMessage = "(reading server response)";
- break;
- case StatusComplete:
- mStatusMessage = "(done)";
- break;
- default:
- {
- // Usually this means that there is a problem with the login
- // server, not with the client. Direct user to status page.
- mStatusMessage = sServerIsDownMsg;
- mStatusURI = sWebsiteURL;
- }
- }
- }
- }
- void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus& status)
- {
- CURLcode code = (CURLcode)status.toULong();
- std::string message;
- switch (code)
- {
- case CURLE_COULDNT_RESOLVE_HOST:
- message = sNotResolvingMsg;
- break;
- case CURLE_SSL_CACERT:
- // Note: CURLE_SSL_CACERT and CURLE_SSL_CACERT may expand to the same
- // value in recent curl versions (seen with curl v7.68).
- #if CURLE_SSL_CACERT != CURLE_SSL_PEER_CERTIFICATE
- case CURLE_SSL_PEER_CERTIFICATE:
- #endif
- message = sNotVerifiedMsg;
- break;
- case CURLE_SSL_CONNECT_ERROR:
- message = sConnectErrorMsg;
- break;
- default:
- break;
- }
- mCurlCode = code;
- setStatus(StatusCURLError, message, sSupportURL);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLXMLRPCTransaction class proper
- ///////////////////////////////////////////////////////////////////////////////
- LLXMLRPCTransaction::LLXMLRPCTransaction(const std::string& uri,
- const std::string& method,
- const LLSD& params)
- : impl(*new Impl(uri, method, params))
- {
- }
- LLXMLRPCTransaction::~LLXMLRPCTransaction()
- {
- delete &impl;
- }
- bool LLXMLRPCTransaction::process()
- {
- return impl.process();
- }
- LLXMLRPCTransaction::EStatus LLXMLRPCTransaction::status(S32* curl_code)
- {
- if (curl_code)
- {
- *curl_code = impl.mStatus == StatusCURLError ? impl.mCurlCode
- : CURLE_OK;
- }
- return impl.mStatus;
- }
- std::string LLXMLRPCTransaction::statusMessage()
- {
- return impl.mStatusMessage;
- }
- std::string LLXMLRPCTransaction::statusURI()
- {
- return impl.mStatusURI;
- }
- const LLSD& LLXMLRPCTransaction::response()
- {
- return impl.mResponseData;
- }
- //static
- void LLXMLRPCTransaction::setSupportURL(const std::string& url)
- {
- Impl::sSupportURL = url;
- }
- //static
- void LLXMLRPCTransaction::setWebsiteURL(const std::string& url)
- {
- Impl::sWebsiteURL = url;
- }
- //static
- void LLXMLRPCTransaction::setVerifyCert(bool verify)
- {
- Impl::sVerifyCert = verify;
- }
- //static
- void LLXMLRPCTransaction::setMessages(const std::string& server_down,
- const std::string& not_resolving,
- const std::string& not_verified,
- const std::string& connect_error)
- {
- Impl::sServerIsDownMsg = server_down;
- Impl::sNotResolvingMsg = not_resolving;
- Impl::sNotVerifiedMsg = not_verified;
- Impl::sConnectErrorMsg = connect_error;
- }
|