diff options
| author | Aimee Linden <aimee@lindenlab.com> | 2010-06-28 16:30:08 +0100 | 
|---|---|---|
| committer | Aimee Linden <aimee@lindenlab.com> | 2010-06-28 16:30:08 +0100 | 
| commit | 81f5c0241436f3b3fb097dd38c108d110540de7a (patch) | |
| tree | 4e91fcc3b71be8e1b142f17f190909ee7bca6157 | |
| parent | 3240b188e958fa6232ddb03bc29adb968c7521ac (diff) | |
EXT-7498 WIP Snapshot Sharing
Reviewed by Tofu.
| -rw-r--r-- | indra/llmessage/llhttpclient.cpp | 11 | ||||
| -rw-r--r-- | indra/llmessage/llurlrequest.cpp | 5 | ||||
| -rw-r--r-- | indra/llmessage/llurlrequest.h | 5 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 22 | ||||
| -rw-r--r-- | indra/newview/llfloatersnapshot.cpp | 182 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 7 | ||||
| -rw-r--r-- | indra/newview/llviewermedia.cpp | 4 | ||||
| -rw-r--r-- | indra/newview/llwebsharing.cpp | 609 | ||||
| -rw-r--r-- | indra/newview/llwebsharing.h | 230 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_snapshot.xml | 34 | 
11 files changed, 1057 insertions, 54 deletions
| diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index e8dc207114..5d9448b42c 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -240,9 +240,14 @@ static void request(  	lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " "  		<< headers << llendl; -    // Insert custom headers is the caller sent any -    if (headers.isMap()) -    { +	// Insert custom headers if the caller sent any +	if (headers.isMap()) +	{ +		if (headers.has("Cookie")) +		{ +			req->allowCookies(); +		} +          LLSD::map_const_iterator iter = headers.beginMap();          LLSD::map_const_iterator end  = headers.endMap(); diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 1e76d10828..295f69e902 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -251,6 +251,11 @@ void LLURLRequest::useProxy(const std::string &proxy)      mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy);  } +void LLURLRequest::allowCookies() +{ +	mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, ""); +} +  // virtual  LLIOPipe::EStatus LLURLRequest::handleError(  	LLIOPipe::EStatus status, diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 69fd22e592..378cc56374 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -189,6 +189,11 @@ public:       */  	void useProxy(const std::string& proxy); +	/** +	 * @brief Turn on cookie handling for this request with CURLOPT_COOKIEFILE. +	 */ +	void allowCookies(); +  public:  	/**   	 * @brief Give this pipe a chance to handle a generated error diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ce42cb6038..e7475114b2 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -552,6 +552,7 @@ set(viewer_SOURCE_FILES      llwearablelist.cpp      llwearabletype.cpp      llweb.cpp +    llwebsharing.cpp      llwind.cpp      llwlanimator.cpp      llwldaycycle.cpp @@ -1076,6 +1077,7 @@ set(viewer_HEADER_FILES      llwearablelist.h      llwearabletype.h      llweb.h +    llwebsharing.h      llwind.h      llwlanimator.h      llwldaycycle.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 50eee73c4c..dd93f1bfa6 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -9483,6 +9483,28 @@        <key>Value</key>        <integer>75</integer>      </map> +    <key>SnapshotSharingEnabled</key> +    <map> +      <key>Comment</key> +      <string>Enable uploading of snapshots to a web service.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map> +    <key>SnapshotConfigURL</key> +    <map> +      <key>Comment</key> +      <string>URL to fetch Snapshot Sharing configuration data from.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>http://photos.apps.staging.avatarsunited.com/viewer_config</string> +    </map>      <key>SnapshotTextureLastResolution</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index f3baa482a0..43ea6143b1 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -46,6 +46,7 @@  #include "llbutton.h"  #include "llcombobox.h"  #include "lleconomy.h" +#include "lllandmarkactions.h"  #include "llsliderctrl.h"  #include "llspinctrl.h"  #include "llviewercontrol.h" @@ -59,6 +60,7 @@  #include "llradiogroup.h"  #include "lltoolfocus.h"  #include "lltoolmgr.h" +#include "llwebsharing.h"  #include "llworld.h"  #include "llagentui.h" @@ -86,8 +88,8 @@  ///----------------------------------------------------------------------------  /// Local function declarations, constants, enums, and typedefs  ///---------------------------------------------------------------------------- -S32 LLFloaterSnapshot::sUIWinHeightLong = 526 ; -S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 230 ; +S32 LLFloaterSnapshot::sUIWinHeightLong = 530 ; +S32 LLFloaterSnapshot::sUIWinHeightShort = LLFloaterSnapshot::sUIWinHeightLong - 240 ;  S32 LLFloaterSnapshot::sUIWinWidth = 215 ;  LLSnapshotFloaterView* gSnapshotFloaterView = NULL; @@ -115,7 +117,8 @@ public:  	{  		SNAPSHOT_POSTCARD,  		SNAPSHOT_TEXTURE, -		SNAPSHOT_LOCAL +		SNAPSHOT_LOCAL, +		SNAPSHOT_WEB  	}; @@ -161,6 +164,7 @@ public:  	void setSnapshotQuality(S32 quality);  	void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }  	void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f); +	void saveWeb();  	LLFloaterPostcard* savePostcard();  	void saveTexture();  	BOOL saveLocal(); @@ -173,6 +177,9 @@ public:  	// Returns TRUE when snapshot generated, FALSE otherwise.  	static BOOL onIdle( void* snapshot_preview ); +	// callback for region name resolve +	void regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z); +  private:  	LLColor4					mColor;  	LLPointer<LLViewerTexture>	mViewerImage[2]; //used to represent the scene when the frame is frozen. @@ -826,9 +833,19 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )  			// delete any existing image  			previewp->mFormattedImage = NULL;  			// now create the new one of the appropriate format. -			// note: postcards hardcoded to use jpeg always. -			LLFloaterSnapshot::ESnapshotFormat format = previewp->getSnapshotType() == SNAPSHOT_POSTCARD -				? LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG : previewp->getSnapshotFormat(); +			// note: postcards and web hardcoded to use jpeg always. +			LLFloaterSnapshot::ESnapshotFormat format; + +			if (previewp->getSnapshotType() == SNAPSHOT_POSTCARD || +				previewp->getSnapshotType() == SNAPSHOT_WEB) +			{ +				format = LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG; +			} +			else +			{ +				format = previewp->getSnapshotFormat(); +			} +  			switch(format)  			{  			case LLFloaterSnapshot::SNAPSHOT_FORMAT_PNG: @@ -1021,6 +1038,33 @@ BOOL LLSnapshotLivePreview::saveLocal()  	return success;  } +void LLSnapshotLivePreview::saveWeb() +{ +	// *FIX: Will break if the window closes because of CloseSnapshotOnKeep! +	// Needs to pass on ownership of the image. +	LLImageJPEG* jpg = dynamic_cast<LLImageJPEG*>(mFormattedImage.get()); +	if(!jpg) +	{ +		llwarns << "Formatted image not a JPEG" << llendl; +		return; +	} + +	LLSD metadata; +	metadata["description"] = getChild<LLLineEditor>("description")->getText(); + +	LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(), +		boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4)); + +	gViewerWindow->playSnapshotAnimAndSound(); +} + +void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z) +{ +	metadata["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString(); + +	LLWebSharing::instance().shareSnapshot(snapshot, metadata); +} +  ///----------------------------------------------------------------------------  /// Class LLFloaterSnapshot::Impl  ///---------------------------------------------------------------------------- @@ -1071,6 +1115,7 @@ public:  private:  	static LLSnapshotLivePreview::ESnapshotType getTypeIndex(LLFloaterSnapshot* floater); +	static LLSD getTypeName(LLSnapshotLivePreview::ESnapshotType index);  	static ESnapshotFormat getFormatIndex(LLFloaterSnapshot* floater);  	static LLViewerWindow::ESnapshotType getLayerType(LLFloaterSnapshot* floater);  	static void comboSetCustom(LLFloaterSnapshot *floater, const std::string& comboname); @@ -1097,16 +1142,50 @@ LLSnapshotLivePreview::ESnapshotType LLFloaterSnapshot::Impl::getTypeIndex(LLFlo  {  	LLSnapshotLivePreview::ESnapshotType index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;  	LLSD value = floater->childGetValue("snapshot_type_radio"); +  	const std::string id = value.asString();  	if (id == "postcard") +	{  		index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD; +	}  	else if (id == "texture") +	{  		index = LLSnapshotLivePreview::SNAPSHOT_TEXTURE; +	}  	else if (id == "local") +	{  		index = LLSnapshotLivePreview::SNAPSHOT_LOCAL; +	} +	else if (id == "share_to_web") +	{ +		index = LLSnapshotLivePreview::SNAPSHOT_WEB; +	} +  	return index;  } +// static +LLSD LLFloaterSnapshot::Impl::getTypeName(LLSnapshotLivePreview::ESnapshotType index) +{ +	std::string id; +	switch (index) +	{ +		case LLSnapshotLivePreview::SNAPSHOT_WEB: +			id = "share_to_web"; +			break; +		case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: +			id = "postcard"; +			break; +		case LLSnapshotLivePreview::SNAPSHOT_TEXTURE: +			id = "texture"; +			break; +		case LLSnapshotLivePreview::SNAPSHOT_LOCAL: +		default: +			id = "local"; +			break; +	} +	return LLSD(id); +}  // static  LLFloaterSnapshot::ESnapshotFormat LLFloaterSnapshot::Impl::getFormatIndex(LLFloaterSnapshot* floater) @@ -1243,11 +1322,14 @@ void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)  void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)  {  	LLRadioGroup* snapshot_type_radio = floater->getChild<LLRadioGroup>("snapshot_type_radio"); -	snapshot_type_radio->setSelectedIndex(gSavedSettings.getS32("LastSnapshotType")); -	LLSnapshotLivePreview::ESnapshotType shot_type = getTypeIndex(floater); -	ESnapshotFormat shot_format = (ESnapshotFormat)gSavedSettings.getS32("SnapshotFormat"); //getFormatIndex(floater);	LLViewerWindow::ESnapshotType layer_type = getLayerType(floater); +	LLSnapshotLivePreview::ESnapshotType shot_type = (LLSnapshotLivePreview::ESnapshotType)gSavedSettings.getS32("LastSnapshotType"); +	snapshot_type_radio->setSelectedByValue(getTypeName(shot_type), true); + +	ESnapshotFormat shot_format = (ESnapshotFormat)gSavedSettings.getS32("SnapshotFormat");  	LLViewerWindow::ESnapshotType layer_type = getLayerType(floater); +	floater->childSetVisible("share_to_web", gSavedSettings.getBOOL("SnapshotSharingEnabled")); +  	floater->childSetVisible("postcard_size_combo", FALSE);  	floater->childSetVisible("texture_size_combo", FALSE);  	floater->childSetVisible("local_size_combo", FALSE); @@ -1257,17 +1339,19 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)  	floater->getChild<LLComboBox>("local_size_combo")->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution"));  	floater->getChild<LLComboBox>("local_format_combo")->selectNthItem(gSavedSettings.getS32("SnapshotFormat")); +	// *TODO: Separate settings for Web images from postcards +	floater->childSetVisible("send_btn",			shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD || +													shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB);  	floater->childSetVisible("upload_btn",			shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE); -	floater->childSetVisible("send_btn",			shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD);  	floater->childSetVisible("save_btn",			shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL);  	floater->childSetEnabled("keep_aspect_check",	shot_type != LLSnapshotLivePreview::SNAPSHOT_TEXTURE && !floater->impl.mAspectRatioCheckOff);  	floater->childSetEnabled("layer_types",			shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL);  	BOOL is_advance = gSavedSettings.getBOOL("AdvanceSnapshot");  	BOOL is_local = shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL; -	BOOL show_slider =  -		shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD -		|| (is_local && shot_format == LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG); +	BOOL show_slider = (shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD || +						shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB || +					   (is_local && shot_format == LLFloaterSnapshot::SNAPSHOT_FORMAT_JPEG));  	floater->childSetVisible("more_btn", !is_advance); // the only item hidden in advanced mode  	floater->childSetVisible("less_btn",				is_advance); @@ -1290,7 +1374,10 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)  	BOOL got_bytes = previewp && previewp->getDataSize() > 0;  	BOOL got_snap = previewp && previewp->getSnapshotUpToDate(); -	floater->childSetEnabled("send_btn",   shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD && got_snap && previewp->getDataSize() <= MAX_POSTCARD_DATASIZE); +	// *TODO: Separate maximum size for Web images from postcards +	floater->childSetEnabled("send_btn",   (shot_type == LLSnapshotLivePreview::SNAPSHOT_POSTCARD || +											shot_type == LLSnapshotLivePreview::SNAPSHOT_WEB) && +											got_snap && previewp->getDataSize() <= MAX_POSTCARD_DATASIZE);  	floater->childSetEnabled("upload_btn", shot_type == LLSnapshotLivePreview::SNAPSHOT_TEXTURE  && got_snap);  	floater->childSetEnabled("save_btn",   shot_type == LLSnapshotLivePreview::SNAPSHOT_LOCAL    && got_snap); @@ -1311,6 +1398,8 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)  	switch(shot_type)  	{ +	  // *TODO: Separate settings for Web images from postcards +	  case LLSnapshotLivePreview::SNAPSHOT_WEB:  	  case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:  		layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;  		floater->childSetValue("layer_types", "colors"); @@ -1402,28 +1491,39 @@ void LLFloaterSnapshot::Impl::onClickKeep(void* data)  {  	LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;  	LLSnapshotLivePreview* previewp = getPreviewView(view); -	 +  	if (previewp)  	{ -		if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_POSTCARD) +		switch (previewp->getSnapshotType())  		{ -			LLFloaterPostcard* floater = previewp->savePostcard(); -			// if still in snapshot mode, put postcard floater in snapshot floaterview -			// and link it to snapshot floater -			if (floater && !gSavedSettings.getBOOL("CloseSnapshotOnKeep")) +		  case LLSnapshotLivePreview::SNAPSHOT_WEB: +			previewp->saveWeb(); +			break; + +		  case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:  			{ -				gFloaterView->removeChild(floater); -				gSnapshotFloaterView->addChild(floater); -				view->addDependentFloater(floater, FALSE); +				LLFloaterPostcard* floater = previewp->savePostcard(); +				// if still in snapshot mode, put postcard floater in snapshot floaterview +				// and link it to snapshot floater +				if (floater && !gSavedSettings.getBOOL("CloseSnapshotOnKeep")) +				{ +					gFloaterView->removeChild(floater); +					gSnapshotFloaterView->addChild(floater); +					view->addDependentFloater(floater, FALSE); +				}  			} -		} -		else if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE) -		{ +			break; + +		  case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:  			previewp->saveTexture(); -		} -		else -		{ +			break; + +		  case LLSnapshotLivePreview::SNAPSHOT_LOCAL:  			previewp->saveLocal(); +			break; + +		  default: +			break;  		}  		if (gSavedSettings.getBOOL("CloseSnapshotOnKeep")) @@ -1648,18 +1748,22 @@ static std::string lastSnapshotWidthName()  {  	switch(gSavedSettings.getS32("LastSnapshotType"))  	{ -	case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "LastSnapshotToEmailWidth"; -	case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:  return "LastSnapshotToInventoryWidth"; -	default:                                       return "LastSnapshotToDiskWidth"; +	  // *TODO: Separate settings for Web snapshots and postcards +	  case LLSnapshotLivePreview::SNAPSHOT_WEB:		 return "LastSnapshotToEmailWidth"; +	  case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "LastSnapshotToEmailWidth"; +	  case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:  return "LastSnapshotToInventoryWidth"; +	  default:                                       return "LastSnapshotToDiskWidth";  	}  }  static std::string lastSnapshotHeightName()  {  	switch(gSavedSettings.getS32("LastSnapshotType"))  	{ -	case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "LastSnapshotToEmailHeight"; -	case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:  return "LastSnapshotToInventoryHeight"; -	default:                                       return "LastSnapshotToDiskHeight"; +	  // *TODO: Separate settings for Web snapshots and postcards +	  case LLSnapshotLivePreview::SNAPSHOT_WEB:	     return "LastSnapshotToEmailHeight"; +	  case LLSnapshotLivePreview::SNAPSHOT_POSTCARD: return "LastSnapshotToEmailHeight"; +	  case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:  return "LastSnapshotToInventoryHeight"; +	  default:                                       return "LastSnapshotToDiskHeight";  	}  } @@ -1773,7 +1877,7 @@ void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data)  //static   void LLFloaterSnapshot::Impl::onCommitSnapshotFormat(LLUICtrl* ctrl, void* data)  { -	LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;		 +	LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;  	if (view)  	{  		gSavedSettings.setS32("SnapshotFormat", getFormatIndex(view)); @@ -1994,6 +2098,12 @@ LLFloaterSnapshot::~LLFloaterSnapshot()  BOOL LLFloaterSnapshot::postBuild()  { +	// Kick start Web Sharing, to fetch its config data if it needs to. +	if (gSavedSettings.getBOOL("SnapshotSharingEnabled")) +	{ +		LLWebSharing::instance().init(); +	} +  	childSetCommitCallback("snapshot_type_radio", Impl::onCommitSnapshotType, this);  	childSetCommitCallback("local_format_combo", Impl::onCommitSnapshotFormat, this); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 96088fed9c..e9efc49e27 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3148,6 +3148,13 @@ bool process_login_success_response()  		}  	} +	// Set the location of the snapshot sharing config endpoint +	std::string snapshot_config_url = response["snapshot_config_url"]; +	if(!snapshot_config_url.empty()) +	{ +		gSavedSettings.setString("SnapshotConfigURL", snapshot_config_url); +	} +  	// Start the process of fetching the OpenID session cookie for this user login  	std::string openid_url = response["openid_url"];  	if(!openid_url.empty()) diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 34e30b3ccd..178d928f57 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -56,6 +56,7 @@  #include "llvoavatar.h"  #include "llvoavatarself.h"  #include "llviewerregion.h" +#include "llwebsharing.h"	// For LLWebSharing::setOpenIDCookie(), *TODO: find a better way to do this!  #include "llevent.h"		// LLSimpleListener  #include "llnotificationsutil.h" @@ -1318,6 +1319,9 @@ void LLViewerMedia::setOpenIDCookie()  		}  		getCookieStore()->setCookiesFromHost(sOpenIDCookie, authority.substr(host_start, host_end - host_start)); + +		// *HACK: Doing this here is nasty, find a better way. +		LLWebSharing::instance().setOpenIDCookie(sOpenIDCookie);  	}  } diff --git a/indra/newview/llwebsharing.cpp b/indra/newview/llwebsharing.cpp new file mode 100644 index 0000000000..2b9e5cc8cb --- /dev/null +++ b/indra/newview/llwebsharing.cpp @@ -0,0 +1,609 @@ +/**  + * @file llwebsharing.cpp + * @author Aimee + * @brief Web Snapshot Sharing + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + *  + * Copyright (c) 2010, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llwebsharing.h" + +#include "llagentui.h" +#include "llbufferstream.h" +#include "llhttpclient.h" +#include "llhttpstatuscodes.h" +#include "llsdserialize.h" +#include "llsdutil.h" +#include "llurl.h" +#include "llviewercontrol.h" + +#include <boost/regex.hpp> +#include <boost/algorithm/string/replace.hpp> + + + +/////////////////////////////////////////////////////////////////////////////// +// +class LLWebSharingConfigResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLWebSharingConfigResponder); +public: +	/// Overrides the default LLSD parsing behaviour, to allow parsing a JSON response. +	virtual void completedRaw(U32 status, const std::string& reason, +							  const LLChannelDescriptors& channels, +							  const LLIOPipe::buffer_ptr_t& buffer) +	{ +		LLSD content; +		LLBufferStream istr(channels, buffer.get()); +		LLPointer<LLSDParser> parser = new LLSDNotationParser(); + +		if (parser->parse(istr, content, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) +		{ +			LL_WARNS("WebSharing") << "Failed to deserialize LLSD from JSON response. " << " [" << status << "]: " << reason << LL_ENDL; +		} +		else +		{ +			completed(status, reason, content); +		} +	} + +	virtual void error(U32 status, const std::string& reason) +	{ +		LL_WARNS("WebSharing") << "Error [" << status << "]: " << reason << LL_ENDL; +	} + +	virtual void result(const LLSD& content) +	{ +		LLWebSharing::instance().receiveConfig(content); +	} +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// +class LLWebSharingOpenIDAuthResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLWebSharingOpenIDAuthResponder); +public: +	/* virtual */ void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		completed(status, reason, content); +	} + +	/* virtual */ void completedRaw(U32 status, const std::string& reason, +									const LLChannelDescriptors& channels, +									const LLIOPipe::buffer_ptr_t& buffer) +	{ +		/// Left empty to override the default LLSD parsing behaviour. +	} + +	virtual void error(U32 status, const std::string& reason) +	{ +		if (HTTP_UNAUTHORIZED == status) +		{ +			LL_WARNS("WebSharing") << "AU account not authenticated." << LL_ENDL; +			// *TODO: No account found on AU, so start the account creation process here. +		} +		else +		{ +			LL_WARNS("WebSharing") << "Error [" << status << "]: " << reason << LL_ENDL; +			LLWebSharing::instance().retryOpenIDAuth(); +		} + +	} + +	virtual void result(const LLSD& content) +	{ +		if (content.has("set-cookie")) +		{ +			// OpenID request succeeded and returned a session cookie. +			LLWebSharing::instance().receiveSessionCookie(content["set-cookie"].asString()); +		} +	} +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// +class LLWebSharingSecurityTokenResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLWebSharingSecurityTokenResponder); +public: +	/// Overrides the default LLSD parsing behaviour, to allow parsing a JSON response. +	virtual void completedRaw(U32 status, const std::string& reason, +							  const LLChannelDescriptors& channels, +							  const LLIOPipe::buffer_ptr_t& buffer) +	{ +		LLSD content; +		LLBufferStream istr(channels, buffer.get()); +		LLPointer<LLSDParser> parser = new LLSDNotationParser(); + +		if (parser->parse(istr, content, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) +		{ +			LL_WARNS("WebSharing") << "Failed to deserialize LLSD from JSON response. " << " [" << status << "]: " << reason << LL_ENDL; +			LLWebSharing::instance().retryOpenIDAuth(); +		} +		else +		{ +			completed(status, reason, content); +		} +	} + +	virtual void error(U32 status, const std::string& reason) +	{ +		LL_WARNS("WebSharing") << "Error [" << status << "]: " << reason << LL_ENDL; +		LLWebSharing::instance().retryOpenIDAuth(); +	} + +	virtual void result(const LLSD& content) +	{ +		if (content[0].has("st") && content[0].has("expires")) +		{ +			const std::string& token   = content[0]["st"].asString(); +			const std::string& expires = content[0]["expires"].asString(); +			if (LLWebSharing::instance().receiveSecurityToken(token, expires)) +			{ +				// Sucessfully received a valid security token. +				return; +			} +		} +		else +		{ +			LL_WARNS("WebSharing") << "No security token received." << LL_ENDL; +		} + +		LLWebSharing::instance().retryOpenIDAuth(); +	} +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// +class LLWebSharingUploadResponder : public LLHTTPClient::Responder +{ +	LOG_CLASS(LLWebSharingUploadResponder); +public: +	/// Overrides the default LLSD parsing behaviour, to allow parsing a JSON response. +	virtual void completedRaw(U32 status, const std::string& reason, +							  const LLChannelDescriptors& channels, +							  const LLIOPipe::buffer_ptr_t& buffer) +	{ +/* +		 // Dump the body, for debugging. + +		 LLBufferStream istr1(channels, buffer.get()); +		 std::ostringstream ostr; +		 std::string body; + +		 while (istr1.good()) +		 { +			char buf[1024]; +			istr1.read(buf, sizeof(buf)); +			body.append(buf, istr1.gcount()); +		 } +		 LL_DEBUGS("WebSharing") << body << LL_ENDL; +*/ +		LLSD content; +		LLBufferStream istr(channels, buffer.get()); +		LLPointer<LLSDParser> parser = new LLSDNotationParser(); + +		if (parser->parse(istr, content, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) +		{ +			LL_WARNS("WebSharing") << "Failed to deserialize LLSD from JSON response. " << " [" << status << "]: " << reason << LL_ENDL; +		} +		else +		{ +			completed(status, reason, content); +		} +	} + +	virtual void error(U32 status, const std::string& reason) +	{ +		LL_WARNS("WebSharing") << "Error [" << status << "]: " << reason << LL_ENDL; +	} + +	virtual void result(const LLSD& content) +	{ +		if (content[0].has("result") && content[0].has("id") && +			content[0]["id"].asString() == "newMediaItem") +		{ +			// *TODO: Upload successful, continue from here to post metadata and create AU activity. +		} +		else +		{ +			LL_WARNS("WebSharing") << "Error [" << content[0]["code"].asString() +								   << "]: " << content[0]["message"].asString() << LL_ENDL; +		} +	} +}; + + + +/////////////////////////////////////////////////////////////////////////////// +// +LLWebSharing::LLWebSharing() +:	mConfig(), +	mSecurityToken(LLSD::emptyMap()), +	mEnabled(false), +	mRetries(0), +	mImage(NULL), +	mMetadata(LLSD::emptyMap()) +{ +} + +void LLWebSharing::init() +{ +	if (!mEnabled) +	{ +		sendConfigRequest(); +	} +} + +bool LLWebSharing::shareSnapshot(LLImageJPEG* snapshot, LLSD& metadata) +{ +	LL_INFOS("WebSharing") << metadata << LL_ENDL; + +	if (mImage) +	{ +		// *TODO: Handle this possibility properly, queue them up? +		LL_WARNS("WebSharing") << "Snapshot upload already in progress." << LL_ENDL; +		return false; +	} + +	mImage = snapshot; +	mMetadata = metadata; + +	// *TODO: Check whether we have a valid security token already and re-use it. +	sendOpenIDAuthRequest(); +	return true; +} + +bool LLWebSharing::setOpenIDCookie(const std::string& cookie) +{ +	LL_DEBUGS("WebSharing") << "Setting OpenID cookie " << cookie << LL_ENDL; +	mOpenIDCookie = cookie; +	return validateConfig(); +} + +bool LLWebSharing::receiveConfig(const LLSD& config) +{ +	LL_DEBUGS("WebSharing") << "Received config data: " << config << LL_ENDL; +	mConfig = config; +	return validateConfig(); +} + +bool LLWebSharing::receiveSessionCookie(const std::string& cookie) +{ +	LL_DEBUGS("WebSharing") << "Received AU session cookie: " << cookie << LL_ENDL; +	mSessionCookie = cookie; + +	// Fetch a security token using the new session cookie. +	LLWebSharing::instance().sendSecurityTokenRequest(); + +	return (!mSessionCookie.empty()); +} + +bool LLWebSharing::receiveSecurityToken(const std::string& token, const std::string& expires) +{ +	mSecurityToken["st"] = token; +	mSecurityToken["expires"] = LLDate(expires); + +	if (!securityTokenIsValid(mSecurityToken)) +	{ +		LL_WARNS("WebSharing") << "Invalid security token received: \"" << token << "\" Expires: " << expires << LL_ENDL; +		return false; +	} + +	LL_DEBUGS("WebSharing") << "Received security token: \"" << token << "\" Expires: " << expires << LL_ENDL; +	mRetries = 0; + +	// Continue the upload process now that we have a security token. +	sendUploadRequest(); + +	return true; +} + +void LLWebSharing::sendConfigRequest() +{ +	std::string config_url = gSavedSettings.getString("SnapshotConfigURL"); +	LL_DEBUGS("WebSharing") << "Requesting Snapshot Sharing config data from: " << config_url << LL_ENDL; + +	LLSD headers = LLSD::emptyMap(); +	headers["Accept"] = "application/json"; + +	LLHTTPClient::get(config_url, new LLWebSharingConfigResponder(), headers); +} + +void LLWebSharing::sendOpenIDAuthRequest() +{ +	std::string auth_url = mConfig["openIdAuthUrl"]; +	LL_DEBUGS("WebSharing") << "Starting OpenID Auth: " << auth_url << LL_ENDL; + +	LLSD headers = LLSD::emptyMap(); +	headers["Cookie"] = mOpenIDCookie; +	headers["Accept"] = "*/*"; + +	// Send request, successful login will trigger fetching a security token. +	LLHTTPClient::get(auth_url, new LLWebSharingOpenIDAuthResponder(), headers); +} + +bool LLWebSharing::retryOpenIDAuth() +{ +	if (mRetries++ >= MAX_AUTH_RETRIES) +	{ +		LL_WARNS("WebSharing") << "Exceeded maximum number of authorization attempts, aborting." << LL_ENDL; +		mRetries = 0; +		return false; +	} + +	LL_WARNS("WebSharing") << "Authorization failed, retrying (" << mRetries << "/" << MAX_AUTH_RETRIES << ")" << LL_ENDL; +	sendOpenIDAuthRequest(); +	return true; +} + +void LLWebSharing::sendSecurityTokenRequest() +{ +	std::string token_url = mConfig["securityTokenUrl"]; +	LL_DEBUGS("WebSharing") << "Fetching security token from: " << token_url << LL_ENDL; + +	LLSD headers = LLSD::emptyMap(); +	headers["Cookie"] = mSessionCookie; + +	headers["Accept"] = "application/json"; +	headers["Content-Type"] = "application/json"; + +	std::ostringstream body; +	body << "{ \"gadgets\": [{ \"url\":\"" +		 << mConfig["gadgetSpecUrl"].asString() +		 << "\" }] }"; + +	// 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, receiving a valid token will trigger snapshot upload. +	LLHTTPClient::postRaw(token_url, data, size, new LLWebSharingSecurityTokenResponder(), headers); +} + +void LLWebSharing::sendUploadRequest() +{ +	LLUriTemplate upload_template(mConfig["openSocialRpcUrlTemplate"].asString()); +	std::string upload_url(upload_template.buildURI(mSecurityToken)); + +	LL_DEBUGS("WebSharing") << "Posting upload to: " << upload_url << LL_ENDL; + +	static const std::string BOUNDARY("------------abcdef012345xyZ"); + +	LLSD headers = LLSD::emptyMap(); +	headers["Cookie"] = mSessionCookie; + +	headers["Accept"] = "application/json"; +	headers["Content-Type"] = "multipart/form-data; boundary=" + BOUNDARY; + +	std::ostringstream body; +	body << "--" << BOUNDARY << "\r\n" +		 << "Content-Disposition: form-data; name=\"request\"\r\n\r\n" +		 << "[{" +		 <<	  "\"method\":\"mediaItems.create\"," +		 <<	  "\"params\": {" +		 <<	    "\"userId\":[\"@me\"]," +		 <<	    "\"groupId\":\"@self\"," +		 <<	    "\"mediaItem\": {" +		 <<	      "\"mimeType\":\"image/jpeg\"," +		 <<	      "\"type\":\"image\"," +		 <<       "\"url\":\"@field:image1\"" +		 <<	    "}" +		 <<	  "}," +		 <<	  "\"id\":\"newMediaItem\"" +		 <<	"}]" +		 <<	"--" << BOUNDARY << "\r\n" +		 <<	"Content-Disposition: form-data; name=\"image1\"\r\n\r\n"; + +	// Insert the image data. +	// *FIX: Treating this as a string will probably screw it up ... +	U8* image_data = mImage->getData(); +	for (S32 i = 0; i < mImage->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(upload_url, data, size, new LLWebSharingUploadResponder(), headers); +} + +bool LLWebSharing::validateConfig() +{ +	// Check the OpenID Cookie has been set. +	if (mOpenIDCookie.empty()) +	{ +		mEnabled = false; +		return mEnabled; +	} + +	if (!mConfig.isMap()) +	{ +		mEnabled = false; +		return mEnabled; +	} + +	// Template to match the received config against. +	LLSD required(LLSD::emptyMap()); +	required["gadgetSpecUrl"] = ""; +	required["loginTokenUrl"] = ""; +	required["openIdAuthUrl"] = ""; +	required["photoPageUrlTemplate"] = ""; +	required["openSocialRpcUrlTemplate"] = ""; +	required["securityTokenUrl"] = ""; +	required["tokenBasedLoginUrlTemplate"] = ""; +	required["viewerIdUrl"] = ""; + +	std::string mismatch(llsd_matches(required, mConfig)); +	if (!mismatch.empty()) +	{ +		LL_WARNS("WebSharing") << "Malformed config data response: " << mismatch << LL_ENDL; +		mEnabled = false; +		return mEnabled; +	} + +	mEnabled = true; +	return mEnabled; +} + +// static +bool LLWebSharing::securityTokenIsValid(LLSD& token) +{ +	return (token.has("st") && +			token.has("expires") && +			(token["st"].asString() != "") && +			(token["expires"].asDate() > LLDate::now())); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// +LLUriTemplate::LLUriTemplate(const std::string& uri_template) +	: +	mTemplate(uri_template) +{ +} + +std::string LLUriTemplate::buildURI(const LLSD& vars) +{ +	// *TODO: Separate parsing the template from building the URI. +	// Parsing only needs to happen on construction/assignnment. + +	static const std::string VAR_NAME_REGEX("[[:alpha:]][[:alnum:]\\._-]*"); +	// Capture var name with and without surrounding {} +	static const std::string VAR_REGEX("\\{(" + VAR_NAME_REGEX + ")\\}"); +	// Capture delimiter and comma separated list of var names. +	static const std::string JOIN_REGEX("\\{-join\\|(&)\\|(" + VAR_NAME_REGEX + "(?:," + VAR_NAME_REGEX + ")*)\\}"); + +	std::string uri = mTemplate; +	boost::smatch results; + +	// Validate and expand join operators : {-join|&|var1,var2,...} + +	boost::regex join_regex(JOIN_REGEX); + +	while (boost::regex_search(uri, results, join_regex)) +	{ +		// Extract the list of var names from the results. +		std::string delim = results[1].str(); +		std::string var_list = results[2].str(); + +		// Expand the list of vars into a query string with their values +		std::string query = expandJoin(delim, var_list, vars); + +		// Substitute the query string into the template. +		uri = boost::regex_replace(uri, join_regex, query, boost::format_first_only); +	} + +	// Expand vars : {var1} + +	boost::regex var_regex(VAR_REGEX); + +	std::set<std::string> var_names; +	std::string::const_iterator start = uri.begin(); +	std::string::const_iterator end = uri.end(); + +	// Extract the var names used. +	while (boost::regex_search(start, end, results, var_regex)) +	{ +		var_names.insert(results[1].str()); +		start = results[0].second; +	} + +	// Replace each var with its value. +	for (std::set<std::string>::const_iterator it = var_names.begin(); it != var_names.end(); ++it) +	{ +		std::string var = *it; +		if (vars.has(var)) +		{ +			boost::replace_all(uri, "{" + var + "}", vars[var].asString()); +		} +	} + +	return uri; +} + +std::string LLUriTemplate::expandJoin(const std::string& delim, const std::string& var_list, const LLSD& vars) +{ +	std::ostringstream query; + +	typedef boost::tokenizer<boost::char_separator<char> > tokenizer; +	boost::char_separator<char> sep(","); +	tokenizer var_names(var_list, sep); +	tokenizer::const_iterator it = var_names.begin(); + +	// First var does not need a delimiter +	if (it != var_names.end()) +	{ +		const std::string& name = *it; +		if (vars.has(name)) +		{ +			// URL encode the value before appending the name=value pair. +			query << name << "=" << escapeURL(vars[name].asString()); +		} +	} + +	for (++it; it != var_names.end(); ++it) +	{ +		const std::string& name = *it; +		if (vars.has(name)) +		{ +			// URL encode the value before appending the name=value pair. +			query << delim << name << "=" << escapeURL(vars[name].asString()); +		} +	} + +	return query.str(); +} + +// static +std::string LLUriTemplate::escapeURL(const std::string& unescaped) +{ +	char* escaped = curl_escape(unescaped.c_str(), unescaped.size()); +	std::string result = escaped; +	curl_free(escaped); +	return result; +} + diff --git a/indra/newview/llwebsharing.h b/indra/newview/llwebsharing.h new file mode 100644 index 0000000000..70046ff1d8 --- /dev/null +++ b/indra/newview/llwebsharing.h @@ -0,0 +1,230 @@ +/**  + * @file llwebsharing.h + * @author Aimee + * @brief Web Snapshot Sharing + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + *  + * Copyright (c) 2010, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLWEBSHARING_H +#define LL_LLWEBSHARING_H + +#include "llimagejpeg.h" +#include "llsingleton.h" + + + +/** + * @class LLWebSharing + * + * Manages authentication to, and interaction with, a web service allowing the + * upload of snapshot images taken within the viewer, using OpenID and the + * OpenSocial APIs. + * http://www.opensocial.org/Technical-Resources/opensocial-spec-v09/RPC-Protocol.html + */ +class LLWebSharing : public LLSingleton<LLWebSharing> +{ +	LOG_CLASS(LLWebSharing); +public: +	/* +	 * Performs initial setup, by requesting config data from the web service if +	 * it has not already been received. +	 */ +	void init(); + +	/* +	 * @return true if both the OpenID cookie and config data have been received. +	 */ +	bool enabled() const { return mEnabled; }; + +	/* +	 * Sets the OpenID cookie to use for login to the web service. +	 * +	 * @param cookie a string containing the OpenID cookie. +	 * +	 * @return true if both the OpenID cookie and config data have been received. +	 */ +	bool setOpenIDCookie(const std::string& cookie); + +	/* +	 * Receive config data used to connect to the web service. +	 * +	 * @param config an LLSD map of URL templates for the web service end-points. +	 * +	 * @return true if both the OpenID cookie and config data have been received. +	 * +	 * @see sendConfigRequest() +	 */ +	bool receiveConfig(const LLSD& config); + +	/* +	 * Receive the session cookie from the web service, which is the result of +	 * the OpenID login process. +	 * +	 * @see sendOpenIDAuthRequest() +	 */ +	bool receiveSessionCookie(const std::string& cookie); + +	/* +	 * Receive a security token for the upload service. +	 * +	 * @see sendSecurityTokenRequest() +	 */ +	bool receiveSecurityToken(const std::string& token, const std::string& expires); + +	/* +	 * Restarts the authentication process if the maximum number of retries has +	 * not been exceeded. +	 * +	 * @return true if retrying, false if LLWebSharing::MAX_AUTH_RETRIES has been exceeded. +	 */ +	bool retryOpenIDAuth(); + +	/* +	 * Post a snapshot to the upload service. +	 * +	 * @return true if accepted for upload, false if already uploading another image. +	 */ +	bool shareSnapshot(LLImageJPEG* snapshot, LLSD& metadata); + +private: +	static const S32 MAX_AUTH_RETRIES = 4; + +	friend class LLSingleton<LLWebSharing>; + +	LLWebSharing(); +	~LLWebSharing() {}; + +	/* +	 * Request a map of URLs and URL templates to the web service end-points. +	 * +	 * @see receiveConfig() +	 */ +	void sendConfigRequest(); + +	/* +	 * Initiate the OpenID login process. +	 * +	 * @see receiveSessionCookie() +	 */ +	void sendOpenIDAuthRequest(); + +	/* +	 * Request a security token for the upload service. +	 * +	 * @see receiveSecurityToken() +	 */ +	void sendSecurityTokenRequest(); + +	/* +	 * Request a security token for the upload service. +	 * +	 * @see receiveSecurityToken() +	 */ +	void sendUploadRequest(); + +	/* +	 * Checks all necessary config information has been received, and sets mEnabled. +	 * +	 * @return true if both the OpenID cookie and config data have been received. +	 */ +	bool validateConfig(); + +	/* +	 * Checks the security token is present and has not expired. +	 * +	 * @param token an LLSD map containing the token string and the time it expires. +	 * +	 * @return true if the token is not empty and has not expired. +	 */ +	static bool securityTokenIsValid(LLSD& token); + +	std::string mOpenIDCookie; +	std::string mSessionCookie; +	LLSD mSecurityToken; + +	LLSD mConfig; +	bool mEnabled; + +	LLPointer<LLImageJPEG> mImage; +	LLSD mMetadata; + +	S32 mRetries; +}; + +/** + * @class LLUriTemplate + * + * @brief Builds complete URIs, given URI template and a map of keys and values + *        to use for substition. + *        Note: This is only a partial implementation of a draft standard required + *        by the web API used by LLWebSharing. + *        See: http://tools.ietf.org/html/draft-gregorio-uritemplate-03 + * + * @see LLWebSharing + */ +class LLUriTemplate +{ +	LOG_CLASS(LLUriTemplate); +public: +	LLUriTemplate(const std::string& uri_template); +	~LLUriTemplate() {}; + +	/* +	 * Builds a complete URI from the template. +	 * +	 * @param vars an LLSD map of keys and values for substitution. +	 * +	 * @return a string containing the complete URI. +	 */ +	std::string buildURI(const LLSD& vars); + +private: +	/* +	 * Builds a URL query string. +	 * +	 * @param delim    a string containing the separator to use between name=value pairs. +	 * @param var_list a string containing a comma separated list of variable names. +	 * @param vars     an LLSD map of keys and values for substitution. +	 * +	 * @return a URL query string. +	 */ +	std::string expandJoin(const std::string& delim, const std::string& var_list, const LLSD& vars); + +	/* +	 * URL escape the given string. +	 * LLWeb::escapeURL() only does a partial escape, so this uses curl_escape() instead. +	 */ +	static std::string escapeURL(const std::string& unescaped); + +	std::string mTemplate; +}; + + + +#endif // LL_LLWEBSHARING_H diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml index 452b2ac664..857932e51a 100644 --- a/indra/newview/skins/default/xui/en/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml @@ -4,48 +4,52 @@   can_minimize="false"   can_close="true"   follows="left|top" - height="516" + height="520"   layout="topleft"   name="Snapshot"   help_topic="snapshot"   save_rect="true"   save_visibility="true"   title="SNAPSHOT PREVIEW" - width="215">    -    <floater.string -     name="share_to_web_url" translate="false"> -http://pdp36.lindenlab.com:12777/ -    </floater.string> + width="215">      <floater.string       name="unknown">          unknown      </floater.string>      <radio_group -     height="58" +     height="70"       label="Snapshot type"       layout="topleft"       left="10"       name="snapshot_type_radio" -     top="25" +     top="20"       width="205"> +<!--          <radio_item -         bottom="19" +         height="16" +         label="Share to Web" +         layout="topleft" +         name="share_to_web" +         top_pad="0" /> +--> +         <radio_item           height="16"           label="Email"           layout="topleft" -         name="postcard" /> +         name="postcard" +         top_pad="2" />          <radio_item -         bottom="38"           height="16"           label="My inventory (L$[AMOUNT])"           layout="topleft" -         name="texture" /> +         name="texture" +         top_pad="2" />          <radio_item -         bottom="57"           height="16"           label="Save to my computer"           layout="topleft" -         name="local" /> +         name="local" +         top_pad="2" />      </radio_group>    <ui_ctrl       height="90" @@ -67,7 +71,7 @@ http://pdp36.lindenlab.com:12777/       left_delta="0"       halign="right"       name="file_size_label" -     top_pad="10" +     top_pad="8"       width="195">          [SIZE] KB      </text> | 
