diff options
-rw-r--r-- | indra/llcommon/llsd.cpp | 147 | ||||
-rw-r--r-- | indra/llcommon/llsd.h | 10 | ||||
-rw-r--r-- | indra/llxml/llxmlnode.cpp | 172 | ||||
-rw-r--r-- | indra/llxml/llxmlnode.h | 9 | ||||
-rw-r--r-- | indra/newview/llxmlrpctransaction.cpp | 183 |
5 files changed, 269 insertions, 252 deletions
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 2bbe06e72f..77fe545c3f 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -1018,153 +1018,6 @@ const LLSD::String& LLSD::asStringRef() const { return safe(impl).asStringRef(); LLSD::String LLSD::asXMLRPCValue() const { return "<value>" + safe(impl).asXMLRPCValue() + "</value>"; } -static bool inline check(bool condition, const char* warning_message) -{ - if (!condition) - { - LL_WARNS() << warning_message << LL_ENDL; - } - - return condition; -} - -static bool parseXMLRPCArrayValue(LLSD& target, LLSD::TreeNode* node) -{ - LLSD::TreeNode* data = node->getFirstChild(); - if (!check(data, "No array inner XML element (<data> expected)") || - !check(data->hasName("data"), "Invalid array inner XML element (<data> expected)") || - !check(!data->getNextSibling(), "Multiple array inner XML elements (single <data> expected)")) - return false; - - for (LLSD::TreeNode* item = data->getFirstChild(); item; item = item->getNextSibling()) - { - LLSD value; - if (!value.fromXMLRPCValue(item)) - return false; - - target.append(value); - } - - return true; -} - -static bool parseXMLRPCStructValue(LLSD& target, LLSD::TreeNode* node) -{ - for (LLSD::TreeNode* item = node->getFirstChild(); item; item = item->getNextSibling()) - { - if (!check(item->hasName("member"), "Invalid struct inner XML element (<member> expected)")) - return false; - - std::string name; - LLSD value; - for (LLSD::TreeNode* subitem = item->getFirstChild(); subitem; subitem = subitem->getNextSibling()) - { - if (subitem->hasName("name")) - { - name = LLStringFn::xml_decode(subitem->getTextContents()); - } - else if (!value.fromXMLRPCValue(subitem)) - { - return false; - } - } - if (!check(!name.empty(), "Empty struct member name")) - return false; - - target.insert(name, value); - } - - return true; -} - -bool LLSD::fromXMLRPCValue(TreeNode* node) -{ - clear(); - - llassert(node); - if (!node) - return false; - - if (!check(node->hasName("value"), "Invalid XML element (<value> expected)")) - return false; - - TreeNode* inner = node->getFirstChild(); - if (!inner) - { - check(false, "No inner XML element (value type expected)"); - // Value with no type qualifier is treated as string - assign(LLStringFn::xml_decode(node->getTextContents())); - return true; - } - - if (!check(!inner->getNextSibling(), "Multiple inner XML elements (single expected)")) - return false; - - if (inner->hasName("string")) - { - assign(LLStringFn::xml_decode(inner->getTextContents())); - return true; - } - - if (inner->hasName("int") || inner->hasName("i4")) - { - assign(std::stoi(inner->getTextContents())); - return true; - } - - if (inner->hasName("double")) - { - assign(std::stod(inner->getTextContents())); - return true; - } - - if (inner->hasName("boolean")) - { - assign(!!std::stoi(inner->getTextContents())); - return true; - } - - if (inner->hasName("dateTime.iso8601")) - { - assign(Date(inner->getTextContents())); - return true; - } - - if (inner->hasName("base64")) - { - std::string decoded = LLBase64::decodeAsString(inner->getTextContents()); - Binary binary(decoded.size()); - memcpy(binary.data(), decoded.data(), decoded.size()); - assign(binary); - return true; - } - - if (inner->hasName("array")) - { - if (!parseXMLRPCArrayValue(*this, inner)) - { - clear(); - return false; - } - return true; - } - - if (inner->hasName("struct")) - { - if (!parseXMLRPCStructValue(*this, inner)) - { - clear(); - return false; - } - return true; - } - - check(false, "Unknown inner XML element (known value type expected)"); - // Value with unknown type qualifier is treated as string - assign(LLStringFn::xml_decode(inner->getTextContents())); - return true; -} - // const char * helpers LLSD::LLSD(const char* v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } void LLSD::assign(const char* v) diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index 781e8d58e9..d2b3548831 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -290,16 +290,6 @@ public: // See http://xmlrpc.com/spec.md String asXMLRPCValue() const; - struct TreeNode - { - virtual bool hasName(const String& name) const = 0; - virtual String getTextContents() const = 0; - virtual TreeNode* getFirstChild() const = 0; - virtual TreeNode* getNextSibling() const = 0; - }; - - bool fromXMLRPCValue(TreeNode* node); - operator Boolean() const { return asBoolean(); } operator Integer() const { return asInteger(); } operator Real() const { return asReal(); } diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 7f6b8093fc..8eb4556828 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -38,7 +38,9 @@ #include "v3math.h" #include "v3dmath.h" #include "v4math.h" +#include "llbase64.h" #include "llquaternion.h" +#include "llsd.h" #include "llstring.h" #include "lluuid.h" #include "lldir.h" @@ -3263,3 +3265,173 @@ S32 LLXMLNode::getLineNumber() { return mLineNumber; } + +bool LLXMLNode::parseXmlRpcArrayValue(LLSD& target) +{ + LLXMLNode* datap = getFirstChild().get(); + if (!datap) + { + LL_WARNS() << "No inner XML element." << LL_ENDL; + return false; + } + if (!datap->hasName("data")) + { + LL_WARNS() << "No inner XML element (<data> expected, got: " + << datap->mName->mString << ")" << LL_ENDL; + return false; + } + if (datap->getNextSibling().get()) + { + LL_WARNS() << "Multiple inner XML elements (single <data> expected)" + << LL_ENDL; + return false; + } + U32 i = 0; + for (LLXMLNode* itemp = datap->getFirstChild().get(); itemp; + itemp = itemp->getNextSibling().get()) + { + LLSD value; + if (!itemp->fromXMLRPCValue(value)) + { + return false; + } + target.append(value); + ++i; + } + return true; +} + +bool LLXMLNode::parseXmlRpcStructValue(LLSD& target) +{ + std::string name; + LLSD value; + for (LLXMLNode* itemp = getFirstChild().get(); itemp; + itemp = itemp->getNextSibling().get()) + { + if (!itemp->hasName("member")) + { + LL_WARNS() << "Invalid inner XML element (<member> expected, got: <" + << itemp->mName->mString << ">" << LL_ENDL; + return false; + } + name.clear(); + value.clear(); + for (LLXMLNode* chilp = itemp->getFirstChild().get(); chilp; + chilp = chilp->getNextSibling().get()) + { + if (chilp->hasName("name")) + { + name = LLStringFn::xml_decode(chilp->getTextContents()); + } + else if (!chilp->fromXMLRPCValue(value)) + { + return false; + } + } + if (name.empty()) + { + LL_WARNS() << "Empty struct member name" << LL_ENDL; + return false; + } + target.insert(name, value); + } + return true; +} + +bool LLXMLNode::fromXMLRPCValue(LLSD& target) +{ + target.clear(); + + if (!hasName("value")) + { + LL_WARNS() << "Invalid XML element (<value> expected), got: <" + << mName->mString << ">" << LL_ENDL; + return false; + } + + LLXMLNode* childp = getFirstChild().get(); + if (!childp) + { + LL_WARNS() << "No inner XML element (value type expected)" << LL_ENDL; + // Value with no type qualifier is treated as string + target.assign(LLStringFn::xml_decode(getTextContents())); + return true; + } + + if (childp->getNextSibling()) + { + LL_WARNS() << "Multiple inner XML elements (single expected)" + << LL_ENDL; + return false; + } + + if (childp->hasName("string")) + { + target.assign(LLStringFn::xml_decode(childp->getTextContents())); + return true; + } + + if (childp->hasName("int") || childp->hasName("i4")) + { + target.assign(std::stoi(childp->getTextContents())); + return true; + } + + if (childp->hasName("double")) + { + target.assign(std::stod(childp->getTextContents())); + return true; + } + + if (childp->hasName("boolean")) + { + target.assign(std::stoi(childp->getTextContents()) != 0); + return true; + } + + if (childp->hasName("dateTime.iso8601")) + { + target.assign(LLSD::Date(childp->getTextContents())); + return true; + } + + if (childp->hasName("base64")) + { + std::string decoded = + LLBase64::decodeAsString(inner->getTextContents()); + size_t size = decoded.size(); + LLSD::Binary binary(size); + if (size) + { + memcpy((void*)binary.data(), (void*)decoded.data(), size); + } + target.assign(binary); + return true; + } + + if (childp->hasName("array")) + { + if (!childp->parseXmlRpcArrayValue(target)) + { + target.clear(); + return false; + } + return true; + } + + if (childp->hasName("struct")) + { + if (!childp->parseXmlRpcStructValue(target)) + { + target.clear(); + return false; + } + return true; + } + + LL_WARNS() << "Unknown inner XML element (known value type expected)" + << LL_ENDL; + // Value with unknown type qualifier is treated as string + target.assign(LLStringFn::xml_decode(childp->getTextContents())); + return true; +} diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index b8f9e1ff69..3769ec8293 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -50,6 +50,7 @@ class LLVector3d; class LLQuaternion; class LLColor4; class LLColor4U; +class LLSD; struct CompareAttributes @@ -284,12 +285,18 @@ public: void setAttributes(ValueType type, U32 precision, Encoding encoding, U32 length); // void appendValue(const std::string& value); // Unused + bool fromXMLRPCValue(LLSD& target); + // Unit Testing void createUnitTest(S32 max_num_children); bool performUnitTest(std::string &error_buffer); protected: bool removeChild(LLXMLNode* child); + bool isFullyDefault(); + + bool parseXmlRpcArrayValue(LLSD& target); + bool parseXmlRpcStructValue(LLSD& target); public: std::string mID; // The ID attribute of this node @@ -328,8 +335,6 @@ protected: static const char *skipNonWhitespace(const char *str); static const char *parseInteger(const char *str, U64 *dest, bool *is_negative, U32 precision, Encoding encoding); static const char *parseFloat(const char *str, F64 *dest, U32 precision, Encoding encoding); - - bool isFullyDefault(); }; #endif // LL_LLXMLNODE diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 55622fb6b7..9dedb685db 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -68,17 +68,13 @@ class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler { public: Handler(LLCore::HttpRequest::ptr_t &request, LLXMLRPCTransaction::Impl *impl); - virtual ~Handler(); - virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + void onCompleted(LLCore::HttpHandle handle, + LLCore::HttpResponse* response) override; typedef std::shared_ptr<LLXMLRPCTransaction::Handler> ptr_t; private: - - bool parseResponse(LLXMLNodePtr root); - bool parseValue(LLSD& target, LLXMLNodePtr source); - LLXMLRPCTransaction::Impl *mImpl; LLCore::HttpRequest::ptr_t mRequest; }; @@ -104,6 +100,8 @@ public: std::string mResponseText; LLSD mResponseData; + bool mHasResponse; + bool mResponseParsed; std::string mCertStore; LLSD mErrorCertData; @@ -120,6 +118,10 @@ public: 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); }; LLXMLRPCTransaction::Handler::Handler(LLCore::HttpRequest::ptr_t &request, @@ -129,10 +131,6 @@ LLXMLRPCTransaction::Handler::Handler(LLCore::HttpRequest::ptr_t &request, { } -LLXMLRPCTransaction::Handler::~Handler() -{ -} - void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { @@ -159,7 +157,6 @@ void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle, return; } - mImpl->setStatus(LLXMLRPCTransaction::StatusComplete); mImpl->mTransferStats = response->getTransferStats(); // The contents of a buffer array are potentially noncontiguous, so we @@ -169,88 +166,12 @@ void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle, body->read(0, mImpl->mResponseText.data(), body->size()); - LLXMLNodePtr root; - if (!LLXMLNode::parseBuffer(mImpl->mResponseText.data(), body->size(), root, nullptr)) - { - LL_WARNS() << "Failed parsing XML response; request URI: " << mImpl->mURI << LL_ENDL; - return; - } - - if (!parseResponse(root)) - return; - - LL_INFOS() << "XML response parsed successfully; request URI: " << mImpl->mURI << LL_ENDL; -} - -struct XMLTreeNode final : public LLSD::TreeNode -{ - XMLTreeNode(const LLXMLNodePtr impl) - : mImpl(impl) - , mFirstChild(impl ? create(impl->getFirstChild()) : nullptr) - , mNextSibling(impl ? create(impl->getNextSibling()) : nullptr) - { - } - - static XMLTreeNode* create(LLXMLNodePtr node) { return node ? new XMLTreeNode(node) : nullptr; } - - virtual bool hasName(const LLSD::String& name) const override { return mImpl && mImpl->hasName(name); } - virtual LLSD::String getTextContents() const override { return mImpl ? mImpl->getTextContents() : LLStringUtil::null; } - virtual TreeNode* getFirstChild() const override { return mFirstChild.get(); } - virtual TreeNode* getNextSibling() const override { return mNextSibling.get(); } - -private: - const LLXMLNodePtr mImpl; - const std::shared_ptr<XMLTreeNode> mFirstChild; - const std::shared_ptr<XMLTreeNode> mNextSibling; -}; - -bool LLXMLRPCTransaction::Handler::parseResponse(LLXMLNodePtr root) -{ - // We have alreasy checked in LLXMLNode::parseBuffer() - // that root contains exactly one child - if (!root->hasName("methodResponse")) - { - LL_WARNS() << "Invalid root element in XML response; request URI: " << mImpl->mURI << LL_ENDL; - return false; - } - - LLXMLNodePtr first = root->getFirstChild(); - LLXMLNodePtr second = first->getFirstChild(); - if (!first->getNextSibling() && second && !second->getNextSibling()) - { - if (first->hasName("fault")) - { - LLSD fault; - if (parseValue(fault, second) && - fault.isMap() && fault.has("faultCode") && fault.has("faultString")) - { - LL_WARNS() << "Request failed;" - << " faultCode: '" << fault.get("faultCode").asString() << "'," - << " faultString: '" << fault.get("faultString").asString() << "'," - << " request URI: " << mImpl->mURI << LL_ENDL; - return false; - } - } - else if (first->hasName("params") && - second->hasName("param") && !second->getNextSibling()) - { - LLXMLNodePtr third = second->getFirstChild(); - if (third && !third->getNextSibling() && parseValue(mImpl->mResponseData, third)) - { - return true; - } - } - } - - LL_WARNS() << "Invalid response format; request URI: " << mImpl->mURI << LL_ENDL; - - return false; -} - -bool LLXMLRPCTransaction::Handler::parseValue(LLSD& target, LLXMLNodePtr source) -{ - XMLTreeNode tn(source); - return target.fromXMLRPCValue(&tn); + // 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); } //========================================================================= @@ -265,6 +186,8 @@ LLXMLRPCTransaction::Impl::Impl : mHttpRequest() , mStatus(LLXMLRPCTransaction::StatusNotStarted) , mURI(uri) + , mHasResponse(false) + , mResponseParsed(false) { LLCore::HttpOptions::ptr_t httpOpts; LLCore::HttpHeaders::ptr_t httpHeaders; @@ -327,6 +250,57 @@ LLXMLRPCTransaction::Impl::Impl mURI, body.get(), httpOpts, httpHeaders, mHandler); } +bool LLXMLRPCTransaction::Impl::parseResponse(LLXMLNodePtr root) +{ + // We have already checked in LLXMLNode::parseBuffer() that root contains + // exactly one child. + if (!root->hasName("methodResponse")) + { + LL_WARNS() << "Invalid root element in XML response; request URI: " + << mURI << LL_ENDL; + 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")) + { + LL_WARNS() << "Request failed. faultCode: '" + << fault.get("faultCode").asString() + << "', faultString: '" + << fault.get("faultString").asString() + << "', request URI: " << mURI << LL_ENDL; + 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; + } + } + } + + LL_WARNS() << "Invalid response format; request URI: " << mURI << LL_ENDL; + return false; +} + +bool LLXMLRPCTransaction::Impl::parseValue(LLSD& target, LLXMLNodePtr src) +{ + return src->fromXMLRPCValue(target); +} + bool LLXMLRPCTransaction::Impl::process() { if (!mPostH || !mHttpRequest) @@ -335,6 +309,29 @@ bool LLXMLRPCTransaction::Impl::process() return true; //failed, quit. } + // 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, nullptr)) + { + LL_WARNS() << "Failed parsing XML in response; request URI: " + << mURI << LL_ENDL; + } + else if (parseResponse(root)) + { + LL_INFOS() << "XMLRPC response parsed successfully; request URI: " + << mURI << LL_ENDL; + } + else + { + LL_WARNS() << "XMLRPC response parsing failed; request URI: " + << mURI << LL_ENDL; + } + mResponseParsed = true; + } + switch (mStatus) { case LLXMLRPCTransaction::StatusComplete: |