From 66664660d7276eee4c43958ca6f9d7ffa31dc32c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 15 Nov 2012 04:07:44 +0100 Subject: [PATCH 1/9] Copied llwebprofile.{h,cpp} from v-d as-is. --- indra/newview/CMakeLists.txt | 2 + indra/newview/llwebprofile.cpp | 305 +++++++++++++++++++++++++++++++++ indra/newview/llwebprofile.h | 69 ++++++++ 3 files changed, 376 insertions(+) create mode 100644 indra/newview/llwebprofile.cpp create mode 100644 indra/newview/llwebprofile.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3ef620b2b..dc0223a63 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -538,6 +538,7 @@ set(viewer_SOURCE_FILES llwearablelist.cpp llwearabletype.cpp llweb.cpp + llwebprofile.cpp llwind.cpp llwlanimator.cpp llwldaycycle.cpp @@ -1047,6 +1048,7 @@ set(viewer_HEADER_FILES llwearablelist.h llwearabletype.h llweb.h + llwebprofile.h llwind.h llwindebug.h llwlanimator.h diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp new file mode 100644 index 000000000..641f338f2 --- /dev/null +++ b/indra/newview/llwebprofile.cpp @@ -0,0 +1,305 @@ +/** + * @file llwebprofile.cpp + * @brief Web profile access. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llwebprofile.h" + +// libs +#include "llbufferstream.h" +#include "llhttpclient.h" +#include "llimagepng.h" +#include "llplugincookiestore.h" + +// newview +#include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions +#include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals + +// third-party +#include "reader.h" // JSON + +/* + * Workflow: + * 1. LLViewerMedia::setOpenIDCookie() + * -> GET https://my-demo.secondlife.com/ via LLViewerMediaWebProfileResponder + * -> LLWebProfile::setAuthCookie() + * 2. LLWebProfile::uploadImage() + * -> GET "https://my-demo.secondlife.com/snapshots/s3_upload_config" via ConfigResponder + * 3. LLWebProfile::post() + * -> POST via PostImageResponder + * -> redirect + * -> GET via PostImageRedirectResponder + */ + +/////////////////////////////////////////////////////////////////////////////// +// LLWebProfileResponders::ConfigResponder + +class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLWebProfileResponders::ConfigResponder); + +public: + ConfigResponder(LLPointer imagep) + : mImagep(imagep) + { + } + + /*virtual*/ void completedRaw( + U32 status, + const std::string& reason, + 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 (status != 200) + { + llwarns << "Failed to get upload config (" << status << ")" << llendl; + LLWebProfile::reportImageUploadStatus(false); + return; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(body, root)) + { + llwarns << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << llendl; + 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 << "]" << llendl; + LLWebProfile::post(mImagep, config, upload_url); + } + +private: + LLPointer mImagep; +}; + +/////////////////////////////////////////////////////////////////////////////// +// LLWebProfilePostImageRedirectResponder +class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder); + +public: + /*virtual*/ void completedRaw( + U32 status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + if (status != 200) + { + llwarns << "Failed to upload image: " << status << " " << reason << llendl; + LLWebProfile::reportImageUploadStatus(false); + return; + } + + LLBufferStream istr(channels, buffer.get()); + std::stringstream strstrm; + strstrm << istr.rdbuf(); + const std::string body = strstrm.str(); + llinfos << "Image uploaded." << llendl; + LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << llendl; + LLWebProfile::reportImageUploadStatus(true); + } + +private: + LLPointer mImagep; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// LLWebProfileResponders::PostImageResponder +class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLWebProfileResponders::PostImageResponder); + +public: + /*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + // 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 (status == 303) + { + LLSD headers = LLViewerMedia::getHeaders(); + headers["Cookie"] = LLWebProfile::getAuthCookie(); + const std::string& redir_url = content["location"]; + LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl; + LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers); + } + else + { + llwarns << "Unexpected POST status: " << status << " " << reason << llendl; + LL_DEBUGS("Snapshots") << "headers: [" << content << "]" << llendl; + LLWebProfile::reportImageUploadStatus(false); + } + } + + // Override just to suppress warnings. + /*virtual*/ void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// LLWebProfile + +std::string LLWebProfile::sAuthCookie; +LLWebProfile::status_callback_t LLWebProfile::mStatusCallback; + +// static +void LLWebProfile::uploadImage(LLPointer 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 << llendl; + LLSD headers = LLViewerMedia::getHeaders(); + headers["Cookie"] = getAuthCookie(); + LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers); +} + +// static +void LLWebProfile::setAuthCookie(const std::string& cookie) +{ + LL_DEBUGS("Snapshots") << "Setting auth cookie: " << cookie << llendl; + sAuthCookie = cookie; +} + +// static +void LLWebProfile::post(LLPointer image, const LLSD& config, const std::string& url) +{ + if (dynamic_cast(image.get()) == 0) + { + llwarns << "Image to upload is not a PNG" << llendl; + llassert(dynamic_cast(image.get()) != 0); + return; + } + + const std::string boundary = "----------------------------0123abcdefab"; + + LLSD headers = LLViewerMedia::getHeaders(); + headers["Cookie"] = getAuthCookie(); + headers["Content-Type"] = "multipart/form-data; boundary=" + boundary; + + 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"; + + 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"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"policy\"\r\n\r\n" + << config["policy"].asString() << "\r\n"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"signature\"\r\n\r\n" + << config["signature"].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"; + + body << "--" << boundary << "\r\n" + << "Content-Disposition: form-data; name=\"file\"; filename=\"snapshot.png\"\r\n" + << "Content-Type: image/png\r\n\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]; + } + + body << "\r\n--" << boundary << "--\r\n"; + + // 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); + + // Send request, successful upload will trigger posting metadata. + LLHTTPClient::postRaw(url, data, size, new LLWebProfileResponders::PostImageResponder(), headers); +} + +// static +void LLWebProfile::reportImageUploadStatus(bool ok) +{ + if (mStatusCallback) + { + mStatusCallback(ok); + } +} + +// static +std::string LLWebProfile::getAuthCookie() +{ + // This is needed to test image uploads on Linux viewer built with OpenSSL 1.0.0 (0.9.8 works fine). + const char* debug_cookie = getenv("LL_SNAPSHOT_COOKIE"); + return debug_cookie ? debug_cookie : sAuthCookie; +} diff --git a/indra/newview/llwebprofile.h b/indra/newview/llwebprofile.h new file mode 100644 index 000000000..10279bffa --- /dev/null +++ b/indra/newview/llwebprofile.h @@ -0,0 +1,69 @@ +/** + * @file llwebprofile.h + * @brief Web profile access. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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_LLWEBPROFILE_H +#define LL_LLWEBPROFILE_H + +#include "llimage.h" + +namespace LLWebProfileResponders +{ + class ConfigResponder; + class PostImageResponder; + class PostImageRedirectResponder; +}; + +/** + * @class LLWebProfile + * + * Manages interaction with, a web service allowing the upload of snapshot images + * taken within the viewer. + */ +class LLWebProfile +{ + LOG_CLASS(LLWebProfile); + +public: + typedef boost::function status_callback_t; + + static void uploadImage(LLPointer image, const std::string& caption, bool add_location); + static void setAuthCookie(const std::string& cookie); + static void setImageUploadResultCallback(status_callback_t cb) { mStatusCallback = cb; } + +private: + friend class LLWebProfileResponders::ConfigResponder; + friend class LLWebProfileResponders::PostImageResponder; + friend class LLWebProfileResponders::PostImageRedirectResponder; + + static void post(LLPointer image, const LLSD& config, const std::string& url); + static void reportImageUploadStatus(bool ok); + static std::string getAuthCookie(); + + static std::string sAuthCookie; + static status_callback_t mStatusCallback; +}; + +#endif // LL_LLWEBPROFILE_H From fdfe0cae879c49d9ff7e888b985a0d193ed793e5 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 15 Nov 2012 13:51:16 +0100 Subject: [PATCH 2/9] More clearly state the implications of the NoVerifySSLCert debug option --- indra/newview/app_settings/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0ccac5736..06dfc90e4 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9104,7 +9104,7 @@ Found in Advanced->Rendering->Info Displays NoVerifySSLCert Comment - Do not verify SSL peers + Do not verify SSL certificates. WARNING: Setting this to TRUE allows anyone to impersonate the server and intercept your data (man in the middle attack). Persist 1 Type From fce64f8f12f0de144cce7aee045438d822ac8bbe Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 16 Nov 2012 02:00:06 +0100 Subject: [PATCH 3/9] Add LLWebProfile and responders. Adds finding and using libjsoncpp. Note that the old cmake file found libjson, not the same thing. Adds Debug Setting WebProfileNonProductionURL (next to already existing WebProfileURL) to mimic V3's behavior and use a different URL for aditi. These Debug Settings are using by (the new) getProfileURL() (copied from V3 with just a minor fix). Adds HippoGridInfo::isInProductionGrid() next to the existing LLViewerLogin::isInProductionGrid that always returned true. The former should only be called SL grids and then only returns true for agni (and false for aditi et al). The latter was changed to now always return true except on SL when the grid isn't agni. The first is used for SL-only cases, the latter for things like colors and for godmode decision logic. V3's llwebprofile.cpp was fixed to compile on singu, with only real difference that I dropped the Content-Type headers for the GET methods. --- indra/cmake/FindJsonCpp.cmake | 61 +--- indra/cmake/JsonCpp.cmake | 2 +- indra/llmessage/aihttptimeoutpolicy.cpp | 1 + indra/newview/CMakeLists.txt | 4 +- indra/newview/app_settings/settings.xml | 13 +- indra/newview/hippogridmanager.cpp | 8 + indra/newview/hippogridmanager.h | 2 + indra/newview/llfloateravatarinfo.cpp | 10 - indra/newview/llfloatermodelpreview.cpp | 2 +- indra/newview/llmarketplacefunctions.cpp | 2 +- indra/newview/llpanelprofile.cpp | 441 +++++++++++++++++++++++ indra/newview/llpanelprofile.h | 106 ++++++ indra/newview/llviewernetwork.cpp | 3 +- indra/newview/llwebprofile.cpp | 55 ++- 14 files changed, 637 insertions(+), 73 deletions(-) create mode 100644 indra/newview/llpanelprofile.cpp create mode 100644 indra/newview/llpanelprofile.h diff --git a/indra/cmake/FindJsonCpp.cmake b/indra/cmake/FindJsonCpp.cmake index 44ab0e769..a48c97396 100644 --- a/indra/cmake/FindJsonCpp.cmake +++ b/indra/cmake/FindJsonCpp.cmake @@ -3,16 +3,18 @@ # - Find JSONCpp # Find the JSONCpp includes and library # This module defines -# JSONCPP_INCLUDE_DIR, where to find json.h, etc. -# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp. -# JSONCPP_FOUND, If false, do not try to use jsoncpp. -# also defined, but not for general use are -# JSONCPP_LIBRARY, where to find the jsoncpp library. +# JSONCPP_FOUND, System has libjsoncpp. +# JSONCPP_INCLUDE_DIRS - The libjsoncpp include directories. +# JSONCPP_LIBRARIES - The libraries needed to use libjsoncpp. +# JSONCPP_DEFINITIONS - Compiler switches required for using libjsoncpp. -FIND_PATH(JSONCPP_INCLUDE_DIR json/json.h -/usr/local/include -/usr/include -) +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_JSONCPP jsoncpp) +SET(JSONCPP_DEFINITIONS ${PC_JSONCPP_CFLAGS_OTHER}) + +FIND_PATH(JSONCPP_INCLUDE_DIR json/reader.h + HINTS ${PC_JSONCPP_INCLUDE_DIR} ${PC_JSONCPP_INCLUDE_DIRS} + PATH_SUFFIXES jsoncpp) # Get the GCC compiler version EXEC_PROGRAM(${CMAKE_CXX_COMPILER} @@ -22,39 +24,16 @@ EXEC_PROGRAM(${CMAKE_CXX_COMPILER} ) # Try to find a library that was compiled with the same compiler version as we currently use. -SET(JSONCPP_NAMES ${JSONCPP_NAMES} libjson_linux-gcc-${_gcc_COMPILER_VERSION}_libmt.so) -IF (STANDALONE) - # On standalone, assume that the system installed library was compiled with the used compiler. - SET(JSONCPP_NAMES ${JSONCPP_NAMES} libjson.so) -ENDIF (STANDALONE) FIND_LIBRARY(JSONCPP_LIBRARY - NAMES ${JSONCPP_NAMES} - PATHS /usr/lib /usr/local/lib - ) + NAMES libjson_linux-gcc-${_gcc_COMPILER_VERSION}_libmt.so libjsoncpp.so + HINTS ${PC_JSONCPP_LIBDIR} ${PC_JSONCPP_LIBRARY_DIRS} + PATHS /usr/lib /usr/local/lib) -IF (JSONCPP_LIBRARY AND JSONCPP_INCLUDE_DIR) - SET(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY}) - SET(JSONCPP_FOUND "YES") -ELSE (JSONCPP_LIBRARY AND JSONCPP_INCLUDE_DIR) - SET(JSONCPP_FOUND "NO") -ENDIF (JSONCPP_LIBRARY AND JSONCPP_INCLUDE_DIR) +SET(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY}) +SET(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR}) +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(JSONCPP DEFAULT_MSG + JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR) -IF (JSONCPP_FOUND) - IF (NOT JSONCPP_FIND_QUIETLY) - MESSAGE(STATUS "Found JSONCpp: ${JSONCPP_LIBRARIES}") - ENDIF (NOT JSONCPP_FIND_QUIETLY) -ELSE (JSONCPP_FOUND) - IF (JSONCPP_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find JSONCpp library") - ENDIF (JSONCPP_FIND_REQUIRED) -ENDIF (JSONCPP_FOUND) - -# Deprecated declarations. -SET (NATIVE_JSONCPP_INCLUDE_PATH ${JSONCPP_INCLUDE_DIR} ) -GET_FILENAME_COMPONENT (NATIVE_JSONCPP_LIB_PATH ${JSONCPP_LIBRARY} PATH) - -MARK_AS_ADVANCED( - JSONCPP_LIBRARY - JSONCPP_INCLUDE_DIR - ) +MARK_AS_ADVANCED(JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR) diff --git a/indra/cmake/JsonCpp.cmake b/indra/cmake/JsonCpp.cmake index 241db3570..2d7c16939 100644 --- a/indra/cmake/JsonCpp.cmake +++ b/indra/cmake/JsonCpp.cmake @@ -2,7 +2,7 @@ include(Prebuilt) -set(JSONCPP_FIND_QUIETLY ON) +set(JSONCPP_FIND_QUIETLY OFF) set(JSONCPP_FIND_REQUIRED ON) if (STANDALONE) diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index 4acd41267..e91ee2e6b 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -889,6 +889,7 @@ P(viewerStatsResponder); P(viewerVoiceAccountProvisionResponder); P(voiceCallCapResponder); P(voiceClientCapResponder); +P(webProfileResponders); P(wholeModelFeeResponder); P(wholeModelUploadResponder); P2(XMLRPCResponder, connect_40s); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dc0223a63..ec97152c2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -17,7 +17,7 @@ endif(FMOD) include(OPENAL) include(FindOpenGL) include(Hunspell) -#include(JsonCpp) +include(JsonCpp) include(LLAddBuildTest) include(LLAudio) include(LLCharacter) @@ -370,6 +370,7 @@ set(viewer_SOURCE_FILES llpanelpermissions.cpp llpanelpick.cpp llpanelplace.cpp + llpanelprofile.cpp llpanelskins.cpp llpanelvolume.cpp llpanelweb.cpp @@ -875,6 +876,7 @@ set(viewer_HEADER_FILES llpanelpermissions.h llpanelpick.h llpanelplace.h + llpanelprofile.h llpanelskins.h llpanelvolume.h llpanelweb.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 06dfc90e4..9c0a13fb9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7402,7 +7402,7 @@ Found in Advanced->Rendering->Info Displays WebProfileURL Comment - URL for Web Profiles + URL for SL Web Profiles Persist 0 Type @@ -7410,6 +7410,17 @@ Found in Advanced->Rendering->Info Displays Value https://my.secondlife.com/[AGENT_NAME] + WebProfileNonProductionURL + + Comment + URL for SL Web Profiles on Non-Production grids + Persist + 0 + Type + String + Value + https://my-demo.secondlife.com/[AGENT_NAME] + HighResSnapshot Comment diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index c0ceef71d..a626baaf8 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -51,6 +51,7 @@ HippoGridInfo::HippoGridInfo(const std::string& gridName) : mGridMessage(""), mXmlState(XML_VOID), mVoiceConnector("SLVoice"), + mIsInProductionGrid(false), mRenderCompat(true), mInvLinks(false), mAutoUpdate(false), @@ -80,6 +81,12 @@ bool HippoGridInfo::isSecondLife() const return (mPlatform == HippoGridInfo::PLATFORM_SECONDLIFE); } +bool HippoGridInfo::isInProductionGrid() const +{ + llassert(mPlatform == HippoGridInfo::PLATFORM_SECONDLIFE); + return mIsInProductionGrid; +} + const std::string& HippoGridInfo::getGridName() const { return mGridName; @@ -211,6 +218,7 @@ void HippoGridInfo::setGridName(const std::string& gridName) { setGridNick(gridName); }*/ + mIsInProductionGrid = gridName == "secondlife"; } void HippoGridInfo::setGridNick(std::string gridNick) diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 4f3ef634e..5d58a1b5f 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -39,6 +39,7 @@ public: Platform getPlatform(); bool isOpenSimulator() const; bool isSecondLife() const; + bool isInProductionGrid() const; // Should only be called if isSecondLife() returns true. const std::string& getGridName() const; const std::string& getGridOwner() const; const std::string& getLoginUri() const; @@ -110,6 +111,7 @@ private: std::string mPasswordUrl; std::string mSearchUrl; std::string mVoiceConnector; + bool mIsInProductionGrid; bool mRenderCompat; bool mInvLinks; bool mAutoUpdate; diff --git a/indra/newview/llfloateravatarinfo.cpp b/indra/newview/llfloateravatarinfo.cpp index f60e24623..224099794 100644 --- a/indra/newview/llfloateravatarinfo.cpp +++ b/indra/newview/llfloateravatarinfo.cpp @@ -294,13 +294,3 @@ LLPreview::EAssetStatus LLFloaterAvatarInfo::getAssetStatus() } return mAssetStatus; } - -std::string getProfileURL(const std::string& agent_name) -{ - std::string url = gSavedSettings.getString("WebProfileURL"); - LLSD subs; - subs["AGENT_NAME"] = agent_name; - url = LLWeb::expandURLSubstitutions(url,subs); - LLStringUtil::toLower(url); - return url; -} diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 89d34dea3..b85b1b3e5 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -455,7 +455,7 @@ BOOL LLFloaterModelPreview::postBuild() std::string validate_url; if (gHippoGridManager->getCurrentGrid()->isSecondLife()) { - if (LLViewerLogin::getInstance()->isInProductionGrid()) + if (gHippoGridManager->getConnectedGrid()->isInProductionGrid()) { validate_url = "http://secondlife.com/my/account/mesh.php"; } diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 545ad927b..65cb7f8b8 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -46,7 +46,7 @@ static std::string getMarketplaceDomain() std::string domain = "secondlife.com"; if (gHippoGridManager->getCurrentGrid()->isSecondLife()) { - if (!LLViewerLogin::getInstance()->isInProductionGrid()) + if (!gHippoGridManager->getConnectedGrid()->isInProductionGrid()) { domain = "secondlife.aditi.lindenlab.com"; } diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp new file mode 100644 index 000000000..42db9fdd6 --- /dev/null +++ b/indra/newview/llpanelprofile.cpp @@ -0,0 +1,441 @@ +/** +* @file llpanelprofile.cpp +* @brief Profile panel implementation +* +* $LicenseInfo:firstyear=2009&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$ +*/ + +#include "llviewerprecompiledheaders.h" +#include "llpanelprofile.h" + +#ifdef AI_UNUSED +#include "llagent.h" +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llcommandhandler.h" +#include "llnotificationsutil.h" +#include "llpanelpicks.h" +#include "lltabcontainer.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" + +static const std::string PANEL_PICKS = "panel_picks"; +#endif // AI_UNUSED + +#include "hippogridmanager.h" +#include "llcontrol.h" +#include "llweb.h" + +std::string getProfileURL(const std::string& agent_name) +{ + std::string url; + + if (gHippoGridManager->getConnectedGrid()->isInProductionGrid()) + { + url = gSavedSettings.getString("WebProfileURL"); + } + else + { + url = gSavedSettings.getString("WebProfileNonProductionURL"); + } + LLSD subs; + subs["AGENT_NAME"] = agent_name; + url = LLWeb::expandURLSubstitutions(url,subs); + LLStringUtil::toLower(url); + return url; +} + +#ifdef AI_UNUSED +class LLProfileHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLProfileHandler() : LLCommandHandler("profile", UNTRUSTED_THROTTLE) { } + + bool handle(const LLSD& params, const LLSD& query_map, + LLMediaCtrl* web) + { + if (params.size() < 1) return false; + std::string agent_name = params[0]; + llinfos << "Profile, agent_name " << agent_name << llendl; + std::string url = getProfileURL(agent_name); + LLWeb::loadURLInternal(url); + + return true; + } +}; +LLProfileHandler gProfileHandler; + +class LLAgentHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { } + + bool handle(const LLSD& params, const LLSD& query_map, + LLMediaCtrl* web) + { + if (params.size() < 2) return false; + LLUUID avatar_id; + if (!avatar_id.set(params[0], FALSE)) + { + return false; + } + + const std::string verb = params[1].asString(); + if (verb == "about") + { + LLAvatarActions::showProfile(avatar_id); + return true; + } + + if (verb == "inspect") + { + LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); + return true; + } + + if (verb == "im") + { + LLAvatarActions::startIM(avatar_id); + return true; + } + + if (verb == "pay") + { + if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAvatarPay")) + { + LLNotificationsUtil::add("NoAvatarPay", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + } + + LLAvatarActions::pay(avatar_id); + return true; + } + + if (verb == "offerteleport") + { + LLAvatarActions::offerTeleport(avatar_id); + return true; + } + + if (verb == "requestfriend") + { + LLAvatarActions::requestFriendshipDialog(avatar_id); + return true; + } + + if (verb == "mute") + { + if (! LLAvatarActions::isBlocked(avatar_id)) + { + LLAvatarActions::toggleBlock(avatar_id); + } + return true; + } + + if (verb == "unmute") + { + if (LLAvatarActions::isBlocked(avatar_id)) + { + LLAvatarActions::toggleBlock(avatar_id); + } + return true; + } + + return false; + } +}; +LLAgentHandler gAgentHandler; + + +//-- LLPanelProfile::ChildStack begins ---------------------------------------- +LLPanelProfile::ChildStack::ChildStack() +: mParent(NULL) +{ +} + +LLPanelProfile::ChildStack::~ChildStack() +{ + while (mStack.size() != 0) + { + view_list_t& top = mStack.back(); + for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it) + { + LLView* viewp = *it; + if (viewp) + { + viewp->die(); + } + } + mStack.pop_back(); + } +} + +void LLPanelProfile::ChildStack::setParent(LLPanel* parent) +{ + llassert_always(parent != NULL); + mParent = parent; +} + +/// Save current parent's child views and remove them from the child list. +bool LLPanelProfile::ChildStack::push() +{ + view_list_t vlist = *mParent->getChildList(); + + for (view_list_t::const_iterator it = vlist.begin(); it != vlist.end(); ++it) + { + LLView* viewp = *it; + mParent->removeChild(viewp); + } + + mStack.push_back(vlist); + dump(); + return true; +} + +/// Restore saved children (adding them back to the child list). +bool LLPanelProfile::ChildStack::pop() +{ + if (mStack.size() == 0) + { + llwarns << "Empty stack" << llendl; + llassert(mStack.size() == 0); + return false; + } + + view_list_t& top = mStack.back(); + for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it) + { + LLView* viewp = *it; + mParent->addChild(viewp); + } + + mStack.pop_back(); + dump(); + return true; +} + +/// Temporarily add all saved children back. +void LLPanelProfile::ChildStack::preParentReshape() +{ + mSavedStack = mStack; + while(mStack.size() > 0) + { + pop(); + } +} + +/// Add the temporarily saved children back. +void LLPanelProfile::ChildStack::postParentReshape() +{ + mStack = mSavedStack; + mSavedStack = stack_t(); + + for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it) + { + const view_list_t& vlist = (*stack_it); + for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it) + { + LLView* viewp = *list_it; + lldebugs << "removing " << viewp->getName() << llendl; + mParent->removeChild(viewp); + } + } +} + +void LLPanelProfile::ChildStack::dump() +{ + unsigned lvl = 0; + lldebugs << "child stack dump:" << llendl; + for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it, ++lvl) + { + std::ostringstream dbg_line; + dbg_line << "lvl #" << lvl << ":"; + const view_list_t& vlist = (*stack_it); + for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it) + { + dbg_line << " " << (*list_it)->getName(); + } + lldebugs << dbg_line.str() << llendl; + } +} + +//-- LLPanelProfile::ChildStack ends ------------------------------------------ + +LLPanelProfile::LLPanelProfile() + : LLPanel() + , mAvatarId(LLUUID::null) +{ + mChildStack.setParent(this); +} + +BOOL LLPanelProfile::postBuild() +{ + LLPanelPicks* panel_picks = findChild(PANEL_PICKS); + panel_picks->setProfilePanel(this); + + getTabContainer()[PANEL_PICKS] = panel_picks; + + return TRUE; +} + +// virtual +void LLPanelProfile::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + // Temporarily add saved children back and reshape them. + mChildStack.preParentReshape(); + LLPanel::reshape(width, height, called_from_parent); + mChildStack.postParentReshape(); +} + +void LLPanelProfile::onOpen(const LLSD& key) +{ + getTabContainer()[PANEL_PICKS]->onOpen(getAvatarId()); + + // support commands to open further pieces of UI + if (key.has("show_tab_panel")) + { + std::string panel = key["show_tab_panel"].asString(); + if (panel == "create_classified") + { + LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); + if (picks) + { + picks->createNewClassified(); + } + } + else if (panel == "classified_details") + { + LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); + if (picks) + { + LLSD params = key; + params.erase("show_tab_panel"); + params.erase("open_tab_name"); + picks->openClassifiedInfo(params); + } + } + else if (panel == "edit_classified") + { + LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); + if (picks) + { + LLSD params = key; + params.erase("show_tab_panel"); + params.erase("open_tab_name"); + picks->openClassifiedEdit(params); + } + } + else if (panel == "create_pick") + { + LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); + if (picks) + { + picks->createNewPick(); + } + } + else if (panel == "edit_pick") + { + LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); + if (picks) + { + LLSD params = key; + params.erase("show_tab_panel"); + params.erase("open_tab_name"); + picks->openPickEdit(params); + } + } + } +} + +void LLPanelProfile::onTabSelected(const LLSD& param) +{ + std::string tab_name = param.asString(); + if (NULL != getTabContainer()[tab_name]) + { + getTabContainer()[tab_name]->onOpen(getAvatarId()); + } +} + +void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params) +{ + // Hide currently visible panel (STORM-690). + mChildStack.push(); + + // Add the panel or bring it to front. + if (panel->getParent() != this) + { + addChild(panel); + } + else + { + sendChildToFront(panel); + } + + panel->setVisible(TRUE); + panel->setFocus(TRUE); // prevent losing focus by the floater + panel->onOpen(params); + + LLRect new_rect = getRect(); + panel->reshape(new_rect.getWidth(), new_rect.getHeight()); + new_rect.setLeftTopAndSize(0, new_rect.getHeight(), new_rect.getWidth(), new_rect.getHeight()); + panel->setRect(new_rect); +} + +void LLPanelProfile::closePanel(LLPanel* panel) +{ + panel->setVisible(FALSE); + + if (panel->getParent() == this) + { + removeChild(panel); + + // Make the underlying panel visible. + mChildStack.pop(); + + // Prevent losing focus by the floater + const child_list_t* child_list = getChildList(); + if (child_list->size() > 0) + { + child_list->front()->setFocus(TRUE); + } + else + { + llwarns << "No underlying panel to focus." << llendl; + } + } +} + +S32 LLPanelProfile::notifyParent(const LLSD& info) +{ + std::string action = info["action"]; + // lets update Picks list after Pick was saved + if("save_new_pick" == action) + { + onOpen(info); + return 1; + } + + return LLPanel::notifyParent(info); +} +#endif // AI_UNUSED diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h new file mode 100644 index 000000000..73539dda2 --- /dev/null +++ b/indra/newview/llpanelprofile.h @@ -0,0 +1,106 @@ +/** +* @file llpanelprofile.h +* @brief Profile panel +* +* $LicenseInfo:firstyear=2009&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_LLPANELPROFILE_H +#define LL_LLPANELPROFILE_H + +#ifdef AI_UNUSED +#include "llpanel.h" +#include "llpanelavatar.h" + +class LLTabContainer; +#endif // AI_UNUSED + +std::string getProfileURL(const std::string& agent_name); + +#ifdef AI_UNUSED +/** +* Base class for Profile View and My Profile. +*/ +class LLPanelProfile : public LLPanel +{ + LOG_CLASS(LLPanelProfile); + +public: + /*virtual*/ BOOL postBuild(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void onOpen(const LLSD& key); + + virtual void openPanel(LLPanel* panel, const LLSD& params); + + virtual void closePanel(LLPanel* panel); + + S32 notifyParent(const LLSD& info); + +protected: + + LLPanelProfile(); + + virtual void onTabSelected(const LLSD& param); + + const LLUUID& getAvatarId() { return mAvatarId; } + + void setAvatarId(const LLUUID& avatar_id) { mAvatarId = avatar_id; } + + typedef std::map profile_tabs_t; + + profile_tabs_t& getTabContainer() { return mTabContainer; } + +private: + + //-- ChildStack begins ---------------------------------------------------- + class ChildStack + { + LOG_CLASS(LLPanelProfile::ChildStack); + public: + ChildStack(); + ~ChildStack(); + void setParent(LLPanel* parent); + + bool push(); + bool pop(); + void preParentReshape(); + void postParentReshape(); + + private: + void dump(); + + typedef LLView::child_list_t view_list_t; + typedef std::list stack_t; + + stack_t mStack; + stack_t mSavedStack; + LLPanel* mParent; + }; + //-- ChildStack ends ------------------------------------------------------ + + profile_tabs_t mTabContainer; + ChildStack mChildStack; + LLUUID mAvatarId; +}; +#endif // AI_UNUSED + +#endif //LL_LLPANELPROFILE_H diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index a2625495c..dde9a9d47 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -77,6 +77,7 @@ bool LLViewerLogin::isSecondLife() bool LLViewerLogin::isInProductionGrid() { - return true; + // Return true (as before) on opensim grids, but return the real thing (agni or not) on SL. + return !gHippoGridManager->getConnectedGrid()->isSecondLife() || gHippoGridManager->getConnectedGrid()->isInProductionGrid(); } diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index 641f338f2..141c746e0 100644 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -35,11 +35,15 @@ #include "llplugincookiestore.h" // newview -#include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions +#include "llpanelprofile.h" // getProfileURL (this is the original location LL put it). #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals // third-party -#include "reader.h" // JSON +#ifdef LL_STANDALONE +#include // JSONCPP +#else +#include "reader.h" // prebuilt jsoncpp is wrongly packaged. +#endif /* * Workflow: @@ -57,7 +61,9 @@ /////////////////////////////////////////////////////////////////////////////// // LLWebProfileResponders::ConfigResponder -class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::Responder +extern AIHTTPTimeoutPolicy webProfileResponders_timeout; + +class LLWebProfileResponders::ConfigResponder : public LLHTTPClient::ResponderWithCompleted { LOG_CLASS(LLWebProfileResponders::ConfigResponder); @@ -71,7 +77,7 @@ public: U32 status, const std::string& reason, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + const buffer_ptr_t& buffer) { LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; @@ -116,13 +122,16 @@ public: LLWebProfile::post(mImagep, config, upload_url); } +protected: + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return webProfileResponders_timeout; } + private: LLPointer mImagep; }; /////////////////////////////////////////////////////////////////////////////// // LLWebProfilePostImageRedirectResponder -class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::Responder +class LLWebProfileResponders::PostImageRedirectResponder : public LLHTTPClient::ResponderWithCompleted { LOG_CLASS(LLWebProfileResponders::PostImageRedirectResponder); @@ -131,7 +140,7 @@ public: U32 status, const std::string& reason, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + const buffer_ptr_t& buffer) { if (status != 200) { @@ -149,6 +158,9 @@ public: LLWebProfile::reportImageUploadStatus(true); } +protected: + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return webProfileResponders_timeout; } + private: LLPointer mImagep; }; @@ -156,11 +168,13 @@ private: /////////////////////////////////////////////////////////////////////////////// // LLWebProfileResponders::PostImageResponder -class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::Responder +class LLWebProfileResponders::PostImageResponder : public LLHTTPClient::ResponderWithCompleted { LOG_CLASS(LLWebProfileResponders::PostImageResponder); public: + /*virtual*/ bool needsHeaders(void) const { return true; } + /*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content) { // Viewer seems to fail to follow a 303 redirect on POST request @@ -168,8 +182,10 @@ public: // Handle it manually. if (status == 303) { - LLSD headers = LLViewerMedia::getHeaders(); - headers["Cookie"] = LLWebProfile::getAuthCookie(); + AIHTTPHeaders headers; + headers.addHeader("Accept", "*/*"); + headers.addHeader("Cookie", LLWebProfile::getAuthCookie()); + headers.addHeader("User-Agent", LLViewerMedia::getCurrentUserAgent()); const std::string& redir_url = content["location"]; LL_DEBUGS("Snapshots") << "Got redirection URL: " << redir_url << llendl; LLHTTPClient::get(redir_url, new LLWebProfileResponders::PostImageRedirectResponder, headers); @@ -185,9 +201,12 @@ public: // Override just to suppress warnings. /*virtual*/ void completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + const buffer_ptr_t& buffer) { } + +protected: + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return webProfileResponders_timeout; } }; /////////////////////////////////////////////////////////////////////////////// @@ -205,8 +224,10 @@ void LLWebProfile::uploadImage(LLPointer image, const std::str config_url += "&add_loc=" + std::string(add_location ? "1" : "0"); LL_DEBUGS("Snapshots") << "Requesting " << config_url << llendl; - LLSD headers = LLViewerMedia::getHeaders(); - headers["Cookie"] = getAuthCookie(); + AIHTTPHeaders headers; + headers.addHeader("Accept", "*/*"); + headers.addHeader("Cookie", LLWebProfile::getAuthCookie()); + headers.addHeader("User-Agent", LLViewerMedia::getCurrentUserAgent()); LLHTTPClient::get(config_url, new LLWebProfileResponders::ConfigResponder(image), headers); } @@ -229,9 +250,11 @@ void LLWebProfile::post(LLPointer image, const LLSD& config, c const std::string boundary = "----------------------------0123abcdefab"; - LLSD headers = LLViewerMedia::getHeaders(); - headers["Cookie"] = getAuthCookie(); - headers["Content-Type"] = "multipart/form-data; boundary=" + boundary; + AIHTTPHeaders headers; + headers.addHeader("Accept", "*/*"); + headers.addHeader("Cookie", LLWebProfile::getAuthCookie()); + headers.addHeader("User-Agent", LLViewerMedia::getCurrentUserAgent()); + headers.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary); std::ostringstream body; @@ -280,7 +303,7 @@ void LLWebProfile::post(LLPointer image, const LLSD& config, c // postRaw() takes ownership of the buffer and releases it later. size_t size = body.str().size(); - U8 *data = new U8[size]; + char* data = new char [size]; memcpy(data, body.str().data(), size); // Send request, successful upload will trigger posting metadata. From 746f419e8022b321583cac579d6c57525e1144b8 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 17 Nov 2012 21:38:55 +0100 Subject: [PATCH 4/9] Fix detection of mIsInProductionGrid and fix getMarketplaceDomain() getMarketplaceDomain() should return aditi.lindenlab.com for aditi, not secondlife.aditi.lindenlab.com. mIsInProductionGrid wasn't set correctly. --- indra/newview/hippogridmanager.cpp | 9 ++++- indra/newview/llmarketplacefunctions.cpp | 42 ++++++++++++++++++++---- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index a626baaf8..4de1c7ad8 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -218,7 +218,6 @@ void HippoGridInfo::setGridName(const std::string& gridName) { setGridNick(gridName); }*/ - mIsInProductionGrid = gridName == "secondlife"; } void HippoGridInfo::setGridNick(std::string gridNick) @@ -228,12 +227,20 @@ void HippoGridInfo::setGridNick(std::string gridNick) { setGridName(gridNick); } + if(gridNick == "secondlife") + { + mIsInProductionGrid = true; + } } void HippoGridInfo::setLoginUri(const std::string& loginUri) { std::string uri = loginUri; mLoginUri = sanitizeUri(uri); + if (utf8str_tolower(LLURI(uri).hostName()) == "login.agni.lindenlab.com") + { + mIsInProductionGrid = true; + } } void HippoGridInfo::setLoginPage(const std::string& loginPage) diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 65cb7f8b8..d4863e96e 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -41,24 +41,54 @@ // Helpers // +static std::string getLoginUriDomain() +{ + LLURI uri(gHippoGridManager->getConnectedGrid()->getLoginUri()); + std::string hostname = uri.hostName(); // Ie, "login..lindenlab.com" + if (hostname.substr(0, 6) == "login.") + { + hostname = hostname.substr(6); // ".lindenlab.com" + } + return hostname; +} + +// Apart from well-known cases, in general this function returns the domain of the loginUri (with the "login." stripped off). +// This should be correct for all SL BETA grids, assuming they have the form of "login..lindenlab.com", in which +// case it returns ".lindenlab.com". +// +// Well-known cases that deviate from this: +// agni --> "secondlife.com" +// damballah --> "secondlife-staging.com" +// static std::string getMarketplaceDomain() { - std::string domain = "secondlife.com"; + std::string domain; if (gHippoGridManager->getCurrentGrid()->isSecondLife()) { - if (!gHippoGridManager->getConnectedGrid()->isInProductionGrid()) + if (gHippoGridManager->getConnectedGrid()->isInProductionGrid()) { - domain = "secondlife.aditi.lindenlab.com"; + domain = "secondlife.com"; // agni + } + else + { + // SecondLife(tm) BETA grid. + // Using the login URI is a bit of a kludge, but it's the best we've got at the moment. + domain = utf8str_tolower(getLoginUriDomain()); // .lindenlab.com; ie, "aditi.lindenlab.com". + std::string::size_type len = domain.length(); + llassert(len > 14 && domain.substr(len - 14) == ".lindenlab.com"); + if (domain == "damballah.lindenlab.com") + { + domain = "secondlife-staging.com"; + } } } else { // TODO: Find out if OpenSim, and Avination adopted any outbox stuffs, if so code HippoGridManager for this // Aurora grid has not. - // For now, reset domain on other grids, so we don't harass LL web services. - domain = ""; //gHippoGridManager->getCurrentGrid()->getMarketPlaceDomain(); + // For now, set domain on other grids to the loginUri domain, so we don't harass LL web services. + domain = getLoginUriDomain(); //gHippoGridManager->getCurrentGrid()->getMarketPlaceDomain(); } - return domain; } From 57b66a9be91a466b42efeeaa68d899ef5cc3b3cc Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 18 Nov 2012 01:28:23 +0100 Subject: [PATCH 5/9] Allow cookies when allowing redirects (next to when sending a Cookie ourselves). --- indra/llmessage/aicurl.cpp | 11 +++++++++++ indra/llmessage/llurlrequest.cpp | 11 ----------- indra/llmessage/llurlrequest.h | 5 ----- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 2353f24bd..e06a01570 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1344,11 +1344,22 @@ void BufferedCurlEasyRequest::prepRequest(AICurlEasyRequest_wat& curl_easy_reque curl_easy_request_w->setReadCallback(&curlReadCallback, lockobj); curl_easy_request_w->setHeaderCallback(&curlHeaderCallback, lockobj); + bool allow_cookies = headers.hasHeader("Cookie"); // Allow up to ten redirects. if (responder->followRedir()) { curl_easy_request_w->setopt(CURLOPT_FOLLOWLOCATION, 1); curl_easy_request_w->setopt(CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); + // This is needed (at least) for authentication after temporary redirection + // to id.secondlife.com for marketplace.secondlife.com. + allow_cookies = true; + } + if (allow_cookies) + { + // Given an empty or non-existing file or by passing the empty string (""), + // this option will enable cookies for this curl handle, making it understand + // and parse received cookies and then use matching cookies in future requests. + curl_easy_request_w->setopt(CURLOPT_COOKIEFILE, ""); } // Keep responder alive. diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 927b64b7a..d2683a88c 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -85,11 +85,6 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string cons void LLURLRequest::initialize_impl(void) { - if (mHeaders.hasHeader("Cookie")) - { - allowCookies(); - } - // If the header is "Pragma" with no value, the caller intends to // force libcurl to drop the Pragma header it so gratuitously inserts. // Before inserting the header, force libcurl to not use the proxy. @@ -199,12 +194,6 @@ void LLURLRequest::useProxy(const std::string &proxy) } #endif -void LLURLRequest::allowCookies() -{ - AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest); - curlEasyRequest_w->setoptString(CURLOPT_COOKIEFILE, ""); -} - bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w) { bool rv = false; diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 8f019310f..fbe97c22f 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -84,11 +84,6 @@ class LLURLRequest : public AICurlEasyRequestStateMachine { /*virtual*/ ~LLURLRequest() { } public: - /** - * @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE. - */ - void allowCookies(void); - /** * @ brief Turn off (or on) the CURLOPT_PROXY header. */ From 549913667748a0e87792d0d777b04efd13ecb28a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 18 Nov 2012 01:29:24 +0100 Subject: [PATCH 6/9] Don't send an Accept header for HEAD requests. --- indra/llmessage/llurlrequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index d2683a88c..479e35d7f 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -100,7 +100,7 @@ void LLURLRequest::initialize_impl(void) // but if they did not specify a Content-Type, then ask the injector. mHeaders.addHeader("Content-Type", mBody->contentType(), AIHTTPHeaders::keep_existing_header); } - else + else if (mAction != HTTP_HEAD) { // Check to see if we have already set Accept or not. If no one // set it, set it to application/llsd+xml since that's what we From f44de434c2e6e4ac6321bd73f9989e6564fb3c34 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 18 Nov 2012 02:07:19 +0100 Subject: [PATCH 7/9] Always use followRedir() to determine if redirections should be followed. Before every HEAD and GET request allowed redirection by default, without setting a limit on the number of redirections. This caused an infinite redirect loop when connecting to marketplace, in combination with the bug that we did not allow cookies. --- indra/llmessage/aicurlthread.cpp | 6 ++++++ indra/llmessage/llhttpclient.h | 3 ++- indra/llmessage/llurlrequest.cpp | 2 -- indra/newview/llmarketplacefunctions.cpp | 3 ++- indra/newview/lltexturefetch.cpp | 2 +- indra/newview/llviewermedia.cpp | 1 + 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 7009d5bf3..3343c0d30 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -2137,6 +2137,12 @@ void BufferedCurlEasyRequest::setStatusAndReason(U32 status, std::string const& mStatus = status; mReason = reason; AICurlInterface::Stats::status_count[AICurlInterface::Stats::status2index(mStatus)]++; + + // Sanity check. If the server replies with a redirect status then we better have that option turned on! + if ((status >= 300 && status < 400) && mResponder && !mResponder->followRedir()) + { + llerrs << "Received " << status << " (" << reason << ") for responder \"" << mTimeoutPolicy->name() << "\" which has no followRedir()!" << llendl; + } } void BufferedCurlEasyRequest::processOutput(void) diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 3e491de82..ec4e88955 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -161,7 +161,7 @@ public: // A derived class should return true if curl should follow redirections. // The default is not to follow redirections. - virtual bool followRedir(void) { return false; } + virtual bool followRedir(void) const { return false; } // Timeout policy to use. virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const = 0; @@ -188,6 +188,7 @@ public: class ResponderHeadersOnly : public ResponderBase { private: /*virtual*/ bool needsHeaders(void) const { return true; } + /*virtual*/ bool followRedir(void) const { return true; } protected: // ResponderBase event diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 479e35d7f..3ffb9e5cc 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -202,13 +202,11 @@ bool LLURLRequest::configure(AICurlEasyRequest_wat const& curlEasyRequest_w) { case HTTP_HEAD: curlEasyRequest_w->setopt(CURLOPT_NOBODY, 1); - curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); rv = true; break; case HTTP_GET: curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); - curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); // Set Accept-Encoding to allow response compression curlEasyRequest_w->setoptString(CURLOPT_ENCODING, mNoCompression ? "identity" : ""); diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index d4863e96e..970db1bd8 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -197,7 +197,8 @@ namespace LLMarketplaceImport public: AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return MPImportGetResponder_timeout; } - bool needsHeaders(void) const { return true; } + /*virtual*/ bool followRedir(void) const { return true; } + /*virtual*/ bool needsHeaders(void) const { return true; } void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 5d4894948..4250ac280 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -408,7 +408,7 @@ public: } } - virtual bool followRedir() + /*virtual*/ bool followRedir() const { return mFollowRedir; } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 94b05c958..cf60073a4 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -168,6 +168,7 @@ public: { } + /* virtual */ bool followRedir(void) const { return true; } /* virtual */ bool needsHeaders(void) const { return true; } /* virtual */ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) From 5947769812369a7988a7f1db30df79e6fc0e4787 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 18 Nov 2012 03:29:01 +0100 Subject: [PATCH 8/9] Fix LLMimeDiscoveryResponder. Extract Content-Type also from a 405 reply. If all else fails, use a default mime-type for url's opened in the browser (start with http: or https:) of "text/html" instead of "none/none". --- indra/newview/llviewermedia.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index cf60073a4..13381f167 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -75,14 +75,15 @@ class LLMimeDiscoveryResponder : public LLHTTPClient::ResponderHeadersOnly { LOG_CLASS(LLMimeDiscoveryResponder); public: - LLMimeDiscoveryResponder( viewer_media_t media_impl) + LLMimeDiscoveryResponder(viewer_media_t media_impl, std::string const& default_mime_type) : mMediaImpl(media_impl), + mDefaultMimeType(default_mime_type), mInitialized(false) {} /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { - if (200 <= status && status < 300) + if (200 <= status && status < 300 || status == 405) // Using HEAD may result in a 405 METHOD NOT ALLOWED, but still have the right Content-TYpe header. { std::string media_type; if (headers.getFirstValue("content-type", media_type)) @@ -92,9 +93,13 @@ public: completeAny(status, mime_type); return; } - llwarns << "LLMimeDiscoveryResponder::completedHeaders: OK HTTP status (" << status << ") but no Content-Type! Received headers: " << headers << llendl; + if (200 <= status && status < 300) + { + llwarns << "LLMimeDiscoveryResponder::completedHeaders: OK HTTP status (" << status << ") but no Content-Type! Received headers: " << headers << llendl; + } } - completeAny(status, "none/none"); + llwarns << "LLMimeDiscoveryResponder::completedHeaders: Got status " << status << ". Using default mime-type: " << mDefaultMimeType << llendl; + completeAny(status, mDefaultMimeType); } void completeAny(U32 status, const std::string& mime_type) @@ -113,6 +118,7 @@ public: public: viewer_media_t mMediaImpl; + std::string mDefaultMimeType; bool mInitialized; }; @@ -1313,7 +1319,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi { if(mime_type.empty()) { - LLHTTPClient::getHeaderOnly( url, new LLMimeDiscoveryResponder(this)); + LLHTTPClient::getHeaderOnly(url, new LLMimeDiscoveryResponder(this, "text/html")); } else if(initializeMedia(mime_type) && (plugin = getMediaPlugin())) { From d6b486524c68d8c8b32643d869c1d7f5a0fc4000 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 7 Dec 2012 00:31:10 +0100 Subject: [PATCH 9/9] Fix include of reader.h --- indra/newview/llwebprofile.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index 141c746e0..31751fddf 100644 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -38,11 +38,12 @@ #include "llpanelprofile.h" // getProfileURL (this is the original location LL put it). #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals -// third-party -#ifdef LL_STANDALONE -#include // JSONCPP +// third-party JSONCPP +#if !defined(LL_STANDALONE) && defined(LINUX64) +// The prebuilt linux64 package is packaged wrongly. +#include // JSONCPP #else -#include "reader.h" // prebuilt jsoncpp is wrongly packaged. +#include // JSONCPP #endif /*