From e1ab7d8a30cc40cbd1d471c67def21508c82ff49 Mon Sep 17 00:00:00 2001
From: Kartic Krishnamurthy <drunkensufi@lindenlab.com>
Date: Wed, 18 Jul 2007 01:28:59 +0000
Subject: [PATCH] svn merge -r63705:65463
 svn+ssh://svn/svn/linden/branches/dpo-3-bug-fix NOTE: r63705 is *not* the
 earliest rev# for dpo-3-bug-fix.

---
 etc/message.xml                      | 33 +++++++++-
 indra/llcommon/lluri.cpp             | 35 ++++++----
 indra/llcommon/lluri.h               | 97 ++++++++++++++++------------
 indra/llmessage/llhttpclient.cpp     | 40 +++++++++++-
 indra/llmessage/llhttpclient.h       |  2 +
 indra/llmessage/llmessageconfig.cpp  | 16 +++++
 indra/llmessage/llmessageconfig.h    |  2 +
 indra/llmessage/llservicebuilder.cpp | 19 +++++-
 indra/newview/llpanelavatar.cpp      | 27 ++++++--
 indra/newview/llpanelavatar.h        | 18 ++++--
 indra/test/llservicebuilder_tut.cpp  | 17 +++++
 11 files changed, 238 insertions(+), 68 deletions(-)

diff --git a/etc/message.xml b/etc/message.xml
index ad2364a5c8e..f8e627334f4 100644
--- a/etc/message.xml
+++ b/etc/message.xml
@@ -243,7 +243,6 @@
 					<key>trusted-sender</key>
 					<boolean>false</boolean>
 				</map>
-
 				<!-- Simulator to simulator unreliable messages -->
 				<key>EdgeDataPacket</key>
 				<map>
@@ -324,6 +323,38 @@
 					<key>trusted-sender</key>
 					<boolean>true</boolean>
 				</map>
+				<key>avatarnotesrequest</key>
+				<map>
+                    <key>service_name</key><string>avatar-notes</string>
+					<key>builder</key>
+					<string>template</string>
+					<key>trusted-sender</key>
+					<boolean>false</boolean>
+				</map>
+				<key>avatarclassifiedsrequest</key>
+				<map>
+                    <key>service_name</key><string>avatar-classifieds</string>
+					<key>builder</key>
+					<string>template</string>
+					<key>trusted-sender</key>
+					<boolean>false</boolean>
+				</map>
+				<key>avatarpickrequest</key>
+				<map>
+                    <key>service_name</key><string>avatar-pick</string>
+					<key>builder</key>
+					<string>template</string>
+					<key>trusted-sender</key>
+					<boolean>false</boolean>
+				</map>
+				<key>pickinforequest</key>
+				<map>
+                    <key>service_name</key><string>pick-info</string>
+					<key>builder</key>
+					<string>template</string>
+					<key>trusted-sender</key>
+					<boolean>false</boolean>
+				</map>
 		  </map>
   	  	<key>capBans</key>
     	<map>
diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp
index 5cb6067990f..22526c9a036 100644
--- a/indra/llcommon/lluri.cpp
+++ b/indra/llcommon/lluri.cpp
@@ -275,9 +275,10 @@ LLURI LLURI::buildHTTP(const std::string& prefix,
 					   const LLSD& query)
 {
 	LLURI uri = buildHTTP(prefix, path);
-	uri.mEscapedQuery = mapToQueryString(query);
 	// break out and escape each query component
-	uri.mEscapedOpaque += "?" + uri.mEscapedQuery ;
+	uri.mEscapedQuery = mapToQueryString(query);
+	uri.mEscapedOpaque += uri.mEscapedQuery ;
+	uri.mEscapedQuery.erase(0,1); // trim the leading '?'
 	return uri;
 }
 
@@ -581,20 +582,30 @@ LLSD LLURI::queryMap(std::string escaped_query_string)
 std::string LLURI::mapToQueryString(const LLSD& queryMap)
 {
 	std::string query_string;
-
 	if (queryMap.isMap())
 	{
-		for (LLSD::map_const_iterator iter = queryMap.beginMap();
-			 iter != queryMap.endMap();
-			 iter++)
+		bool first_element = true;
+		LLSD::map_const_iterator iter = queryMap.beginMap();
+		LLSD::map_const_iterator end = queryMap.endMap();
+		std::ostringstream ostr;
+		for (; iter != end; ++iter)
 		{
-			query_string += escapeQueryVariable(iter->first) +
-				(iter->second.isUndefined() ? "" : "=" + escapeQueryValue(iter->second.asString())) + "&" ;
+			if(first_element)
+			{
+				ostr << "?";
+				first_element = false;
+			}
+			else
+			{
+				ostr << "&";
+			}
+			ostr << escapeQueryVariable(iter->first);
+			if(iter->second.isDefined())
+			{
+				ostr << "=" <<  escapeQueryValue(iter->second.asString());
+			}
 		}
-		//if (queryMap.size() > 0)
-		//{
-		//	query_string += "?" + query_string ;
-		//}
+		query_string = ostr.str();
 	}
 	return query_string;
 }
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h
index 9ec51b4b1ab..00eacf6536d 100644
--- a/indra/llcommon/lluri.h
+++ b/indra/llcommon/lluri.h
@@ -38,34 +38,42 @@ class LLURI
 	  
   // construct from escaped string, as would be transmitted on the net
 
-  ~LLURI();
+	~LLURI();
 
-  static LLURI buildHTTP(const std::string& prefix,
-			 const LLSD& path);
-  static LLURI buildHTTP(const std::string& prefix,
-			 const LLSD& path,
-			 const LLSD& query);
-	// prefix is either a full URL prefix of the form "http://example.com:8080",
-	// or it can be simply a host and optional port like "example.com" or 
-	// "example.com:8080", in these cases, the "http://" will be added
+	static LLURI buildHTTP(
+		const std::string& prefix,
+		const LLSD& path);
 
-  static LLURI buildHTTP(const std::string& host,
-			 const U32& port,
-			 const LLSD& path);
-  static LLURI buildHTTP(const std::string& host,
-			 const U32& port,
-			 const LLSD& path,
-			 const LLSD& query);
-  std::string asString() const;
-  // the whole URI, escaped as needed
+	static LLURI buildHTTP(
+		const std::string& prefix,
+		const LLSD& path,
+		const LLSD& query);
+	///< prefix is either a full URL prefix of the form
+	/// "http://example.com:8080", or it can be simply a host and
+	/// optional port like "example.com" or "example.com:8080", in
+	/// these cases, the "http://" will be added
+
+	static LLURI buildHTTP(
+		const std::string& host,
+		const U32& port,
+		const LLSD& path);
+	static LLURI buildHTTP(
+		const std::string& host,
+		const U32& port,
+		const LLSD& path,
+		const LLSD& query);
+
+	std::string asString() const;
+	///< the whole URI, escaped as needed
   
-  // Parts of a URI
-  // These functions return parts of the decoded URI.  The returned
-  // strings are un-escaped as needed
+	/** @name Parts of a URI */
+	//@{
+	// These functions return parts of the decoded URI.  The returned
+	// strings are un-escaped as needed
   
-  // for all schemes
-  std::string scheme() const;		// ex.: "http", note lack of colon
-  std::string opaque() const;		// everything after the colon
+	// for all schemes
+	std::string scheme() const;		///< ex.: "http", note lack of colon
+	std::string opaque() const;		///< everything after the colon
   
   // for schemes that follow path like syntax (http, https, ftp)
   std::string authority() const;	// ex.: "host.com:80"
@@ -81,27 +89,34 @@ class LLURI
   const std::string& escapedQuery() const { return mEscapedQuery; }
   LLSD queryMap() const;			// above decoded into a map
   static LLSD queryMap(std::string escaped_query_string);
-  static std::string mapToQueryString(const LLSD& queryMap);
 
-  // Escaping Utilities
-  // Escape a string by urlencoding all the characters that aren't in the allowed string.
-  static std::string escape(const std::string& str);
-  static std::string escape(const std::string& str, const std::string & allowed); 
-  static std::string unescape(const std::string& str);
+	/**
+	 * @brief given a name value map, return a serialized query string.
+	 *
+
+	 * @param query_map a map of name value. every value must be
+	 * representable as a string.
+	 * @return Returns an url query string of '?n1=v1&n2=v2&...'
+	 */
+	static std::string mapToQueryString(const LLSD& query_map);
 
-	// Functions for building specific URIs for web services
-	// *NOTE: DEPRECATED. use the service builder instead.
-	//static LLURI buildBulkAgentNamesURI(LLApp* app);
-	//static LLURI buildAgentSessionURI(const LLUUID& agent_id, LLApp* app);
-	//static LLURI buildAgentLoginInfoURI(const LLUUID& agent_id, const std::string& dataserver);
-	//static LLURI buildAgentNameURI(const LLUUID& agent_id, LLApp* app);
+	/** @name Escaping Utilities */
+	//@{
+	// Escape a string by urlencoding all the characters that aren't
+	// in the allowed string.
+	static std::string escape(const std::string& str);
+	static std::string escape(
+		const std::string& str,
+		const std::string & allowed); 
+	static std::string unescape(const std::string& str);
+	//@}
 
 private:
-  std::string mScheme;
-  std::string mEscapedOpaque;
-  std::string mEscapedAuthority;
-  std::string mEscapedPath;
-  std::string mEscapedQuery;
+	std::string mScheme;
+	std::string mEscapedOpaque;
+	std::string mEscapedAuthority;
+	std::string mEscapedPath;
+	std::string mEscapedQuery;
 };
 
 #endif // LL_LLURI_H
diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp
index 1bc6d742f1a..c798e6473c3 100644
--- a/indra/llmessage/llhttpclient.cpp
+++ b/indra/llmessage/llhttpclient.cpp
@@ -232,6 +232,7 @@ static void request(
 	LLURLRequest::ERequestAction method,
 	Injector* body_injector,
 	LLHTTPClient::ResponderPtr responder,
+    const LLSD& headers,
 	const F32 timeout=HTTP_REQUEST_EXPIRY_SECS)
 {
 	if (!LLHTTPClient::hasPump())
@@ -243,6 +244,19 @@ static void request(
 
 	LLURLRequest *req = new LLURLRequest(method, url);
 	req->requestEncoding("");
+
+    if (headers.isMap())
+    {
+        LLSD::map_const_iterator iter = headers.beginMap();
+        LLSD::map_const_iterator end  = headers.endMap();
+
+        for (; iter != end; ++iter)
+        {
+            std::ostringstream header;
+            header << iter->first << ": " << iter->second.asString() ;
+            req->addHeader(header.str().c_str());
+        }
+    }
 	if (!gCABundle.empty())
 	{
 		req->checkRootCertificate(true, gCABundle.c_str());
@@ -267,17 +281,37 @@ static void request(
 	theClientPump->addChain(chain, timeout);
 }
 
+static void request(
+	const std::string& url,
+	LLURLRequest::ERequestAction method,
+	Injector* body_injector,
+	LLHTTPClient::ResponderPtr responder,
+	const F32 timeout=HTTP_REQUEST_EXPIRY_SECS)
+{
+    request(url, method, body_injector, responder, LLSD(), timeout);
+}
+
+void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const LLSD& headers, const F32 timeout)
+{
+	request(url, LLURLRequest::HTTP_GET, NULL, responder, headers, timeout);
+}
+
 void LLHTTPClient::get(const std::string& url, ResponderPtr responder, const F32 timeout)
 {
-	request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout);
+	get(url, responder, LLSD(), timeout);
 }
 
-void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const F32 timeout)
+void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const LLSD& headers, const F32 timeout)
 {
 	LLURI uri;
 	
 	uri = LLURI::buildHTTP(url, LLSD::emptyArray(), query);
-	get(uri.asString(), responder, timeout);
+	get(uri.asString(), responder, headers, timeout);
+}
+
+void LLHTTPClient::get(const std::string& url, const LLSD& query, ResponderPtr responder, const F32 timeout)
+{
+	get(url, query, responder, LLSD(), timeout);
 }
 
 // A simple class for managing data returned from a curl http request.
diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h
index 447bd691ba2..e3074ee7071 100644
--- a/indra/llmessage/llhttpclient.h
+++ b/indra/llmessage/llhttpclient.h
@@ -59,7 +59,9 @@ class LLHTTPClient
 	typedef boost::intrusive_ptr<Responder>	ResponderPtr;
 	
 	static void get(const std::string& url, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
+	static void get(const std::string& url, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
 	static void get(const std::string& url, const LLSD& query, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
+	static void get(const std::string& url, const LLSD& query, ResponderPtr, const LLSD& headers, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
 	static void put(const std::string& url, const LLSD& body, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
 		///< non-blocking
 	static void post(const std::string& url, const LLSD& body, ResponderPtr, const F32 timeout=HTTP_REQUEST_EXPIRY_SECS);
diff --git a/indra/llmessage/llmessageconfig.cpp b/indra/llmessage/llmessageconfig.cpp
index dd2d725d324..25bdc9fb168 100644
--- a/indra/llmessage/llmessageconfig.cpp
+++ b/indra/llmessage/llmessageconfig.cpp
@@ -232,3 +232,19 @@ bool LLMessageConfig::isCapBanned(const std::string& cap_name)
 {
 	return LLMessageConfigFile::instance().isCapBanned(cap_name);
 }
+
+// return the web-service path to use for a given
+// message. This entry *should* match the entry
+// in simulator.xml!
+LLSD LLMessageConfig::getConfigForMessage(const std::string& msg_name)
+{
+	if (sServerName.empty())
+	{
+		llerrs << "LLMessageConfig::isMessageTrusted(name) before"
+				<< " LLMessageConfig::initClass()" << llendl;
+	}
+	LLMessageConfigFile& file = LLMessageConfigFile::instance();
+	// LLSD for the CamelCase message name
+	LLSD config = file.mMessages[msg_name];
+	return config;
+}
diff --git a/indra/llmessage/llmessageconfig.h b/indra/llmessage/llmessageconfig.h
index a99cdc46fa7..9cc20fe98b5 100644
--- a/indra/llmessage/llmessageconfig.h
+++ b/indra/llmessage/llmessageconfig.h
@@ -10,6 +10,7 @@
 #define LL_MESSAGECONFIG_H
 
 #include <string>
+#include "llsd.h"
 
 class LLSD;
 
@@ -30,5 +31,6 @@ class LLMessageConfig
 	static SenderTrust getSenderTrustedness(const std::string& msg_name);
 	static bool isValidMessage(const std::string& msg_name);
 	static bool isCapBanned(const std::string& cap_name);
+	static LLSD getConfigForMessage(const std::string& msg_name);
 };
 #endif // LL_MESSAGECONFIG_H
diff --git a/indra/llmessage/llservicebuilder.cpp b/indra/llmessage/llservicebuilder.cpp
index 8c34a506ded..806a888b2b3 100644
--- a/indra/llmessage/llservicebuilder.cpp
+++ b/indra/llmessage/llservicebuilder.cpp
@@ -89,7 +89,7 @@ std::string LLServiceBuilder::buildServiceURI(
 	const LLSD& option_map)
 {
 	std::string service_url = buildServiceURI(service_name);
-
+    
 	// Find the Service Name
 	if(!service_url.empty() && option_map.isMap())
 	{
@@ -108,6 +108,23 @@ std::string LLServiceBuilder::buildServiceURI(
 					find_pos,
 					variable_name.length(),
 					(*option_itr).second.asString());
+				continue;
+			}
+			variable_name.assign("{%");
+			variable_name.append((*option_itr).first);
+			variable_name.append("}");
+			find_pos = service_url.find(variable_name);
+			if(find_pos != std::string::npos)
+			{
+				std::string query_str = LLURI::mapToQueryString(
+					(*option_itr).second);
+				if(!query_str.empty())
+				{
+					service_url.replace(
+						find_pos,
+						variable_name.length(),
+						query_str);
+				}
 			}
 		}
 	}
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index daea0847593..2567684fa9f 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -71,6 +71,7 @@ BOOL LLPanelAvatar::sAllowFirstLife = FALSE;
 // RN: move these to lldbstrings.h
 static const S32 DB_USER_FAVORITES_STR_LEN = 254;
 
+const char LOADING_MSG[] = "Loading...";
 static const char IM_DISABLED_TOOLTIP[] = "Instant Message (IM).\nDisabled because you do not have their card.";
 static const char IM_ENABLED_TOOLTIP[] = "Instant Message (IM)";
 static const S32 LEFT = HPAD;
@@ -810,7 +811,7 @@ void LLPanelAvatarNotes::refresh()
 
 void LLPanelAvatarNotes::clearControls()
 {
-	childSetText("notes edit", "Loading...");
+	childSetText("notes edit", LOADING_MSG);
 	childSetEnabled("notes edit", false);
 }
 
@@ -1251,6 +1252,8 @@ LLPanelAvatar::LLPanelAvatar(
 	mAvatarID( LLUUID::null ),	// mAvatarID is set with 'setAvatar' or 'setAvatarID'
 	mHaveProperties(FALSE),
 	mHaveStatistics(FALSE),
+	mHaveNotes(false),
+	mLastNotes(),
 	mAllowEdit(allow_edit)
 {
 
@@ -1440,6 +1443,8 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
 
 		mPanelNotes->clearControls();
 		mPanelNotes->setDataRequested(false);
+		mHaveNotes = false;
+		mLastNotes.clear();
 
 		// Request just the first two pages of data.  The picks,
 		// classifieds, and notes will be requested when that panel
@@ -1451,8 +1456,8 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
 			if (mAllowEdit)
 			{
 				// OK button disabled until properties data arrives
-				childSetVisible("OK",TRUE);
-				childSetEnabled("OK",TRUE);
+				childSetVisible("OK", true);
+				childSetEnabled("OK", false);
 				childSetVisible("Cancel",TRUE);
 				childSetEnabled("Cancel",TRUE);
 			}
@@ -1747,7 +1752,19 @@ void LLPanelAvatar::sendAvatarPropertiesRequest()
 void LLPanelAvatar::sendAvatarNotesUpdate()
 {
 	std::string notes = mPanelNotes->childGetValue("notes edit").asString();
-	
+
+	if (!mHaveNotes
+		&& (notes.empty() || notes == LOADING_MSG))
+	{
+		// no notes from server and no user updates
+		return;
+	}
+	if (notes == mLastNotes)
+	{
+		// Avatar notes unchanged
+		return;
+	}
+
 	LLMessageSystem *msg = gMessageSystem;
 
 	msg->newMessage("AvatarNotesUpdate");
@@ -2155,6 +2172,8 @@ void LLPanelAvatar::processAvatarNotesReply(LLMessageSystem *msg, void**)
 		msg->getString("Data", "Notes", DB_USER_NOTE_SIZE, text);
 		self->childSetValue("notes edit", text);
 		self->childSetEnabled("notes edit", true);
+		self->mHaveNotes = true;
+		self->mLastNotes = text;
 	}
 }
 
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
index cbc4c55b523..1469be2e9d9 100644
--- a/indra/newview/llpanelavatar.h
+++ b/indra/newview/llpanelavatar.h
@@ -303,16 +303,15 @@ class LLPanelAvatar : public LLPanel
 	static void onClickCSR(		void *userdata);
 	static void onClickMute(	void *userdata);
 
+private:
+	void enableOKIfReady();
+
 	static void finishKick(S32 option, const LLString& text, void* userdata);
 	static void finishFreeze(S32 option, const LLString& text, void* userdata);
 	static void finishUnfreeze(S32 option, const LLString& text, void* userdata);
 
 	static void showProfileCallback(S32 option, void *userdata);
 
-	// Teen users are not allowed to see or enter data into the first life page,
-	// or their own about/interests text entry fields.
-	static BOOL sAllowFirstLife;
-
 	static	void*	createPanelAvatar(void*	data);
 	static	void*	createFloaterAvatarInfo(void*	data);
 	static	void*	createPanelAvatarSecondLife(void*	data);
@@ -333,13 +332,20 @@ class LLPanelAvatar : public LLPanel
 	LLPanelAvatarWeb*			mPanelWeb;
 
 	LLDropTarget* 				mDropTarget;
+
+	// Teen users are not allowed to see or enter data into the first life page,
+	// or their own about/interests text entry fields.
+	static BOOL sAllowFirstLife;
 	
-protected:
-	void						enableOKIfReady();
+private:
 	LLUUID						mAvatarID;			// for which avatar is this window?
 	BOOL						mIsFriend;			// Are we friends?
 	BOOL						mHaveProperties;
 	BOOL						mHaveStatistics;
+	// only update note if data received from database and
+	// note is changed from database version
+	bool						mHaveNotes;
+	std::string					mLastNotes;
 	LLTabContainerCommon*		mTab;
 	BOOL						mAllowEdit;
 
diff --git a/indra/test/llservicebuilder_tut.cpp b/indra/test/llservicebuilder_tut.cpp
index b4a814ebb99..e782cfbb6f1 100644
--- a/indra/test/llservicebuilder_tut.cpp
+++ b/indra/test/llservicebuilder_tut.cpp
@@ -72,5 +72,22 @@ namespace tut
 		std::string test_url = mServiceBuilder.buildServiceURI("ServiceBuilderTest", data_map);
 		ensure_equals("Replacement URL Creation for Non-existant Service", test_url , "/agent/{$agent-id}/name");
 	}
+
+	template<> template<>
+	void ServiceBuilderTestObject::test<5>()
+	{
+		LLSD test_block;
+		test_block["service-builder"] = "/proc/{$proc}{%params}";
+		mServiceBuilder.createServiceDefinition("ServiceBuilderTest", test_block["service-builder"]);	
+		LLSD data_map;
+		data_map["proc"] = "do/something/useful";
+		data_map["params"]["estate_id"] = 1;
+		data_map["params"]["query"] = "public";
+		std::string test_url = mServiceBuilder.buildServiceURI("ServiceBuilderTest", data_map);
+		ensure_equals(
+			"two part URL Creation",
+			test_url ,
+			"/proc/do/something/useful?estate_id=1&query=public");
+	}
 }
 
-- 
GitLab