From aa2b978bf59dd74083bec86c61a29fefb4ebb14c Mon Sep 17 00:00:00 2001
From: Kelly Washington <kelly@lindenlab.com>
Date: Tue, 24 Feb 2009 19:29:36 +0000
Subject: [PATCH] merge -r 112783:112799
 linden/branches/kelly/lsl-http-in-merge to linden/trunk

---
 indra/llcommon/lllslconstants.h               |  14 ++
 indra/llinventory/llparcel.h                  |   2 +-
 indra/llmessage/llhttpnode.cpp                |  10 +-
 indra/llmessage/llhttpnode.h                  |  15 +-
 indra/llmessage/lliohttpserver.cpp            |  71 ++++--
 indra/llmessage/lliohttpserver.h              |  12 ++
 indra/llmessage/llservicebuilder.cpp          |  22 +-
 indra/lscript/CMakeLists.txt                  |   3 +
 indra/lscript/llscriptresource.h              |  64 ++++++
 indra/lscript/llscriptresourceconsumer.h      |  56 +++++
 indra/lscript/llscriptresourcepool.h          |  46 ++++
 indra/lscript/lscript_byteformat.h            |   9 +-
 indra/lscript/lscript_compile/indra.l         |  24 ++-
 indra/lscript/lscript_compile/indra.y         |  21 ++
 .../lscript/lscript_compile/lscript_tree.cpp  | 109 ++++++++++
 indra/lscript/lscript_compile/lscript_tree.h  |  30 ++-
 indra/lscript/lscript_execute/CMakeLists.txt  |   6 +
 .../lscript_execute/llscriptresource.cpp      |  86 ++++++++
 .../llscriptresourceconsumer.cpp              | 101 +++++++++
 .../lscript_execute/llscriptresourcepool.cpp  |  39 ++++
 .../lscript_execute/lscript_execute.cpp       |   6 +
 .../lscript_execute/lscript_readlso.cpp       |  10 +
 .../lscript_library/lscript_library.cpp       |   6 +
 indra/newview/app_settings/keywords.ini       |   5 +
 indra/newview/llfloatertopobjects.cpp         |   6 +
 indra/test/CMakeLists.txt                     |   6 +
 indra/test/llhttpnode_tut.cpp                 |   3 +-
 indra/test/llscriptresource_tut.cpp           | 203 ++++++++++++++++++
 indra/test/lltranscode_tut.cpp                |  94 ++++++++
 indra/test/message_tut.cpp                    |   1 +
 30 files changed, 1048 insertions(+), 32 deletions(-)
 create mode 100644 indra/lscript/llscriptresource.h
 create mode 100644 indra/lscript/llscriptresourceconsumer.h
 create mode 100644 indra/lscript/llscriptresourcepool.h
 create mode 100644 indra/lscript/lscript_execute/llscriptresource.cpp
 create mode 100644 indra/lscript/lscript_execute/llscriptresourceconsumer.cpp
 create mode 100644 indra/lscript/lscript_execute/llscriptresourcepool.cpp
 create mode 100644 indra/test/llscriptresource_tut.cpp
 create mode 100644 indra/test/lltranscode_tut.cpp

diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h
index 289f9995e57..7cd854febd0 100644
--- a/indra/llcommon/lllslconstants.h
+++ b/indra/llcommon/lllslconstants.h
@@ -188,4 +188,18 @@ const S32 OBJECT_CREATOR = 8;
 // llTextBox() magic token string - yes this is a hack.  sue me.
 const std::string TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!";
 
+// changed() event flags
+const U32	CHANGED_NONE = 0x0;
+const U32	CHANGED_INVENTORY = 0x1;
+const U32	CHANGED_COLOR = 0x2;
+const U32	CHANGED_SHAPE = 0x4;
+const U32	CHANGED_SCALE = 0x8;
+const U32	CHANGED_TEXTURE = 0x10;
+const U32	CHANGED_LINK = 0x20;
+const U32	CHANGED_ALLOWED_DROP = 0x40;
+const U32	CHANGED_OWNER = 0x80;
+const U32	CHANGED_REGION = 0x100;
+const U32	CHANGED_TELEPORT = 0x200;
+const U32	CHANGED_REGION_START = 0x400;
+
 #endif
diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h
index 6e3024908ed..12d0fdf1e90 100644
--- a/indra/llinventory/llparcel.h
+++ b/indra/llinventory/llparcel.h
@@ -243,7 +243,7 @@ class LLParcel
 	void	setAllParcelFlags(U32 flags);
 	void	setParcelFlag(U32 flag, BOOL b);
 
-	void	setArea(S32 area, S32 sim_object_limit);
+	virtual void setArea(S32 area, S32 sim_object_limit);
 	void	setDiscountRate(F32 rate);
 
 	void	setAllowModify(BOOL b)	{ setParcelFlag(PF_CREATE_OBJECTS, b); }
diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp
index 0114ad07136..2ba900a533d 100644
--- a/indra/llmessage/llhttpnode.cpp
+++ b/indra/llmessage/llhttpnode.cpp
@@ -36,8 +36,8 @@
 #include <boost/tokenizer.hpp>
 
 #include "llstl.h"
+#include "lliohttpserver.h" // for string constants
 
-static const std::string CONTEXT_REQUEST("request");
 static const std::string CONTEXT_WILDCARD("wildcard");
 
 /**
@@ -181,7 +181,8 @@ void  LLHTTPNode::options(ResponsePtr response, const LLSD& context) const
 	//llinfos << "options context: " << context << llendl;
 
 	// default implementation constructs an url to the documentation.
-	std::string host = context[CONTEXT_REQUEST]["headers"]["host"].asString();
+	std::string host(
+		context[CONTEXT_REQUEST][CONTEXT_HEADERS]["host"].asString());
 	if(host.empty())
 	{
 		response->status(400, "Bad Request -- need Host header");
@@ -475,6 +476,11 @@ void LLSimpleResponse::result(const LLSD& result)
 	status(200, "OK");
 }
 
+void LLSimpleResponse::extendedResult(S32 code, const std::string& body, const LLSD& headers)
+{
+	status(code,body);
+}
+
 void LLSimpleResponse::status(S32 code, const std::string& message)
 {
 	mCode = code;
diff --git a/indra/llmessage/llhttpnode.h b/indra/llmessage/llhttpnode.h
index a6f14f65457..4f80db47f17 100644
--- a/indra/llmessage/llhttpnode.h
+++ b/indra/llmessage/llhttpnode.h
@@ -38,7 +38,6 @@
 
 class LLChainIOFactory;
 
-
 /**
  * These classes represent the HTTP framework: The URL tree, and the LLSD
  * REST interface that such nodes implement.
@@ -100,6 +99,11 @@ class LLHTTPNode
 		 */
 		virtual void result(const LLSD&) = 0;
 
+		/**
+		 * @brief return status code and message with headers.
+		 */
+		virtual void extendedResult(S32 code, const std::string& message, const LLSD& headers) = 0;
+
 		/**
 		 * @brief return status code and reason string on http header,
 		 * but do not return a payload.
@@ -218,6 +222,14 @@ class LLHTTPNode
 	const LLHTTPNode* rootNode() const;
 	const LLHTTPNode* findNode(const std::string& name) const;
 
+
+	enum EHTTPNodeContentType
+	{
+		CONTENT_TYPE_LLSD,
+		CONTENT_TYPE_TEXT
+	};
+
+	virtual EHTTPNodeContentType getContentType() const { return CONTENT_TYPE_LLSD; }
 	//@}
 
 	/* @name Description system
@@ -277,6 +289,7 @@ class LLSimpleResponse : public LLHTTPNode::Response
 	static LLPointer<LLSimpleResponse> create();
 	
 	void result(const LLSD& result);
+	void extendedResult(S32 code, const std::string& body, const LLSD& headers);
 	void status(S32 code, const std::string& message);
 
 	void print(std::ostream& out) const;
diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp
index 6486e5e3e0c..0895af091b8 100644
--- a/indra/llmessage/lliohttpserver.cpp
+++ b/indra/llmessage/lliohttpserver.cpp
@@ -57,15 +57,15 @@
 #include <boost/tokenizer.hpp>
 
 static const char HTTP_VERSION_STR[] = "HTTP/1.0";
-static const std::string CONTEXT_REQUEST("request");
-static const std::string CONTEXT_RESPONSE("response");
-static const std::string CONTEXT_VERB("verb");
-static const std::string CONTEXT_HEADERS("headers");
-static const std::string HTTP_VERB_GET("GET");
-static const std::string HTTP_VERB_PUT("PUT");
-static const std::string HTTP_VERB_POST("POST");
-static const std::string HTTP_VERB_DELETE("DELETE");
-static const std::string HTTP_VERB_OPTIONS("OPTIONS");
+const std::string CONTEXT_REQUEST("request");
+const std::string CONTEXT_RESPONSE("response");
+const std::string CONTEXT_VERB("verb");
+const std::string CONTEXT_HEADERS("headers");
+const std::string HTTP_VERB_GET("GET");
+const std::string HTTP_VERB_PUT("PUT");
+const std::string HTTP_VERB_POST("POST");
+const std::string HTTP_VERB_DELETE("DELETE");
+const std::string HTTP_VERB_OPTIONS("OPTIONS");
 
 static LLIOHTTPServer::timing_callback_t sTimingCallback = NULL;
 static void* sTimingCallbackData = NULL;
@@ -104,6 +104,7 @@ class LLHTTPPipe : public LLIOPipe
 
 		// from LLHTTPNode::Response
 		virtual void result(const LLSD&);
+		virtual void extendedResult(S32 code, const std::string& body, const LLSD& headers);
 		virtual void status(S32 code, const std::string& message);
 
 		void nullPipe();
@@ -122,7 +123,8 @@ class LLHTTPPipe : public LLIOPipe
 		STATE_DELAYED,
 		STATE_LOCKED,
 		STATE_GOOD_RESULT,
-		STATE_STATUS_RESULT
+		STATE_STATUS_RESULT,
+		STATE_EXTENDED_RESULT
 	};
 	State mState;
 
@@ -180,14 +182,32 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl(
 		{
             LLPerfBlock putblock("http_put");
 			LLSD input;
-			LLSDSerialize::fromXML(input, istr);
+			if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_LLSD)
+			{
+				LLSDSerialize::fromXML(input, istr);
+			}
+			else if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_TEXT)
+			{
+				std::stringstream strstrm;
+				strstrm << istr.rdbuf();
+				input = strstrm.str();
+			}
 			mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input);
 		}
 		else if(verb == HTTP_VERB_POST)
 		{
             LLPerfBlock postblock("http_post");
 			LLSD input;
-			LLSDSerialize::fromXML(input, istr);
+			if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_LLSD)
+			{
+				LLSDSerialize::fromXML(input, istr);
+			}
+			else if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_TEXT)
+			{
+				std::stringstream strstrm;
+				strstrm << istr.rdbuf();
+				input = strstrm.str();
+			}
 			mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input);
 		}
 		else if(verb == HTTP_VERB_DELETE)
@@ -262,7 +282,16 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl(
 			context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
 			context[CONTEXT_RESPONSE]["statusMessage"] = mStatusMessage;
 			LLBufferStream ostr(channels, buffer.get());
-			ostr << mStatusMessage << std::ends;
+			ostr << mStatusMessage;
+
+			return STATUS_DONE;
+		}
+		case STATE_EXTENDED_RESULT:
+		{
+			context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = mHeaders;
+			context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
+			LLBufferStream ostr(channels, buffer.get());
+			ostr << mStatusMessage;
 
 			return STATUS_DONE;
 		}
@@ -309,6 +338,21 @@ void LLHTTPPipe::Response::result(const LLSD& r)
 	mPipe->unlockChain();	
 }
 
+void LLHTTPPipe::Response::extendedResult(S32 code, const std::string& body, const LLSD& headers)
+{
+	if(! mPipe)
+	{
+		llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl;
+		return;
+	}
+
+	mPipe->mStatusCode = code;
+	mPipe->mStatusMessage = body;
+	mPipe->mHeaders = headers;
+	mPipe->mState = STATE_EXTENDED_RESULT;
+	mPipe->unlockChain();
+}
+
 // virtual
 void LLHTTPPipe::Response::status(S32 code, const std::string& message)
 {
@@ -409,6 +453,7 @@ LLIOPipe::EStatus LLHTTPResponseHeader::process_impl(
 		}
 		
 		ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n";
+
 		S32 content_length = buffer->countAfter(channels.in(), NULL);
 		if(0 < content_length)
 		{
diff --git a/indra/llmessage/lliohttpserver.h b/indra/llmessage/lliohttpserver.h
index fef3f9fc773..d1c9bdde856 100644
--- a/indra/llmessage/lliohttpserver.h
+++ b/indra/llmessage/lliohttpserver.h
@@ -40,6 +40,18 @@
 
 class LLPumpIO;
 
+// common strings use for populating the context. bascally 'request',
+// 'wildcard', and 'headers'.
+extern const std::string CONTEXT_REQUEST;
+extern const std::string CONTEXT_RESPONSE;
+extern const std::string CONTEXT_VERB;
+extern const std::string CONTEXT_HEADERS;
+extern const std::string HTTP_VERB_GET;
+extern const std::string HTTP_VERB_PUT;
+extern const std::string HTTP_VERB_POST;
+extern const std::string HTTP_VERB_DELETE;
+extern const std::string HTTP_VERB_OPTIONS;
+
 class LLIOHTTPServer
 {
 public:
diff --git a/indra/llmessage/llservicebuilder.cpp b/indra/llmessage/llservicebuilder.cpp
index dcce00c1384..3f07147ebca 100644
--- a/indra/llmessage/llservicebuilder.cpp
+++ b/indra/llmessage/llservicebuilder.cpp
@@ -86,6 +86,14 @@ void LLServiceBuilder::createServiceDefinition(
 	}
 }
 
+static
+bool starts_with(const std::string& text, const char* prefix)
+{
+	return text.substr(0, strlen(prefix)) == prefix;
+}
+
+// TODO: Build a real services.xml for windows development.
+//       and remove the base_url logic below.
 std::string LLServiceBuilder::buildServiceURI(const std::string& service_name)
 {
 	std::ostringstream service_url;
@@ -96,7 +104,19 @@ std::string LLServiceBuilder::buildServiceURI(const std::string& service_name)
 		LLApp* app = LLApp::instance();
 		if(app)
 		{
-			LLSD base_url = app->getOption("services-base-url");
+			// We define a base-url for some development configurations
+			// In production neither of these are defined and all services have full urls
+			LLSD base_url;
+
+			if (starts_with(service_name,"cap"))
+			{
+				base_url = app->getOption("cap-base-url");
+			}
+
+			if (base_url.asString().empty())
+			{
+				base_url = app->getOption("services-base-url");
+			}
 			service_url << base_url.asString();
 		}
 		service_url << mServiceMap[service_name];
diff --git a/indra/lscript/CMakeLists.txt b/indra/lscript/CMakeLists.txt
index c655aef4d9c..937e2ec0dc2 100644
--- a/indra/lscript/CMakeLists.txt
+++ b/indra/lscript/CMakeLists.txt
@@ -1,6 +1,9 @@
 # -*- cmake -*-
 
 set(lscript_HEADER_FILES
+    llscriptresource.h
+    llscriptresourceconsumer.h
+    llscriptresourcepool.h
     lscript_alloc.h
     lscript_byteconvert.h
     lscript_byteformat.h
diff --git a/indra/lscript/llscriptresource.h b/indra/lscript/llscriptresource.h
new file mode 100644
index 00000000000..2aa0af882d6
--- /dev/null
+++ b/indra/lscript/llscriptresource.h
@@ -0,0 +1,64 @@
+/** 
+ * @file llscriptresource.h
+ * @brief LLScriptResource class definition
+ *
+ * $LicenseInfo:firstyear=2008&license=internal$
+ * 
+ * Copyright (c) 2008, Linden Research, Inc.
+ * 
+ * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+ * this source code is governed by the Linden Lab Source Code Disclosure
+ * Agreement ("Agreement") previously entered between you and Linden
+ * Lab. By accessing, using, copying, modifying or distributing this
+ * software, you acknowledge that you have been informed of your
+ * obligations under the Agreement 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_LLSCRIPTRESOURCE_H
+#define LL_LLSCRIPTRESOURCE_H
+
+#include "stdtypes.h"
+
+// An LLScriptResource is a limited resource per ID.
+class LLScriptResource
+{
+public:
+	LLScriptResource();
+
+	// If amount resources are available will mark amount resouces 
+	// used and returns true
+	// Otherwise returns false and doesn't mark any resources used.
+	bool request(S32 amount = 1);
+
+	// Release amount resources from use if at least amount resources are used and return true
+	// If amount is more than currently used no resources are released and return false
+	bool release(S32 amount = 1);
+
+	// Returns how many resources are available
+	S32 getAvailable() const;
+
+	// Sets the total amount of available resources
+	// It is possible to set the amount to less than currently used
+	// Most likely to happen on parcel ownership change
+	void setTotal(S32 amount);
+
+	// Get the total amount of available resources
+	S32 getTotal() const;
+
+	// Get the number of resources used
+	S32 getUsed() const;
+
+	// true if more resources used than total available
+	bool isOverLimit() const;
+
+private:
+	S32 mTotal; // How many resources have been set aside
+	S32 mUsed; // How many resources are currently in use
+};
+
+#endif // LL_LLSCRIPTRESOURCE_H
diff --git a/indra/lscript/llscriptresourceconsumer.h b/indra/lscript/llscriptresourceconsumer.h
new file mode 100644
index 00000000000..1ec0026996b
--- /dev/null
+++ b/indra/lscript/llscriptresourceconsumer.h
@@ -0,0 +1,56 @@
+/** 
+ * @file llscriptresourceconsumer.h
+ * @brief An interface for a script resource consumer.
+ *
+ * $LicenseInfo:firstyear=2008&license=internal$
+ * 
+ * Copyright (c) 2008, Linden Research, Inc.
+ * 
+ * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+ * this source code is governed by the Linden Lab Source Code Disclosure
+ * Agreement ("Agreement") previously entered between you and Linden
+ * Lab. By accessing, using, copying, modifying or distributing this
+ * software, you acknowledge that you have been informed of your
+ * obligations under the Agreement 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_LLSCRIPTRESOURCECONSUMER_H
+#define LL_LLSCRIPTRESOURCECONSUMER_H
+
+#include "linden_common.h"
+
+class LLScriptResourcePool;
+
+// Entities that use limited script resources 
+// should implement this interface
+
+class LLScriptResourceConsumer
+{
+public:	
+	LLScriptResourceConsumer();
+
+	virtual ~LLScriptResourceConsumer() { }
+
+	// Get the number of public urls used by this consumer.
+	virtual S32 getUsedPublicURLs() const = 0;
+
+	// Get the resource pool this consumer is currently using.
+	LLScriptResourcePool& getScriptResourcePool();
+	const LLScriptResourcePool& getScriptResourcePool() const;
+
+	bool switchScriptResourcePools(LLScriptResourcePool& new_pool);
+	bool canUseScriptResourcePool(const LLScriptResourcePool& resource_pool);
+	bool isInPool(const LLScriptResourcePool& resource_pool);
+
+protected:
+	virtual void setScriptResourcePool(LLScriptResourcePool& pool);
+
+	LLScriptResourcePool* mScriptResourcePool;
+};
+
+#endif // LL_LLSCRIPTRESOURCECONSUMER_H
diff --git a/indra/lscript/llscriptresourcepool.h b/indra/lscript/llscriptresourcepool.h
new file mode 100644
index 00000000000..ca4afeb3c65
--- /dev/null
+++ b/indra/lscript/llscriptresourcepool.h
@@ -0,0 +1,46 @@
+/** 
+ * @file llscriptresourcepool.h
+ * @brief A collection of LLScriptResources
+ *
+ * $LicenseInfo:firstyear=2008&license=internal$
+ * 
+ * Copyright (c) 2008, Linden Research, Inc.
+ * 
+ * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+ * this source code is governed by the Linden Lab Source Code Disclosure
+ * Agreement ("Agreement") previously entered between you and Linden
+ * Lab. By accessing, using, copying, modifying or distributing this
+ * software, you acknowledge that you have been informed of your
+ * obligations under the Agreement 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_LLSCRIPTRESOURCEPOOL_H
+#define LL_LLSCRIPTRESOURCEPOOL_H
+
+#include "llscriptresource.h"
+
+// This is just a holder for LLSimResources
+class LLScriptResourcePool
+{
+public:	
+	LLScriptResourcePool();
+	// ~LLSimResourceMgr();
+
+	LLScriptResource& getPublicURLResource();
+	const LLScriptResource& getPublicURLResource() const;
+
+	// An empty resource pool.
+	static LLScriptResourcePool null;
+
+private:
+	LLScriptResource mLSLPublicURLs;
+};
+
+
+
+#endif
diff --git a/indra/lscript/lscript_byteformat.h b/indra/lscript/lscript_byteformat.h
index 7333d47f150..ba2c46bef25 100644
--- a/indra/lscript/lscript_byteformat.h
+++ b/indra/lscript/lscript_byteformat.h
@@ -360,6 +360,7 @@ typedef enum e_lscript_state_event_type
 	LSTT_OBJECT_REZ,
 	LSTT_REMOTE_DATA,
 	LSTT_HTTP_RESPONSE,
+	LSTT_HTTP_REQUEST,
 	LSTT_EOF,
 	
 	LSTT_STATE_BEGIN = LSTT_STATE_ENTRY,
@@ -401,7 +402,8 @@ const U64 LSCRIPTStateBitField[LSTT_EOF] =
 	0x0000000020000000,		//  LSTT_MOVING_END
 	0x0000000040000000,		//  LSTT_OBJECT_REZ
 	0x0000000080000000,		//  LSTT_REMOTE_DATA
-	0x0000000100000000LL     // LSTT_HTTP_RESPOSE
+	0x0000000100000000LL,	// LSTT_HTTP_RESPOSE
+	0x0000000200000000LL 	// LSTT_HTTP_REQUEST
 };
 
 inline S32 get_event_handler_jump_position(U64 bit_field, LSCRIPTStateEventType type)
@@ -551,5 +553,10 @@ const U32 LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_EOF] =
 	(0x1 << 11),//	SCRIPT_PERMISSION_CONTROL_CAMERA
 };
 
+// http_request string constants
+extern const char* URL_REQUEST_GRANTED;
+extern const char* URL_REQUEST_DENIED;
+extern const U64 LSL_HTTP_REQUEST_TIMEOUT;
+
 #endif
 
diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l
index 2616b47c736..ac524326fcd 100644
--- a/indra/lscript/lscript_compile/indra.l
+++ b/indra/lscript/lscript_compile/indra.l
@@ -118,6 +118,7 @@ extern "C" { int yyerror(const char *fmt, ...); }
 "object_rez"			{ count(); return(OBJECT_REZ); }
 "remote_data"			{ count(); return(REMOTE_DATA); }
 "http_response"			{ count(); return(HTTP_RESPONSE); }
+"http_request"			{ count(); return(HTTP_REQUEST); }
 "."						{ count(); return(PERIOD); }
 
 
@@ -221,16 +222,17 @@ extern "C" { int yyerror(const char *fmt, ...); }
 "INVENTORY_ALL"						{ count(); yylval.ival = LLAssetType::AT_NONE; return(INTEGER_CONSTANT); }
 "INVENTORY_NONE"					{ count(); yylval.ival = LLAssetType::AT_NONE; return(INTEGER_CONSTANT); }
 
-"CHANGED_INVENTORY"		{ count(); yylval.ival = 0x1; return(INTEGER_CONSTANT); }	
-"CHANGED_COLOR"			{ count(); yylval.ival = 0x2; return(INTEGER_CONSTANT); }	
-"CHANGED_SHAPE"			{ count(); yylval.ival = 0x4; return(INTEGER_CONSTANT); }	
-"CHANGED_SCALE"			{ count(); yylval.ival = 0x8; return(INTEGER_CONSTANT); }	
-"CHANGED_TEXTURE"		{ count(); yylval.ival = 0x10; return(INTEGER_CONSTANT); }	
-"CHANGED_LINK"			{ count(); yylval.ival = 0x20; return(INTEGER_CONSTANT); }	
-"CHANGED_ALLOWED_DROP"	{ count(); yylval.ival = 0x40; return(INTEGER_CONSTANT); }	
-"CHANGED_OWNER"			{ count(); yylval.ival = 0x80; return(INTEGER_CONSTANT); }	
-"CHANGED_REGION"		{ count(); yylval.ival = 0x100; return(INTEGER_CONSTANT); }	
-"CHANGED_TELEPORT"		{ count(); yylval.ival = 0x200; return(INTEGER_CONSTANT); }	
+"CHANGED_INVENTORY"		{ count(); yylval.ival = CHANGED_INVENTORY; return(INTEGER_CONSTANT); }	
+"CHANGED_COLOR"			{ count(); yylval.ival = CHANGED_COLOR; return(INTEGER_CONSTANT); }	
+"CHANGED_SHAPE"			{ count(); yylval.ival = CHANGED_SHAPE; return(INTEGER_CONSTANT); }	
+"CHANGED_SCALE"			{ count(); yylval.ival = CHANGED_SCALE; return(INTEGER_CONSTANT); }	
+"CHANGED_TEXTURE"		{ count(); yylval.ival = CHANGED_TEXTURE; return(INTEGER_CONSTANT); }	
+"CHANGED_LINK"			{ count(); yylval.ival = CHANGED_LINK; return(INTEGER_CONSTANT); }	
+"CHANGED_ALLOWED_DROP"	{ count(); yylval.ival = CHANGED_ALLOWED_DROP; return(INTEGER_CONSTANT); }	
+"CHANGED_OWNER"			{ count(); yylval.ival = CHANGED_OWNER; return(INTEGER_CONSTANT); }	
+"CHANGED_REGION"		{ count(); yylval.ival = CHANGED_REGION; return(INTEGER_CONSTANT); }	
+"CHANGED_TELEPORT"		{ count(); yylval.ival = CHANGED_TELEPORT; return(INTEGER_CONSTANT); }	
+"CHANGED_REGION_START"		{ count(); yylval.ival = CHANGED_REGION_START; return(INTEGER_CONSTANT); }	
 
 "OBJECT_UNKNOWN_DETAIL"	{ count(); yylval.ival = OBJECT_UNKNOWN_DETAIL; return(INTEGER_CONSTANT); }
 "OBJECT_NAME"			{ count(); yylval.ival = OBJECT_NAME; return(INTEGER_CONSTANT); }
@@ -252,6 +254,8 @@ extern "C" { int yyerror(const char *fmt, ...); }
 
 "NULL_KEY"				{ yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, "00000000-0000-0000-0000-000000000000"); return(STRING_CONSTANT); }
 "EOF"					{ yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, "\n\n\n"); return(STRING_CONSTANT); }
+"URL_REQUEST_GRANTED"	{ yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, URL_REQUEST_GRANTED); return(STRING_CONSTANT); }
+"URL_REQUEST_DENIED"	{ yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, URL_REQUEST_DENIED); return(STRING_CONSTANT); }
 
 "PI"					{ count(); yylval.fval = F_PI; return(FP_CONSTANT); }
 "TWO_PI"				{ count(); yylval.fval = F_TWO_PI; return(FP_CONSTANT); }
diff --git a/indra/lscript/lscript_compile/indra.y b/indra/lscript/lscript_compile/indra.y
index fdc240c7720..e4b10ffdd9d 100644
--- a/indra/lscript/lscript_compile/indra.y
+++ b/indra/lscript/lscript_compile/indra.y
@@ -92,6 +92,7 @@
 %token					LINK_MESSAGE
 %token					REMOTE_DATA
 %token					HTTP_RESPONSE
+%token					HTTP_REQUEST
 
 %token <sval>			IDENTIFIER
 %token <sval>			STATE_DEFAULT
@@ -195,6 +196,7 @@
 %type <event>			object_rez
 %type <event>			remote_data
 %type <event>			http_response
+%type <event>			http_request
 %type <event>			link_message
 %type <event>			timer
 %type <event>			chat
@@ -848,6 +850,11 @@ event
 		$$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
 		gAllocationManager->addAllocation($$);
 	}
+	| http_request compound_statement														
+	{  
+		$$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+		gAllocationManager->addAllocation($$);
+	}
 	;
 	
 state_entry
@@ -1216,6 +1223,20 @@ http_response
 	}
 	;
 
+http_request
+	: HTTP_REQUEST '(' LLKEY IDENTIFIER ','  STRING IDENTIFIER ',' STRING IDENTIFIER ')'															
+	{  
+		LLScriptIdentifier	*id1 = new LLScriptIdentifier(gLine, gColumn, $4);	
+		gAllocationManager->addAllocation(id1);
+		LLScriptIdentifier	*id2 = new LLScriptIdentifier(gLine, gColumn, $7);	
+		gAllocationManager->addAllocation(id2);
+		LLScriptIdentifier	*id3 = new LLScriptIdentifier(gLine, gColumn, $10);	
+		gAllocationManager->addAllocation(id3);
+		$$ = new LLScriptHTTPRequestEvent(gLine, gColumn, id1, id2, id3);
+		gAllocationManager->addAllocation($$);
+	}
+	;
+	
 compound_statement
 	: '{' '}'																		
 	{  
diff --git a/indra/lscript/lscript_compile/lscript_tree.cpp b/indra/lscript/lscript_compile/lscript_tree.cpp
index 51983bb41b3..e291d4c6f86 100644
--- a/indra/lscript/lscript_compile/lscript_tree.cpp
+++ b/indra/lscript/lscript_compile/lscript_tree.cpp
@@ -3291,6 +3291,110 @@ S32 LLScriptHTTPResponseEvent::getSize()
 	return 16;
 }
 
+void LLScriptHTTPRequestEvent::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+	if (gErrorToText.getErrors())
+	{
+		return;
+	}
+	switch(pass)
+	{
+	case LSCP_PRETTY_PRINT:
+	case LSCP_EMIT_ASSEMBLY:
+		fdotabs(fp, tabs, tabsize);
+		fprintf(fp, "http_request( key ");
+		mRequestId->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		fprintf(fp, ", string ");
+		mMethod->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		fprintf(fp, ", string ");
+		mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		fprintf(fp, " )\n");
+		break;
+		
+	case LSCP_SCOPE_PASS1:
+		checkForDuplicateHandler(fp, this, scope, "http_request");
+		if (scope->checkEntry(mRequestId->mName))
+		{
+			gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+		}
+		else
+		{
+			mRequestId->mScopeEntry = scope->addEntry(mRequestId->mName, LIT_VARIABLE, LST_KEY);
+		}
+
+		if (scope->checkEntry(mMethod->mName))
+		{
+			gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+		}
+		else
+		{
+			mMethod->mScopeEntry = scope->addEntry(mMethod->mName, LIT_VARIABLE, LST_STRING);
+		}
+		
+		if (scope->checkEntry(mBody->mName))
+		{
+			gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+		}
+		else
+		{
+			mBody->mScopeEntry = scope->addEntry(mBody->mName, LIT_VARIABLE, LST_STRING);
+		}
+		break;
+		
+	case LSCP_RESOURCE:
+		{
+			// we're just tryng to determine how much space the variable needs
+			if (mRequestId->mScopeEntry)
+			{
+				mRequestId->mScopeEntry->mOffset = (S32)count;
+				mRequestId->mScopeEntry->mSize = 4;
+				count += mRequestId->mScopeEntry->mSize;
+
+				mMethod->mScopeEntry->mOffset = (S32)count;
+				mMethod->mScopeEntry->mSize = 4;
+				count += mMethod->mScopeEntry->mSize;
+
+				mBody->mScopeEntry->mOffset = (S32)count;
+				mBody->mScopeEntry->mSize = 4;
+				count += mBody->mScopeEntry->mSize;
+			}
+		}
+		break;
+		
+	case LSCP_EMIT_BYTE_CODE:
+		{
+#ifdef LSL_INCLUDE_DEBUG_INFO
+			char name[] = "http_request";
+			chunk->addBytes(name, strlen(name) + 1); 		/*Flawfinder: ignore*/
+			chunk->addBytes(mRequestId->mName, strlen(mRequestId->mName) + 1); 		/*Flawfinder: ignore*/
+			chunk->addBytes(mMethod->mName, strlen(mMethod->mName) + 1); 		/*Flawfinder: ignore*/
+			chunk->addBytes(mBody->mName, strlen(mBody->mName) + 1); 		/*Flawfinder: ignore*/
+#endif
+		}
+		break;
+	case LSCP_EMIT_CIL_ASSEMBLY:
+	        fdotabs(fp, tabs, tabsize);
+   	        fprintf(fp, "http_request( valuetype [ScriptTypes]LindenLab.SecondLife.Key ");
+		mRequestId->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		fprintf(fp, ", string ");
+		mMethod->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		fprintf(fp, ", string ");
+		mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		fprintf(fp, " )\n");
+		break;
+	default:
+		mRequestId->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		mMethod->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+		break;
+	}
+}
+
+S32 LLScriptHTTPRequestEvent::getSize()
+{
+	// key + string + string = 12
+	return 12;
+}
 
 void LLScriptMoneyEvent::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
 {
@@ -9658,6 +9762,11 @@ void LLScriptEventHandler::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCom
 			mScopeEntry->mFunctionArgs.addType(LST_LIST);
 			mScopeEntry->mFunctionArgs.addType(LST_STRING);
 			break;
+		case LSTT_HTTP_REQUEST:
+			mScopeEntry->mFunctionArgs.addType(LST_KEY);
+			mScopeEntry->mFunctionArgs.addType(LST_STRING);
+			mScopeEntry->mFunctionArgs.addType(LST_STRING);
+			break;
 
 		default:
 			break;
diff --git a/indra/lscript/lscript_compile/lscript_tree.h b/indra/lscript/lscript_compile/lscript_tree.h
index 369d775e5d5..12c16908af2 100644
--- a/indra/lscript/lscript_compile/lscript_tree.h
+++ b/indra/lscript/lscript_compile/lscript_tree.h
@@ -759,12 +759,12 @@ class LLScriptHTTPResponseEvent : public LLScriptEvent
 {
 public:
 	LLScriptHTTPResponseEvent(S32 line, S32 col,
-		LLScriptIdentifier *reqeust_id,
+		LLScriptIdentifier *request_id,
 		LLScriptIdentifier *status,
 		LLScriptIdentifier *metadata,
 		LLScriptIdentifier *body)
 		: LLScriptEvent(line, col, LSTT_HTTP_RESPONSE),
-		 mRequestId(reqeust_id), mStatus(status), mMetadata(metadata), mBody(body)
+		 mRequestId(request_id), mStatus(status), mMetadata(metadata), mBody(body)
 	{
 	}
 
@@ -783,6 +783,32 @@ class LLScriptHTTPResponseEvent : public LLScriptEvent
 	LLScriptIdentifier	*mBody;
 };
 
+class LLScriptHTTPRequestEvent : public LLScriptEvent
+{
+public:
+	LLScriptHTTPRequestEvent(S32 line, S32 col,
+		LLScriptIdentifier *request_id,
+		LLScriptIdentifier *method,
+		LLScriptIdentifier *body)
+		: LLScriptEvent(line, col, LSTT_HTTP_REQUEST),
+		 mRequestId(request_id), mMethod(method), mBody(body)
+	{
+	}
+
+	void recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass,
+		LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope,
+		LSCRIPTType &type, LSCRIPTType basetype, U64 &count,
+		LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap,
+		S32 stacksize, LLScriptScopeEntry *entry,
+		S32 entrycount, LLScriptLibData **ldata);
+		
+	S32 getSize();
+
+	LLScriptIdentifier	*mRequestId;
+	LLScriptIdentifier	*mMethod;
+	LLScriptIdentifier	*mBody;
+};
+
 class LLScriptRezEvent : public LLScriptEvent
 {
 public:
diff --git a/indra/lscript/lscript_execute/CMakeLists.txt b/indra/lscript/lscript_execute/CMakeLists.txt
index f30915bab00..3a16ffdc010 100644
--- a/indra/lscript/lscript_execute/CMakeLists.txt
+++ b/indra/lscript/lscript_execute/CMakeLists.txt
@@ -12,6 +12,9 @@ include_directories(
     )
 
 set(lscript_execute_SOURCE_FILES
+    llscriptresource.cpp
+    llscriptresourceconsumer.cpp
+    llscriptresourcepool.cpp
     lscript_execute.cpp
     lscript_heapruntime.cpp
     lscript_readlso.cpp
@@ -20,6 +23,9 @@ set(lscript_execute_SOURCE_FILES
 set(lscript_execute_HEADER_FILES
     CMakeLists.txt
 
+    ../llscriptresource.h
+    ../llscriptresourceconsumer.h
+    ../llscriptresourcepool.h
     ../lscript_execute.h
     ../lscript_rt_interface.h
     lscript_heapruntime.h
diff --git a/indra/lscript/lscript_execute/llscriptresource.cpp b/indra/lscript/lscript_execute/llscriptresource.cpp
new file mode 100644
index 00000000000..cc802987b9a
--- /dev/null
+++ b/indra/lscript/lscript_execute/llscriptresource.cpp
@@ -0,0 +1,86 @@
+/** 
+ * @file llscriptresource.cpp
+ * @brief LLScriptResource class implementation for managing limited resources
+ *
+ * $LicenseInfo:firstyear=2008&license=internal$
+ * 
+ * Copyright (c) 2008, Linden Research, Inc.
+ * 
+ * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+ * this source code is governed by the Linden Lab Source Code Disclosure
+ * Agreement ("Agreement") previously entered between you and Linden
+ * Lab. By accessing, using, copying, modifying or distributing this
+ * software, you acknowledge that you have been informed of your
+ * obligations under the Agreement 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 "llscriptresource.h"
+#include "llerror.h"
+
+LLScriptResource::LLScriptResource()
+: mTotal(0),
+  mUsed(0)
+{
+}
+
+bool LLScriptResource::request(S32 amount /* = 1 */)
+{
+	if (mUsed + amount <= mTotal)
+	{
+		mUsed += amount;
+		return true;
+	}
+
+	return false;
+}
+
+bool LLScriptResource::release(S32 amount /* = 1 */)
+{
+	if (mUsed >= amount)
+	{
+		mUsed -= amount;
+		return true;
+	}
+
+	return false;
+}
+
+S32 LLScriptResource::getAvailable() const
+{
+	if (mUsed > mTotal)
+	{
+		// It is possible after a parcel ownership change for more than total to be used
+		// In this case the user of this class just wants to know 
+		// whether or not they can use a resource
+		return 0;
+	}
+	return (mTotal - mUsed);
+}
+
+void LLScriptResource::setTotal(S32 amount)
+{
+	// This may cause this resource to be over spent
+	// such that more are in use than total allowed
+	// Until those resources are released getAvailable will return 0.
+	mTotal = amount;
+}
+
+S32 LLScriptResource::getTotal() const
+{
+	return mTotal;
+}
+
+S32 LLScriptResource::getUsed() const
+{
+	return mUsed;
+}
+
+bool LLScriptResource::isOverLimit() const
+{
+	return (mUsed > mTotal);
+}
diff --git a/indra/lscript/lscript_execute/llscriptresourceconsumer.cpp b/indra/lscript/lscript_execute/llscriptresourceconsumer.cpp
new file mode 100644
index 00000000000..6a5b28e2577
--- /dev/null
+++ b/indra/lscript/lscript_execute/llscriptresourceconsumer.cpp
@@ -0,0 +1,101 @@
+/** 
+ * @file llscriptresourceconsumer.cpp
+ * @brief An interface for a script resource consumer.
+ *
+ * $LicenseInfo:firstyear=2008&license=internal$
+ * 
+ * Copyright (c) 2008, Linden Research, Inc.
+ * 
+ * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+ * this source code is governed by the Linden Lab Source Code Disclosure
+ * Agreement ("Agreement") previously entered between you and Linden
+ * Lab. By accessing, using, copying, modifying or distributing this
+ * software, you acknowledge that you have been informed of your
+ * obligations under the Agreement 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 "llscriptresourceconsumer.h"
+
+#include "llscriptresourcepool.h"
+
+LLScriptResourceConsumer::LLScriptResourceConsumer()
+	: mScriptResourcePool(&LLScriptResourcePool::null)
+{ }
+
+// Get the resource pool this consumer is currently using.
+// virtual 
+LLScriptResourcePool& LLScriptResourceConsumer::getScriptResourcePool()
+{
+	return *mScriptResourcePool;
+}
+
+// Get the resource pool this consumer is currently using.
+// virtual 
+const LLScriptResourcePool& LLScriptResourceConsumer::getScriptResourcePool() const
+{
+	return *mScriptResourcePool;
+}
+
+// virtual
+void LLScriptResourceConsumer::setScriptResourcePool(LLScriptResourcePool& new_pool)
+{
+	mScriptResourcePool = &new_pool;
+}
+
+bool LLScriptResourceConsumer::switchScriptResourcePools(LLScriptResourcePool& new_pool)
+{
+	if (&new_pool == &LLScriptResourcePool::null)
+	{
+		llwarns << "New pool is null" << llendl;
+	}
+
+	if (isInPool(new_pool))
+	{
+		return true;
+	}
+
+	if (!canUseScriptResourcePool(new_pool))
+	{
+		return false;
+	}
+
+	S32 used_urls = getUsedPublicURLs();
+	
+	getScriptResourcePool().getPublicURLResource().release( used_urls );
+	setScriptResourcePool(new_pool);
+	getScriptResourcePool().getPublicURLResource().request( used_urls );
+	
+	return true;
+}
+
+bool LLScriptResourceConsumer::canUseScriptResourcePool(const LLScriptResourcePool& resource_pool)
+{
+	if (isInPool(resource_pool))
+	{
+		return true;
+	}
+
+	if (resource_pool.getPublicURLResource().getAvailable() < getUsedPublicURLs())
+	{
+		return false;
+	}
+	
+	return true;
+}
+
+bool LLScriptResourceConsumer::isInPool(const LLScriptResourcePool& resource_pool)
+{
+	const LLScriptResourcePool& current_pool = getScriptResourcePool();
+	if ( &resource_pool == &current_pool )
+	{
+		// This consumer is already in this pool
+		return true;
+	}
+	return false;
+}
+
diff --git a/indra/lscript/lscript_execute/llscriptresourcepool.cpp b/indra/lscript/lscript_execute/llscriptresourcepool.cpp
new file mode 100644
index 00000000000..2ec67c87dd1
--- /dev/null
+++ b/indra/lscript/lscript_execute/llscriptresourcepool.cpp
@@ -0,0 +1,39 @@
+/** 
+ * @file llscriptresourcepool.cpp
+ * @brief Collection of limited script resources
+ *
+ * $LicenseInfo:firstyear=2002&license=internal$
+ * 
+ * Copyright (c) 2002-2007, Linden Research, Inc.
+ * 
+ * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
+ * this source code is governed by the Linden Lab Source Code Disclosure
+ * Agreement ("Agreement") previously entered between you and Linden
+ * Lab. By accessing, using, copying, modifying or distributing this
+ * software, you acknowledge that you have been informed of your
+ * obligations under the Agreement 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 "llscriptresourcepool.h"
+
+LLScriptResourcePool LLScriptResourcePool::null;
+
+LLScriptResourcePool::LLScriptResourcePool() 
+{ 
+
+}
+
+LLScriptResource& LLScriptResourcePool::getPublicURLResource()
+{
+	return mLSLPublicURLs;
+}
+
+const LLScriptResource& LLScriptResourcePool::getPublicURLResource() const
+{
+	return mLSLPublicURLs;
+}
diff --git a/indra/lscript/lscript_execute/lscript_execute.cpp b/indra/lscript/lscript_execute/lscript_execute.cpp
index daa17f371cd..b2b54cdd7a7 100644
--- a/indra/lscript/lscript_execute/lscript_execute.cpp
+++ b/indra/lscript/lscript_execute/lscript_execute.cpp
@@ -68,6 +68,12 @@ const char* LSCRIPTRunTimeFaultStrings[LSRF_EOF] =		/*Flawfinder: ignore*/
 void LLScriptExecuteLSL2::startRunning() {}
 void LLScriptExecuteLSL2::stopRunning() {}
 
+const char* URL_REQUEST_GRANTED = "URL_REQUEST_GRANTED";
+const char* URL_REQUEST_DENIED = "URL_REQUEST_DENIED";
+
+// HTTP Requests to LSL scripts will time out after 25 seconds.
+const U64 LSL_HTTP_REQUEST_TIMEOUT = 25 * USEC_PER_SEC; 
+
 LLScriptExecuteLSL2::LLScriptExecuteLSL2(LLFILE *fp)
 {
 	U8  sizearray[4];
diff --git a/indra/lscript/lscript_execute/lscript_readlso.cpp b/indra/lscript/lscript_execute/lscript_readlso.cpp
index f45e64e5de7..3b10cc67c1a 100644
--- a/indra/lscript/lscript_execute/lscript_readlso.cpp
+++ b/indra/lscript/lscript_execute/lscript_readlso.cpp
@@ -625,6 +625,16 @@ void LLScriptLSOParse::printStates(LLFILE *fp)
 						bytestream2char(name, mRawData, event_offset, sizeof(name));
 						fprintf(fp, "\t\tstring %s\n", name);
 						break;
+					case LSTT_HTTP_REQUEST:	// LSTT_HTTP_REQUEST
+						bytestream2char(name, mRawData, event_offset, sizeof(name));
+						fprintf(fp, "%s\n", name);
+						bytestream2char(name, mRawData, event_offset, sizeof(name));
+						fprintf(fp, "\t\tkey %s\n", name);
+						bytestream2char(name, mRawData, event_offset, sizeof(name));
+						fprintf(fp, "\t\tstring %s\n", name);
+						bytestream2char(name, mRawData, event_offset, sizeof(name));
+						fprintf(fp, "\t\tstring %s\n", name);
+						break;
 					default:
 						break;
 					}
diff --git a/indra/lscript/lscript_library/lscript_library.cpp b/indra/lscript/lscript_library/lscript_library.cpp
index 55ecbcf2a1b..0342c974292 100644
--- a/indra/lscript/lscript_library/lscript_library.cpp
+++ b/indra/lscript/lscript_library/lscript_library.cpp
@@ -451,6 +451,12 @@ void LLScriptLibrary::init()
 
 	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSHA1String", "s", "s", "string llSHA1String(string sr)\nPerforms a SHA1 security Hash.  Returns a 40 character hex string."));
 
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetFreeURLs", "i", NULL, "integer llGetFreeURLs()\nreturns the available urls for the current script"));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRequestURL", "k", NULL, "key llRequestURL()\nRequests one HTTP:// url for use by this object\nTriggers an http_server event with results."));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRequestSecureURL", "k", NULL, "key llRequestSecureURL()\nRequests one HTTPS:// (SSL) url for use by this object\nTriggers an http_server event with results."));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llReleaseURL", NULL, "s", "llReleaseURL(string url)\nReleases the specified URL, it will no longer be usable."));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llHTTPResponse", NULL, "kis", "llHTTPResponse(key id, integer status, string body)\nResponds to request id with status and body."));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetHTTPHeader", "s", "ks", "string llGetHTTPHeader(key id, string header)\nGet the value for header for request id."));
 
 	// energy, sleep, dummy_func, name, return type, parameters, help text, gods-only
 
diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini
index 0ab977d8e9b..cd7b533e7a7 100644
--- a/indra/newview/app_settings/keywords.ini
+++ b/indra/newview/app_settings/keywords.ini
@@ -50,6 +50,7 @@ link_message	link_message(integer sender_num, integer num, string str, key id):T
 changed			changed( integer change ):Triggered various event change the task:(test change with CHANGED_INVENTORY, CHANGED_COLOR, CHANGED_SHAPE, CHANGED_SCALE, CHANGED_TEXTURE, CHANGED_LINK, CHANGED_ALLOWED_DROP, CHANGED_OWNER, CHANGED_REGION, CHANGED_TELEPORT)
 remote_data     remote_data(integer event_type, key channel, key message_id, string sender,integer idata, string sdata):Triggered by various XML-RPC calls (event_type will be one of REMOTE_DATA_CHANNEL, REMOTE_DATA_REQUEST, REMOTE_DATA_REPLY)
 http_response   http_response(key request_id, integer status, list metadata, string body):Triggered when task receives a response to one of its llHTTPRequests
+http_request	http_request(key id, string method, string body):Triggered when task receives an http request against a public URL
 
 # integer constants
 [word .1, .1, .5]
@@ -317,6 +318,7 @@ CHANGED_ALLOWED_DROP Parameter of changed event handler used to indicate a user
 CHANGED_OWNER		Parameter of changed event handler used to indicate change to task's owner ONLY when an object is sold as original or deeded to group
 CHANGED_REGION		Parameter of changed event handler used to indicate the region has changed
 CHANGED_TELEPORT	Parameter of changed event handler used to indicate teleport has completed
+CHANGED_REGION_START	Parameter of changed event handler used to indicate the region has been restarted
 
 TYPE_INTEGER		Indicates that the list entry is holding an integer
 TYPE_FLOAT			Indicates that the list entry is holding an float
@@ -519,6 +521,9 @@ TEXTURE_DEFAULT			UUID for the "Default Media" texture
 TEXTURE_PLYWOOD			UUID for the default "Plywood" texture
 TEXTURE_TRANSPARENT		UUID for the "White - Transparent" texture
 
+URL_REQUEST_GRANTED		Used with http_request when a public URL is successfully granted
+URL_REQUEST_DENIED		Used with http_request when a public URL is not available
+
 # float constants
 [word .3, .1, .5]
 PI					3.1415926535897932384626433832795
diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp
index 16428ce6d1c..fa6ba162ec9 100644
--- a/indra/newview/llfloatertopobjects.cpp
+++ b/indra/newview/llfloatertopobjects.cpp
@@ -172,6 +172,7 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data)
 		std::string owner_buf;
 		F32 mono_score = 0.f;
 		bool have_extended_data = false;
+		S32 public_urls = 0;
 
 		msg->getU32Fast(_PREHASH_ReportData, _PREHASH_TaskLocalID, task_local_id, block);
 		msg->getUUIDFast(_PREHASH_ReportData, _PREHASH_TaskID, task_id, block);
@@ -186,6 +187,7 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data)
 			have_extended_data = true;
 			msg->getU32("DataExtended", "TimeStamp", time_stamp, block);
 			msg->getF32("DataExtended", "MonoScore", mono_score, block);
+			msg->getS32(_PREHASH_ReportData,"PublicURLs",public_urls,block);
 		}
 
 		LLSD element;
@@ -216,6 +218,10 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data)
 			element["columns"][5]["column"] = "mono_time";
 			element["columns"][5]["value"] = llformat("%0.3f", mono_score);
 			element["columns"][5]["font"] = "SANSSERIF";
+
+			element["columns"][6]["column"] = "URLs";
+			element["columns"][6]["value"] = llformat("%d", public_urls);
+			element["columns"][6]["font"] = "SANSSERIF";
 		}
 		
 		list->addElement(element);
diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt
index 01c291be7bc..0000cc237bf 100644
--- a/indra/test/CMakeLists.txt
+++ b/indra/test/CMakeLists.txt
@@ -10,6 +10,7 @@ include(LLMath)
 include(LLMessage)
 include(LLVFS)
 include(LLXML)
+include(LScript)
 include(Linking)
 
 include_directories(
@@ -20,6 +21,7 @@ include_directories(
     ${LLINVENTORY_INCLUDE_DIRS}
     ${LLVFS_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
+    ${LSCRIPT_INCLUDE_DIRS}
     )
 
 set(test_SOURCE_FILES
@@ -48,6 +50,7 @@ set(test_SOURCE_FILES
     llquaternion_tut.cpp
     llrandom_tut.cpp
     llsaleinfo_tut.cpp
+    llscriptresource_tut.cpp
     llsdmessagebuilder_tut.cpp
     llsdmessagereader_tut.cpp
     llsd_new_tut.cpp
@@ -58,6 +61,7 @@ set(test_SOURCE_FILES
     llstring_tut.cpp
     lltemplatemessagebuilder_tut.cpp
     lltiming_tut.cpp
+    lltranscode_tut.cpp
     lltut.cpp
     lluri_tut.cpp
     lluuidhashmap_tut.cpp
@@ -109,7 +113,9 @@ target_link_libraries(test
     ${LLMATH_LIBRARIES}
     ${LLVFS_LIBRARIES}
     ${LLXML_LIBRARIES}
+    ${LSCRIPT_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
+    ${APRICONV_LIBRARIES}
     ${PTHREAD_LIBRARY}
     ${WINDOWS_LIBRARIES}
     ${DL_LIBRARY}
diff --git a/indra/test/llhttpnode_tut.cpp b/indra/test/llhttpnode_tut.cpp
index 8654971f9be..e17d1e0f245 100644
--- a/indra/test/llhttpnode_tut.cpp
+++ b/indra/test/llhttpnode_tut.cpp
@@ -86,7 +86,8 @@ namespace tut
 
 			void result(const LLSD& result) { mResult = result; }
 			void status(S32 code, const std::string& message) { }
-
+			void extendedResult(S32 code, const std::string& message, const LLSD& headers) { }
+			
 		private:
 			Response() {;} // Must be accessed through LLPointer.
 		};
diff --git a/indra/test/llscriptresource_tut.cpp b/indra/test/llscriptresource_tut.cpp
new file mode 100644
index 00000000000..e384c275a3b
--- /dev/null
+++ b/indra/test/llscriptresource_tut.cpp
@@ -0,0 +1,203 @@
+/** 
+ * @file llscriptresource_tut.cpp
+ * @brief Test LLScriptResource
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * 
+ * Copyright (c) 2006-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 <tut/tut.h>
+#include "linden_common.h"
+
+#include "lltut.h"
+
+#include "llscriptresource.h"
+#include "llscriptresourceconsumer.h"
+#include "llscriptresourcepool.h"
+
+class TestConsumer : public LLScriptResourceConsumer
+{
+public:
+	TestConsumer()
+		: mUsedURLs(0)
+	{ }
+
+	// LLScriptResourceConsumer interface:
+	S32 getUsedPublicURLs() const
+	{
+		return mUsedURLs;
+	}
+
+	// Test details:
+	S32 mUsedURLs;
+};
+
+
+namespace tut
+{
+	class LLScriptResourceTestData
+	{
+	};
+	
+	typedef test_group<LLScriptResourceTestData> LLScriptResourceTestGroup;
+	typedef LLScriptResourceTestGroup::object		LLScriptResourceTestObject;
+	LLScriptResourceTestGroup scriptResourceTestGroup("scriptResource");
+
+	template<> template<>
+	void LLScriptResourceTestObject::test<1>()
+	{
+		LLScriptResource resource;
+		U32 total = 42;
+		
+		resource.setTotal(total);
+		ensure_equals("Verify set/get total", resource.getTotal(), total);
+		ensure_equals("Verify all resources are initially available",resource.getAvailable(),total);
+
+		// Requesting too many, releasing non-allocated
+		ensure("Request total + 1 resources should fail",!resource.request(total + 1));
+		ensure_equals("Verify all resources available after failed request",resource.getAvailable(),total);
+
+		ensure("Releasing resources when none allocated should fail",!resource.release());
+		ensure_equals("All resources should be available after failed release",resource.getAvailable(),total);
+
+		ensure("Request one resource", resource.request());
+		ensure_equals("Verify available resources after successful request",resource.getAvailable(),total - 1);
+
+		// Is this right?  Or should we release all used resources if we try to release more than are currently used?
+		ensure("Release more resources than allocated",!resource.release(2));
+		ensure_equals("Verify resource availability after failed release",resource.getAvailable(),total - 1);
+
+		ensure("Release a resource",resource.release());
+		ensure_equals("Verify all resources available after successful release",resource.getAvailable(),total);
+	}
+
+
+	template<> template<>
+	void LLScriptResourceTestObject::test<2>()
+	{
+		LLScriptResource resource;
+		U32 total = 42;
+
+		resource.setTotal(total);
+
+		S32 resources_to_request = 30;
+		ensure("Get multiple resources resources",resource.request(resources_to_request));
+		ensure_equals("Verify available resources is correct after request of multiple resources",resource.getAvailable(), total - resources_to_request);
+
+		S32 resources_to_release = (resources_to_request / 2);
+		ensure("Release some resources",resource.release(resources_to_release));
+
+		S32 expected_available = (total - resources_to_request + resources_to_release);
+		ensure_equals("Verify available resources after release of some resources",resource.getAvailable(), expected_available);
+
+		resources_to_release = (resources_to_request - resources_to_release);
+		ensure("Release remaining resources",resource.release(resources_to_release));
+
+		ensure_equals("Verify available resources after release of remaining resources",resource.getAvailable(), total);
+	}
+
+	template<> template<>
+	void LLScriptResourceTestObject::test<3>()
+	{
+		LLScriptResource resource;
+
+		U32 total = 42;
+		resource.setTotal(total);
+
+		ensure("Request all resources",resource.request(total));
+
+		U32 low_total = 10;
+		ensure("Release all resources",resource.release(total));
+		ensure_equals("Verify all resources available after releasing",resource.getAvailable(),total);
+
+		resource.setTotal(low_total);
+		ensure_equals("Verify low total resources are available after set",resource.getAvailable(),low_total);
+	}
+	
+
+	template<> template<>
+	void LLScriptResourceTestObject::test<4>()
+	{
+		S32 big_resource_total = 100;
+		S32 small_resource_total = 10;
+		LLScriptResourcePool big_pool;
+		big_pool.getPublicURLResource().setTotal(big_resource_total);
+		LLScriptResourcePool small_pool;
+		small_pool.getPublicURLResource().setTotal(small_resource_total);
+
+		TestConsumer consumer;
+		LLScriptResourcePool& initial_pool = consumer.getScriptResourcePool();
+		ensure("Initial resource pool is 'null'.", (&initial_pool == &LLScriptResourcePool::null));
+
+		consumer.switchScriptResourcePools(big_pool);
+		LLScriptResourcePool& get_pool = consumer.getScriptResourcePool();
+		ensure("Get resource that was set.", (&big_pool == &get_pool));
+
+		ensure_equals("No public urls in use yet.", consumer.getUsedPublicURLs(),0);
+
+		S32 request_urls = 5;
+		consumer.mUsedURLs = request_urls;
+		consumer.getScriptResourcePool().getPublicURLResource().request(request_urls);
+
+		ensure_equals("Available urls on big_pool is 5 less than total.",
+			big_pool.getPublicURLResource().getAvailable(), big_resource_total - request_urls);
+
+		ensure("Switching from big pool to small pool",
+			   consumer.switchScriptResourcePools(small_pool));
+
+		ensure_equals("All resources available to big pool again",
+			big_pool.getPublicURLResource().getAvailable(), big_resource_total);
+
+		ensure_equals("Available urls on small pool is 5 less than total.",
+			small_pool.getPublicURLResource().getAvailable(), small_resource_total - request_urls);
+
+		ensure("Switching from small pool to big pool",
+			   consumer.switchScriptResourcePools(big_pool));
+
+		consumer.getScriptResourcePool().getPublicURLResource().release(request_urls);
+
+		request_urls = 50; // Too many for the small_pool
+
+		consumer.mUsedURLs = request_urls;
+		consumer.getScriptResourcePool().getPublicURLResource().request(request_urls);
+
+		// Verify big pool has them
+		ensure_equals("Available urls on big pool is 50 less than total.",
+			big_pool.getPublicURLResource().getAvailable(), big_resource_total - request_urls);
+
+		// Verify can't switch to small_pool
+		ensure("Switching to small pool with too many resources",
+			   !consumer.switchScriptResourcePools(small_pool));
+
+		// Verify big pool still accounting for used resources
+		ensure_equals("Available urls on big_pool is still 50 less than total.",
+			big_pool.getPublicURLResource().getAvailable(), big_resource_total - request_urls);
+		
+		// Verify small pool still has all resources available.
+		ensure_equals("All resources in small pool are still available.",
+			small_pool.getPublicURLResource().getAvailable(), small_resource_total);
+	}
+}
diff --git a/indra/test/lltranscode_tut.cpp b/indra/test/lltranscode_tut.cpp
new file mode 100644
index 00000000000..8abf9dc2245
--- /dev/null
+++ b/indra/test/lltranscode_tut.cpp
@@ -0,0 +1,94 @@
+/** 
+ * @file llscriptresource_tut.cpp
+ * @brief Test LLScriptResource
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * 
+ * Copyright (c) 2006-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 <tut/tut.h>
+#include "linden_common.h"
+
+#include "lltut.h"
+
+#include "../newsim/lltranscode.cpp" // include TU to pull in newsim implementation.
+
+static const char test_utf8[] = "Edelwei\xc3\x9f";
+static const char test_utf7[] = "Edelwei+AN8-";
+static const char test_latin1[] = "Edelwei\xdf";
+static const char test_latin2[] = "Edelwei\xdf";
+
+namespace tut
+{
+	class LLTranscodeTestData
+	{
+	};
+	
+	typedef test_group<LLTranscodeTestData> LLTranscodeTestGroup;
+	typedef LLTranscodeTestGroup::object LLTranscodeTestObject;
+	LLTranscodeTestGroup transcodeTestGroup("transcode");
+
+	template<> template<>
+	void LLTranscodeTestObject::test<1>()
+	{
+#if LL_WINDOWS
+		skip("Windows APR libs can't transcode.");
+#endif
+		// Test utf8
+		std::stringstream input;
+		std::stringstream output;
+
+		input.str(test_utf7);
+		output.clear();
+		LLTranscode::transcode("charset=UTF-7", input, output);
+		ensure_equals("UTF-7 to UTF-8 transcoding", output.str(),
+						  std::string(test_utf8));
+
+		input.str(test_latin1);
+		output.clear();
+		LLTranscode::transcode("", input, output);
+		ensure_equals("Default (latin_1) to UTF8 transcoding", output.str(),
+			std::string(test_utf8));
+		
+		input.str(test_latin1);
+		output.clear();
+		LLTranscode::transcode("charset=iso-8859-1", input, output);
+		ensure_equals("latin_1 (ISO-8859-1) to UTF8 transcoding", output.str(),
+			std::string(test_utf8));
+	
+		input.str(test_latin2);
+		output.clear();
+		LLTranscode::transcode("charset=iso-8859-2", input, output);
+		ensure_equals("latin_2 (ISO-8859-2) to UTF8 transcoding", output.str(),
+			std::string(test_utf8));
+		
+		input.str(test_utf8);
+		output.clear();
+		LLTranscode::transcode("charset=utf-8", input, output);
+		ensure_equals("UTF8 to UTF8 transcoding", output.str(),
+			std::string(test_utf8));
+	}
+}
diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp
index e6322346239..3fede2608cf 100644
--- a/indra/test/message_tut.cpp
+++ b/indra/test/message_tut.cpp
@@ -51,6 +51,7 @@ namespace
 		{
 			mStatus = code;
 		}
+		virtual void extendedResult(S32 code, const std::string& message, const LLSD& headers) { }
 		S32 mStatus;
 	};
 }
-- 
GitLab