From 2ea5ac0c43e3e28d2b1774f5367d099271a1da32 Mon Sep 17 00:00:00 2001
From: Alexander Gavriliuk <alexandrgproductengine@lindenlab.com>
Date: Mon, 1 Jul 2024 13:34:50 +0200
Subject: #1111 Remove xmlrpc-epi

---
 autobuild.xml                                      |  60 --
 indra/cmake/CMakeLists.txt                         |   1 -
 indra/cmake/LLCommon.cmake                         |   2 -
 indra/cmake/XmlRpcEpi.cmake                        |  11 -
 indra/llcommon/llfile.cpp                          |  18 +
 indra/llcommon/llfile.h                            |   2 +
 indra/llcommon/llsd.cpp                            | 201 +++++-
 indra/llcommon/llsd.h                              |  20 +-
 indra/llcommon/llstring.cpp                        |  69 ++
 indra/llcommon/llstring.h                          |  14 +
 indra/llmessage/CMakeLists.txt                     |   3 -
 indra/llmessage/llfiltersd2xmlrpc.cpp              | 778 ---------------------
 indra/llmessage/llfiltersd2xmlrpc.h                | 271 -------
 indra/llxml/llxmlnode.cpp                          |  39 +-
 indra/llxml/llxmlnode.h                            |  12 +-
 indra/newview/llcurrencyuimanager.cpp              | 137 ++--
 indra/newview/llfloaterbuyland.cpp                 | 201 ++----
 indra/newview/lllogininstance.cpp                  |   5 +-
 indra/newview/llslurl.cpp                          |   6 +-
 indra/newview/llversioninfo.cpp                    |  22 +-
 indra/newview/llversioninfo.h                      |  26 +-
 indra/newview/llxmlrpclistener.cpp                 | 278 ++------
 indra/newview/llxmlrpclistener.h                   |   3 -
 indra/newview/llxmlrpctransaction.cpp              | 361 ++++------
 indra/newview/llxmlrpctransaction.h                |  92 +--
 .../newview/skins/default/xui/da/floater_about.xml |   1 -
 .../newview/skins/default/xui/de/floater_about.xml |   1 -
 .../newview/skins/default/xui/en/floater_about.xml |   1 -
 .../newview/skins/default/xui/es/floater_about.xml |   1 -
 .../newview/skins/default/xui/fr/floater_about.xml |   1 -
 .../newview/skins/default/xui/it/floater_about.xml |   1 -
 .../newview/skins/default/xui/ja/floater_about.xml |   1 -
 .../newview/skins/default/xui/pt/floater_about.xml |   1 -
 .../newview/skins/default/xui/ru/floater_about.xml |   1 -
 .../newview/skins/default/xui/tr/floater_about.xml |   1 -
 .../newview/skins/default/xui/zh/floater_about.xml |   1 -
 indra/viewer_components/login/lllogin.cpp          |  41 +-
 37 files changed, 723 insertions(+), 1961 deletions(-)
 delete mode 100644 indra/cmake/XmlRpcEpi.cmake
 delete mode 100644 indra/llmessage/llfiltersd2xmlrpc.cpp
 delete mode 100644 indra/llmessage/llfiltersd2xmlrpc.h

diff --git a/autobuild.xml b/autobuild.xml
index c47661df18..2e673f79ce 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2670,66 +2670,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
         <key>name</key>
         <string>vlc-bin</string>
       </map>
-      <key>xmlrpc-epi</key>
-      <map>
-        <key>platforms</key>
-        <map>
-          <key>darwin64</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>aa12611374876196b3ebb6bda8d419a697217b8b</string>
-              <key>hash_algorithm</key>
-              <string>sha1</string>
-              <key>url</key>
-              <string>https://github.com/secondlife/3p-xmlrpc-epi/releases/download/v0.54.1.8a05acf/xmlrpc_epi-0.54.1.8a05acf-darwin64-8a05acf.tar.zst</string>
-            </map>
-            <key>name</key>
-            <string>darwin64</string>
-          </map>
-          <key>linux64</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>ad0c8b41ee4b4de216382bec46ee1c25962a3f12</string>
-              <key>hash_algorithm</key>
-              <string>sha1</string>
-              <key>url</key>
-              <string>https://github.com/secondlife/3p-xmlrpc-epi/releases/download/v0.54.1.8a05acf/xmlrpc_epi-0.54.1.8a05acf-linux64-8a05acf.tar.zst</string>
-            </map>
-            <key>name</key>
-            <string>linux64</string>
-          </map>
-          <key>windows64</key>
-          <map>
-            <key>archive</key>
-            <map>
-              <key>hash</key>
-              <string>e53fd38c14b8c47c7c84dead8a1b211bb8be170c</string>
-              <key>hash_algorithm</key>
-              <string>sha1</string>
-              <key>url</key>
-              <string>https://github.com/secondlife/3p-xmlrpc-epi/releases/download/v0.54.1.8a05acf/xmlrpc_epi-0.54.1.8a05acf-windows64-8a05acf.tar.zst</string>
-            </map>
-            <key>name</key>
-            <string>windows64</string>
-          </map>
-        </map>
-        <key>license</key>
-        <string>xmlrpc-epi</string>
-        <key>license_file</key>
-        <string>LICENSES/xmlrpc-epi.txt</string>
-        <key>copyright</key>
-        <string>Copyright: (C) 2000 Epinions, Inc.</string>
-        <key>version</key>
-        <string>0.54.1.8a05acf</string>
-        <key>name</key>
-        <string>xmlrpc-epi</string>
-        <key>description</key>
-        <string>XMLRPC Library</string>
-      </map>
       <key>vulkan_gltf</key>
       <map>
         <key>platforms</key>
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 0f338931c0..c067105f68 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -63,7 +63,6 @@ set(cmake_SOURCE_FILES
         VisualLeakDetector.cmake
         LibVLCPlugin.cmake
         WebRTC.cmake
-        XmlRpcEpi.cmake
         xxHash.cmake
         ZLIBNG.cmake
         )
diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake
index 9e3707ff17..dd43ca4916 100644
--- a/indra/cmake/LLCommon.cmake
+++ b/indra/cmake/LLCommon.cmake
@@ -6,5 +6,3 @@ include(EXPAT)
 include(Tracy)
 include(xxHash)
 include(ZLIBNG)
-
-include(XmlRpcEpi)
diff --git a/indra/cmake/XmlRpcEpi.cmake b/indra/cmake/XmlRpcEpi.cmake
deleted file mode 100644
index 6409f9d6e2..0000000000
--- a/indra/cmake/XmlRpcEpi.cmake
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- cmake -*-
-include(Prebuilt)
-
-include_guard()
-add_library( ll::xmlrpc-epi INTERFACE IMPORTED )
-
-use_system_binary( xmlrpc-epi )
-
-use_prebuilt_binary(xmlrpc-epi)
-target_link_libraries(ll::xmlrpc-epi INTERFACE xmlrpc-epi )
-target_include_directories( ll::xmlrpc-epi SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include)
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index ddf239f306..9045324bf2 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -248,6 +248,24 @@ int LLFile::close(LLFILE * file)
     return ret_value;
 }
 
+std::string LLFile::getContents(const std::string& filename)
+{
+    LLFILE* fp = fopen(filename, "rb"); /* Flawfinder: ignore */
+    if (fp)
+    {
+        fseek(fp, 0, SEEK_END);
+        U32 length = ftell(fp);
+        fseek(fp, 0, SEEK_SET);
+
+        std::vector<char> buffer(length);
+        size_t nread = fread(buffer.data(), 1, length, fp);
+        fclose(fp);
+
+        return std::string(buffer.data(), nread);
+    }
+
+    return LLStringUtil::null;
+}
 
 int LLFile::remove(const std::string& filename, int supress_error)
 {
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 2564671b13..74110343fc 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -67,6 +67,8 @@ public:
 
     static  int     close(LLFILE * file);
 
+    static std::string getContents(const std::string& filename);
+
     // perms is a permissions mask like 0777 or 0700.  In most cases it will
     // be overridden by the user's umask.  It is ignored on Windows.
     // mkdir() considers "directory already exists" to be SUCCESS.
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
index 663ceac22b..b36ff7d263 100644
--- a/indra/llcommon/llsd.cpp
+++ b/indra/llcommon/llsd.cpp
@@ -30,6 +30,7 @@
 #include "linden_common.h"
 #include "llsd.h"
 
+#include "llbase64.h"
 #include "llerror.h"
 #include "../llmath/llmath.h"
 #include "llformat.h"
@@ -142,6 +143,8 @@ public:
 
     virtual const String& asStringRef() const { static const std::string empty; return empty; }
 
+    virtual String asXMLRPCValue() const { return "<nil/>"; }
+
     virtual bool has(const String&) const       { return false; }
     virtual LLSD get(const String&) const       { return LLSD(); }
     virtual LLSD getKeys() const                { return LLSD::emptyArray(); }
@@ -222,6 +225,8 @@ namespace
         virtual LLSD::Integer   asInteger() const   { return mValue ? 1 : 0; }
         virtual LLSD::Real      asReal() const      { return mValue ? 1 : 0; }
         virtual LLSD::String    asString() const;
+
+        virtual LLSD::String asXMLRPCValue() const { return mValue ? "<boolean>1</boolean>" : "<boolean>0</boolean>"; }
     };
 
     LLSD::String ImplBoolean::asString() const
@@ -243,6 +248,8 @@ namespace
         virtual LLSD::Integer   asInteger() const   { return mValue; }
         virtual LLSD::Real      asReal() const      { return mValue; }
         virtual LLSD::String    asString() const;
+
+        virtual LLSD::String asXMLRPCValue() const { return "<int>" + std::to_string(mValue) + "</int>"; }
     };
 
     LLSD::String ImplInteger::asString() const
@@ -259,6 +266,8 @@ namespace
         virtual LLSD::Integer   asInteger() const;
         virtual LLSD::Real      asReal() const      { return mValue; }
         virtual LLSD::String    asString() const;
+
+        virtual LLSD::String asXMLRPCValue() const { return "<double>" + std::to_string(mValue) + "</double>"; }
     };
 
     LLSD::Boolean ImplReal::asBoolean() const
@@ -286,9 +295,11 @@ namespace
         virtual LLSD::URI       asURI() const   { return LLURI(mValue); }
         virtual size_t          size() const    { return mValue.size(); }
         virtual const LLSD::String& asStringRef() const { return mValue; }
+
+        virtual LLSD::String asXMLRPCValue() const { return "<string>" + LLStringFn::xml_encode(mValue) + "</string>"; }
     };
 
-    LLSD::Integer   ImplString::asInteger() const
+    LLSD::Integer ImplString::asInteger() const
     {
         // This must treat "1.23" not as an error, but as a number, which is
         // then truncated down to an integer.  Hence, this code doesn't call
@@ -298,7 +309,7 @@ namespace
         return (int)asReal();
     }
 
-    LLSD::Real      ImplString::asReal() const
+    LLSD::Real ImplString::asReal() const
     {
         F64 v = 0.0;
         std::istringstream i_stream(mValue);
@@ -323,6 +334,8 @@ namespace
 
         virtual LLSD::String    asString() const{ return mValue.asString(); }
         virtual LLSD::UUID      asUUID() const  { return mValue; }
+
+        virtual LLSD::String asXMLRPCValue() const { return "<string>" + mValue.asString() + "</string>"; }
     };
 
 
@@ -344,6 +357,8 @@ namespace
         }
         virtual LLSD::String    asString() const{ return mValue.asString(); }
         virtual LLSD::Date      asDate() const  { return mValue; }
+
+        virtual LLSD::String asXMLRPCValue() const { return "<dateTime.iso8601>" + mValue.toHTTPDateString("%FT%T") + "</dateTime.iso8601>"; }
     };
 
 
@@ -355,6 +370,8 @@ namespace
 
         virtual LLSD::String    asString() const{ return mValue.asString(); }
         virtual LLSD::URI       asURI() const   { return mValue; }
+
+        virtual LLSD::String asXMLRPCValue() const { return "<string>" + LLStringFn::xml_encode(mValue.asString()) + "</string>"; }
     };
 
 
@@ -365,13 +382,15 @@ namespace
         ImplBinary(const LLSD::Binary& v) : Base(v) { }
 
         virtual const LLSD::Binary& asBinary() const{ return mValue; }
+
+        virtual LLSD::String asXMLRPCValue() const { return "<base64>" + LLBase64::encode(mValue.data(), mValue.size()) + "</base64>"; }
     };
 
 
     class ImplMap : public LLSD::Impl
     {
     private:
-        typedef std::map<LLSD::String, LLSD>    DataMap;
+        typedef std::map<LLSD::String, LLSD> DataMap;
 
         DataMap mData;
 
@@ -387,6 +406,19 @@ namespace
 
         virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
 
+        virtual LLSD::String asXMLRPCValue() const
+        {
+            std::ostringstream os;
+            os << "<struct>";
+            for (const auto& it : mData)
+            {
+                os << "<member><name>" << LLStringFn::xml_encode(it.first) << "</name>"
+                    << it.second.asXMLRPCValue() << "</member>";
+            }
+            os << "</struct>";
+            return os.str();
+        }
+
         virtual bool has(const LLSD::String&) const;
 
         using LLSD::Impl::get; // Unhiding get(size_t)
@@ -511,7 +543,7 @@ namespace
     class ImplArray : public LLSD::Impl
     {
     private:
-        typedef std::vector<LLSD>   DataVector;
+        typedef std::vector<LLSD> DataVector;
 
         DataVector mData;
 
@@ -527,6 +559,18 @@ namespace
 
         virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
 
+        virtual LLSD::String asXMLRPCValue() const
+        {
+            std::ostringstream os;
+            os << "<array><data>";
+            for (const auto& it : mData)
+            {
+                os << it.asXMLRPCValue();
+            }
+            os << "</data></array>";
+            return os.str();
+        }
+
         using LLSD::Impl::get; // Unhiding get(LLSD::String)
         using LLSD::Impl::erase; // Unhiding erase(LLSD::String)
         using LLSD::Impl::ref; // Unhiding ref(LLSD::String)
@@ -872,6 +916,155 @@ const LLSD::Binary& LLSD::asBinary() const  { return safe(impl).asBinary(); }
 
 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 a5e735b561..5532decfc3 100644
--- a/indra/llcommon/llsd.h
+++ b/indra/llcommon/llsd.h
@@ -259,10 +259,24 @@ public:
         UUID    asUUID() const;
         Date    asDate() const;
         URI     asURI() const;
-        const Binary&   asBinary() const;
+        const Binary& asBinary() const;
 
         // asStringRef on any non-string type will return a ref to an empty string.
-        const String&   asStringRef() const;
+        const String& asStringRef() const;
+
+        // Return "<value><((type))>((scalar value or recursive calls))</((type))></value>"
+        // 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(); }
@@ -275,7 +289,7 @@ public:
 
         // This is needed because most platforms do not automatically
         // convert the boolean negation as a bool in an if statement.
-        bool operator!() const {return !asBoolean();}
+        bool operator!() const { return !asBoolean(); }
     //@}
 
     /** @name Character Pointer Helpers
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 514d73b24b..6f3d193d6b 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -1208,6 +1208,75 @@ namespace LLStringFn
         return output;
     }
 
+    using literals_t = std::map<char, std::string>;
+    static const literals_t xml_elem_literals =
+    {
+        { '<', "&lt;" },
+        { '>', "&gt;" },
+        { '&', "&amp;" }
+    };
+    static const literals_t xml_attr_literals =
+    {
+        { '"', "&quot;" },
+        { '\'', "&apos;" }
+    };
+
+    static void literals_encode(std::string& text, const literals_t& literals)
+    {
+        for (const std::pair<char, std::string> it : literals)
+        {
+            std::string::size_type pos = 0;
+            while ((pos = text.find(it.first, pos)) != std::string::npos)
+            {
+                text.replace(pos, 1, it.second);
+                pos += it.second.size();
+            }
+        }
+    }
+
+    static void literals_decode(std::string& text, const literals_t& literals)
+    {
+        for (const std::pair<char, std::string> it : literals)
+        {
+            std::string::size_type pos = 0;
+            while ((pos = text.find(it.second, pos)) != std::string::npos)
+            {
+                text[pos++] = it.first;
+                text.erase(pos, it.second.size() - 1);
+            }
+        }
+    }
+
+    /**
+     * @brief Replace all characters that are not allowed in XML 1.0
+     * with corresponding literals: [ < > & ] => [ &lt; &gt; &amp; ]
+     */
+    std::string xml_encode(const std::string& input, bool for_attribute)
+    {
+        std::string result(input);
+        literals_encode(result, xml_elem_literals);
+        if (for_attribute)
+        {
+            literals_encode(result, xml_attr_literals);
+        }
+        return result;
+    }
+
+    /**
+     * @brief Replace some of XML literals that are defined in XML 1.0
+     * with corresponding characters: [ &lt; &gt; &amp; ] => [ < > & ]
+     */
+    std::string xml_decode(const std::string& input, bool for_attribute)
+    {
+        std::string result(input);
+        literals_decode(result, xml_elem_literals);
+        if (for_attribute)
+        {
+            literals_decode(result, xml_attr_literals);
+        }
+        return result;
+    }
+
     /**
      * @brief Replace all control characters (c < 0x20) with replacement in
      * string.
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 123f4184b5..b69a068830 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -889,6 +889,20 @@ namespace LLStringFn
     LL_COMMON_API std::string strip_invalid_xml(const std::string& input);
 
 
+    /**
+     * @brief Replace all characters that are not allowed in XML 1.0
+     * with corresponding literals: [ < > & ] => [ &lt; &gt; &amp; ]
+     */
+    LL_COMMON_API std::string xml_encode(const std::string& input, bool for_attribute = false);
+
+
+    /**
+     * @brief Replace some of XML literals that are defined in XML 1.0
+     * with corresponding characters: [ &lt; &gt; &amp; ] => [ < > & ]
+     */
+    LL_COMMON_API std::string xml_decode(const std::string& input, bool for_attribute = false);
+
+
     /**
      * @brief Replace all control characters (0 <= c < 0x20) with replacement in
      * string.   This is safe for utf-8
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 5322139304..b2757a7306 100644
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -27,7 +27,6 @@ set(llmessage_SOURCE_FILES
     lldatapacker.cpp
     lldispatcher.cpp
     llexperiencecache.cpp
-    llfiltersd2xmlrpc.cpp
     llgenericstreamingmessage.cpp
     llhost.cpp
     llhttpnode.cpp
@@ -111,7 +110,6 @@ set(llmessage_HEADER_FILES
     lleventflags.h
     llexperiencecache.h
     llextendedstatus.h
-    llfiltersd2xmlrpc.h
     llfollowcamparams.h
     llgenericstreamingmessage.h
     llhost.h
@@ -193,7 +191,6 @@ target_link_libraries(
         llfilesystem
         llmath
         llcorehttp
-        ll::xmlrpc-epi
 )
 target_include_directories( llmessage  INTERFACE   ${CMAKE_CURRENT_SOURCE_DIR})
 
diff --git a/indra/llmessage/llfiltersd2xmlrpc.cpp b/indra/llmessage/llfiltersd2xmlrpc.cpp
deleted file mode 100644
index 84b56d54bf..0000000000
--- a/indra/llmessage/llfiltersd2xmlrpc.cpp
+++ /dev/null
@@ -1,778 +0,0 @@
-/**
- * @file llfiltersd2xmlrpc.cpp
- * @author Phoenix
- * @date 2005-04-26
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-/**
- * xml rpc request:
- * <code>
- * <?xml version="1.0"?>
- * <methodCall><methodName>examples.getStateName</methodName>
- * <params><param><value><i4>41</i4></value></param></params>
- * </methodCall>
- * </code>
- *
- * xml rpc response:
- * <code>
- * <?xml version="1.0"?>
- * <methodResponse>
- * <params><param><value><string>South Dakota</string></value></param></params>
- * </methodResponse>
- * </code>
- *
- * xml rpc fault:
- * </code>
- * <?xml version="1.0"?>
- * <methodResponse>
- * <fault><value><struct>
- * <member><name>faultCode</name><value><int>4</int></value></member>
- * <member><name>faultString</name><value><string>...</string></value></member>
- * </struct></value></fault>
- * </methodResponse>
- * </code>
- *
- * llsd rpc request:
- * <code>
- * { 'method':'...', 'parameter':...]}
- * </code>
- *
- * llsd rpc response:
- * <code>
- * { 'response':... }
- * </code>
- *
- * llsd rpc fault:
- * <code>
- * { 'fault': {'code':i..., 'description':'...'} }
- * </code>
- *
- */
-
-#include "linden_common.h"
-#include "llfiltersd2xmlrpc.h"
-
-#include <sstream>
-#include <iterator>
-
-#ifdef LL_USESYSTEMLIBS
-#include <xmlrpc.h>
-#else
-#include <xmlrpc-epi/xmlrpc.h>
-#endif
-
-#include "apr_base64.h"
-
-#include "llbuffer.h"
-#include "llbufferstream.h"
-#include "llfasttimer.h"
-#include "llmemorystream.h"
-#include "llsd.h"
-#include "llsdserialize.h"
-#include "lluuid.h"
-
-// spammy mode
-//#define LL_SPEW_STREAM_OUT_DEBUGGING 1
-
-/**
- * String constants
- */
-static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
-static const char XMLRPC_REQUEST_HEADER_1[] = "<methodCall><methodName>";
-static const char XMLRPC_REQUEST_HEADER_2[] = "</methodName><params>";
-static const char XMLRPC_REQUEST_FOOTER[] = "</params></methodCall>";
-static const char XMLRPC_METHOD_RESPONSE_HEADER[] = "<methodResponse>";
-static const char XMLRPC_METHOD_RESPONSE_FOOTER[] = "</methodResponse>";
-static const char XMLRPC_RESPONSE_HEADER[] = "<params><param>";
-static const char XMLRPC_RESPONSE_FOOTER[] = "</param></params>";
-static const char XMLRPC_FAULT_1[] = "<fault><value><struct><member><name>faultCode</name><value><int>";
-static const char XMLRPC_FAULT_2[] = "</int></value></member><member><name>faultString</name><value><string>";
-static const char XMLRPC_FAULT_3[] = "</string></value></member></struct></value></fault>";
-static const char LLSDRPC_RESPONSE_HEADER[] = "{'response':";
-static const char LLSDRPC_RESPONSE_FOOTER[] = "}";
-const char LLSDRPC_REQUEST_HEADER_1[] = "{'method':'";
-const char LLSDRPC_REQUEST_HEADER_2[] = "', 'parameter': ";
-const char LLSDRPC_REQUEST_FOOTER[] = "}";
-static const char LLSDRPC_FAULT_HADER_1[] = "{ 'fault': {'code':i";
-static const char LLSDRPC_FAULT_HADER_2[] = ", 'description':";
-static const char LLSDRPC_FAULT_FOOTER[] = "} }";
-static const S32 DEFAULT_PRECISION = 20;
-
-/**
- * LLFilterSD2XMLRPC
- */
-LLFilterSD2XMLRPC::LLFilterSD2XMLRPC()
-{
-}
-
-LLFilterSD2XMLRPC::~LLFilterSD2XMLRPC()
-{
-}
-
-std::string xml_escape_string(const std::string& in)
-{
-    std::ostringstream out;
-    std::string::const_iterator it = in.begin();
-    std::string::const_iterator end = in.end();
-    for(; it != end; ++it)
-    {
-        switch((*it))
-        {
-        case '<':
-            out << "&lt;";
-            break;
-        case '>':
-            out << "&gt;";
-            break;
-        case '&':
-            out << "&amp;";
-            break;
-        case '\'':
-            out << "&apos;";
-            break;
-        case '"':
-            out << "&quot;";
-            break;
-        default:
-            out << (*it);
-            break;
-        }
-    }
-    return out.str();
-}
-
-void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
-{
-    ostr << "<value>";
-    switch(sd.type())
-    {
-    case LLSD::TypeMap:
-    {
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(map) BEGIN" << LL_ENDL;
-#endif
-        ostr << "<struct>";
-        if(ostr.fail())
-        {
-            LL_INFOS() << "STREAM FAILURE writing struct" << LL_ENDL;
-        }
-        LLSD::map_const_iterator it = sd.beginMap();
-        LLSD::map_const_iterator end = sd.endMap();
-        for(; it != end; ++it)
-        {
-            ostr << "<member><name>" << xml_escape_string((*it).first)
-                << "</name>";
-            streamOut(ostr, (*it).second);
-            if(ostr.fail())
-            {
-                LL_INFOS() << "STREAM FAILURE writing '" << (*it).first
-                        << "' with sd type " << (*it).second.type() << LL_ENDL;
-            }
-            ostr << "</member>";
-        }
-        ostr << "</struct>";
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(map) END" << LL_ENDL;
-#endif
-        break;
-    }
-    case LLSD::TypeArray:
-    {
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(array) BEGIN" << LL_ENDL;
-#endif
-        ostr << "<array><data>";
-        LLSD::array_const_iterator it = sd.beginArray();
-        LLSD::array_const_iterator end = sd.endArray();
-        for(; it != end; ++it)
-        {
-            streamOut(ostr, *it);
-            if(ostr.fail())
-            {
-                LL_INFOS() << "STREAM FAILURE writing array element sd type "
-                        << (*it).type() << LL_ENDL;
-            }
-        }
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(array) END" << LL_ENDL;
-#endif
-        ostr << "</data></array>";
-        break;
-    }
-    case LLSD::TypeUndefined:
-        // treat undefined as a bool with a false value.
-    case LLSD::TypeBoolean:
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(bool)" << LL_ENDL;
-#endif
-        ostr << "<boolean>" << (sd.asBoolean() ? "1" : "0") << "</boolean>";
-        break;
-    case LLSD::TypeInteger:
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(int)" << LL_ENDL;
-#endif
-        ostr << "<i4>" << sd.asInteger() << "</i4>";
-        break;
-    case LLSD::TypeReal:
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(real)" << LL_ENDL;
-#endif
-        ostr << "<double>" << sd.asReal() << "</double>";
-        break;
-    case LLSD::TypeString:
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(string)" << LL_ENDL;
-#endif
-        ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
-        break;
-    case LLSD::TypeUUID:
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(uuid)" << LL_ENDL;
-#endif
-        // serialize it as a string
-        ostr << "<string>" << sd.asString() << "</string>";
-        break;
-    case LLSD::TypeURI:
-    {
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(uri)" << LL_ENDL;
-#endif
-        // serialize it as a string
-        ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
-        break;
-    }
-    case LLSD::TypeBinary:
-    {
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(binary)" << LL_ENDL;
-#endif
-        // this is pretty inefficient, but we'll deal with that
-        // problem when it becomes one.
-        ostr << "<base64>";
-        LLSD::Binary buffer = sd.asBinary();
-        if(!buffer.empty())
-        {
-            // *TODO: convert to LLBase64
-            int b64_buffer_length = apr_base64_encode_len(static_cast<int>(buffer.size()));
-            char* b64_buffer = new char[b64_buffer_length];
-            b64_buffer_length = apr_base64_encode_binary(
-                b64_buffer,
-                &buffer[0],
-                static_cast<int>(buffer.size()));
-            ostr.write(b64_buffer, b64_buffer_length - 1);
-            delete[] b64_buffer;
-        }
-        ostr << "</base64>";
-        break;
-    }
-    case LLSD::TypeDate:
-#if LL_SPEW_STREAM_OUT_DEBUGGING
-        LL_INFOS() << "streamOut(date)" << LL_ENDL;
-#endif
-        // no need to escape this since it will be alpha-numeric.
-        ostr << "<dateTime.iso8601>" << sd.asString() << "</dateTime.iso8601>";
-        break;
-    default:
-        // unhandled type
-        LL_WARNS() << "Unhandled structured data type: " << sd.type()
-            << LL_ENDL;
-        break;
-    }
-    ostr << "</value>";
-}
-
-/**
- * LLFilterSD2XMLRPCResponse
- */
-
-LLFilterSD2XMLRPCResponse::LLFilterSD2XMLRPCResponse()
-{
-}
-
-LLFilterSD2XMLRPCResponse::~LLFilterSD2XMLRPCResponse()
-{
-}
-
-
-// virtual
-LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
-    const LLChannelDescriptors& channels,
-    buffer_ptr_t& buffer,
-    bool& eos,
-    LLSD& context,
-    LLPumpIO* pump)
-{
-    LL_PROFILE_ZONE_SCOPED;
-
-    PUMP_DEBUG;
-    // This pipe does not work if it does not have everyting. This
-    // could be addressed by making a stream parser for llsd which
-    // handled partial information.
-    if(!eos)
-    {
-        return STATUS_BREAK;
-    }
-
-    PUMP_DEBUG;
-    // we have everyting in the buffer, so turn the structure data rpc
-    // response into an xml rpc response.
-    LLBufferStream stream(channels, buffer.get());
-    stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
-    LLSD sd;
-    LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
-
-    PUMP_DEBUG;
-    LLIOPipe::EStatus rv = STATUS_ERROR;
-    if(sd.has("response"))
-    {
-        PUMP_DEBUG;
-        // it is a normal response. pack it up and ship it out.
-        stream.precision(DEFAULT_PRECISION);
-        stream << XMLRPC_RESPONSE_HEADER;
-        streamOut(stream, sd["response"]);
-        stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER;
-        rv = STATUS_DONE;
-    }
-    else if(sd.has("fault"))
-    {
-        PUMP_DEBUG;
-        // it is a fault.
-        stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger()
-            << XMLRPC_FAULT_2
-            << xml_escape_string(sd["fault"]["description"].asString())
-            << XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER;
-        rv = STATUS_DONE;
-    }
-    else
-    {
-        LL_WARNS() << "Unable to determine the type of LLSD response." << LL_ENDL;
-    }
-    PUMP_DEBUG;
-    return rv;
-}
-
-/**
- * LLFilterSD2XMLRPCRequest
- */
-LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest()
-{
-}
-
-LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest(const char* method)
-{
-    if(method)
-    {
-        mMethod.assign(method);
-    }
-}
-
-LLFilterSD2XMLRPCRequest::~LLFilterSD2XMLRPCRequest()
-{
-}
-
-// virtual
-LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
-    const LLChannelDescriptors& channels,
-    buffer_ptr_t& buffer,
-    bool& eos,
-    LLSD& context,
-    LLPumpIO* pump)
-{
-    LL_PROFILE_ZONE_SCOPED;
-    // This pipe does not work if it does not have everyting. This
-    // could be addressed by making a stream parser for llsd which
-    // handled partial information.
-    PUMP_DEBUG;
-    if(!eos)
-    {
-        LL_INFOS() << "!eos" << LL_ENDL;
-        return STATUS_BREAK;
-    }
-
-    // See if we can parse it
-    LLBufferStream stream(channels, buffer.get());
-    LLSD sd;
-    LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
-    if(stream.fail())
-    {
-        LL_INFOS() << "STREAM FAILURE reading structure data." << LL_ENDL;
-    }
-
-    PUMP_DEBUG;
-    // We can get the method and parameters from either the member
-    // function or passed in via the buffer. We prefer the buffer if
-    // we found a parameter and a method, or fall back to using
-    // mMethod and putting everyting in the buffer into the parameter.
-    std::string method;
-    LLSD param_sd;
-    if(sd.has("method") && sd.has("parameter"))
-    {
-        method = sd["method"].asString();
-        param_sd = sd["parameter"];
-    }
-    else
-    {
-        method = mMethod;
-        param_sd = sd;
-    }
-    if(method.empty())
-    {
-        LL_WARNS() << "SD -> XML Request no method found." << LL_ENDL;
-        return STATUS_ERROR;
-    }
-
-    PUMP_DEBUG;
-    // We have a method, and some kind of parameter, so package it up
-    // and send it out.
-    LLBufferStream ostream(channels, buffer.get());
-    ostream.precision(DEFAULT_PRECISION);
-    if(ostream.fail())
-    {
-        LL_INFOS() << "STREAM FAILURE setting precision" << LL_ENDL;
-    }
-    ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1
-        << xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2;
-    if(ostream.fail())
-    {
-        LL_INFOS() << "STREAM FAILURE writing method headers" << LL_ENDL;
-    }
-    switch(param_sd.type())
-    {
-    case LLSD::TypeMap:
-        // If the params are a map, then we do not want to iterate
-        // through them since the iterators returned will be map
-        // ordered un-named values, which will lose the names, and
-        // only stream the values, turning it into an array.
-        ostream << "<param>";
-        streamOut(ostream, param_sd);
-        ostream << "</param>";
-        break;
-    case LLSD::TypeArray:
-    {
-
-        LLSD::array_iterator it = param_sd.beginArray();
-        LLSD::array_iterator end = param_sd.endArray();
-        for(; it != end; ++it)
-        {
-            ostream << "<param>";
-            streamOut(ostream, *it);
-            ostream << "</param>";
-        }
-        break;
-    }
-    default:
-        ostream << "<param>";
-        streamOut(ostream, param_sd);
-        ostream << "</param>";
-        break;
-    }
-
-    stream << XMLRPC_REQUEST_FOOTER;
-    return STATUS_DONE;
-}
-
-/**
- * LLFilterXMLRPCResponse2LLSD
- */
-// this is a c function here since it's really an implementation
-// detail that requires a header file just get the definition of the
-// parameters.
-LLIOPipe::EStatus stream_out(std::ostream& ostr, XMLRPC_VALUE value)
-{
-    XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(value);
-    LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
-    switch(type)
-    {
-    case xmlrpc_type_base64:
-    {
-        S32 len = XMLRPC_GetValueStringLen(value);
-        const char* buf = XMLRPC_GetValueBase64(value);
-        ostr << " b(";
-        if((len > 0) && buf)
-        {
-            ostr << len << ")\"";
-            ostr.write(buf, len);
-            ostr << "\"";
-        }
-        else
-        {
-            ostr << "0)\"\"";
-        }
-        break;
-    }
-    case xmlrpc_type_boolean:
-        //LL_DEBUGS() << "stream_out() bool" << LL_ENDL;
-        ostr << " " << (XMLRPC_GetValueBoolean(value) ? "true" : "false");
-        break;
-    case xmlrpc_type_datetime:
-        ostr << " d\"" << XMLRPC_GetValueDateTime_ISO8601(value) << "\"";
-        break;
-    case xmlrpc_type_double:
-        ostr << " r" << XMLRPC_GetValueDouble(value);
-        //LL_DEBUGS() << "stream_out() double" << XMLRPC_GetValueDouble(value)
-        //       << LL_ENDL;
-        break;
-    case xmlrpc_type_int:
-        ostr << " i" << XMLRPC_GetValueInt(value);
-        //LL_DEBUGS() << "stream_out() integer:" << XMLRPC_GetValueInt(value)
-        //       << LL_ENDL;
-        break;
-    case xmlrpc_type_string:
-        //LL_DEBUGS() << "stream_out() string: " << str << LL_ENDL;
-        ostr << " s(" << XMLRPC_GetValueStringLen(value) << ")'"
-            << XMLRPC_GetValueString(value) << "'";
-        break;
-    case xmlrpc_type_array: // vector
-    case xmlrpc_type_mixed: // vector
-    {
-        //LL_DEBUGS() << "stream_out() array" << LL_ENDL;
-        ostr << " [";
-        U32 needs_comma = 0;
-        XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
-        while(current && (LLIOPipe::STATUS_OK == status))
-        {
-            if(needs_comma++) ostr << ",";
-            status = stream_out(ostr, current);
-            current = XMLRPC_VectorNext(value);
-        }
-        ostr << "]";
-        break;
-    }
-    case xmlrpc_type_struct: // still vector
-    {
-        //LL_DEBUGS() << "stream_out() struct" << LL_ENDL;
-        ostr << " {";
-        std::string name;
-        U32 needs_comma = 0;
-        XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
-        while(current && (LLIOPipe::STATUS_OK == status))
-        {
-            if(needs_comma++) ostr << ",";
-            name.assign(XMLRPC_GetValueID(current));
-            ostr << "'" << LLSDNotationFormatter::escapeString(name) << "':";
-            status = stream_out(ostr, current);
-            current = XMLRPC_VectorNext(value);
-        }
-        ostr << "}";
-        break;
-    }
-    case xmlrpc_type_empty:
-    case xmlrpc_type_none:
-    default:
-        status = LLIOPipe::STATUS_ERROR;
-        LL_WARNS() << "Found an empty xmlrpc type.." << LL_ENDL;
-        // not much we can do here...
-        break;
-    };
-    return status;
-}
-
-LLFilterXMLRPCResponse2LLSD::LLFilterXMLRPCResponse2LLSD()
-{
-}
-
-LLFilterXMLRPCResponse2LLSD::~LLFilterXMLRPCResponse2LLSD()
-{
-}
-
-LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
-    const LLChannelDescriptors& channels,
-    buffer_ptr_t& buffer,
-    bool& eos,
-    LLSD& context,
-    LLPumpIO* pump)
-{
-    LL_PROFILE_ZONE_SCOPED;
-
-    PUMP_DEBUG;
-    if(!eos) return STATUS_BREAK;
-    if(!buffer) return STATUS_ERROR;
-
-    PUMP_DEBUG;
-    // *FIX: This technique for reading data is far from optimal. We
-    // need to have some kind of istream interface into the xml
-    // parser...
-    S32 bytes = buffer->countAfter(channels.in(), NULL);
-    if(!bytes) return STATUS_ERROR;
-    char* buf = new char[bytes + 1];
-    buf[bytes] = '\0';
-    buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
-
-    //LL_DEBUGS() << "xmlrpc response: " << buf << LL_ENDL;
-
-    PUMP_DEBUG;
-    XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML(
-        buf,
-        bytes,
-        NULL);
-    if(!response)
-    {
-        LL_WARNS() << "XML -> SD Response unable to parse xml." << LL_ENDL;
-        delete[] buf;
-        return STATUS_ERROR;
-    }
-
-    PUMP_DEBUG;
-    LLBufferStream stream(channels, buffer.get());
-    stream.precision(DEFAULT_PRECISION);
-    if(XMLRPC_ResponseIsFault(response))
-    {
-        PUMP_DEBUG;
-        stream << LLSDRPC_FAULT_HADER_1
-               << XMLRPC_GetResponseFaultCode(response)
-               << LLSDRPC_FAULT_HADER_2;
-        const char* fault_str = XMLRPC_GetResponseFaultString(response);
-        std::string fault_string;
-        if(fault_str)
-        {
-            fault_string.assign(fault_str);
-        }
-        stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
-           << "'" <<LLSDRPC_FAULT_FOOTER;
-    }
-    else
-    {
-        PUMP_DEBUG;
-        stream << LLSDRPC_RESPONSE_HEADER;
-        XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
-        if(param)
-        {
-            stream_out(stream, param);
-        }
-        stream << LLSDRPC_RESPONSE_FOOTER;
-    }
-    PUMP_DEBUG;
-    XMLRPC_RequestFree(response, 1);
-    delete[] buf;
-    PUMP_DEBUG;
-    return STATUS_DONE;
-}
-
-/**
- * LLFilterXMLRPCRequest2LLSD
- */
-LLFilterXMLRPCRequest2LLSD::LLFilterXMLRPCRequest2LLSD()
-{
-}
-
-LLFilterXMLRPCRequest2LLSD::~LLFilterXMLRPCRequest2LLSD()
-{
-}
-
-LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
-    const LLChannelDescriptors& channels,
-    buffer_ptr_t& buffer,
-    bool& eos,
-    LLSD& context,
-    LLPumpIO* pump)
-{
-    LL_PROFILE_ZONE_SCOPED;
-    PUMP_DEBUG;
-    if(!eos) return STATUS_BREAK;
-    if(!buffer) return STATUS_ERROR;
-
-    PUMP_DEBUG;
-    // *FIX: This technique for reading data is far from optimal. We
-    // need to have some kind of istream interface into the xml
-    // parser...
-    S32 bytes = buffer->countAfter(channels.in(), NULL);
-    if(!bytes) return STATUS_ERROR;
-    char* buf = new char[bytes + 1];
-    buf[bytes] = '\0';
-    buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
-
-    //LL_DEBUGS() << "xmlrpc request: " << buf << LL_ENDL;
-
-    // Check the value in the buffer. XMLRPC_REQUEST_FromXML will report a error code 4 if
-    // values that are less than 0x20 are passed to it, except
-    // 0x09: Horizontal tab; 0x0a: New Line; 0x0d: Carriage
-    U8* cur_pBuf = (U8*)buf;
-    U8 cur_char;
-    for (S32 i=0; i<bytes; i++)
-    {
-        cur_char = *cur_pBuf;
-        if (   cur_char < 0x20
-            && 0x09 != cur_char
-            && 0x0a != cur_char
-            && 0x0d != cur_char )
-        {
-            *cur_pBuf = '?';
-        }
-        ++cur_pBuf;
-    }
-
-    PUMP_DEBUG;
-    XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML(
-        buf,
-        bytes,
-        NULL);
-    if(!request)
-    {
-        LL_WARNS() << "XML -> SD Request process parse error." << LL_ENDL;
-        delete[] buf;
-        return STATUS_ERROR;
-    }
-
-    PUMP_DEBUG;
-    LLBufferStream stream(channels, buffer.get());
-    stream.precision(DEFAULT_PRECISION);
-    const char* name = XMLRPC_RequestGetMethodName(request);
-    stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "")
-           << LLSDRPC_REQUEST_HEADER_2;
-    XMLRPC_VALUE param = XMLRPC_RequestGetData(request);
-    if(param)
-    {
-        PUMP_DEBUG;
-        S32 size = XMLRPC_VectorSize(param);
-        if(size > 1)
-        {
-            // if there are multiple parameters, stuff the values into
-            // an array so that the next step in the chain can read them.
-            stream << "[";
-        }
-        XMLRPC_VALUE current = XMLRPC_VectorRewind(param);
-        bool needs_comma = false;
-        while(current)
-        {
-            if(needs_comma)
-            {
-                stream << ",";
-            }
-            needs_comma = true;
-            stream_out(stream, current);
-            current = XMLRPC_VectorNext(param);
-        }
-        if(size > 1)
-        {
-            // close the array
-            stream << "]";
-        }
-    }
-    stream << LLSDRPC_REQUEST_FOOTER;
-    XMLRPC_RequestFree(request, 1);
-    delete[] buf;
-    PUMP_DEBUG;
-    return STATUS_DONE;
-}
-
diff --git a/indra/llmessage/llfiltersd2xmlrpc.h b/indra/llmessage/llfiltersd2xmlrpc.h
deleted file mode 100644
index 55938d3e2b..0000000000
--- a/indra/llmessage/llfiltersd2xmlrpc.h
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * @file llfiltersd2xmlrpc.h
- * @author Phoenix
- * @date 2005-04-26
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLFILTERSD2XMLRPC_H
-#define LL_LLFILTERSD2XMLRPC_H
-
-/**
- * These classes implement the necessary pipes for translating between
- * xmlrpc and llsd rpc. The llsd rpcs mechanism was developed as an
- * extensible and easy to parse serialization grammer which maintains
- * a time efficient in-memory representation.
- */
-
-#include <iosfwd>
-#include "lliopipe.h"
-
-/**
- * @class LLFilterSD2XMLRPC
- * @brief Filter from serialized LLSD to an XMLRPC method call
- *
- * This clas provides common functionality for the LLFilterSD2XMLRPRC
- * request and response classes.
- */
-class LLFilterSD2XMLRPC : public LLIOPipe
-{
-public:
-    LLFilterSD2XMLRPC();
-    virtual ~LLFilterSD2XMLRPC();
-
-protected:
-    /**
-     * @brief helper method
-     */
-    void streamOut(std::ostream& ostr, const LLSD& sd);
-};
-
-/**
- * @class LLFilterSD2XMLRPCResponse
- * @brief Filter from serialized LLSD to an XMLRPC response
- *
- * This class filters a serialized LLSD object to an xmlrpc
- * repsonse. Since resonses are limited to a single param, the xmlrprc
- * response only serializes it as one object.
- * This class correctly handles normal llsd responses as well as llsd
- * rpc faults.
- *
- * For example, if given:
- * <code>{'response':[ i200, r3.4, {"foo":"bar"} ]}</code>
- * Would generate:
- * <code>
- *  <?xml version="1.0"?>
- *  <methodResponse><params><param><array><data>
- *    <value><int>200</int></value>
- *    <value><double>3.4</double></value>
- *    <value><struct><member>
- *      <name>foo</name><value><string>bar</string></value></member>
- *      </struct></value>
- *  </data></array></param></params></methodResponse>
- * </code>
- */
-class LLFilterSD2XMLRPCResponse : public LLFilterSD2XMLRPC
-{
-public:
-    // constructor
-    LLFilterSD2XMLRPCResponse();
-
-    // destructor
-    virtual ~LLFilterSD2XMLRPCResponse();
-
-    /* @name LLIOPipe virtual implementations
-     */
-    //@{
-protected:
-    /**
-     * @brief Process the data in buffer.
-     */
-    virtual EStatus process_impl(
-        const LLChannelDescriptors& channels,
-        buffer_ptr_t& buffer,
-        bool& eos,
-        LLSD& context,
-        LLPumpIO* pump);
-    //@}
-};
-
-/**
- * @class LLFilterSD2XMLRPCRequest
- * @brief Filter from serialized LLSD to an XMLRPC method call
- *
- * This class will accept any kind of serialized LLSD object, but you
- * probably want to have an array on the outer boundary since this
- * object will interpret each element in the top level LLSD as a
- * parameter into the xmlrpc spec.
- *
- * For example, you would represent 3 params as:
- * <code>
- *  {'method'='foo', 'parameter':[i200, r3.4, {"foo":"bar"}]}
- * </code>
- * To generate:
- * <code>
- *  <?xml version="1.0"?>
- *  <methodCall><params>
- *  <param><value><int>200</int></value></param>
- *  <param><value><double>3.4</double></value></param>
- *  <param><value><struct><member>
- *    <name>foo</name><value><string>bar</string></value></member>
- *    </struct></value></param>
- *  </params></methodCall>
- *
- * This class will accept 2 different kinds of encodings. The first
- * just an array of params as long as you specify the method in the
- * constructor. It will also accept a structured data in the form:
- * {'method':'$method_name', 'parameter':[...] } In the latter form, the
- * encoded 'method' will be used regardless of the construction of the
- * object, and the 'parameter' will be used as parameter to the call.
- */
-class LLFilterSD2XMLRPCRequest : public LLFilterSD2XMLRPC
-{
-public:
-    // constructor
-    LLFilterSD2XMLRPCRequest();
-
-    // constructor
-    LLFilterSD2XMLRPCRequest(const char* method);
-
-    // destructor
-    virtual ~LLFilterSD2XMLRPCRequest();
-
-    /* @name LLIOPipe virtual implementations
-     */
-    //@{
-protected:
-    /**
-     * @brief Process the data in buffer.
-     */
-    virtual EStatus process_impl(
-        const LLChannelDescriptors& channels,
-        buffer_ptr_t& buffer,
-        bool& eos,
-        LLSD& context,
-        LLPumpIO* pump);
-    //@}
-
-protected:
-    // The method name of this request.
-    std::string mMethod;
-};
-
-/**
- * @class LLFilterXMLRPCResponse2LLSD
- * @brief Filter from serialized XMLRPC method response to LLSD
- *
- * The xmlrpc spec states that responses can only have one element
- * which can be of any supported type.
- * This takes in  xml of the form:
- * <code>
- * <?xml version=\"1.0\"?><methodResponse><params><param>
- * <value><string>ok</string></value></param></params></methodResponse>
- * </code>
- * And processes it into:
- *  <code>'ok'</code>
- *
- */
-class LLFilterXMLRPCResponse2LLSD : public LLIOPipe
-{
-public:
-    // constructor
-    LLFilterXMLRPCResponse2LLSD();
-
-    // destructor
-    virtual ~LLFilterXMLRPCResponse2LLSD();
-
-    /* @name LLIOPipe virtual implementations
-     */
-    //@{
-protected:
-    /**
-     * @brief Process the data in buffer.
-     */
-    virtual EStatus process_impl(
-        const LLChannelDescriptors& channels,
-        buffer_ptr_t& buffer,
-        bool& eos,
-        LLSD& context,
-        LLPumpIO* pump);
-    //@}
-
-protected:
-};
-
-/**
- * @class LLFilterXMLRPCRequest2LLSD
- * @brief Filter from serialized XMLRPC method call to LLSD
- *
- * This takes in  xml of the form:
- * <code>
- *  <?xml version=\"1.0\"?><methodCall>
- *  <methodName>repeat</methodName>
- *  <params>
- *  <param><value><i4>4</i4></value></param>
- *  <param><value><string>ok</string></value></param>
- *  </params></methodCall>
- * </code>
- * And processes it into:
- *  <code>{ 'method':'repeat', 'params':[i4, 'ok'] }</code>
- */
-class LLFilterXMLRPCRequest2LLSD : public LLIOPipe
-{
-public:
-    // constructor
-    LLFilterXMLRPCRequest2LLSD();
-
-    // destructor
-    virtual ~LLFilterXMLRPCRequest2LLSD();
-
-    /* @name LLIOPipe virtual implementations
-     */
-    //@{
-protected:
-    /**
-     * @brief Process the data in buffer.
-     */
-    virtual EStatus process_impl(
-        const LLChannelDescriptors& channels,
-        buffer_ptr_t& buffer,
-        bool& eos,
-        LLSD& context,
-        LLPumpIO* pump);
-    //@}
-
-protected:
-};
-
-/**
- * @brief This function takes string, and escapes it appropritately
- * for inclusion as xml data.
- */
-std::string xml_escape_string(const std::string& in);
-
-/**
- * @brief Externally available constants
- */
-extern const char LLSDRPC_REQUEST_HEADER_1[];
-extern const char LLSDRPC_REQUEST_HEADER_2[];
-extern const char LLSDRPC_REQUEST_FOOTER[];
-
-#endif // LL_LLFILTERSD2XMLRPC_H
diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp
index 0fd4516844..7f6b8093fc 100644
--- a/indra/llxml/llxmlnode.cpp
+++ b/indra/llxml/llxmlnode.cpp
@@ -653,32 +653,24 @@ bool LLXMLNode::updateNode(
 // static
 bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXMLNode* defaults_tree)
 {
-    // Read file
-    LL_DEBUGS("XMLNode") << "parsing XML file: " << filename << LL_ENDL;
-    LLFILE* fp = LLFile::fopen(filename, "rb");     /* Flawfinder: ignore */
-    if (fp == NULL)
+    std::string xml = LLFile::getContents(filename);
+    if (xml.empty())
     {
-        node = NULL ;
-        return false;
+        LL_WARNS("XMLNode") << "no XML file: " << filename << LL_ENDL;
+    }
+    else if (parseBuffer(xml.data(), xml.size(), node, defaults_tree))
+    {
+        return true;
     }
-    fseek(fp, 0, SEEK_END);
-    U32 length = ftell(fp);
-    fseek(fp, 0, SEEK_SET);
-
-    U8* buffer = new U8[length+1];
-    size_t nread = fread(buffer, 1, length, fp);
-    buffer[nread] = 0;
-    fclose(fp);
 
-    bool rv = parseBuffer(buffer, static_cast<U32>(nread), node, defaults_tree);
-    delete [] buffer;
-    return rv;
+    node = nullptr;
+    return false;
 }
 
 // static
 bool LLXMLNode::parseBuffer(
-    U8* buffer,
-    U32 length,
+    const char* buffer,
+    U64 length,
     LLXMLNodePtr& node,
     LLXMLNode* defaults)
 {
@@ -693,20 +685,25 @@ bool LLXMLNode::parseBuffer(
 
     file_node->mParser = &my_parser;
 
-    XML_SetUserData(my_parser, (void *)file_node_ptr);
+    XML_SetUserData(my_parser, file_node_ptr);
 
     // Do the parsing
-    if (XML_Parse(my_parser, (const char *)buffer, length, true) != XML_STATUS_OK)
+    bool success = XML_STATUS_OK == XML_Parse(my_parser, buffer, (int)length, true);
+    if (!success)
     {
         LL_WARNS() << "Error parsing xml error code: "
                 << XML_ErrorString(XML_GetErrorCode(my_parser))
                 << " on line " << XML_GetCurrentLineNumber(my_parser)
+                << ", column " << XML_GetCurrentColumnNumber(my_parser)
                 << LL_ENDL;
     }
 
     // Deinit
     XML_ParserFree(my_parser);
 
+    if (!success)
+        return false;
+
     if (!file_node->mChildren || file_node->mChildren->map.size() != 1)
     {
         LL_WARNS() << "Parse failure - wrong number of top-level nodes xml."
diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h
index b8e29bbfef..b8f9e1ff69 100644
--- a/indra/llxml/llxmlnode.h
+++ b/indra/llxml/llxmlnode.h
@@ -129,20 +129,20 @@ public:
     void addChild(LLXMLNodePtr& new_child);
     void setParent(LLXMLNodePtr& new_parent); // reparent if necessary
 
-    // Serialization
+    // Deserialization
     static bool parseFile(
         const std::string& filename,
         LLXMLNodePtr& node,
-        LLXMLNode* defaults_tree);
+        LLXMLNode* defaults = nullptr);
     static bool parseBuffer(
-        U8* buffer,
-        U32 length,
+        const char* buffer,
+        U64 length,
         LLXMLNodePtr& node,
-        LLXMLNode* defaults);
+        LLXMLNode* defaults = nullptr);
     static bool parseStream(
         std::istream& str,
         LLXMLNodePtr& node,
-        LLXMLNode* defaults);
+        LLXMLNode* defaults = nullptr);
     static bool updateNode(
         LLXMLNodePtr& node,
         LLXMLNodePtr& update_node);
diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp
index 06c87343e2..8a4ab091a3 100644
--- a/indra/newview/llcurrencyuimanager.cpp
+++ b/indra/newview/llcurrencyuimanager.cpp
@@ -111,16 +111,17 @@ public:
     bool hasEstimate() const;
     std::string getLocalEstimate() const;
 
-    void startTransaction(TransactionType type,
-        const char* method, LLXMLRPCValue params);
+    void startTransaction(TransactionType type, const char* method, const LLSD& params);
+
+    // return true if update needed
     bool checkTransaction();
-        // return true if update needed
 
     void setError(const std::string& message, const std::string& uri);
     void clearError();
 
+    // return true if update needed
     bool considerUpdateCurrency();
-        // return true if update needed
+
     void currencyKey(S32);
     static void onCurrencyKey(LLLineEditor* caller, void* data);
 
@@ -160,32 +161,29 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()
         return;
     }
 
-    LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
-    keywordArgs.appendString("agentId", gAgent.getID().asString());
-    keywordArgs.appendString(
-        "secureSessionId",
-        gAgent.getSecureSessionID().asString());
-    keywordArgs.appendString("language", LLUI::getLanguage());
-    keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
-    keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel());
-    keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
-    keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
-    keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
+    const LLVersionInfo& vi(LLVersionInfo::instance());
+
+    LLSD params = LLSD::emptyMap();
+    params["agentId"] = gAgent.getID().asString();
+    params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+    params["language"] = LLUI::getLanguage();
+    params["currencyBuy"] = mUserCurrencyBuy;
+    params["viewerChannel"] = vi.getChannel();
+    params["viewerMajorVersion"] = vi.getMajor();
+    params["viewerMinorVersion"] = vi.getMinor();
+    params["viewerPatchVersion"] = vi.getPatch();
     // With GitHub builds, the build number is too big to fit in a 32-bit int,
-    // and XMLRPC_VALUE doesn't deal with integers wider than int. Use string.
-    keywordArgs.appendString("viewerBuildVersion", stringize(LLVersionInfo::instance().getBuild()));
-
-    LLXMLRPCValue params = LLXMLRPCValue::createArray();
-    params.append(keywordArgs);
+    // and XMLRPC value doesn't deal with integers wider than int. Use string.
+    params["viewerBuildVersion"] = std::to_string(vi.getBuild());
 
     startTransaction(TransactionCurrency, "getCurrencyQuote", params);
 }
 
 void LLCurrencyUIManager::Impl::finishCurrencyInfo()
 {
-    LLXMLRPCValue result = mTransaction->responseValue();
+    const LLSD& result = mTransaction->response();
 
-    bool success = result["success"].asBool();
+    bool success = result["success"].asBoolean();
     if (!success)
     {
         setError(
@@ -195,24 +193,24 @@ void LLCurrencyUIManager::Impl::finishCurrencyInfo()
         return;
     }
 
-    LLXMLRPCValue currency = result["currency"];
+    const LLSD& currency = result["currency"];
 
     // old XML-RPC server: estimatedCost = value in US cents
-    mUSDCurrencyEstimated = currency["estimatedCost"].isValid();
+    mUSDCurrencyEstimated = currency.has("estimatedCost");
     if (mUSDCurrencyEstimated)
     {
-        mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInt();
+        mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInteger();
     }
 
     // newer XML-RPC server: estimatedLocalCost = local currency string
-    mLocalCurrencyEstimated = currency["estimatedLocalCost"].isValid();
+    mLocalCurrencyEstimated = currency.has("estimatedLocalCost");
     if (mLocalCurrencyEstimated)
     {
         mLocalCurrencyEstimatedCost = currency["estimatedLocalCost"].asString();
         mSupportsInternationalBilling = true;
     }
 
-    S32 newCurrencyBuy = currency["currencyBuy"].asInt();
+    S32 newCurrencyBuy = currency["currencyBuy"].asInteger();
     if (newCurrencyBuy != mUserCurrencyBuy)
     {
         mUserCurrencyBuy = newCurrencyBuy;
@@ -224,36 +222,36 @@ void LLCurrencyUIManager::Impl::finishCurrencyInfo()
 
 void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
 {
-    LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
-    keywordArgs.appendString("agentId", gAgent.getID().asString());
-    keywordArgs.appendString(
-        "secureSessionId",
-        gAgent.getSecureSessionID().asString());
-    keywordArgs.appendString("language", LLUI::getLanguage());
-    keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
+    const LLVersionInfo& vi(LLVersionInfo::instance());
+
+    LLSD params = LLSD::emptyMap();
+    params["agentId"] = gAgent.getID().asString();
+    params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+    params["language"] = LLUI::getLanguage();
+    params["currencyBuy"] = mUserCurrencyBuy;
+    params["confirm"] = mSiteConfirm;
+    params["viewerChannel"] = vi.getChannel();
+    params["viewerMajorVersion"] = vi.getMajor();
+    params["viewerMinorVersion"] = vi.getMinor();
+    params["viewerPatchVersion"] = vi.getPatch();
+    // With GitHub builds, the build number is too big to fit in a 32-bit int,
+    // and XMLRPC value doesn't deal with integers wider than int. Use string.
+    params["viewerBuildVersion"] = std::to_string(vi.getBuild());
+
     if (mUSDCurrencyEstimated)
     {
-        keywordArgs.appendInt("estimatedCost", mUSDCurrencyEstimatedCost);
+        params["estimatedCost"] = mUSDCurrencyEstimatedCost;
     }
+
     if (mLocalCurrencyEstimated)
     {
-        keywordArgs.appendString("estimatedLocalCost", mLocalCurrencyEstimatedCost);
+        params["estimatedLocalCost"] = mLocalCurrencyEstimatedCost;
     }
-    keywordArgs.appendString("confirm", mSiteConfirm);
+
     if (!password.empty())
     {
-        keywordArgs.appendString("password", password);
+        params["password"] = password;
     }
-    keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel());
-    keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
-    keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
-    keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
-    // With GitHub builds, the build number is too big to fit in a 32-bit int,
-    // and XMLRPC_VALUE doesn't deal with integers wider than int. Use string.
-    keywordArgs.appendString("viewerBuildVersion", stringize(LLVersionInfo::instance().getBuild()));
-
-    LLXMLRPCValue params = LLXMLRPCValue::createArray();
-    params.append(keywordArgs);
 
     startTransaction(TransactionBuy, "buyCurrency", params);
 
@@ -263,9 +261,9 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
 
 void LLCurrencyUIManager::Impl::finishCurrencyBuy()
 {
-    LLXMLRPCValue result = mTransaction->responseValue();
+    const LLSD& result = mTransaction->response();
 
-    bool success = result["success"].asBool();
+    bool success = result["success"].asBoolean();
     if (!success)
     {
         setError(
@@ -282,7 +280,7 @@ void LLCurrencyUIManager::Impl::finishCurrencyBuy()
 }
 
 void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
-        const char* method, LLXMLRPCValue params)
+        const char* method, const LLSD& params)
 {
     static std::string transactionURI;
     if (transactionURI.empty())
@@ -293,12 +291,7 @@ void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
     delete mTransaction;
 
     mTransactionType = type;
-    mTransaction = new LLXMLRPCTransaction(
-        transactionURI,
-        method,
-        params,
-        false /* don't use gzip */
-        );
+    mTransaction = new LLXMLRPCTransaction(transactionURI, method, params);
 
     clearError();
 }
@@ -352,12 +345,17 @@ bool LLCurrencyUIManager::Impl::checkTransaction()
     {
         setError(mTransaction->statusMessage(), mTransaction->statusURI());
     }
-    else {
+    else
+    {
         switch (mTransactionType)
         {
-            case TransactionCurrency:   finishCurrencyInfo();   break;
-            case TransactionBuy:        finishCurrencyBuy();    break;
-            default: ;
+        case TransactionCurrency:
+            finishCurrencyInfo();
+            break;
+        case TransactionBuy:
+            finishCurrencyBuy();
+            break;
+        default:;
         }
     }
 
@@ -385,9 +383,8 @@ void LLCurrencyUIManager::Impl::clearError()
 
 bool LLCurrencyUIManager::Impl::considerUpdateCurrency()
 {
-    if (mCurrencyChanged
-    &&  !mTransaction
-    &&  mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
+    if (mCurrencyChanged && !mTransaction &&
+        mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
     {
         updateCurrencyInfo();
         return true;
@@ -408,7 +405,8 @@ void LLCurrencyUIManager::Impl::currencyKey(S32 value)
 
     mUserCurrencyBuy = value;
 
-    if (hasEstimate()) {
+    if (hasEstimate())
+    {
         clearEstimate();
         //cannot just simply refresh the whole UI, as the edit field will
         // get reset and the cursor will change...
@@ -421,8 +419,7 @@ void LLCurrencyUIManager::Impl::currencyKey(S32 value)
 }
 
 // static
-void LLCurrencyUIManager::Impl::onCurrencyKey(
-        LLLineEditor* caller, void* data)
+void LLCurrencyUIManager::Impl::onCurrencyKey(LLLineEditor* caller, void* data)
 {
     S32 value = atoi(caller->getText().c_str());
     LLCurrencyUIManager::Impl* self = (LLCurrencyUIManager::Impl*)data;
@@ -589,14 +586,12 @@ bool LLCurrencyUIManager::inProcess()
 
 bool LLCurrencyUIManager::canCancel()
 {
-    return impl.mTransactionType != Impl::TransactionBuy;
+    return !buying();
 }
 
 bool LLCurrencyUIManager::canBuy()
 {
-    return impl.mTransactionType == Impl::TransactionNone
-        && impl.hasEstimate()
-        && impl.mUserCurrencyBuy > 0;
+    return !inProcess() && impl.hasEstimate() && impl.mUserCurrencyBuy > 0;
 }
 
 bool LLCurrencyUIManager::buying()
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp
index 570a223908..11505e3047 100644
--- a/indra/newview/llfloaterbuyland.cpp
+++ b/indra/newview/llfloaterbuyland.cpp
@@ -182,7 +182,7 @@ public:
 
     void refreshUI();
 
-    void startTransaction(TransactionType type, const LLXMLRPCValue& params);
+    void startTransaction(TransactionType type, const LLSD& params);
     bool checkTransaction();
 
     void tellUserError(const std::string& message, const std::string& uri);
@@ -396,11 +396,10 @@ void LLFloaterBuyLandUI::updateParcelInfo()
     // Can't have more than region max tasks, regardless of parcel
     // object bonus factor.
     LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
-    if(region)
+    if (region)
     {
         S32 max_tasks_per_region = (S32)region->getMaxTasks();
-        mParcelSupportedObjects = llmin(
-            mParcelSupportedObjects, max_tasks_per_region);
+        mParcelSupportedObjects = llmin(mParcelSupportedObjects, max_tasks_per_region);
     }
 
     mParcelSoldWithObjects = parcel->getSellWithObjects();
@@ -423,7 +422,7 @@ void LLFloaterBuyLandUI::updateParcelInfo()
 
     // checks that we can buy the land
 
-    if(mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
+    if (mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
     {
         mCannotBuyReason = getString("cant_buy_for_group");
         return;
@@ -492,85 +491,56 @@ void LLFloaterBuyLandUI::updateParcelInfo()
 void LLFloaterBuyLandUI::updateCovenantInfo()
 {
     LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
-    if(!region) return;
+    if (!region)
+        return;
 
     U8 sim_access = region->getSimAccess();
     std::string rating = LLViewerRegion::accessToString(sim_access);
 
     LLTextBox* region_name = getChild<LLTextBox>("region_name_text");
-    if (region_name)
-    {
-        std::string region_name_txt = region->getName() + " ("+rating +")";
-        region_name->setText(region_name_txt);
+    std::string region_name_txt = region->getName() + " ("+rating +")";
+    region_name->setText(region_name_txt);
 
-        LLIconCtrl* rating_icon = getChild<LLIconCtrl>("rating_icon");
-        LLRect rect = rating_icon->getRect();
-        S32 region_name_width = llmin(region_name->getRect().getWidth(), region_name->getTextBoundingRect().getWidth());
-        S32 icon_left_pad = region_name->getRect().mLeft + region_name_width + ICON_PAD;
-        region_name->setToolTip(region_name->getText());
-        rating_icon->setRect(rect.setOriginAndSize(icon_left_pad, rect.mBottom, rect.getWidth(), rect.getHeight()));
+    LLIconCtrl* rating_icon = getChild<LLIconCtrl>("rating_icon");
+    LLRect rect = rating_icon->getRect();
+    S32 region_name_width = llmin(region_name->getRect().getWidth(), region_name->getTextBoundingRect().getWidth());
+    S32 icon_left_pad = region_name->getRect().mLeft + region_name_width + ICON_PAD;
+    region_name->setToolTip(region_name->getText());
+    rating_icon->setRect(rect.setOriginAndSize(icon_left_pad, rect.mBottom, rect.getWidth(), rect.getHeight()));
 
-        switch(sim_access)
-        {
-        case SIM_ACCESS_PG:
-            rating_icon->setValue(getString("icon_PG"));
-            break;
+    switch (sim_access)
+    {
+    case SIM_ACCESS_PG:
+        rating_icon->setValue(getString("icon_PG"));
+        break;
 
-        case SIM_ACCESS_ADULT:
-            rating_icon->setValue(getString("icon_R"));
-            break;
+    case SIM_ACCESS_ADULT:
+        rating_icon->setValue(getString("icon_R"));
+        break;
 
-        default:
-            rating_icon->setValue(getString("icon_M"));
-        }
+    default:
+        rating_icon->setValue(getString("icon_M"));
     }
 
     LLTextBox* region_type = getChild<LLTextBox>("region_type_text");
-    if (region_type)
-    {
-        region_type->setText(region->getLocalizedSimProductName());
-        region_type->setToolTip(region->getLocalizedSimProductName());
-    }
+    region_type->setText(region->getLocalizedSimProductName());
+    region_type->setToolTip(region->getLocalizedSimProductName());
 
     LLTextBox* resellable_clause = getChild<LLTextBox>("resellable_clause");
-    if (resellable_clause)
-    {
-        if (region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL))
-        {
-            resellable_clause->setText(getString("can_not_resell"));
-        }
-        else
-        {
-            resellable_clause->setText(getString("can_resell"));
-        }
-    }
+    const char* can_resell = region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL) ? "can_not_resell" : "can_resell";
+    resellable_clause->setText(getString(can_resell));
 
     LLTextBox* changeable_clause = getChild<LLTextBox>("changeable_clause");
-    if (changeable_clause)
-    {
-        if (region->getRegionFlag(REGION_FLAGS_ALLOW_PARCEL_CHANGES))
-        {
-            changeable_clause->setText(getString("can_change"));
-        }
-        else
-        {
-            changeable_clause->setText(getString("can_not_change"));
-        }
-    }
+    const char* can_change = region->getRegionFlag(REGION_FLAGS_ALLOW_PARCEL_CHANGES) ? "can_change" : "can_not_change";
+    changeable_clause->setText(getString(can_change));
 
     LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("agree_covenant");
-    if(check)
-    {
-        check->set(false);
-        check->setEnabled(true);
-        check->setCommitCallback(onChangeAgreeCovenant, this);
-    }
+    check->set(false);
+    check->setEnabled(true);
+    check->setCommitCallback(onChangeAgreeCovenant, this);
 
     LLTextBox* box = getChild<LLTextBox>("covenant_text");
-    if(box)
-    {
-        box->setVisible(false);
-    }
+    box->setVisible(false);
 
     // send EstateCovenantInfo message
     LLMessageSystem *msg = gMessageSystem;
@@ -584,10 +554,9 @@ void LLFloaterBuyLandUI::updateCovenantInfo()
 // static
 void LLFloaterBuyLandUI::onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data)
 {
-    LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)user_data;
-    if(self)
+    if (user_data)
     {
-        self->refreshUI();
+        ((LLFloaterBuyLandUI*)user_data)->refreshUI();
     }
 }
 
@@ -626,13 +595,13 @@ void LLFloaterBuyLandUI::updateFloaterEstateName(const std::string& name)
 void LLFloaterBuyLandUI::updateFloaterLastModified(const std::string& text)
 {
     LLTextBox* editor = getChild<LLTextBox>("covenant_timestamp_text");
-    if (editor) editor->setText(text);
+    editor->setText(text);
 }
 
 void LLFloaterBuyLandUI::updateFloaterEstateOwnerName(const std::string& name)
 {
     LLTextBox* box = getChild<LLTextBox>("estate_owner_text");
-    if (box) box->setText(name);
+    box->setText(name);
 }
 
 void LLFloaterBuyLandUI::updateWebSiteInfo()
@@ -640,9 +609,10 @@ void LLFloaterBuyLandUI::updateWebSiteInfo()
     S32 askBillableArea = mIsForGroup ? 0 : mParcelBillableArea;
     S32 askCurrencyBuy = mCurrency.getAmount();
 
-    if (mTransaction && mTransactionType == TransactionPreflight
-    &&  mPreflightAskBillableArea == askBillableArea
-    &&  mPreflightAskCurrencyBuy == askCurrencyBuy)
+    if (mTransaction &&
+        mTransactionType == TransactionPreflight &&
+        mPreflightAskBillableArea == askBillableArea &&
+        mPreflightAskCurrencyBuy == askCurrencyBuy)
     {
         return;
     }
@@ -664,27 +634,21 @@ void LLFloaterBuyLandUI::updateWebSiteInfo()
     mSiteCurrencyEstimatedCost = 0;
 #endif
 
-    LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
-    keywordArgs.appendString("agentId", gAgent.getID().asString());
-    keywordArgs.appendString(
-        "secureSessionId",
-        gAgent.getSecureSessionID().asString());
-    keywordArgs.appendString("language", LLUI::getLanguage());
-    keywordArgs.appendInt("billableArea", mPreflightAskBillableArea);
-    keywordArgs.appendInt("currencyBuy", mPreflightAskCurrencyBuy);
-
-    LLXMLRPCValue params = LLXMLRPCValue::createArray();
-    params.append(keywordArgs);
+    LLSD params = LLSD::emptyMap();
+    params["agentId"] = gAgent.getID().asString();
+    params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+    params["language"] = LLUI::getLanguage();
+    params["billableArea"] = mPreflightAskBillableArea;
+    params["currencyBuy"] = mPreflightAskCurrencyBuy;
 
     startTransaction(TransactionPreflight, params);
 }
 
 void LLFloaterBuyLandUI::finishWebSiteInfo()
 {
+    const LLSD& result = mTransaction->response();
 
-    LLXMLRPCValue result = mTransaction->responseValue();
-
-    mSiteValid = result["success"].asBool();
+    mSiteValid = result["success"].asBoolean();
     if (!mSiteValid)
     {
         tellUserError(
@@ -694,31 +658,30 @@ void LLFloaterBuyLandUI::finishWebSiteInfo()
         return;
     }
 
-    LLXMLRPCValue membership = result["membership"];
-    mSiteMembershipUpgrade = membership["upgrade"].asBool();
+    const LLSD& membership = result["membership"];
+    mSiteMembershipUpgrade = membership["upgrade"].asBoolean();
     mSiteMembershipAction = membership["action"].asString();
     mSiteMembershipPlanIDs.clear();
     mSiteMembershipPlanNames.clear();
-    LLXMLRPCValue levels = membership["levels"];
-    for (LLXMLRPCValue level = levels.rewind();
-        level.isValid();
-        level = levels.next())
+    const LLSD& levels = membership["levels"];
+    for (auto it = levels.beginArray(); it != levels.endArray(); ++it)
     {
+        const LLSD& level = *it;
         mSiteMembershipPlanIDs.push_back(level["id"].asString());
         mSiteMembershipPlanNames.push_back(level["description"].asString());
     }
     mUserPlanChoice = 0;
 
-    LLXMLRPCValue landUse = result["landUse"];
-    mSiteLandUseUpgrade = landUse["upgrade"].asBool();
+    const LLSD& landUse = result["landUse"];
+    mSiteLandUseUpgrade = landUse["upgrade"].asBoolean();
     mSiteLandUseAction = landUse["action"].asString();
 
-    LLXMLRPCValue currency = result["currency"];
-    if (currency["estimatedCost"].isValid())
+    const LLSD& currency = result["currency"];
+    if (currency.has("estimatedCost"))
     {
-        mCurrency.setUSDEstimate(currency["estimatedCost"].asInt());
+        mCurrency.setUSDEstimate(currency["estimatedCost"].asInteger());
     }
-    if (currency["estimatedLocalCost"].isValid())
+    if (currency.has("estimatedLocalCost"))
     {
         mCurrency.setLocalEstimate(currency["estimatedLocalCost"].asString());
     }
@@ -760,35 +723,30 @@ void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password)
         }
     }
 
-    LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
-    keywordArgs.appendString("agentId", gAgent.getID().asString());
-    keywordArgs.appendString(
-        "secureSessionId",
-        gAgent.getSecureSessionID().asString());
-    keywordArgs.appendString("language", LLUI::getLanguage());
-    keywordArgs.appendString("levelId", newLevel);
-    keywordArgs.appendInt("billableArea",
-        mIsForGroup ? 0 : mParcelBillableArea);
-    keywordArgs.appendInt("currencyBuy", mCurrency.getAmount());
-    keywordArgs.appendInt("estimatedCost", mCurrency.getUSDEstimate());
-    keywordArgs.appendString("estimatedLocalCost", mCurrency.getLocalEstimate());
-    keywordArgs.appendString("confirm", mSiteConfirm);
+    LLSD params = LLSD::emptyMap();
+    params["agentId"] = gAgent.getID().asString();
+    params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+    params["language"] = LLUI::getLanguage();
+    params["levelId"] = newLevel;
+    params["billableArea"] = mIsForGroup ? 0 : mParcelBillableArea;
+    params["currencyBuy"] = mCurrency.getAmount();
+    params["estimatedCost"] = mCurrency.getUSDEstimate();
+    params["estimatedLocalCost"] = mCurrency.getLocalEstimate();
+    params["confirm"] = mSiteConfirm;
+
     if (!password.empty())
     {
-        keywordArgs.appendString("password", password);
+        params["password"] = password;
     }
 
-    LLXMLRPCValue params = LLXMLRPCValue::createArray();
-    params.append(keywordArgs);
-
     startTransaction(TransactionBuy, params);
 }
 
 void LLFloaterBuyLandUI::finishWebSitePrep()
 {
-    LLXMLRPCValue result = mTransaction->responseValue();
+    const LLSD& result = mTransaction->response();
 
-    bool success = result["success"].asBool();
+    bool success = result["success"].asBoolean();
     if (!success)
     {
         tellUserError(
@@ -850,7 +808,7 @@ void LLFloaterBuyLandUI::updateGroupName(const LLUUID& id,
     }
 }
 
-void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCValue& params)
+void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLSD& params)
 {
     delete mTransaction;
     mTransaction = NULL;
@@ -878,12 +836,7 @@ void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCVa
             return;
     }
 
-    mTransaction = new LLXMLRPCTransaction(
-        transaction_uri,
-        method,
-        params,
-        false /* don't use gzip */
-        );
+    mTransaction = new LLXMLRPCTransaction(transaction_uri, method, params);
 }
 
 bool LLFloaterBuyLandUI::checkTransaction()
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 282a273be6..d015c0ed95 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -33,9 +33,6 @@
 #include "stringize.h"
 #include "llsdserialize.h"
 
-// llmessage (!)
-#include "llfiltersd2xmlrpc.h" // for xml_escape_string()
-
 // login
 #include "lllogin.h"
 
@@ -612,7 +609,7 @@ std::string construct_start_string()
                         << position[VX] << "&"
                         << position[VY] << "&"
                         << position[VZ]);
-            start = xml_escape_string(unescaped_start);
+            start = LLStringFn::xml_encode(unescaped_start, true);
             break;
         }
         case LLSLURL::HOME_LOCATION:
diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp
index 432ec3899a..d80cf2e80e 100644
--- a/indra/newview/llslurl.cpp
+++ b/indra/newview/llslurl.cpp
@@ -32,13 +32,15 @@
 #include "llpanellogin.h"
 #include "llviewercontrol.h"
 #include "llviewernetwork.h"
-#include "llfiltersd2xmlrpc.h"
+
 #include "curl/curl.h"
+
 const char* LLSLURL::SLURL_HTTP_SCHEME       = "http";
 const char* LLSLURL::SLURL_HTTPS_SCHEME      = "https";
 const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife";
 const char* LLSLURL::SLURL_SECONDLIFE_PATH   = "secondlife";
 const char* LLSLURL::SLURL_COM               = "slurl.com";
+
 // For DnD - even though www.slurl.com redirects to slurl.com in a browser, you  can copy and drag
 // text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this
 // version is required also.
@@ -437,7 +439,7 @@ std::string LLSLURL::getLoginString() const
             LL_WARNS("AppInit") << "Unexpected SLURL type (" << (int)mType << ")for login string" << LL_ENDL;
             break;
     }
-    return  xml_escape_string(unescaped_start.str());
+    return  LLStringFn::xml_encode(unescaped_start.str(), true);
 }
 
 bool LLSLURL::operator ==(const LLSLURL& rhs)
diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp
index a571b5544b..4e8320b72a 100644
--- a/indra/newview/llversioninfo.cpp
+++ b/indra/newview/llversioninfo.cpp
@@ -76,37 +76,37 @@ LLVersionInfo::~LLVersionInfo()
 {
 }
 
-S32 LLVersionInfo::getMajor()
+S32 LLVersionInfo::getMajor() const
 {
     return LL_VIEWER_VERSION_MAJOR;
 }
 
-S32 LLVersionInfo::getMinor()
+S32 LLVersionInfo::getMinor() const
 {
     return LL_VIEWER_VERSION_MINOR;
 }
 
-S32 LLVersionInfo::getPatch()
+S32 LLVersionInfo::getPatch() const
 {
     return LL_VIEWER_VERSION_PATCH;
 }
 
-U64 LLVersionInfo::getBuild()
+U64 LLVersionInfo::getBuild() const
 {
     return LL_VIEWER_VERSION_BUILD;
 }
 
-std::string LLVersionInfo::getVersion()
+std::string LLVersionInfo::getVersion() const
 {
     return version;
 }
 
-std::string LLVersionInfo::getShortVersion()
+std::string LLVersionInfo::getShortVersion() const
 {
     return short_version;
 }
 
-std::string LLVersionInfo::getChannelAndVersion()
+std::string LLVersionInfo::getChannelAndVersion() const
 {
     if (mVersionChannel.empty())
     {
@@ -117,7 +117,7 @@ std::string LLVersionInfo::getChannelAndVersion()
     return mVersionChannel;
 }
 
-std::string LLVersionInfo::getChannel()
+std::string LLVersionInfo::getChannel() const
 {
     return mWorkingChannelName;
 }
@@ -128,7 +128,7 @@ void LLVersionInfo::resetChannel(const std::string& channel)
     mVersionChannel.clear(); // Reset version and channel string til next use.
 }
 
-LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity()
+LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() const
 {
     ViewerMaturity maturity;
 
@@ -166,12 +166,12 @@ LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity()
 }
 
 
-std::string LLVersionInfo::getBuildConfig()
+std::string LLVersionInfo::getBuildConfig() const
 {
     return build_configuration;
 }
 
-std::string LLVersionInfo::getReleaseNotes()
+std::string LLVersionInfo::getReleaseNotes() const
 {
     return mReleaseNotes;
 }
diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h
index aed43263a6..237b37a084 100644
--- a/indra/newview/llversioninfo.h
+++ b/indra/newview/llversioninfo.h
@@ -52,38 +52,38 @@ public:
     ~LLVersionInfo();
 
     /// return the major version number as an integer
-    S32 getMajor();
+    S32 getMajor() const;
 
     /// return the minor version number as an integer
-    S32 getMinor();
+    S32 getMinor() const;
 
     /// return the patch version number as an integer
-    S32 getPatch();
+    S32 getPatch() const;
 
     /// return the build number as an integer
-    U64 getBuild();
+    U64 getBuild() const;
 
     /// return the full viewer version as a string like "2.0.0.200030"
-    std::string getVersion();
+    std::string getVersion() const;
 
     /// return the viewer version as a string like "2.0.0"
-    std::string getShortVersion();
+    std::string getShortVersion() const;
 
     /// return the viewer version and channel as a string
     /// like "Second Life Release 2.0.0.200030"
-    std::string getChannelAndVersion();
+    std::string getChannelAndVersion() const;
 
     /// return the channel name, e.g. "Second Life"
-    std::string getChannel();
+    std::string getChannel() const;
 
     /// return the CMake build type
-    std::string getBuildConfig();
+    std::string getBuildConfig() const;
 
     /// reset the channel name used by the viewer.
     void resetChannel(const std::string& channel);
 
     /// return the bit width of an address
-    S32 getAddressSize() { return ADDRESS_SIZE; }
+    S32 getAddressSize() const { return ADDRESS_SIZE; }
 
     typedef enum
     {
@@ -92,11 +92,11 @@ public:
         BETA_VIEWER,
         RELEASE_VIEWER
     } ViewerMaturity;
-    ViewerMaturity getViewerMaturity();
+    ViewerMaturity getViewerMaturity() const;
 
     /// get the release-notes URL, once it becomes available -- until then,
     /// return empty string
-    std::string getReleaseNotes();
+    std::string getReleaseNotes() const;
 
 private:
     std::string version;
@@ -107,7 +107,7 @@ private:
     std::string mWorkingChannelName;
     // Storage for the "version and channel" string.
     // This will get reset too.
-    std::string mVersionChannel;
+    mutable std::string mVersionChannel;
     std::string build_configuration;
     std::string mReleaseNotes;
     // Store unique_ptrs to the next couple things so we don't have to explain
diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp
index 1148e81fd5..7c7bd98bcd 100644
--- a/indra/newview/llxmlrpclistener.cpp
+++ b/indra/newview/llxmlrpclistener.cpp
@@ -39,12 +39,6 @@
 #include <boost/scoped_ptr.hpp>
 #include <boost/range.hpp>          // boost::begin(), boost::end()
 
-#ifdef LL_USESYSTEMLIBS
-#include <xmlrpc.h>
-#else
-#include <xmlrpc-epi/xmlrpc.h>
-#endif
-
 #include "curl/curl.h"
 
 // other Linden headers
@@ -178,13 +172,6 @@ public:
 
 static const CURLcodeMapper sCURLcodeMapper;
 
-LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname):
-    mBoundListener(LLEventPumps::instance().
-                   obtain(pumpname).
-                   listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1)))
-{
-}
-
 /**
  * Capture an outstanding LLXMLRPCTransaction and poll it periodically until
  * done.
@@ -213,38 +200,20 @@ public:
         mMethod(command["method"]),
         mReplyPump(command["reply"])
     {
-        // LL_ERRS if any of these are missing
-        const char* required[] = { "uri", "method", "reply" };
-        // optional: "options" (array of string)
-        // Validate the request
-        std::set<std::string> missing;
-        for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri)
+        // LL_ERRS if any of these keys are missing or empty
+        if (mUri.empty() || mMethod.empty() || mReplyPump.empty())
         {
-            // If the command does not contain this required entry, add it to 'missing'.
-            if (! command.has(*ri))
-            {
-                missing.insert(*ri);
-            }
-        }
-        if (! missing.empty())
-        {
-            LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: ";
-            const char* separator = "";
-            for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end());
-                 mi != mend; ++mi)
-            {
-                LL_CONT << separator << *mi;
-                separator = ", ";
-            }
-            LL_CONT << LL_ENDL;
+            LL_ERRS("LLXMLRPCListener")
+                << "Some params are missing: "
+                << "reply: '" << mReplyPump << "', "
+                << "method: '" << mMethod << "', "
+                << "uri: '" << mUri << "'"
+                << LL_ENDL;
         }
 
-        // Build the XMLRPC request.
-        XMLRPC_REQUEST request = XMLRPC_RequestNew();
-        XMLRPC_RequestSetMethodName(request, mMethod.c_str());
-        XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
-        XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
-        LLSD params(command["params"]);
+        LLSD request_params = LLSD::emptyMap();
+
+        LLSD params = command.get("params");
         if (params.isMap())
         {
             for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap());
@@ -252,44 +221,33 @@ public:
             {
                 std::string name(pi->first);
                 LLSD param(pi->second);
-                if (param.isString())
+                switch (param.type())
                 {
-                    XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0);
-                }
-                else if (param.isInteger() || param.isBoolean())
-                {
-                    XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger());
-                }
-                else if (param.isReal())
-                {
-                    XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal());
-                }
-                else
-                {
-                    LL_ERRS("LLXMLRPCListener") << mMethod << " request param "
-                                                << name << " has unknown type: " << param << LL_ENDL;
+                case LLSD::TypeString:
+                case LLSD::TypeInteger:
+                case LLSD::TypeReal:
+                    request_params.insert(name, param);
+                    break;
+                case LLSD::TypeBoolean:
+                    request_params.insert(name, param.asInteger());
+                    break;
+                default:
+                    LL_ERRS("LLXMLRPCListener") << mMethod
+                        << " request param '" << name << "' has unknown type: " << param << LL_ENDL;
                 }
             }
         }
-        LLSD options(command["options"]);
+
+        LLSD options = command.get("options");
         if (options.isArray())
         {
-            XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array);
-            for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray());
-                 oi != oend; ++oi)
-            {
-                XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0);
-            }
-            XMLRPC_AddValueToVector(xparams, xoptions);
+            request_params.insert("options", options);
         }
-        XMLRPC_RequestSetData(request, xparams);
 
-        mTransaction.reset(new LLXMLRPCTransaction(mUri, request, true, command.has("http_params")? LLSD(command["http_params"]) : LLSD()));
+        LLSD http_params = command.get("http_params");
+        mTransaction.reset(new LLXMLRPCTransaction(mUri, mMethod, request_params, http_params));
         mPreviousStatus = mTransaction->status(NULL);
 
-        // Free the XMLRPC_REQUEST object and the attached data values.
-        XMLRPC_RequestFree(request, 1);
-
         // Now ensure that we get regular callbacks to poll for completion.
         mBoundListener =
             LLEventPumps::instance().
@@ -323,7 +281,7 @@ public:
         data["error"] = "";
         data["transfer_rate"] = 0.0;
         LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump));
-        if (! done)
+        if (!done)
         {
             // Not done yet, carry on.
             if (status == LLXMLRPCTransaction::StatusDownloading
@@ -367,10 +325,8 @@ public:
         // Given 'message', need we care?
         if (status == LLXMLRPCTransaction::StatusComplete)
         {
-            // Success! Parse data.
-            std::string status_string(data["status"]);
-            data["responses"] = parseResponse(status_string);
-            data["status"] = status_string;
+            // Success! Retrieve response data.
+            data["responses"] = mTransaction->response();
         }
 
         // whether successful or not, send reply on requested LLEventPump
@@ -388,159 +344,6 @@ public:
     }
 
 private:
-    /// Derived from LLUserAuth::parseResponse() and parseOptionInto()
-    LLSD parseResponse(std::string& status_string)
-    {
-        // Extract every member into data["responses"] (a map of string
-        // values).
-        XMLRPC_REQUEST response = mTransaction->response();
-        if (! response)
-        {
-            LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL;
-            return LLSD();
-        }
-
-        XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
-        if (! param)
-        {
-            LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL;
-            return LLSD();
-        }
-
-        // Now, parse everything
-        return parseValues(status_string, "", param);
-    }
-
-    LLSD parseValue(std::string& status_string, const std::string& key, const std::string& key_pfx, XMLRPC_VALUE param)
-    {
-        LLSD response;
-
-        XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(param);
-        switch (type)
-        {
-            case xmlrpc_type_empty:
-                LL_INFOS("LLXMLRPCListener") << "Empty result for key " << key_pfx << key << LL_ENDL;
-                break;
-            case xmlrpc_type_base64:
-                {
-                    S32 len = XMLRPC_GetValueStringLen(param);
-                    const char* buf = XMLRPC_GetValueBase64(param);
-                    if ((len > 0) && buf)
-                    {
-                        // During implementation this code was not tested
-                        // If you encounter this, please make sure this is correct,
-                        // then remove llassert
-                        llassert(0);
-
-                        LLSD::Binary data;
-                        data.resize(len);
-                        memcpy((void*)&data[0], (void*)buf, len);
-                        response = data;
-                    }
-                    else
-                    {
-                        LL_WARNS("LLXMLRPCListener") << "Potentially malformed xmlrpc_type_base64 for key "
-                            << key_pfx << key << LL_ENDL;
-                    }
-                    break;
-                }
-            case xmlrpc_type_boolean:
-                {
-                    response = LLSD::Boolean(XMLRPC_GetValueBoolean(param));
-                    LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
-                    break;
-                }
-            case xmlrpc_type_datetime:
-                {
-                    std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(param));
-                    LL_DEBUGS("LLXMLRPCListener") << "val: " << iso8601_date << LL_ENDL;
-                    response = LLSD::Date(iso8601_date);
-                    break;
-                }
-            case xmlrpc_type_double:
-                {
-                    response = LLSD::Real(XMLRPC_GetValueDouble(param));
-                    LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
-                    break;
-                }
-            case xmlrpc_type_int:
-                {
-                    response = LLSD::Integer(XMLRPC_GetValueInt(param));
-                    LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
-                    break;
-                }
-            case xmlrpc_type_string:
-                {
-                    response = LLSD::String(XMLRPC_GetValueString(param));
-                    LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
-                    break;
-                }
-            case xmlrpc_type_mixed:
-            case xmlrpc_type_array:
-                {
-                    // We expect this to be an array of submaps. Walk the array,
-                    // recursively parsing each submap and collecting them.
-                    LLSD array;
-                    int i = 0;          // for descriptive purposes
-                    for (XMLRPC_VALUE row = XMLRPC_VectorRewind(param); row;
-                         row = XMLRPC_VectorNext(param), ++i)
-                    {
-                        // Recursive call. For the lower-level key_pfx, if 'key'
-                        // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
-                        // nested call, a subkey "bar" will then be logged as
-                        // "foo[0]:bar", and so forth.
-                        // Parse the scalar subkey/value pairs from this array
-                        // entry into a temp submap. Collect such submaps in 'array'.
-
-                        array.append(parseValue(status_string, "",
-                                                 STRINGIZE(key_pfx << key << '[' << i << "]:"),
-                                                 row));
-                    }
-                    // Having collected an 'array' of 'submap's, insert that whole
-                    // 'array' as the value of this 'key'.
-                    response = array;
-                    break;
-                }
-            case xmlrpc_type_struct:
-                {
-                    response = parseValues(status_string,
-                                              STRINGIZE(key_pfx << key << ':'),
-                                              param);
-                    break;
-                }
-            case xmlrpc_type_none: // Not expected
-            default:
-                // whoops - unrecognized type
-                LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key "
-                    << key_pfx << key << LL_ENDL;
-                response = STRINGIZE("<bad XMLRPC type " << type << '>');
-                status_string = "BadType";
-        }
-        return response;
-    }
-
-    /**
-     * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
-     * @param key_pfx Used to describe a given key in log messages. At top
-     * level, pass "". When parsing an options array, pass the top-level key
-     * name of the array plus the index of the array entry; to this we'll
-     * append the subkey of interest.
-     * @param param XMLRPC_VALUE iterator. At top level, pass
-     * XMLRPC_RequestGetData(XMLRPC_REQUEST).
-     */
-    LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
-    {
-        LLSD responses;
-        for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
-             current = XMLRPC_VectorNext(param))
-        {
-            std::string key(XMLRPC_GetValueID(current));
-            LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
-            responses.insert(key, parseValue(status_string, key, key_pfx, current));
-        }
-        return responses;
-    }
-
     const LLReqID mReqID;
     const std::string mUri;
     const std::string mMethod;
@@ -550,11 +353,18 @@ private:
     LLXMLRPCTransaction::EStatus mPreviousStatus; // To detect state changes.
 };
 
-bool LLXMLRPCListener::process(const LLSD& command)
+LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname)
+: mBoundListener(LLEventPumps::instance().obtain(pumpname).listen
+(
+    "LLXMLRPCListener",
+    [&](const LLSD& command) -> bool
+    {
+        // Allocate a new heap Poller, but do not save a pointer to it. Poller
+        // will check its own status and free itself on completion of the request.
+        (new Poller(command));
+        // Conventional event listener return
+        return false;
+    }
+))
 {
-    // Allocate a new heap Poller, but do not save a pointer to it. Poller
-    // will check its own status and free itself on completion of the request.
-    (new Poller(command));
-    // conventional event listener return
-    return false;
 }
diff --git a/indra/newview/llxmlrpclistener.h b/indra/newview/llxmlrpclistener.h
index aaed98eec5..fd75acb8b1 100644
--- a/indra/newview/llxmlrpclistener.h
+++ b/indra/newview/llxmlrpclistener.h
@@ -42,9 +42,6 @@ public:
     /// Specify the pump name on which to listen
     LLXMLRPCListener(const std::string& pumpname);
 
-    /// Handle request events on the event pump specified at construction time
-    bool process(const LLSD& command);
-
 private:
     LLTempBoundListener mBoundListener;
 };
diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp
index ec6e22cd7a..55622fb6b7 100644
--- a/indra/newview/llxmlrpctransaction.cpp
+++ b/indra/newview/llxmlrpctransaction.cpp
@@ -42,22 +42,11 @@
 #include "bufferarray.h"
 #include "llversioninfo.h"
 #include "llviewercontrol.h"
+#include "llxmlnode.h"
 #include "stringize.h"
 
 // Have to include these last to avoid queue redefinition!
 
-#ifdef LL_USESYSTEMLIBS
-#include <xmlrpc.h>
-#else
-#include <xmlrpc-epi/xmlrpc.h>
-#endif
-// <xmlrpc-epi/queue.h> contains a harmful #define queue xmlrpc_queue. This
-// breaks any use of std::queue. Ditch that #define: if any of our code wants
-// to reference xmlrpc_queue, let it reference it directly.
-#if defined(queue)
-#undef queue
-#endif
-
 #include "llappviewer.h"
 #include "lltrans.h"
 
@@ -75,111 +64,6 @@ namespace boost
 // nothing.
 static LLXMLRPCListener listener("LLXMLRPCTransaction");
 
-LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
-{
-    return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
-}
-
-std::string LLXMLRPCValue::asString() const
-{
-    const char* s = XMLRPC_GetValueString(mV);
-    return s ? s : "";
-}
-
-int     LLXMLRPCValue::asInt() const    { return XMLRPC_GetValueInt(mV); }
-bool    LLXMLRPCValue::asBool() const   { return XMLRPC_GetValueBoolean(mV) != 0; }
-double  LLXMLRPCValue::asDouble() const { return XMLRPC_GetValueDouble(mV); }
-
-LLXMLRPCValue LLXMLRPCValue::rewind()
-{
-    return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
-}
-
-LLXMLRPCValue LLXMLRPCValue::next()
-{
-    return LLXMLRPCValue(XMLRPC_VectorNext(mV));
-}
-
-bool LLXMLRPCValue::isValid() const
-{
-    return mV != NULL;
-}
-
-LLXMLRPCValue LLXMLRPCValue::createArray()
-{
-    return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
-}
-
-LLXMLRPCValue LLXMLRPCValue::createStruct()
-{
-    return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
-}
-
-
-void LLXMLRPCValue::append(LLXMLRPCValue& v)
-{
-    XMLRPC_AddValueToVector(mV, v.mV);
-}
-
-void LLXMLRPCValue::appendString(const std::string& v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
-}
-
-void LLXMLRPCValue::appendInt(int v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
-}
-
-void LLXMLRPCValue::appendBool(bool v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
-}
-
-void LLXMLRPCValue::appendDouble(double v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
-}
-
-
-void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
-{
-    XMLRPC_SetValueID(v.mV, id, 0);
-    XMLRPC_AddValueToVector(mV, v.mV);
-}
-
-void LLXMLRPCValue::appendString(const char* id, const std::string& v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
-}
-
-void LLXMLRPCValue::appendInt(const char* id, int v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
-}
-
-void LLXMLRPCValue::appendBool(const char* id, bool v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
-}
-
-void LLXMLRPCValue::appendDouble(const char* id, double v)
-{
-    XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
-}
-
-void LLXMLRPCValue::cleanup()
-{
-    XMLRPC_CleanupValue(mV);
-    mV = NULL;
-}
-
-XMLRPC_VALUE LLXMLRPCValue::getValue() const
-{
-    return mV;
-}
-
-
 class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler
 {
 public:
@@ -192,6 +76,9 @@ public:
 
 private:
 
+    bool parseResponse(LLXMLNodePtr root);
+    bool parseValue(LLSD& target, LLXMLNodePtr source);
+
     LLXMLRPCTransaction::Impl *mImpl;
     LLCore::HttpRequest::ptr_t mRequest;
 };
@@ -213,26 +100,26 @@ public:
     LLCore::HttpHandle  mPostH;
 
     std::string         mURI;
-
     std::string         mProxyAddress;
 
     std::string         mResponseText;
-    XMLRPC_REQUEST      mResponse;
+    LLSD                mResponseData;
+
     std::string         mCertStore;
-    LLSD mErrorCertData;
+    LLSD                mErrorCertData;
 
-    Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams);
-    Impl(const std::string& uri,
-        const std::string& method, LLXMLRPCValue params, bool useGzip);
-    ~Impl();
+    Impl
+    (
+        const std::string& uri,
+        const std::string& method,
+        const LLSD& params,
+        const LLSD& httpParams
+    );
 
     bool process();
 
     void setStatus(EStatus code, const std::string& message = "", const std::string& uri = "");
     void setHttpStatus(const LLCore::HttpStatus &status);
-
-private:
-    void init(XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams);
 };
 
 LLXMLRPCTransaction::Handler::Handler(LLCore::HttpRequest::ptr_t &request,
@@ -275,89 +162,113 @@ void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle,
     mImpl->setStatus(LLXMLRPCTransaction::StatusComplete);
     mImpl->mTransferStats = response->getTransferStats();
 
-    // the contents of a buffer array are potentially noncontiguous, so we
+    // The contents of a buffer array are potentially noncontiguous, so we
     // will need to copy them into an contiguous block of memory for XMLRPC.
     LLCore::BufferArray *body = response->getBody();
-    char * bodydata = new char[body->size()];
+    mImpl->mResponseText.resize(body->size());
 
-    body->read(0, bodydata, body->size());
+    body->read(0, mImpl->mResponseText.data(), body->size());
 
-    mImpl->mResponse = XMLRPC_REQUEST_FromXML(bodydata, static_cast<int>(body->size()), 0);
+    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;
+    }
 
-    delete[] bodydata;
+    if (!parseResponse(root))
+        return;
 
-    bool        hasError = false;
-    bool        hasFault = false;
-    int         faultCode = 0;
-    std::string faultString;
+    LL_INFOS() << "XML response parsed successfully; request URI: " << mImpl->mURI << LL_ENDL;
+}
 
-    LLXMLRPCValue error(XMLRPC_RequestGetError(mImpl->mResponse));
-    if (error.isValid())
+struct XMLTreeNode final : public LLSD::TreeNode
+{
+    XMLTreeNode(const LLXMLNodePtr impl)
+        : mImpl(impl)
+        , mFirstChild(impl ? create(impl->getFirstChild()) : nullptr)
+        , mNextSibling(impl ? create(impl->getNextSibling()) : nullptr)
     {
-        hasError = true;
-        faultCode = error["faultCode"].asInt();
-        faultString = error["faultString"].asString();
     }
-    else if (XMLRPC_ResponseIsFault(mImpl->mResponse))
+
+    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"))
     {
-        hasFault = true;
-        faultCode = XMLRPC_GetResponseFaultCode(mImpl->mResponse);
-        faultString = XMLRPC_GetResponseFaultString(mImpl->mResponse);
+        LL_WARNS() << "Invalid root element in XML response; request URI: " << mImpl->mURI << LL_ENDL;
+        return false;
     }
 
-    if (hasError || hasFault)
+    LLXMLNodePtr first = root->getFirstChild();
+    LLXMLNodePtr second = first->getFirstChild();
+    if (!first->getNextSibling() && second && !second->getNextSibling())
     {
-        mImpl->setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
-
-        LL_WARNS() << "LLXMLRPCTransaction XMLRPC "
-            << (hasError ? "error " : "fault ")
-            << faultCode << ": "
-            << faultString << LL_ENDL;
-        LL_WARNS() << "LLXMLRPCTransaction request URI: "
-            << mImpl->mURI << LL_ENDL;
+        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;
 
-LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
-        XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams)
-    : mHttpRequest(),
-      mStatus(LLXMLRPCTransaction::StatusNotStarted),
-      mURI(uri),
-      mResponse(0)
-{
-    init(request, useGzip, httpParams);
+    return false;
 }
 
-
-LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
-        const std::string& method, LLXMLRPCValue params, bool useGzip)
-    : mHttpRequest(),
-      mStatus(LLXMLRPCTransaction::StatusNotStarted),
-      mURI(uri),
-      mResponse(0)
+bool LLXMLRPCTransaction::Handler::parseValue(LLSD& target, LLXMLNodePtr source)
 {
-    XMLRPC_REQUEST request = XMLRPC_RequestNew();
-    XMLRPC_RequestSetMethodName(request, method.c_str());
-    XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
-    XMLRPC_RequestSetData(request, params.getValue());
-
-    init(request, useGzip, LLSD());
-    // DEV-28398: without this XMLRPC_RequestFree() call, it looks as though
-    // the 'request' object is simply leaked. It's less clear to me whether we
-    // should also ask to free request value data (second param 1), since the
-    // data come from 'params'.
-    XMLRPC_RequestFree(request, 1);
+    XMLTreeNode tn(source);
+    return target.fromXMLRPCValue(&tn);
 }
 
-void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams)
+//=========================================================================
+
+LLXMLRPCTransaction::Impl::Impl
+(
+    const std::string& uri,
+    const std::string& method,
+    const LLSD& params,
+    const LLSD& http_params
+)
+    : mHttpRequest()
+    , mStatus(LLXMLRPCTransaction::StatusNotStarted)
+    , mURI(uri)
 {
     LLCore::HttpOptions::ptr_t httpOpts;
     LLCore::HttpHeaders::ptr_t httpHeaders;
 
-
     if (!mHttpRequest)
     {
         mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest);
@@ -366,37 +277,34 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const
     // LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer
     httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
 
-    // delay between repeats will start from 5 sec and grow to 20 sec with each repeat
+    // Delay between repeats will start from 5 sec and grow to 20 sec with each repeat
     httpOpts->setMinBackoff(5E6L);
     httpOpts->setMaxBackoff(20E6L);
 
-    httpOpts->setTimeout(httpParams.has("timeout") ? httpParams["timeout"].asInteger() : 40L);
-    if (httpParams.has("retries"))
+    httpOpts->setTimeout(http_params.has("timeout") ? http_params["timeout"].asInteger() : 40L);
+    if (http_params.has("retries"))
     {
-        httpOpts->setRetries(httpParams["retries"].asInteger());
+        httpOpts->setRetries(http_params["retries"].asInteger());
     }
-    if (httpParams.has("DNSCacheTimeout"))
+    if (http_params.has("DNSCacheTimeout"))
     {
-        httpOpts->setDNSCacheTimeout(httpParams["DNSCacheTimeout"].asInteger());
+        httpOpts->setDNSCacheTimeout(http_params["DNSCacheTimeout"].asInteger());
     }
 
     bool vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert");
     mCertStore = gSavedSettings.getString("CertStore");
 
-    httpOpts->setSSLVerifyPeer( vefifySSLCert );
-    httpOpts->setSSLVerifyHost( vefifySSLCert ? 2 : 0);
+    httpOpts->setSSLVerifyPeer(vefifySSLCert);
+    httpOpts->setSSLVerifyHost(vefifySSLCert ? 2 : 0);
 
     // LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer
     httpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders());
 
     httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML);
 
-    std::string user_agent = stringize(
-        LLVersionInfo::instance().getChannel(), ' ',
-        LLVersionInfo::instance().getMajor(), '.',
-        LLVersionInfo::instance().getMinor(), '.',
-        LLVersionInfo::instance().getPatch(), " (",
-        LLVersionInfo::instance().getBuild(), ')');
+    const LLVersionInfo& vi(LLVersionInfo::instance());
+    std::string user_agent = vi.getChannel() + llformat(" %d.%d.%d (%llu)",
+        vi.getMajor(), vi.getMinor(), vi.getPatch(), vi.getBuild());
 
     httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent);
 
@@ -404,31 +312,19 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const
     //This might help with bug #503 */
     //httpOpts->setDNSCacheTimeout(-1);
 
-    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>";
 
-    // TODO: See if there is a way to serialize to a preallocated buffer I'm
-    // not fond of the copy here.
-    int requestSize(0);
-    char * requestText = XMLRPC_REQUEST_ToXML(request, &requestSize);
-
-    body->append(requestText, requestSize);
+    LLCore::BufferArray::ptr_t body = LLCore::BufferArray::ptr_t(new LLCore::BufferArray());
 
-    XMLRPC_Free(requestText);
+    body->append(request.c_str(), request.size());
 
-    mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler( mHttpRequest, this ));
+    mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler(mHttpRequest, this));
 
     mPostH = mHttpRequest->requestPost(LLCore::HttpRequest::DEFAULT_POLICY_ID,
         mURI, body.get(), httpOpts, httpHeaders, mHandler);
-
-}
-
-
-LLXMLRPCTransaction::Impl::~Impl()
-{
-    if (mResponse)
-    {
-        XMLRPC_RequestFree(mResponse, 1);
-    }
 }
 
 bool LLXMLRPCTransaction::Impl::process()
@@ -539,18 +435,16 @@ void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus &status)
 
 }
 
-
-LLXMLRPCTransaction::LLXMLRPCTransaction(
-    const std::string& uri, XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams)
-: impl(* new Impl(uri, request, useGzip, httpParams))
-{ }
-
-
-LLXMLRPCTransaction::LLXMLRPCTransaction(
+LLXMLRPCTransaction::LLXMLRPCTransaction
+(
     const std::string& uri,
-    const std::string& method, LLXMLRPCValue params, bool useGzip)
-: impl(* new Impl(uri, method, params, useGzip))
-{ }
+    const std::string& method,
+    const LLSD& params,
+    const LLSD& http_params
+)
+: impl(*new Impl(uri, method, params, http_params))
+{
+}
 
 LLXMLRPCTransaction::~LLXMLRPCTransaction()
 {
@@ -590,14 +484,9 @@ std::string LLXMLRPCTransaction::statusURI()
     return impl.mStatusURI;
 }
 
-XMLRPC_REQUEST LLXMLRPCTransaction::response()
-{
-    return impl.mResponse;
-}
-
-LLXMLRPCValue LLXMLRPCTransaction::responseValue()
+const LLSD& LLXMLRPCTransaction::response()
 {
-    return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
+    return impl.mResponseData;
 }
 
 
diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h
index 4c8796f936..f7a38f5f90 100644
--- a/indra/newview/llxmlrpctransaction.h
+++ b/indra/newview/llxmlrpctransaction.h
@@ -29,73 +29,22 @@
 
 #include <string>
 
-typedef struct _xmlrpc_request* XMLRPC_REQUEST;
-typedef struct _xmlrpc_value* XMLRPC_VALUE;
-    // foward decl of types from xmlrpc.h (this usage is type safe)
-class LLCertificate;
-
-class LLXMLRPCValue
-    // a c++ wrapper around XMLRPC_VALUE
-{
-public:
-    LLXMLRPCValue()                     : mV(NULL) { }
-    LLXMLRPCValue(XMLRPC_VALUE value)   : mV(value) { }
-
-    bool isValid() const;
-
-    std::string asString()  const;
-    int         asInt()     const;
-    bool        asBool()    const;
-    double      asDouble()  const;
-
-    LLXMLRPCValue operator[](const char*) const;
-
-    LLXMLRPCValue rewind();
-    LLXMLRPCValue next();
-
-    static LLXMLRPCValue createArray();
-    static LLXMLRPCValue createStruct();
-
-    void append(LLXMLRPCValue&);
-    void appendString(const std::string&);
-    void appendInt(int);
-    void appendBool(bool);
-    void appendDouble(double);
-    void appendValue(LLXMLRPCValue&);
-
-    void append(const char*, LLXMLRPCValue&);
-    void appendString(const char*, const std::string&);
-    void appendInt(const char*, int);
-    void appendBool(const char*, bool);
-    void appendDouble(const char*, double);
-    void appendValue(const char*, LLXMLRPCValue&);
-
-    void cleanup();
-        // only call this on the top level created value
-
-    XMLRPC_VALUE getValue() const;
-
-private:
-    XMLRPC_VALUE mV;
-};
-
-
+/// An asynchronous request and responses via XML-RPC
 class LLXMLRPCTransaction
-    // an asynchronous request and responses via XML-RPC
 {
 public:
-    LLXMLRPCTransaction(const std::string& uri,
-        XMLRPC_REQUEST request, bool useGzip = true, const LLSD& httpParams = LLSD());
-        // does not take ownership of the request object
-        // request can be freed as soon as the transaction is constructed
-
-    LLXMLRPCTransaction(const std::string& uri,
-        const std::string& method, LLXMLRPCValue params, bool useGzip = true);
-        // *does* take control of the request value, you must not free it
+    LLXMLRPCTransaction
+    (
+        const std::string& uri,
+        const std::string& method,
+        const LLSD& params,
+        const LLSD& http_params = LLSD()
+    );
 
     ~LLXMLRPCTransaction();
 
-    typedef enum e_status {
+    typedef enum e_status
+    {
         StatusNotStarted,
         StatusStarted,
         StatusDownloading,
@@ -105,26 +54,25 @@ public:
         StatusOtherError
     } EStatus;
 
+    /// Run the request a little, returns true when done
     bool process();
-        // run the request a little, returns true when done
 
+    /// Return a status, and extended CURL code, if code isn't null
     EStatus status(int* curlCode);
-        // return status, and extended CURL code, if code isn't null
 
     LLSD getErrorCertData();
+
+    /// Return a message string, suitable for showing the user
     std::string statusMessage();
-        // return a message string, suitable for showing the user
+
+    /// Return a URI for the user with more information (can be empty)
     std::string statusURI();
-        // return a URI for the user with more information
-        // can be empty
 
-    XMLRPC_REQUEST response();
-    LLXMLRPCValue responseValue();
-        // only valid if StatusComplete, otherwise NULL
-        // retains ownership of the result object, don't free it
+    /// Only non-empty if StatusComplete, otherwise Undefined
+    const LLSD& response();
 
+    /// Only valid if StsatusComplete, otherwise 0.0
     F64 transferRate();
-        // only valid if StsatusComplete, otherwise 0.0
 
 private:
     class Handler;
@@ -133,6 +81,4 @@ private:
     Impl& impl;
 };
 
-
-
 #endif // LLXMLRPCTRANSACTION_H
diff --git a/indra/newview/skins/default/xui/da/floater_about.xml b/indra/newview/skins/default/xui/da/floater_about.xml
index 7bcae69779..604eb7c58f 100644
--- a/indra/newview/skins/default/xui/da/floater_about.xml
+++ b/indra/newview/skins/default/xui/da/floater_about.xml
@@ -69,7 +69,6 @@ OpenSSL Copyright (C) 1998-2002 The OpenSSL Project.
 PCRE Copyright (c) 1997-2008 University of Cambridge
 SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
 SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
 xxHash Copyright (C) 2012-2020 Yann Collet.
 zlib Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler.
 google-perftools Copyright (c) 2005, Google Inc.
diff --git a/indra/newview/skins/default/xui/de/floater_about.xml b/indra/newview/skins/default/xui/de/floater_about.xml
index 10ccf0d5da..320db7f654 100644
--- a/indra/newview/skins/default/xui/de/floater_about.xml
+++ b/indra/newview/skins/default/xui/de/floater_about.xml
@@ -28,7 +28,6 @@ mit Open-Source-Beiträgen von:</text>
         PCRE Copyright (c) 1997-2012 University of Cambridge.
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga.
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com).
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-loup Gailly und Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index ff2fa93cbb..126cd84d56 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -111,7 +111,6 @@ Dummy Name replaced at run time
         PCRE Copyright (c) 1997-2012 University of Cambridge
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/es/floater_about.xml b/indra/newview/skins/default/xui/es/floater_about.xml
index e14ba32f69..8103a95376 100644
--- a/indra/newview/skins/default/xui/es/floater_about.xml
+++ b/indra/newview/skins/default/xui/es/floater_about.xml
@@ -28,7 +28,6 @@ con contribuciones de código abierto de:</text>
         PCRE Copyright (c) 1997-2012 University of Cambridge
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-loup Gailly y Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/fr/floater_about.xml b/indra/newview/skins/default/xui/fr/floater_about.xml
index 09da1fb5fd..b6ea621177 100644
--- a/indra/newview/skins/default/xui/fr/floater_about.xml
+++ b/indra/newview/skins/default/xui/fr/floater_about.xml
@@ -28,7 +28,6 @@ avec les contributions Open Source de :</text>
         PCRE Copyright (c) 1997-2012 University of Cambridge
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-Loup Gailly et Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/it/floater_about.xml b/indra/newview/skins/default/xui/it/floater_about.xml
index 7e195d3ca9..77be47d749 100644
--- a/indra/newview/skins/default/xui/it/floater_about.xml
+++ b/indra/newview/skins/default/xui/it/floater_about.xml
@@ -28,7 +28,6 @@ con contributi open source da:</text>
         PCRE Copyright (c) 1997-2012 University of Cambridge
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-loup Gailly e Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/ja/floater_about.xml b/indra/newview/skins/default/xui/ja/floater_about.xml
index 12d763be37..6cd22f6a31 100644
--- a/indra/newview/skins/default/xui/ja/floater_about.xml
+++ b/indra/newview/skins/default/xui/ja/floater_about.xml
@@ -33,7 +33,6 @@ OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
 PCRE Copyright (c) 1997-2012 University of Cambridge
 SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
 SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
 xxHash Copyright (C) 2012-2020 Yann Collet.
 zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/pt/floater_about.xml b/indra/newview/skins/default/xui/pt/floater_about.xml
index aaed728f84..0e95c53109 100644
--- a/indra/newview/skins/default/xui/pt/floater_about.xml
+++ b/indra/newview/skins/default/xui/pt/floater_about.xml
@@ -28,7 +28,6 @@ com contribuições de código aberto de:</text>
         PCRE Copyright (c) 1997-2012 University of Cambridge
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/ru/floater_about.xml b/indra/newview/skins/default/xui/ru/floater_about.xml
index a65a979ccd..22827bc397 100644
--- a/indra/newview/skins/default/xui/ru/floater_about.xml
+++ b/indra/newview/skins/default/xui/ru/floater_about.xml
@@ -28,7 +28,6 @@
         PCRE (c) 1997-2012, Кембриджский университет
         SDL (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib (C) 1995-2012 Jean-loup Gailly и Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/tr/floater_about.xml b/indra/newview/skins/default/xui/tr/floater_about.xml
index 40ca3707c3..ca21bee464 100644
--- a/indra/newview/skins/default/xui/tr/floater_about.xml
+++ b/indra/newview/skins/default/xui/tr/floater_about.xml
@@ -28,7 +28,6 @@ açık kaynak kod katkısında bulunanlar şunlardır:</text>
         PCRE Telif Hakkı (c) 1997-2012 University of Cambridge
         SDL Telif Hakkı (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Telif Hakkı (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Telif Hakkı (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Telif Hakkı (C) 1995-2012 Jean-loup Gailly ve Mark Adler.
 
diff --git a/indra/newview/skins/default/xui/zh/floater_about.xml b/indra/newview/skins/default/xui/zh/floater_about.xml
index a56ae753d1..727f598894 100644
--- a/indra/newview/skins/default/xui/zh/floater_about.xml
+++ b/indra/newview/skins/default/xui/zh/floater_about.xml
@@ -28,7 +28,6 @@
         PCRE Copyright (c) 1997-2012 University of Cambridge
         SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
         SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-        xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
         xxHash Copyright (C) 2012-2020 Yann Collet.
         zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
 
diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp
index 2a0468f3ad..48fecb4f22 100644
--- a/indra/viewer_components/login/lllogin.cpp
+++ b/indra/viewer_components/login/lllogin.cpp
@@ -66,6 +66,16 @@ public:
     LLEventPump& getEventPump() { return mPump; }
 
 private:
+    LLSD hidePasswd(const LLSD& data)
+    {
+        LLSD result(data);
+        if (result.has("params") && result["params"].has("passwd"))
+        {
+            result["params"]["passwd"] = "*******";
+        }
+        return result;
+    }
+
     LLSD getProgressEventLLSD(const std::string& state, const std::string& change,
                            const LLSD& data = LLSD())
     {
@@ -74,15 +84,16 @@ private:
         status_data["change"] = change;
         status_data["progress"] = 0.0f;
 
-        if(mAuthResponse.has("transfer_rate"))
+        if (mAuthResponse.has("transfer_rate"))
         {
             status_data["transfer_rate"] = mAuthResponse["transfer_rate"];
         }
 
-        if(data.isDefined())
+        if (data.isDefined())
         {
             status_data["data"] = data;
         }
+
         return status_data;
     }
 
@@ -119,17 +130,18 @@ private:
 
 void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params)
 {
-    LL_DEBUGS("LLLogin") << " connect with  uri '" << uri << "', login_params " << login_params << LL_ENDL;
+    LL_DEBUGS("LLLogin") << " connect with uri '" << uri << "', login_params " << login_params << LL_ENDL;
 
     // Launch a coroutine with our login_() method. Run the coroutine until
     // its first wait; at that point, return here.
     std::string coroname =
-        LLCoros::instance().launch("LLLogin::Impl::login_",
-                                   boost::bind(&Impl::loginCoro, this, uri, login_params));
-    LL_DEBUGS("LLLogin") << " connected with  uri '" << uri << "', login_params " << login_params << LL_ENDL;
+        LLCoros::instance().launch("LLLogin::Impl::login_", [&]() { loginCoro(uri, login_params); });
+
+    LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL;
 }
 
-namespace {
+namespace
+{
 // Instantiate this rendezvous point at namespace scope so it's already
 // present no matter how early the updater might post to it.
 // Use an LLEventMailDrop, which has future-like semantics: regardless of the
@@ -140,12 +152,8 @@ static LLEventMailDrop sSyncPoint("LoginSync");
 
 void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
 {
-    LLSD printable_params = login_params;
-    if (printable_params.has("params")
-        && printable_params["params"].has("passwd"))
-    {
-        printable_params["params"]["passwd"] = "*******";
-    }
+    LLSD printable_params = hidePasswd(login_params);
+
     try
     {
         LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::getName()
@@ -171,12 +179,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
             ++attempts;
             LLSD progress_data;
             progress_data["attempt"] = attempts;
-            progress_data["request"] = request;
-            if (progress_data["request"].has("params")
-                && progress_data["request"]["params"].has("passwd"))
-            {
-                progress_data["request"]["params"]["passwd"] = "*******";
-            }
+            progress_data["request"] = hidePasswd(request);
             sendProgressEvent("offline", "authenticating", progress_data);
 
             // We expect zero or more "Downloading" status events, followed by
-- 
cgit v1.2.3