summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rwxr-xr-xindra/llcommon/CMakeLists.txt5
-rw-r--r--indra/llcommon/llsdjson.cpp78
-rw-r--r--indra/llcommon/llsdjson.h59
-rwxr-xr-xindra/llcorehttp/httpheaders.cpp18
-rwxr-xr-xindra/llcorehttp/httpheaders.h6
-rwxr-xr-xindra/llmessage/CMakeLists.txt4
-rw-r--r--indra/llmessage/llcorehttputil.cpp82
-rw-r--r--indra/llmessage/llcorehttputil.h12
-rwxr-xr-xindra/newview/llwebprofile.cpp372
-rwxr-xr-xindra/newview/llwebprofile.h12
10 files changed, 449 insertions, 199 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 1459b9ada2..9086691375 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 0000000000..2afdba388a
--- /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 0000000000..cdf9fed500
--- /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 73c92c8f10..e03b1b080d 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 940f92183c..51bd76a01d 100755
--- a/indra/llcorehttp/httpheaders.h
+++ b/indra/llcorehttp/httpheaders.h
@@ -146,13 +146,17 @@ public:
// 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 f6ca6a3634..51b8ed6c62 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 cf34029dfe..05d2e84f88 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);
}
@@ -438,6 +458,58 @@ LLSD HttpCoroRawHandler::handleSuccess(LLCore::HttpResponse * response, LLCore::
}
//========================================================================
+/// 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 35e5b0aa2d..d6219318f9 100644
--- a/indra/llmessage/llcorehttputil.h
+++ b/indra/llmessage/llcorehttputil.h
@@ -373,6 +373,18 @@ public:
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 ddb7f7bfce..3d371e629f 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
@@ -55,139 +59,6 @@
*/
///////////////////////////////////////////////////////////////////////////////
-// 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
std::string LLWebProfile::sAuthCookie;
@@ -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 10279bffac..604ef7aff7 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 @@ public:
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();