From c437a9c4ec865c38366c8057010d24311888ecb1 Mon Sep 17 00:00:00 2001
From: Rider Linden <rider@lindenlab.com>
Date: Wed, 20 May 2015 17:37:27 -0700
Subject: [PATCH] Webprofile converted to coroutine. Added JSON->LLSD converter
 Added corohandler for JSON data

---
 indra/llcommon/CMakeLists.txt      |   5 +
 indra/llcommon/llsdjson.cpp        |  78 ++++++
 indra/llcommon/llsdjson.h          |  59 +++++
 indra/llcorehttp/httpheaders.cpp   |  18 ++
 indra/llcorehttp/httpheaders.h     |   6 +-
 indra/llmessage/CMakeLists.txt     |   4 +
 indra/llmessage/llcorehttputil.cpp |  82 +++++++
 indra/llmessage/llcorehttputil.h   |  12 +
 indra/newview/llwebprofile.cpp     | 372 ++++++++++++++---------------
 indra/newview/llwebprofile.h       |  12 +-
 10 files changed, 449 insertions(+), 199 deletions(-)
 create mode 100644 indra/llcommon/llsdjson.cpp
 create mode 100644 indra/llcommon/llsdjson.h

diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 1459b9ada27..90866913759 100755
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -8,6 +8,7 @@ include(LLCommon)
 include(Linking)
 include(Boost)
 include(LLSharedLibs)
+include(JsonCpp)
 include(GoogleBreakpad)
 include(GooglePerfTools)
 include(Copy3rdPartyLibs)
@@ -17,6 +18,7 @@ include(URIPARSER)
 include_directories(
     ${EXPAT_INCLUDE_DIRS}
     ${LLCOMMON_INCLUDE_DIRS}
+    ${JSONCPP_INCLUDE_DIR}
     ${ZLIB_INCLUDE_DIRS}
     ${BREAKPAD_INCLUDE_DIRECTORIES}
     ${URIPARSER_INCLUDE_DIRS}
@@ -85,6 +87,7 @@ set(llcommon_SOURCE_FILES
     llrefcount.cpp
     llrun.cpp
     llsd.cpp
+	llsdjson.cpp
     llsdparam.cpp
     llsdserialize.cpp
     llsdserialize_xml.cpp
@@ -193,6 +196,7 @@ set(llcommon_HEADER_FILES
     llrefcount.h
     llsafehandle.h
     llsd.h
+	llsdjson.h
     llsdparam.h
     llsdserialize.h
     llsdserialize_xml.h
@@ -260,6 +264,7 @@ target_link_libraries(
     ${APRUTIL_LIBRARIES}
     ${APR_LIBRARIES}
     ${EXPAT_LIBRARIES}
+    ${JSONCPP_LIBRARIES}
     ${ZLIB_LIBRARIES}
     ${WINDOWS_LIBRARIES}
     ${BOOST_PROGRAM_OPTIONS_LIBRARY}
diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp
new file mode 100644
index 00000000000..2afdba388ae
--- /dev/null
+++ b/indra/llcommon/llsdjson.cpp
@@ -0,0 +1,78 @@
+/** 
+ * @file llsdjson.cpp
+ * @brief LLSD flexible data system
+ *
+ * $LicenseInfo:firstyear=2015&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2015, 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$
+ */
+
+// Must turn on conditional declarations in header file so definitions end up
+// with proper linkage.
+#define LLSD_DEBUG_INFO
+#include "linden_common.h"
+
+#include "llsdjson.h"
+
+#include "llerror.h"
+#include "../llmath/llmath.h"
+
+//=========================================================================
+LLSD LlsdFromJson(const Json::Value &val)
+{
+    LLSD result;
+
+    switch (val.type())
+    {
+    default:
+    case Json::nullValue:
+        break;
+    case Json::intValue:
+        result = LLSD(static_cast<LLSD::Integer>(val.asInt()));
+        break;
+    case Json::uintValue:
+        result = LLSD(static_cast<LLSD::Integer>(val.asUInt()));
+        break;
+    case Json::realValue:
+        result = LLSD(static_cast<LLSD::Real>(val.asDouble()));
+        break;
+    case Json::stringValue:
+        result = LLSD(static_cast<LLSD::String>(val.asString()));
+        break;
+    case Json::booleanValue:
+        result = LLSD(static_cast<LLSD::Boolean>(val.asBool()));
+        break;
+    case Json::arrayValue:
+        result = LLSD::emptyArray();
+        for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it)
+        {
+            result.append(LlsdFromJson((*it)));
+        }
+        break;
+    case Json::objectValue:
+        result = LLSD::emptyMap();
+        for (Json::ValueConstIterator it = val.begin(); it != val.end(); ++it)
+        {
+            result[it.memberName()] = LlsdFromJson((*it));
+        }
+        break;
+    }
+    return result;
+}
diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h
new file mode 100644
index 00000000000..cdf9fed5007
--- /dev/null
+++ b/indra/llcommon/llsdjson.h
@@ -0,0 +1,59 @@
+/** 
+ * @file llsdjson.cpp
+ * @brief LLSD flexible data system
+ *
+ * $LicenseInfo:firstyear=2015&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2015, 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_LLSDJSON_H
+#define LL_LLSDJSON_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "stdtypes.h"
+
+#include "llsd.h"
+#include "value.h"
+
+/// Convert a parsed JSON structure into LLSD maintaining member names and 
+/// array indexes.
+/// JSON/JavaScript types are converted as follows:
+/// 
+/// JSON Type     | LLSD Type
+/// --------------+--------------
+///  null         |  undefined
+///  integer      |  LLSD::Integer
+///  unsigned     |  LLSD::Integer
+///  real/numeric |  LLSD::Real
+///  string       |  LLSD::String
+///  boolean      |  LLSD::Boolean
+///  array        |  LLSD::Array
+///  object       |  LLSD::Map
+///  
+/// For maps and arrays child entries will be converted and added to the structure.
+/// Order is preserved for an array but not for objects.
+LLSD LlsdFromJson(const Json::Value &val);
+
+
+#endif // LL_LLSDJSON_H
diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp
index 73c92c8f102..e03b1b080df 100755
--- a/indra/llcorehttp/httpheaders.cpp
+++ b/indra/llcorehttp/httpheaders.cpp
@@ -118,6 +118,24 @@ const std::string * HttpHeaders::find(const std::string &name) const
 	return NULL;
 }
 
+void HttpHeaders::remove(const char *name)
+{
+    remove(std::string(name));
+}
+
+void HttpHeaders::remove(const std::string &name)
+{
+    iterator iend(end());
+    for (iterator iter(begin()); iend != iter; ++iter)
+    {
+        if ((*iter).first == name)
+        {
+            mHeaders.erase(iter);
+            return;
+        }
+    }
+}
+
 
 // Standard Iterators
 HttpHeaders::iterator HttpHeaders::begin()
diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h
index 940f92183ce..51bd76a01d9 100755
--- a/indra/llcorehttp/httpheaders.h
+++ b/indra/llcorehttp/httpheaders.h
@@ -146,13 +146,17 @@ class HttpHeaders : public LLCoreInt::RefCounted
 	//					a pointer to a std::string in the container.
 	//					Pointer is valid only for the lifetime of
 	//					the container or until container is modifed.
-	
 	const std::string * find(const std::string &name) const;
 	const std::string * find(const char * name) const
 	{
 		return find(std::string(name));
 	}
 
+    // Remove the header from the list if found.
+    // 
+    void remove(const std::string &name);
+    void remove(const char *name);
+
 	// Count of headers currently in the list.
 	size_type size() const
 		{
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index f6ca6a3634e..51b8ed6c629 100755
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -14,6 +14,7 @@ include(LLAddBuildTest)
 include(Python)
 include(Tut)
 include(Python)
+include(JsonCpp)
 
 include_directories (${CMAKE_CURRENT_SOURCE_DIR})
 
@@ -23,6 +24,7 @@ include_directories(
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
     ${LLVFS_INCLUDE_DIRS}
+    ${JSONCPP_INCLUDE_DIR}
     )
 
 set(llmessage_SOURCE_FILES
@@ -229,6 +231,7 @@ target_link_libraries(
   ${LLVFS_LIBRARIES}
   ${LLMATH_LIBRARIES}
   ${CARES_LIBRARIES}
+  ${JSONCPP_LIBRARIES}
   ${OPENSSL_LIBRARIES}
   ${CRYPTO_LIBRARIES}
   ${XMLRPCEPI_LIBRARIES}
@@ -254,6 +257,7 @@ if (LL_TESTS)
     ${LLCOMMON_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
     ${LLCOREHTTP_LIBRARIES}
+    ${JSONCPP_LIBRARIES}
     ${BOOST_CONTEXT_LIBRARY}
     ${BOOST_COROUTINE_LIBRARY}
     ${GOOGLEMOCK_LIBRARIES}
diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp
index cf34029dfe6..05d2e84f883 100644
--- a/indra/llmessage/llcorehttputil.cpp
+++ b/indra/llmessage/llcorehttputil.cpp
@@ -32,7 +32,10 @@
 #include <iterator>
 #include "llcorehttputil.h"
 #include "llhttpconstants.h"
+#include "llsd.h"
+#include "llsdjson.h"
 #include "llsdserialize.h"
+#include "reader.h" 
 
 using namespace LLCore;
 
@@ -252,6 +255,23 @@ void HttpCoroHandler::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespons
     }
 
     buildStatusEntry(response, status, result);
+
+#if 0
+    // commenting out, but keeping since this can be useful for debugging
+    if (!status)
+    {
+        LLSD &httpStatus = result[HttpCoroutineAdapter::HTTP_RESULTS];
+
+        LLCore::BufferArray *body = response->getBody();
+        LLCore::BufferArrayStream bas(body);
+        LLSD::Binary bodyData;
+        bodyData.reserve(response->getBodySize());
+        bas >> std::noskipws;
+        bodyData.assign(std::istream_iterator<U8>(bas), std::istream_iterator<U8>());
+        httpStatus["error_body"] = bodyData;
+    }
+#endif
+
     mReplyPump.post(result);
 }
 
@@ -437,6 +457,58 @@ LLSD HttpCoroRawHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::
     return result;
 }
 
+//========================================================================
+/// The HttpCoroJSONHandler is a specialization of the LLCore::HttpHandler for 
+/// interacting with coroutines. 
+/// 
+/// In addition to the normal "http_results" the returned LLSD will contain 
+/// JSON entries will be converted into an LLSD map.  All results are considered 
+/// strings
+///                      
+class HttpCoroJSONHandler : public HttpCoroHandler
+{
+public:
+    HttpCoroJSONHandler(LLEventStream &reply);
+
+    virtual LLSD handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status);
+};
+
+//-------------------------------------------------------------------------
+HttpCoroJSONHandler::HttpCoroJSONHandler(LLEventStream &reply) :
+    HttpCoroHandler(reply)
+{
+}
+
+LLSD HttpCoroJSONHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::HttpStatus &status)
+{
+    LLSD result = LLSD::emptyMap();
+
+    BufferArray * body(response->getBody());
+    if (!body || !body->size())
+    {
+        return result;
+    }
+
+    LLCore::BufferArrayStream bas(body);
+    Json::Value jsonRoot;
+
+    try
+    {
+        bas >> jsonRoot;
+    }
+    catch (std::runtime_error e)
+    {   // deserialization failed.  Record the reason and pass back an empty map for markup.
+        status = LLCore::HttpStatus(499, std::string(e.what()));
+        return result;
+    }
+
+    // Convert the JSON structure to LLSD
+    result = LlsdFromJson(jsonRoot);
+
+    return result;
+}
+
+
 //========================================================================
 HttpRequestPumper::HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request) :
     mHttpRequest(request)
@@ -614,6 +686,16 @@ LLSD HttpCoroutineAdapter::getRawAndYield(LLCoros::self & self, LLCore::HttpRequ
     return getAndYield_(self, request, url, options, headers, httpHandler);
 }
 
+LLSD HttpCoroutineAdapter::getJsonAndYield(LLCoros::self & self, LLCore::HttpRequest::ptr_t request,
+    const std::string & url, LLCore::HttpOptions::ptr_t options, LLCore::HttpHeaders::ptr_t headers)
+{
+    LLEventStream  replyPump(mAdapterName + "Reply", true);
+    HttpCoroHandler::ptr_t httpHandler = HttpCoroHandler::ptr_t(new HttpCoroJSONHandler(replyPump));
+
+    return getAndYield_(self, request, url, options, headers, httpHandler);
+}
+
+
 LLSD HttpCoroutineAdapter::getAndYield_(LLCoros::self & self, LLCore::HttpRequest::ptr_t &request,
     const std::string & url,
     LLCore::HttpOptions::ptr_t &options, LLCore::HttpHeaders::ptr_t &headers, 
diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h
index 35e5b0aa2d0..d6219318f9e 100644
--- a/indra/llmessage/llcorehttputil.h
+++ b/indra/llmessage/llcorehttputil.h
@@ -373,6 +373,18 @@ class HttpCoroutineAdapter
             headers);
     }
 
+    LLSD getJsonAndYield(LLCoros::self & self, LLCore::HttpRequest::ptr_t request,
+        const std::string & url,
+        LLCore::HttpOptions::ptr_t options = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions(), false),
+        LLCore::HttpHeaders::ptr_t headers = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders(), false));
+    LLSD getJsonndYield(LLCoros::self & self, LLCore::HttpRequest::ptr_t &request,
+        const std::string & url, LLCore::HttpHeaders::ptr_t &headers)
+    {
+        return getJsonAndYield(self, request, url,
+            LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions(), false),
+            headers);
+    }
+
 
     /// Execute a DELETE transaction on the supplied URL and yield execution of 
     /// the coroutine until a result is available.
diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp
index ddb7f7bfce1..3d371e629fa 100755
--- a/indra/newview/llwebprofile.cpp
+++ b/indra/newview/llwebprofile.cpp
@@ -34,10 +34,14 @@
 #include "llimagepng.h"
 #include "llplugincookiestore.h"
 
+#include "llsdserialize.h"
+
 // newview
 #include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions
 #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals
 
+#include "llcorehttputil.h"
+
 // third-party
 #include "reader.h" // JSON
 
@@ -54,139 +58,6 @@
  *    -> GET <redirect_url> via PostImageRedirectResponder
  */
 
-///////////////////////////////////////////////////////////////////////////////
-// LLWebProfileResponders::ConfigResponder
-
-class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder
-{
-	LOG_CLASS(LLWebProfileResponders::ConfigResponder);
-
-public:
-	ConfigResponder(LLPointer<LLImageFormatted> imagep)
-	:	mImagep(imagep)
-	{
-	}
-
-	// *TODO: Check for 'application/json' content type, and parse json at the base class.
-	/*virtual*/ void completedRaw(
-		const LLChannelDescriptors& channels,
-		const LLIOPipe::buffer_ptr_t& buffer)
-	{
-		LLBufferStream istr(channels, buffer.get());
-		std::stringstream strstrm;
-		strstrm << istr.rdbuf();
-		const std::string body = strstrm.str();
-
-		if (getStatus() != HTTP_OK)
-		{
-			LL_WARNS() << "Failed to get upload config " << dumpResponse() << LL_ENDL;
-			LLWebProfile::reportImageUploadStatus(false);
-			return;
-		}
-
-		Json::Value root;
-		Json::Reader reader;
-		if (!reader.parse(body, root))
-		{
-			LL_WARNS() << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << LL_ENDL;
-			LLWebProfile::reportImageUploadStatus(false);
-			return;
-		}
-
-		// *TODO: 404 = not supported by the grid
-		// *TODO: increase timeout or handle 499 Expired
-
-		// Convert config to LLSD.
-		const Json::Value data = root["data"];
-		const std::string upload_url = root["url"].asString();
-		LLSD config;
-		config["acl"]						= data["acl"].asString();
-		config["AWSAccessKeyId"]			= data["AWSAccessKeyId"].asString();
-		config["Content-Type"]				= data["Content-Type"].asString();
-		config["key"]						= data["key"].asString();
-		config["policy"]					= data["policy"].asString();
-		config["success_action_redirect"]	= data["success_action_redirect"].asString();
-		config["signature"]					= data["signature"].asString();
-		config["add_loc"]					= data.get("add_loc", "0").asString();
-		config["caption"]					= data.get("caption", "").asString();
-
-		// Do the actual image upload using the configuration.
-		LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << LL_ENDL;
-		LLWebProfile::post(mImagep, config, upload_url);
-	}
-
-private:
-	LLPointer<LLImageFormatted> mImagep;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// LLWebProfilePostImageRedirectResponder
-class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder
-{
-	LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder);
-
-public:
-	/*virtual*/ void completedRaw(
-		const LLChannelDescriptors& channels,
-		const LLIOPipe::buffer_ptr_t& buffer)
-	{
-		if (getStatus() != HTTP_OK)
-		{
-			LL_WARNS() << "Failed to upload image " << dumpResponse() << LL_ENDL;
-			LLWebProfile::reportImageUploadStatus(false);
-			return;
-		}
-
-		LLBufferStream istr(channels, buffer.get());
-		std::stringstream strstrm;
-		strstrm << istr.rdbuf();
-		const std::string body = strstrm.str();
-		LL_INFOS() << "Image uploaded." << LL_ENDL;
-		LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << LL_ENDL;
-		LLWebProfile::reportImageUploadStatus(true);
-	}
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// LLWebProfileResponders::PostImageResponder
-class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder
-{
-	LOG_CLASS(LLWebProfileResponders::PostImageResponder);
-
-public:
-	/*virtual*/ void completedRaw(const LLChannelDescriptors& channels,
-								  const LLIOPipe::buffer_ptr_t& buffer)
-	{
-		// Viewer seems to fail to follow a 303 redirect on POST request
-		// (URLRequest Error: 65, Send failed since rewinding of the data stream failed).
-		// Handle it manually.
-		if (getStatus() == HTTP_SEE_OTHER)
-		{
-			LLSD headers = LLViewerMedia::getHeaders();
-			headers[HTTP_OUT_HEADER_COOKIE] = LLWebProfile::getAuthCookie();
-			const std::string& redir_url = getResponseHeader(HTTP_IN_HEADER_LOCATION);
-			if (redir_url.empty())
-			{
-				LL_WARNS() << "Received empty redirection URL " << dumpResponse() << LL_ENDL;
-				LL_DEBUGS("Snapshots") << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
-				LLWebProfile::reportImageUploadStatus(false);
-			}
-			else
-			{
-				LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << LL_ENDL;
-				LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers);
-			}
-		}
-		else
-		{
-			LL_WARNS() << "Unexpected POST response " << dumpResponse() << LL_ENDL;
-			LL_DEBUGS("Snapshots") << "[headers:" << getResponseHeaders() << "]" << LL_ENDL;
-			LLWebProfile::reportImageUploadStatus(false);
-		}
-	}
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 // LLWebProfile
 
@@ -196,15 +67,9 @@ LLWebProfile::status_callback_t LLWebProfile::mStatusCallback;
 // static
 void LLWebProfile::uploadImage(LLPointer<LLImageFormatted> image, const std::string& caption, bool add_location)
 {
-	// Get upload configuration data.
-	std::string config_url(getProfileURL(LLStringUtil::null) + "snapshots/s3_upload_config");
-	config_url += "?caption=" + LLURI::escape(caption);
-	config_url += "&add_loc=" + std::string(add_location ? "1" : "0");
-
-	LL_DEBUGS("Snapshots") << "Requesting " << config_url << LL_ENDL;
-	LLSD headers = LLViewerMedia::getHeaders();
-	headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie();
-	LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers);
+    LLCoros::instance().launch("LLWebProfile::uploadImageCoro",
+        boost::bind(&LLWebProfile::uploadImageCoro, _1, image, caption, add_location));
+
 }
 
 // static
@@ -214,74 +79,193 @@ void LLWebProfile::setAuthCookie(const std::string& cookie)
 	sAuthCookie = cookie;
 }
 
-// static
-void LLWebProfile::post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url)
+
+/*static*/
+LLCore::HttpHeaders::ptr_t LLWebProfile::buildDefaultHeaders()
 {
-	if (dynamic_cast<LLImagePNG*>(image.get()) == 0)
-	{
-		LL_WARNS() << "Image to upload is not a PNG" << LL_ENDL;
-		llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0);
-		return;
-	}
+    LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders);
+    LLSD headers = LLViewerMedia::getHeaders();
 
-	const std::string boundary = "----------------------------0123abcdefab";
+    for (LLSD::map_iterator it = headers.beginMap(); it != headers.endMap(); ++it)
+    {
+        httpHeaders->append((*it).first, (*it).second.asStringRef());
+    }
 
-	LLSD headers = LLViewerMedia::getHeaders();
-	headers[HTTP_OUT_HEADER_COOKIE] = getAuthCookie();
-	headers[HTTP_OUT_HEADER_CONTENT_TYPE] = "multipart/form-data; boundary=" + boundary;
+    return httpHeaders;
+}
 
-	std::ostringstream body;
 
-	// *NOTE: The order seems to matter.
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"key\"\r\n\r\n"
-			<< config["key"].asString() << "\r\n";
+/*static*/
+void LLWebProfile::uploadImageCoro(LLCoros::self& self, LLPointer<LLImageFormatted> image, std::string caption, bool addLocation)
+{
+    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy));
+    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+    LLCore::HttpHeaders::ptr_t httpHeaders;
+
+    if (dynamic_cast<LLImagePNG*>(image.get()) == 0)
+    {
+        LL_WARNS() << "Image to upload is not a PNG" << LL_ENDL;
+        llassert(dynamic_cast<LLImagePNG*>(image.get()) != 0);
+        return;
+    }
+
+    httpOpts->setWantHeaders(true);
+
+    // Get upload configuration data.
+    std::string configUrl(getProfileURL(std::string()) + "snapshots/s3_upload_config");
+    configUrl += "?caption=" + LLURI::escape(caption);
+    configUrl += "&add_loc=" + std::string(addLocation ? "1" : "0");
+
+    LL_DEBUGS("Snapshots") << "Requesting " << configUrl << LL_ENDL;
+
+    httpHeaders = buildDefaultHeaders();
+    httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie());
+
+    LLSD result = httpAdapter->getJsonAndYield(self, httpRequest, configUrl, httpOpts, httpHeaders);
+
+    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+    if (!status)
+    {
+        std::ostringstream ostm;
+        LLSDSerialize::toPrettyXML(httpResults, ostm);
+        LL_WARNS("Snapshots") << "Failed to get image upload config" << LL_ENDL;
+        LL_WARNS("Snapshots") << ostm.str() << LL_ENDL;
+        LLWebProfile::reportImageUploadStatus(false);
+        return;
+    }
+
+    // Ready to build our image post body.
+
+    const LLSD &data = result["data"];
+    const std::string &uploadUrl = result["url"].asStringRef();
+    const std::string boundary = "----------------------------0123abcdefab";
+
+    // a new set of headers.
+    httpHeaders = buildDefaultHeaders();
+    httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie());
+    httpHeaders->remove(HTTP_OUT_HEADER_CONTENT_TYPE);
+    httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
+    
+    LLCore::BufferArray::ptr_t body = LLWebProfile::buildPostData(data, image, boundary);
+
+    result = httpAdapter->postAndYield(self, httpRequest, uploadUrl, body, httpOpts, httpHeaders);
+
+    {
+        std::ostringstream ostm;
+        LLSDSerialize::toPrettyXML(result, ostm);
+        LL_WARNS("Snapshots") << ostm.str() << LL_ENDL;
+    }
+    body.reset();
+    httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+    status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+    if (!status && (status != LLCore::HttpStatus(HTTP_SEE_OTHER)))
+    {
+        LL_WARNS("Snapshots") << "Failed to upload image data." << LL_ENDL;
+        LLWebProfile::reportImageUploadStatus(false);
+        return;
+    }
+
+    LLSD resultHeaders = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
+
+    httpHeaders = buildDefaultHeaders();
+    httpHeaders->append(HTTP_OUT_HEADER_COOKIE, getAuthCookie());
+
+    const std::string& redirUrl = resultHeaders[HTTP_IN_HEADER_LOCATION].asStringRef();
+
+    if (redirUrl.empty())
+    {
+        LL_WARNS("Snapshots") << "Received empty redirection URL in post image." << LL_ENDL;
+        LLWebProfile::reportImageUploadStatus(false);
+    }
+
+    LL_DEBUGS("Snapshots") << "Got redirection URL: " << redirUrl << LL_ENDL;
+
+    result = httpAdapter->getRawAndYield(self, httpRequest, redirUrl, httpOpts, httpHeaders);
+
+    httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+    status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+    if (status != LLCore::HttpStatus(HTTP_OK))
+    {
+        LL_WARNS("Snapshots") << "Failed to upload image." << LL_ENDL;
+        LLWebProfile::reportImageUploadStatus(false);
+        return;
+    }
+
+    LLSD raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW];
+//    const LLSD::Binary &rawBin = raw.asBinary();
+//    std::istringstream rawresult(rawBin.begin(), rawBin.end());
+
+//    LLBufferStream istr(channels, buffer.get());
+//     std::stringstream strstrm;
+//     strstrm << istr.rdbuf();
+//     const std::string body = strstrm.str();
+    LL_INFOS("Snapshots") << "Image uploaded." << LL_ENDL;
+    LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << raw.asString() << "]" << LL_ENDL;
+    LLWebProfile::reportImageUploadStatus(true);
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n"
-			<< config["AWSAccessKeyId"].asString() << "\r\n";
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"acl\"\r\n\r\n"
-			<< config["acl"].asString() << "\r\n";
+}
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n"
-			<< config["Content-Type"].asString() << "\r\n";
+/*static*/
+LLCore::BufferArray::ptr_t LLWebProfile::buildPostData(const LLSD &data, LLPointer<LLImageFormatted> &image, const std::string &boundary)
+{
+    LLCore::BufferArray::ptr_t body(new LLCore::BufferArray);
+    LLCore::BufferArrayStream bas(body.get());
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"policy\"\r\n\r\n"
-			<< config["policy"].asString() << "\r\n";
+    //    std::ostringstream body;
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"signature\"\r\n\r\n"
-			<< config["signature"].asString() << "\r\n";
+    // *NOTE: The order seems to matter.
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"key\"\r\n\r\n"
+        << data["key"].asString() << "\r\n";
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n"
-			<< config["success_action_redirect"].asString() << "\r\n";
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\n"
+        << data["AWSAccessKeyId"].asString() << "\r\n";
 
-	body	<< "--" << boundary << "\r\n"
-			<< "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n"
-			<< "Content-Type: image/png\r\n\r\n";
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"acl\"\r\n\r\n"
+        << data["acl"].asString() << "\r\n";
 
-	// Insert the image data.
-	// *FIX: Treating this as a string will probably screw it up ...
-	U8* image_data = image->getData();
-	for (S32 i = 0; i < image->getDataSize(); ++i)
-	{
-		body << image_data[i];
-	}
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"Content-Type\"\r\n\r\n"
+        << data["Content-Type"].asString() << "\r\n";
+
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n"
+        << data["policy"].asString() << "\r\n";
+
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n"
+        << data["signature"].asString() << "\r\n";
+
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"success_action_redirect\"\r\n\r\n"
+        << data["success_action_redirect"].asString() << "\r\n";
+
+    bas << "--" << boundary << "\r\n"
+        << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n"
+        << "Content-Type: image/png\r\n\r\n";
 
-	body <<	"\r\n--" << boundary << "--\r\n";
+    // Insert the image data.
+    //char *datap = (char *)(image->getData());
+    //bas.write(datap, image->getDataSize());
+    U8* image_data = image->getData();
+    for (S32 i = 0; i < image->getDataSize(); ++i)
+    {
+        bas << image_data[i];
+    }
 
-	// postRaw() takes ownership of the buffer and releases it later.
-	size_t size = body.str().size();
-	U8 *data = new U8[size];
-	memcpy(data, body.str().data(), size);
+    bas << "\r\n--" << boundary << "--\r\n";
 
-	// Send request, successful upload will trigger posting metadata.
-	LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers);
+    return body;
 }
 
 // static
diff --git a/indra/newview/llwebprofile.h b/indra/newview/llwebprofile.h
index 10279bffacc..604ef7aff76 100755
--- a/indra/newview/llwebprofile.h
+++ b/indra/newview/llwebprofile.h
@@ -28,6 +28,10 @@
 #define LL_LLWEBPROFILE_H
 
 #include "llimage.h"
+#include "lleventcoro.h"
+#include "llcoros.h"
+#include "httpheaders.h"
+#include "bufferarray.h"
 
 namespace LLWebProfileResponders
 {
@@ -54,11 +58,11 @@ class LLWebProfile
 	static void setImageUploadResultCallback(status_callback_t cb) { mStatusCallback = cb; }
 
 private:
-	friend class LLWebProfileResponders::ConfigResponder;
-	friend class LLWebProfileResponders::PostImageResponder;
-	friend class LLWebProfileResponders::PostImageRedirectResponder;
+    static LLCore::HttpHeaders::ptr_t buildDefaultHeaders();
+
+    static void uploadImageCoro(LLCoros::self& self, LLPointer<LLImageFormatted> image, std::string caption, bool add_location);
+    static LLCore::BufferArray::ptr_t buildPostData(const LLSD &data, LLPointer<LLImageFormatted> &image, const std::string &boundary);
 
-	static void post(LLPointer<LLImageFormatted> image, const LLSD& config, const std::string& url);
 	static void reportImageUploadStatus(bool ok);
 	static std::string getAuthCookie();
 
-- 
GitLab