From ce82adec2a0124fe27f363653d2652b0f5c61f04 Mon Sep 17 00:00:00 2001
From: McCabe Maxsted
Date: Fri, 16 Oct 2009 16:39:44 -0700
Subject: Applied RLVa-1.0.4e_20091010_SL-1.22.11-diff.patch

---
 ChangeLog.txt                                  |  21 +++++
 linden/indra/newview/app_settings/settings.xml |   2 +-
 linden/indra/newview/llagent.cpp               |   4 +-
 linden/indra/newview/llchatbar.cpp             |  18 ++--
 linden/indra/newview/llinventorybridge.cpp     |   2 +-
 linden/indra/newview/llpanelinventory.cpp      |   2 +-
 linden/indra/newview/llviewermenu.cpp          |   2 +-
 linden/indra/newview/llviewermessage.cpp       |   9 +-
 linden/indra/newview/llviewertexteditor.cpp    |   2 +-
 linden/indra/newview/rlvdefines.h              |  17 +++-
 linden/indra/newview/rlvextensions.cpp         |  34 ++++++-
 linden/indra/newview/rlvextensions.h           |   2 +
 linden/indra/newview/rlvhandler.cpp            | 122 ++++++++++++++++++-------
 linden/indra/newview/rlvhandler.h              | 108 +++++++++-------------
 linden/indra/newview/rlvhelper.cpp             |  67 +++++++-------
 linden/indra/newview/rlvhelper.h               |  55 ++++++++++-
 16 files changed, 306 insertions(+), 161 deletions(-)

diff --git a/ChangeLog.txt b/ChangeLog.txt
index 2dbb5ca..a029365 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,24 @@
+2009-10-16 McCabe Maxsted  <hakushakukun@gmail.com>
+
+	* Applied RLVa-1.0.4e_20091010_SL-1.22.11-diff.patch.
+
+	  modified:   linden/indra/newview/app_settings/settings.xml
+	  modified:   linden/indra/newview/llagent.cpp
+	  modified:   linden/indra/newview/llchatbar.cpp
+	  modified:   linden/indra/newview/llinventorybridge.cpp
+	  modified:   linden/indra/newview/llpanelinventory.cpp
+	  modified:   linden/indra/newview/llviewermenu.cpp
+	  modified:   linden/indra/newview/llviewermessage.cpp
+	  modified:   linden/indra/newview/llviewertexteditor.cpp
+	  modified:   linden/indra/newview/rlvdefines.h
+	  modified:   linden/indra/newview/rlvextensions.cpp
+	  modified:   linden/indra/newview/rlvextensions.h
+	  modified:   linden/indra/newview/rlvhandler.cpp
+	  modified:   linden/indra/newview/rlvhandler.h
+	  modified:   linden/indra/newview/rlvhelper.cpp
+	  modified:   linden/indra/newview/rlvhelper.h
+	  
+
 2009-10-13 McCabe Maxsted  <hakushakukun@gmail.com>
 
 	* Applied patch for VWR-6787 by Alissa Sabre - 'none' text in group window not translatable.
diff --git a/linden/indra/newview/app_settings/settings.xml b/linden/indra/newview/app_settings/settings.xml
index 55e6131..15bb319 100644
--- a/linden/indra/newview/app_settings/settings.xml
+++ b/linden/indra/newview/app_settings/settings.xml
@@ -65,7 +65,7 @@
     <key>Type</key>
     <string>Boolean</string>
     <key>Value</key>
-    <integer>0</integer>
+    <integer>1</integer>
   </map>
   <key>RLVaHideLockedLayers</key>
   <map>
diff --git a/linden/indra/newview/llagent.cpp b/linden/indra/newview/llagent.cpp
index 022e1cc..8b118e0 100644
--- a/linden/indra/newview/llagent.cpp
+++ b/linden/indra/newview/llagent.cpp
@@ -5991,7 +5991,7 @@ void LLAgent::teleportViaLandmark(const LLUUID& landmark_asset_id)
 {
 // [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d)
 	if ( (rlv_handler_t::isEnabled()) &&
-		 ( (gRlvHandler.hasBehaviour("tplm")) || 
+		 ( (gRlvHandler.hasBehaviour(RLV_BHVR_TPLM)) || 
 		   ((gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) && (mAvatarObject.notNull()) && (mAvatarObject->mIsSitting)) ))
 	{
 		return;
@@ -6065,7 +6065,7 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
 // [RLVa:KB] - Alternate: Snowglobe-1.0 | Checked: 2009-07-07 (RLVa-1.0.0d)
 	// If we're getting teleported due to @tpto we should disregard any @tploc=n or @unsit=n restrictions from the same object
 	if ( (rlv_handler_t::isEnabled()) &&
-		 ( (gRlvHandler.hasBehaviourExcept("tploc", gRlvHandler.getCurrentObject())) ||
+		 ( (gRlvHandler.hasBehaviourExcept(RLV_BHVR_TPLOC, gRlvHandler.getCurrentObject())) ||
 		   ( (mAvatarObject.notNull()) && (mAvatarObject->mIsSitting) && 
 			 (gRlvHandler.hasBehaviourExcept(RLV_BHVR_UNSIT, gRlvHandler.getCurrentObject()))) ) )
 	{
diff --git a/linden/indra/newview/llchatbar.cpp b/linden/indra/newview/llchatbar.cpp
index 608fb54..710677a 100644
--- a/linden/indra/newview/llchatbar.cpp
+++ b/linden/indra/newview/llchatbar.cpp
@@ -634,11 +634,11 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
 	if ( (0 == channel) && (rlv_handler_t::isEnabled()) )
 	{
 		// Adjust the (public) chat "volume" on chat and gestures (also takes care of playing the proper animation)
-		if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour("chatnormal")) )
+		if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATNORMAL)) )
 			type = CHAT_TYPE_WHISPER;
-		else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour("chatshout")) )
+		else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATSHOUT)) )
 			type = CHAT_TYPE_NORMAL;
-		else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour("chatwhisper")) )
+		else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATWHISPER)) )
 			type = CHAT_TYPE_NORMAL;
 
 		animate &= !gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT);
@@ -692,11 +692,11 @@ void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channe
 		if (0 == channel)
 		{
 			// (We already did this before, but LLChatHandler::handle() calls this directly)
-			if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour("chatnormal")) )
+			if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATNORMAL)) )
 				type = CHAT_TYPE_WHISPER;
-			else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour("chatshout")) )
+			else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATSHOUT)) )
 				type = CHAT_TYPE_NORMAL;
-			else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour("chatwhisper")) )
+			else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATWHISPER)) )
 				type = CHAT_TYPE_NORMAL;
 
 			// Redirect chat if needed
@@ -707,20 +707,20 @@ void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channe
 			}
 
 			// Filter public chat if sendchat restricted (and filter anything that redirchat didn't redirect)
-			if ( (gRlvHandler.hasBehaviour("sendchat")) || (gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) )
+			if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHAT)) || (gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) )
 				gRlvHandler.filterChat(utf8_out_text, true);
 		}
 		else
 		{
 			// Don't allow chat on a non-public channel if sendchannel restricted (unless the channel is an exception)
-			if ( (gRlvHandler.hasBehaviour("sendchannel")) && (!gRlvHandler.hasBehaviour("sendchannel", llformat("%d", channel))) )
+			if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHANNEL)) && (!gRlvHandler.isException(RLV_BHVR_SENDCHANNEL, channel)) )
 				return;
 
 			// Don't allow chat on debug channel if @sendchat, @redirchat or @rediremote restricted (shows as public chat on viewers)
 			if (channel >= CHAT_CHANNEL_DEBUG)
 			{
 				bool fIsEmote = rlvIsEmote(utf8_out_text);
-				if ( (gRlvHandler.hasBehaviour("sendchat")) || 
+				if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHAT)) || 
 					 ((!fIsEmote) && (gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT))) || 
 					 ((fIsEmote) && (gRlvHandler.hasBehaviour(RLV_BHVR_REDIREMOTE))) )
 				{
diff --git a/linden/indra/newview/llinventorybridge.cpp b/linden/indra/newview/llinventorybridge.cpp
index 0d5625c..5060d21 100644
--- a/linden/indra/newview/llinventorybridge.cpp
+++ b/linden/indra/newview/llinventorybridge.cpp
@@ -2943,7 +2943,7 @@ void open_notecard(LLViewerInventoryItem* inv_item,
 				   BOOL take_focus)
 {
 // [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.0.0c)
-	if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.hasBehaviour("viewnote")) )
+	if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.hasBehaviour(RLV_BHVR_VIEWNOTE)) )
 	{
 		return;
 	}
diff --git a/linden/indra/newview/llpanelinventory.cpp b/linden/indra/newview/llpanelinventory.cpp
index 7ab850f..0ae2aec 100644
--- a/linden/indra/newview/llpanelinventory.cpp
+++ b/linden/indra/newview/llpanelinventory.cpp
@@ -1377,7 +1377,7 @@ void LLTaskNotecardBridge::openItem()
 		return;
 	}
 // [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.0.0c)
-	if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour("viewnote")) || (!gRlvHandler.isDetachable(object))) )
+	if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_VIEWNOTE)) || (!gRlvHandler.isDetachable(object))) )
 	{
 		return;
 	}
diff --git a/linden/indra/newview/llviewermenu.cpp b/linden/indra/newview/llviewermenu.cpp
index ceb5d5f..4493ddf 100644
--- a/linden/indra/newview/llviewermenu.cpp
+++ b/linden/indra/newview/llviewermenu.cpp
@@ -1423,7 +1423,7 @@ void init_debug_rlva_menu(LLMenuGL* menu)
 
 	#ifdef RLV_EXTENSION_ENABLE_WEAR
 		if (gSavedSettings.controlExists(RLV_SETTING_ENABLEWEAR))
-			menu->append(new LLMenuItemCheckGL("Enable Wear", menu_toggle_control, NULL, menu_check_control, (void*)RLV_SETTING_ENABLEWEAR));
+			menu->append(new LLMenuItemCheckGL("Enable Wear", menu_toggle_control, rlvEnableWearEnabler, menu_check_control, (void*)RLV_SETTING_ENABLEWEAR));
 		menu->appendSeparator();
 	#endif // RLV_EXTENSION_ENABLE_WEAR
 
diff --git a/linden/indra/newview/llviewermessage.cpp b/linden/indra/newview/llviewermessage.cpp
index 3baeec0..c403e3e 100644
--- a/linden/indra/newview/llviewermessage.cpp
+++ b/linden/indra/newview/llviewermessage.cpp
@@ -2485,7 +2485,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
 		color.setVec(1.f,1.f,1.f,1.f);
 		msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg);
 
-// [RLVa:KB] - Checked: 2009-08-04 (RLVa-1.0.1d) | Modified: RLVa-1.0.1d
+// [RLVa:KB] - Checked: 2009-10-06 (RLVa-1.0.4d) | Modified: RLVa-1.0.4d
 		if ( (rlv_handler_t::isEnabled()) && 
 			 (CHAT_TYPE_START != chat.mChatType) && (CHAT_TYPE_STOP != chat.mChatType) && (CHAT_TYPE_OWNER != chat.mChatType) )
 		{
@@ -2495,7 +2495,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
 			// Filtering "rules":
 			//   avatar  => filter all avie text (unless it's this avie or they're an exemption)
 			//   objects => filter everything except attachments this avie owns
-			if ( ((CHAT_SOURCE_AGENT == chat.mSourceType) && (from_id != gAgent.getID())) || (!is_owned_by_me) || (!is_attachment) )
+			if ( ( (CHAT_SOURCE_AGENT == chat.mSourceType) && (from_id != gAgent.getID()) ) || 
+				 ( (CHAT_SOURCE_OBJECT == chat.mSourceType) && ((!is_owned_by_me) || (!is_attachment)) ) )
 			{
 				if (!rlvIsEmote(mesg))
 				{
@@ -4933,7 +4934,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
 // [RLVa:KB] - Version: 1.22.11 | Checked: 2009-07-10 (RLVa-1.0.0g) | Modified: RLVa-0.2.0e
 		S32 rlvQuestionsOther = questions;
 
-		if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.hasBehaviour("acceptpermission")) )
+		if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.hasBehaviour(RLV_BHVR_ACCEPTPERMISSION)) )
 		{
 			LLViewerObject* pObj = gObjectList.findObject(taskid);
 			if (pObj)
@@ -5308,7 +5309,7 @@ void handle_lure(LLDynamicArray<LLUUID>& ids)
 		for (LLDynamicArray<LLUUID>::iterator it = ids.begin(); it != ids.end(); ++it)
 		{
 			const LLRelationship* pBuddyInfo = LLAvatarTracker::instance().getBuddyInfo(*it);
-			if ( (!gRlvHandler.isException(RLV_BHVR_TPLURE, *it)) &&
+			if ( (!gRlvHandler.isException(RLV_BHVR_TPLURE, *it, RLV_CHECK_PERMISSIVE)) &&
 				 ((!pBuddyInfo) || (!pBuddyInfo->isOnline()) || (!pBuddyInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))) )
 			{
 				delete userdata;
diff --git a/linden/indra/newview/llviewertexteditor.cpp b/linden/indra/newview/llviewertexteditor.cpp
index 76b752c..8f4a944 100644
--- a/linden/indra/newview/llviewertexteditor.cpp
+++ b/linden/indra/newview/llviewertexteditor.cpp
@@ -96,7 +96,7 @@ public:
 		else
 		{
 // [RLVa:KB] - Checked: 2009-07-06 (RLVa-1.0.0c)
-			if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.hasBehaviour("viewnote")) )
+			if ( (rlv_handler_t::isEnabled()) && (gRlvHandler.hasBehaviour(RLV_BHVR_VIEWNOTE)) )
 			{
 				return;
 			}
diff --git a/linden/indra/newview/rlvdefines.h b/linden/indra/newview/rlvdefines.h
index 4e6a4d4..d1e0b75 100644
--- a/linden/indra/newview/rlvdefines.h
+++ b/linden/indra/newview/rlvdefines.h
@@ -41,13 +41,14 @@
 
 // Version of the specifcation we support
 const S32 RLV_VERSION_MAJOR = 1;
-const S32 RLV_VERSION_MINOR = 20;
-const S32 RLV_VERSION_PATCH = 2;
+const S32 RLV_VERSION_MINOR = 21;
+const S32 RLV_VERSION_PATCH = 0;
+const S32 RLV_VERSION_BUILD = 0;
 
 // Implementation version
 const S32 RLVa_VERSION_MAJOR = 1;
 const S32 RLVa_VERSION_MINOR = 0;
-const S32 RLVa_VERSION_PATCH = 3;
+const S32 RLVa_VERSION_PATCH = 4;
 const S32 RLVa_VERSION_BUILD = 4;
 
 // The official viewer version we're patching against
@@ -136,6 +137,7 @@ enum ERlvBehaviour {
 	RLV_BHVR_SHOWLOC,				// "showloc"
 	RLV_BHVR_TPTO,					// "tpto"
 	RLV_BHVR_ACCEPTTP,				// "accepttp"
+	RLV_BHVR_ACCEPTPERMISSION,		// "acceptpermission"
 	RLV_BHVR_SHOWNAMES,				// "shownames"
 	RLV_BHVR_FLY,					// "fly"
 	RLV_BHVR_GETSITID,				// "getsitid"
@@ -147,6 +149,9 @@ enum ERlvBehaviour {
 	RLV_BHVR_SHOWHOVERTEXTHUD,		// "showhovertexthud"
 	RLV_BHVR_SHOWHOVERTEXT,			// "showhovertext"
 	RLV_BHVR_NOTIFY,				// "notify"
+	RLV_BHVR_DEFAULTWEAR,			// "defaultwear"
+	RLV_BHVR_VERSIONNUM,			// "versionnum"
+	RLV_BHVR_PERMISSIVE,			// "permissive"
 
 	RLV_BHVR_COUNT,
 	RLV_BHVR_UNKNOWN
@@ -174,6 +179,12 @@ enum ERlvCmdRet {
 	RLV_RET_UNKNOWN					// Command unkown
 };
 
+enum ERlvExceptionCheck {
+	RLV_CHECK_PERMISSIVE,			// Exception can be set by any object
+	RLV_CHECK_STRICT,				// Exception must be set by all objects holding the restriction
+	RLV_CHECK_DEFAULT				// Permissive or strict will be determined by currently enforced restrictions
+};
+
 // ============================================================================
 // Settings
 
diff --git a/linden/indra/newview/rlvextensions.cpp b/linden/indra/newview/rlvextensions.cpp
index 10d1c46..f31c62e 100644
--- a/linden/indra/newview/rlvextensions.cpp
+++ b/linden/indra/newview/rlvextensions.cpp
@@ -12,13 +12,14 @@
 // ============================================================================
 
 std::map<std::string, S16> RlvExtGetSet::m_DbgAllowed;
+std::map<std::string, std::string> RlvExtGetSet::m_PseudoDebug;
 
 // Checked: 2009-06-03 (RLVa-0.2.0h) | Modified: RLVa-0.2.0h
 RlvExtGetSet::RlvExtGetSet()
 {
 	if (!m_DbgAllowed.size())	// m_DbgAllowed is static and should only be initialized once
 	{
-		m_DbgAllowed.insert(std::pair<std::string, S16>("AvatarSex", DBG_READ | DBG_PSEUDO));
+		m_DbgAllowed.insert(std::pair<std::string, S16>("AvatarSex", DBG_READ | DBG_WRITE | DBG_PSEUDO));
 		m_DbgAllowed.insert(std::pair<std::string, S16>("RenderResolutionDivisor", DBG_READ | DBG_WRITE));
 		#ifdef RLV_EXTENSION_CMD_GETSETDEBUG_EX
 			m_DbgAllowed.insert(std::pair<std::string, S16>(RLV_SETTING_FORBIDGIVETORLV, DBG_READ));
@@ -172,19 +173,27 @@ std::string RlvExtGetSet::onGetDebug(std::string strSetting)
 	return std::string();
 }
 
-// Checked: 2009-06-03 (RLVa-0.2.0h) | Added: RLVa-0.2.0h
+// Checked: 2009-10-03 (RLVa-1.0.4e) | Added: RLVa-1.0.4e
 std::string RlvExtGetSet::onGetPseudoDebug(const std::string& strSetting)
 {
 	// Skip sanity checking because it's all done in RlvExtGetSet::onGetDebug() already
 	if ("AvatarSex" == strSetting)
 	{
-		if (gAgent.getAvatarObject())
-			return llformat("%d", (gAgent.getAvatarObject()->getSex() == SEX_MALE)); // [See LLFloaterCustomize::LLFloaterCustomize()]
+		std::map<std::string, std::string>::const_iterator itPseudo = m_PseudoDebug.find(strSetting);
+		if (itPseudo != m_PseudoDebug.end())
+		{
+			return itPseudo->second;
+		}
+		else
+		{
+			if (gAgent.getAvatarObject())
+				return llformat("%d", (gAgent.getAvatarObject()->getSex() == SEX_MALE)); // [See LLFloaterCustomize::LLFloaterCustomize()]
+		}
 	}
 	return std::string();
 }
 
-// Checked: 2009-06-03 (RLVa-0.2.0h) | Modified: RLVa-0.2.0h
+// Checked: 2009-10-10 (RLVa-1.0.4e) | Modified: RLVa-1.0.4e
 void RlvExtGetSet::onSetDebug(std::string strSetting, const std::string& strValue)
 {
 	S16 dbgFlags;
@@ -219,6 +228,21 @@ void RlvExtGetSet::onSetDebug(std::string strSetting, const std::string& strValu
 				pSetting->setPersist( (pSetting->isDefault()) ? ((dbgFlags & DBG_PERSIST) == DBG_PERSIST) : false );
 			}
 		}
+		else
+		{
+			onSetPseudoDebug(strSetting, strValue);
+		}
+	}
+}
+
+// Checked: 2009-10-10 (RLVa-1.0.4e) | Modified: RLVa-1.0.4e
+void RlvExtGetSet::onSetPseudoDebug(const std::string& strSetting, const std::string& strValue)
+{
+	if ("AvatarSex" == strSetting)
+	{
+		BOOL fValue;
+		if (LLStringUtil::convertToBOOL(strValue, fValue))
+			m_PseudoDebug[strSetting] = strValue;
 	}
 }
 
diff --git a/linden/indra/newview/rlvextensions.h b/linden/indra/newview/rlvextensions.h
index 5c10c58..5720ba0 100644
--- a/linden/indra/newview/rlvextensions.h
+++ b/linden/indra/newview/rlvextensions.h
@@ -24,6 +24,7 @@ protected:
 	std::string onGetDebug(std::string strSetting);
 	std::string onGetPseudoDebug(const std::string& strSetting);
 	void        onSetDebug(std::string strSetting, const std::string& strValue);
+	void        onSetPseudoDebug(const std::string& strSetting, const std::string& strValue);
 
 	std::string onGetEnv(std::string strSetting);
 	void        onSetEnv(std::string strSetting, const std::string& strValue);
@@ -33,6 +34,7 @@ protected:
 public:
 	enum { DBG_READ = 0x01, DBG_WRITE = 0x02, DBG_PERSIST = 0x04, DBG_PSEUDO = 0x08 };
 	static std::map<std::string, S16> m_DbgAllowed;
+	static std::map<std::string, std::string> m_PseudoDebug;
 
 	static bool findDebugSetting(/*[in,out]*/ std::string& strSetting, /*[out]*/ S16& flags);
 	static S16  getDebugSettingFlags(const std::string& strSetting);
diff --git a/linden/indra/newview/rlvhandler.cpp b/linden/indra/newview/rlvhandler.cpp
index 6044aa9..69e2e2f 100644
--- a/linden/indra/newview/rlvhandler.cpp
+++ b/linden/indra/newview/rlvhandler.cpp
@@ -286,7 +286,7 @@ bool RlvHandler::setDetachable(S32 idxAttachPt, const LLUUID& idRlvObj, bool fDe
 	if (!fDetachable)
 	{
 		#ifdef RLV_EXPERIMENTAL_FIRSTUSE
-			LLFirstUse::useRlvDetach();
+			//LLFirstUse::useRlvDetach();
 		#endif // RLV_EXPERIMENTAL_FIRSTUSE
 
 		// NOTE: m_Attachments can contain duplicate <idxAttachPt, idRlvObj> pairs (ie @detach:spine=n,detach=n from an attachment on spine)
@@ -338,27 +338,47 @@ bool RlvHandler::setDetachable(S32 idxAttachPt, const LLUUID& idRlvObj, bool fDe
 // Behaviour related functions
 //
 
-bool RlvHandler::hasBehaviourExcept(const std::string& strBehaviour, const LLUUID& idObj) const
-{
-	for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
-		if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(strBehaviour)) )
-			return true;
-	return false;
-}
-
 bool RlvHandler::hasBehaviourExcept(ERlvBehaviour eBehaviour, const std::string& strOption, const LLUUID& idObj) const
 {
 	for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
-		if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(eBehaviour, strOption)) )
+		if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(eBehaviour, strOption, false)) )
 			return true;
 	return false;
 }
 
-bool RlvHandler::hasBehaviourExcept(const std::string& strBehaviour, const std::string& strOption, const LLUUID& idObj) const
+// Checked: 2009-10-04 (RLVa-1.0.4c) | Modified: RLVa-1.0.4c
+bool RlvHandler::isException(ERlvBehaviour eBhvr, const RlvExceptionOption& varOption, ERlvExceptionCheck typeCheck) const
 {
-	for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
-		if ( (idObj != itObj->second.m_UUID) && (itObj->second.hasBehaviour(strBehaviour, strOption)) )
-			return true;
+	// We need to "strict check" exceptions only if: the restriction is actually in place *and* (isPermissive(eBhvr) == FALSE)
+	if (RLV_CHECK_DEFAULT == typeCheck)
+		typeCheck = ( (hasBehaviour(eBhvr)) && (!isPermissive(eBhvr)) ) ? RLV_CHECK_STRICT : RLV_CHECK_PERMISSIVE;
+
+	std::list<LLUUID> objList;
+	if (RLV_CHECK_STRICT == typeCheck)
+	{
+		// If we're "strict checking" then we need the UUID of every object that currently has 'eBhvr' restricted
+		for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
+			if (itObj->second.hasBehaviour(eBhvr, !hasBehaviour(RLV_BHVR_PERMISSIVE)))
+				objList.push_back(itObj->first);
+	}
+
+	for (rlv_exception_map_t::const_iterator itException = m_Exceptions.lower_bound(eBhvr), 
+			endException = m_Exceptions.upper_bound(eBhvr); itException != endException; ++itException)
+	{
+		if (itException->second.varOption == varOption)
+		{
+			// For permissive checks we just return on the very first match
+			if (RLV_CHECK_PERMISSIVE == typeCheck)
+				return true;
+
+			// For strict checks we don't return until the list is empty (every object with 'eBhvr' restricted also contains the exception)
+			std::list<LLUUID>::iterator itList = std::find(objList.begin(), objList.end(), itException->second.idObject);
+			if (itList != objList.end())
+				objList.erase(itList);
+			if (objList.empty())
+				return true;
+		}
+	}
 	return false;
 }
 
@@ -509,7 +529,11 @@ BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
 	const std::string& strOption = rlvCmd.getOption();
 
 	if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) )
+	{
+		if (rlvCmd.isStrict())
+			addException(uuid, RLV_BHVR_PERMISSIVE, eBehaviour);
 		m_Behaviours[eBehaviour]++;
+	}
 
 	switch (eBehaviour)
 	{
@@ -649,7 +673,7 @@ BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
 		case RLV_BHVR_FARTOUCH:
 			{
 				#ifdef RLV_EXPERIMENTAL_FIRSTUSE
-					LLFirstUse::useRlvFartouch();
+					//LLFirstUse::useRlvFartouch();
 				#endif // RLV_EXPERIMENTAL_FIRSTUSE
 			}
 			break;
@@ -695,9 +719,9 @@ BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
 		case RLV_BHVR_SHOWHOVERTEXT:		// @showhovertext:<uuid>=n	- Checked: 2009-07-09 (RLVa-0.2.2a) | Modified: RLVa-1.0.0f
 			{
 				LLUUID idException(strOption);
-				if (!idException.isNull())	// If there's an option it should be a valid UUID
+				if (idException.notNull())	// If there's an option it should be a valid UUID
 				{
-					addException(eBehaviour, LLUUID(strOption));
+					addException(uuid, eBehaviour, idException);
 
 					// Clear the object's hover text
 					LLViewerObject* pObj = gObjectList.findObject(idException);
@@ -717,6 +741,13 @@ BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
 				}
 			}
 			break;
+		case RLV_BHVR_SENDCHANNEL:			// @sendchannel:<uuid>=add	- Checked: 2009-10-05 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
+			{
+				S32 nChannel;	// If there's an option it should be a valid (=positive and non-zero) chat channel
+				if ( (!strOption.empty()) && (LLStringUtil::convertToS32(strOption, nChannel)) && (nChannel > 0) )
+					addException(uuid, eBehaviour, nChannel);
+			}
+			break;
 		case RLV_BHVR_RECVCHAT:				// @recvchat:<uuid>=add		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 		case RLV_BHVR_RECVEMOTE:			// @recvemote:<uuid>=add	- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 		case RLV_BHVR_RECVIM:				// @recvim:<uuid>=add		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
@@ -724,16 +755,20 @@ BOOL RlvHandler::processAddCommand(const LLUUID& uuid, const RlvCommand& rlvCmd)
 		case RLV_BHVR_TPLURE:				// @tplure:<uuid>=add		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 		case RLV_BHVR_ACCEPTTP:				// @accepttp:<uuid>=add		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 			{
-				addException(eBehaviour, LLUUID(strOption));
+				LLUUID idException(strOption);
+				if (idException.notNull())	// If there's an option it should be a valid UUID
+					addException(uuid, eBehaviour, LLUUID(strOption));
 			}
 			break;
-		default:
+		case RLV_BHVR_UNKNOWN:
 			{
 				// Give our observers a chance to handle any command we don't
 				RlvEvent rlvEvent(uuid, rlvCmd);
 				m_Emitter.update(&RlvObserver::onAddCommand, rlvEvent);
 			}
 			break;
+		default:
+			break;
 	}
 	return TRUE; // Add command success/failure is decided by RlvObject::addCommand()
 }
@@ -756,7 +791,11 @@ BOOL RlvHandler::processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvC
 	const std::string& strOption = rlvCmd.getOption();
 
 	if ( (RLV_BHVR_UNKNOWN != eBehaviour) && (strOption.empty()) )
+	{
+		if (rlvCmd.isStrict())
+			removeException(uuid, RLV_BHVR_PERMISSIVE, eBehaviour);
 		m_Behaviours[eBehaviour]--;
+	}
 
 	switch (eBehaviour)
 	{
@@ -842,9 +881,9 @@ BOOL RlvHandler::processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvC
 		case RLV_BHVR_SHOWHOVERTEXT:		// @showhovertext:<uuid>=y	- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 			{
 				LLUUID idException(strOption);
-				if (!idException.isNull())	// If there's an option it should be a valid UUID
+				if (idException.notNull())	// If there's an option it should be a valid UUID
 				{
-					removeException(eBehaviour, LLUUID(strOption));
+					removeException(uuid, eBehaviour, idException);
 
 					// Restore the object's hover text
 					LLViewerObject* pObj = gObjectList.findObject(idException);
@@ -869,6 +908,13 @@ BOOL RlvHandler::processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvC
 				}
 			}
 			break;
+		case RLV_BHVR_SENDCHANNEL:			// @sendchannel:<uuid>=rem	- Checked: 2009-10-05 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
+			{
+				S32 nChannel;	// If there's an option it should be a valid (=positive and non-zero) chat channel
+				if ( (!strOption.empty()) && (LLStringUtil::convertToS32(strOption, nChannel)) && (nChannel > 0) )
+					removeException(uuid, eBehaviour, nChannel);
+			}
+			break;
 		case RLV_BHVR_RECVCHAT:				// @recvchat:<uuid>=rem		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 		case RLV_BHVR_RECVEMOTE:			// @recvemote:<uui>=red		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 		case RLV_BHVR_RECVIM:				// @recvim:<uuid>=rem		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
@@ -876,16 +922,20 @@ BOOL RlvHandler::processRemoveCommand(const LLUUID& uuid, const RlvCommand& rlvC
 		case RLV_BHVR_TPLURE:				// @recvim:<uuid>=rem		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 		case RLV_BHVR_ACCEPTTP:				// @accepttp:<uuid>=rem		- Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
 			{
-				removeException(eBehaviour, LLUUID(strOption));
+				LLUUID idException(strOption);
+				if (idException.notNull())	// If there's an option it should be a valid UUID
+					removeException(uuid, eBehaviour, LLUUID(strOption));
 			}
 			break;
-		default:
+		case RLV_BHVR_UNKNOWN:
 			{
 				// Give our observers a chance to handle any command we don't
 				RlvEvent rlvEvent(uuid, rlvCmd);
 				m_Emitter.update(&RlvObserver::onRemoveCommand, rlvEvent);
 			}
 			break;
+		default:
+			break;
 	}
 	return TRUE; // Remove commands don't fail, doesn't matter what we return here
 }
@@ -1006,13 +1056,15 @@ BOOL RlvHandler::processForceCommand(const LLUUID& idObj, const RlvCommand& rlvC
 				}
 			}
 			break;
-		default:
+		case RLV_BHVR_UNKNOWN:
 			{
 				// Give our observers a chance to handle any command we don't
 				RlvEvent rlvEvent(idObj, rlvCmd);
 				fHandled = m_Emitter.update(&RlvObserver::onForceCommand, rlvEvent);
 			}
 			break;
+		default:
+			break;
 	}
 	return fHandled; // If we handled it then it'll still be TRUE; if an observer doesn't handle it'll be FALSE
 }
@@ -1030,6 +1082,9 @@ BOOL RlvHandler::processReplyCommand(const LLUUID& uuid, const RlvCommand& rlvCm
 		case RLV_BHVR_VERSION:			// @version=<channel>				  - Checked: 2009-07-12 (RLVa-1.0.0h)
 			strReply = getVersionString();
 			break;
+		case RLV_BHVR_VERSIONNUM:		// @versionnum=<channel>			  - Checked: 2009-10-04 (RLVa-1.0.4b) | Added: RLVa-1.0.4b
+			strReply = getVersionNumString();
+			break;
 		case RLV_BHVR_GETOUTFIT:		// @getoufit[:<layer>]=<channel>	  - Checked: 2009-07-12 (RLVa-1.0.0h) | Modified: RLVa-0.2.0d
 			{
 				// (Quirk: RLV 1.16.1 will execute @getoutfit=<channel> if <layer> is invalid, so we need to as well)
@@ -1225,13 +1280,15 @@ BOOL RlvHandler::processReplyCommand(const LLUUID& uuid, const RlvCommand& rlvCm
 				strReply = uuid.asString();
 			}
 			break;
-		default:
+		case RLV_BHVR_UNKNOWN:
 			{
 				// Give our observers a chance to handle any command we don't
 				RlvEvent rlvEvent(uuid, rlvCmd);
 				return m_Emitter.update(&RlvObserver::onReplyCommand, rlvEvent);
 			}
 			break;
+		default:
+			break;
 	}
 
 	if (fHandled)
@@ -1318,7 +1375,7 @@ void RlvHandler::onAttach(LLViewerJointAttachment* pAttachPt, bool fFullyLoaded)
 			itObj->second.m_fLookup = true;
 
 		// In both cases we should check for "@detach=n" and actually lock down the attachment point it got attached to
-		if (itObj->second.hasBehaviour(RLV_BHVR_DETACH))
+		if (itObj->second.hasBehaviour(RLV_BHVR_DETACH, false))
 		{
 			// (Copy/paste from processAddCommand)
 			setDetachable(idxAttachPt, pObj->getID(), false);
@@ -1569,7 +1626,7 @@ void RlvHandler::filterChat(std::string& strUTF8Text, bool fFilterEmote) const
 			{
 				strUTF8Text = "...";				// Emote contains illegal character (or character sequence)
 			}
-			else if (!hasBehaviour("emote"))
+			else if (!hasBehaviour(RLV_BHVR_EMOTE))
 			{
 				int idx = strUTF8Text.find('.');	// Truncate at 20 characters or at the dot (whichever is shorter)
 				strUTF8Text = utf8str_chtruncate(strUTF8Text, ( (idx > 0) && (idx < 20) ) ? idx + 1 : 20);
@@ -1674,17 +1731,18 @@ bool RlvHandler::redirectChatOrEmote(const std::string& strUTF8Text) const
 			return false;	// @sendchat wouldn't filter it so @redirchat won't redirect it either
 	}
 
-	bool fSendChannel = hasBehaviour("sendchannel");
+	bool fSendChannel = hasBehaviour(RLV_BHVR_SENDCHANNEL); S32 nChannel = 0;
 	for (rlv_object_map_t::const_iterator itObj = m_Objects.begin(); itObj != m_Objects.end(); ++itObj)
 	{
 		for (rlv_command_list_t::const_iterator itCmd = itObj->second.m_Commands.begin(), 
 				endCmd = itObj->second.m_Commands.end(); itCmd != endCmd; ++itCmd)
 		{
-			if ( ( ((!fIsEmote) && (RLV_BHVR_REDIRCHAT == itCmd->getBehaviourType())) ||
-				   ((fIsEmote) && (RLV_BHVR_REDIREMOTE == itCmd->getBehaviourType())) ) && 
-				 ( (!fSendChannel) || (hasBehaviour("sendchannel", itCmd->getOption())) ) )
+			if ( ( ((!fIsEmote) && (RLV_BHVR_REDIRCHAT == itCmd->getBehaviourType())) ||	// Redirect if: (not an emote and @redirchat
+				   ((fIsEmote) && (RLV_BHVR_REDIREMOTE == itCmd->getBehaviourType())) ) &&	// OR an emote and @rediremote)
+				 (LLStringUtil::convertToS32(itCmd->getOption(), nChannel)) &&				// AND the channel is a number
+				 ( (!fSendChannel) || (isException(RLV_BHVR_SENDCHANNEL, nChannel)) ) )		// AND we're allowed to send to that channel
 			{
-				rlvSendChatReply(itCmd->getOption(), strUTF8Text);
+				rlvSendChatReply(nChannel, strUTF8Text);
 			}
 		}
 	}
diff --git a/linden/indra/newview/rlvhandler.h b/linden/indra/newview/rlvhandler.h
index 4470632..5295a72 100644
--- a/linden/indra/newview/rlvhandler.h
+++ b/linden/indra/newview/rlvhandler.h
@@ -18,7 +18,7 @@
 typedef std::map<LLUUID, RlvObject> rlv_object_map_t;
 typedef std::multimap<S32, LLUUID> rlv_detach_map_t;
 typedef std::map<S32, LLUUID> rlv_reattach_map_t;
-typedef std::multimap<LLUUID, ERlvBehaviour> rlv_exception_map_t;
+typedef std::multimap<ERlvBehaviour, RlvException> rlv_exception_map_t;
 
 class RlvHandler
 {
@@ -29,7 +29,7 @@ public:
 	// --------------------------------
 
 	/*
-	 * Rule checking functions
+	 * Attachment point helper functions
 	 */
 public:
 	// Returns a pointer to the attachment point for a supplied parameter
@@ -42,45 +42,45 @@ public:
 	S32                      getAttachPointIndex(const LLViewerJointAttachment* pObj) const;
 	bool                     hasAttachPointName(const LLInventoryItem* pItem, bool fStrict) const;
 
-	// Returns TRUE is at least one object contains the specified behaviour (and optional parameter)
+	// --------------------------------
+
+	/*
+	 * Rule checking functions
+	 */
 	// NOTE: - to check @detach=n    -> hasLockedAttachment() / hasLockedHUD() / isDetachable()
+	//       - to check exceptions   -> isException()
 	//       - to check @addoutfit=n -> isWearable()
 	//       - to check @remoutfit=n -> isRemovable()
-	//       - to check exceptions   -> isException()
-	//  (You *can* use hasBehaviour(); the specialized ones just don't have to iterate over all the objects)
+public:
+	// Returns TRUE is at least one object contains the specified behaviour (and optional option)
 	bool hasBehaviour(ERlvBehaviour eBehaviour) const { return (eBehaviour < RLV_BHVR_COUNT) ? (0 != m_Behaviours[eBehaviour]) : false; }
-	bool hasBehaviour(const std::string& strBehaviour) const;
 	bool hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption) const;
-	bool hasBehaviour(const std::string& strBehaviour, const std::string& strOption) const;
-
-	// Returns TRUE if at least one object (except the specified one) contains the specified behaviour
+	// Returns TRUE if at least one object (except the specified one) contains the specified behaviour (and optional option)
 	bool hasBehaviourExcept(ERlvBehaviour eBehaviour, const LLUUID& idObj) const;
-	bool hasBehaviourExcept(const std::string& strBehaviour, const LLUUID& uuid) const;
 	bool hasBehaviourExcept(ERlvBehaviour eBehaviour, const std::string& strOption, const LLUUID& idObj) const;
-	bool hasBehaviourExcept(const std::string& strBehaviour, const std::string& strOption, const LLUUID& idObj) const;
 
-	// Returns TRUE if there is at least 1 undetachable attachment
+	// Returns TRUE if there is at least 1 non-detachable attachment
 	bool hasLockedAttachment() const { return (0 != m_Attachments.size()); }
-	// Returns TRUE if there is at least 1 undetachable HUD attachment
+	// Returns TRUE if there is at least 1 non-detachable HUD attachment
 	bool hasLockedHUD() const;
-
 	// Returns TRUE if the specified attachment point is detachable
 	bool isDetachable(S32 idxAttachPt) const { return (idxAttachPt) && (m_Attachments.find(idxAttachPt) == m_Attachments.end()); }
 	bool isDetachable(const LLInventoryItem* pItem) const;
 	bool isDetachable(LLViewerJointAttachment* pAttachPt) const;
 	bool isDetachable(LLViewerObject* pObj) const;
-	// Returns TRUE if the specified attachment point is set undetachable by anything other than pObj (or one of its children)
+	// Returns TRUE if the specified attachment point is set non-detachable by anything other than pObj (or one of its children)
 	bool isDetachableExcept(S32 idxAttachPt, LLViewerObject* pObj) const;
-	// Marks the specified attachment point as (un)detachable (return value indicates success ; used by unit tests)
+	// Marks the specified attachment point as (non-)detachable (return value indicates success ; used by unit tests)
 	bool setDetachable(S32 idxAttachPt, const LLUUID& idRlvObj, bool fDetachable);
 	bool setDetachable(LLViewerObject* pObj, const LLUUID& idRlvObj, bool fDetachable);
 
-	// Adds or removes an exception for the specified restriction
-	void addException(ERlvBehaviour eBehaviour, const LLUUID& uuid);
-	void removeException(ERlvBehaviour eBehaviour, const LLUUID& uuid);
-	// Returns TRUE is the specified UUID is exempt from a restriction (tplure/sendim/recvim/etc)
-	bool isException(ERlvBehaviour eBehaviour, const LLUUID& uuid) const;
-	bool isException(const std::string& strBehaviour, const LLUUID& uuid) const;
+	// Adds or removes an exception for the specified behaviour
+	void addException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption);
+	void removeException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption);
+	// Returns TRUE if the specified option was added as an exception for the specified behaviour
+	bool isException(ERlvBehaviour eBhvr, const RlvExceptionOption& varOption, ERlvExceptionCheck typeCheck = RLV_CHECK_DEFAULT) const;
+	// Returns TRUE if the specified behaviour should behave "permissive" (rather than "strict"/"secure")
+	bool isPermissive(ERlvBehaviour eBhvr) const;
 
 	// Returns TRUE if the specified layer is removable (use hasBehaviour(RLV_BHVR_REMOUTFIT) for the general case)
 	bool isRemovable(EWearableType type) const	{ return (type < WT_COUNT) ? (0 == m_LayersRem[type]) : true; }
@@ -121,6 +121,7 @@ public:
 	void               filterNames(std::string& strUTF8Text) const;						// @shownames
 	const std::string& getAnonym(const std::string& strName) const;						// @shownames
 	std::string        getVersionString() const;										// @version
+	std::string        getVersionNumString() const;										// @versionnum
 	BOOL               isAgentNearby(const LLUUID& uuid) const;							// @shownames
 	bool               redirectChatOrEmote(const std::string& strUTF8Test) const;		// @redirchat and @rediremote
 
@@ -274,11 +275,10 @@ extern rlv_handler_t gRlvHandler;
 // Inlined member functions
 //
 
-// Checked: 2009-07-09 (RLVa-1.0.0f)
-inline void RlvHandler::addException(ERlvBehaviour eBehaviour, const LLUUID& uuid)
+// Checked: 2009-10-04 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
+inline void RlvHandler::addException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption)
 {
-	if (!uuid.isNull())
-		m_Exceptions.insert(std::pair<LLUUID, ERlvBehaviour>(uuid, eBehaviour));
+	m_Exceptions.insert(std::pair<ERlvBehaviour, RlvException>(eBhvr, RlvException(idObj, eBhvr, varOption)));
 }
 
 // Checked: 2009-07-09 (RLVa-1.0.0f) | Modified: RLVa-1.0.0f
@@ -288,7 +288,7 @@ inline bool RlvHandler::canShowHoverText(LLViewerObject *pObj) const
 		    !( (hasBehaviour(RLV_BHVR_SHOWHOVERTEXTALL)) ||
 			   ( (hasBehaviour(RLV_BHVR_SHOWHOVERTEXTWORLD)) && (!pObj->isHUDAttachment()) ) ||
 			   ( (hasBehaviour(RLV_BHVR_SHOWHOVERTEXTHUD)) && (pObj->isHUDAttachment()) ) ||
-			   (isException(RLV_BHVR_SHOWHOVERTEXT, pObj->getID())) ) );
+			   (isException(RLV_BHVR_SHOWHOVERTEXT, pObj->getID(), RLV_CHECK_PERMISSIVE)) ) );
 }
 
 // Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
@@ -321,6 +321,12 @@ inline std::string RlvHandler::getVersionString()  const
 		RLVa_VERSION_MAJOR, RLVa_VERSION_MINOR, RLVa_VERSION_PATCH);
 }
 
+// Checked: 2009-10-04 (RLVa-1.0.4b) | Added: RLVa-1.0.4b
+inline std::string RlvHandler::getVersionNumString()  const
+{
+	return llformat("%d%02d%02d%02d", RLV_VERSION_MAJOR, RLV_VERSION_MINOR, RLV_VERSION_PATCH, RLV_VERSION_BUILD);
+}
+
 // Checked: 2009-05-23 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
 inline bool RlvHandler::hasAttachPointName(const LLInventoryItem *pItem, bool fStrict) const
 {
@@ -334,18 +340,6 @@ inline bool RlvHandler::hasBehaviour(ERlvBehaviour eBehaviour, const std::string
 }
 
 // Checked:
-inline bool RlvHandler::hasBehaviour(const std::string& strBehaviour) const
-{
-	return hasBehaviourExcept(strBehaviour, LLUUID::null);
-}
-
-// Checked:
-inline bool RlvHandler::hasBehaviour(const std::string& strBehaviour, const std::string& strOption) const
-{
-	return hasBehaviourExcept(strBehaviour, strOption, LLUUID::null);
-}
-
-// Checked:
 inline bool RlvHandler::hasBehaviourExcept(ERlvBehaviour eBehaviour, const LLUUID& idObj) const
 {
 	return hasBehaviourExcept(eBehaviour, std::string(), idObj);
@@ -379,22 +373,11 @@ inline bool RlvHandler::isDetachable(LLViewerObject* pObj) const
 	return (pObj == NULL) || (!pObj->isAttachment()) || (isDetachable(getAttachPointIndex(pObj)));
 }
 
-// Checked:
-inline bool RlvHandler::isException(ERlvBehaviour eBehaviour, const LLUUID& uuid) const
-{
-	for (rlv_exception_map_t::const_iterator itException = m_Exceptions.lower_bound(uuid), 
-			endException = m_Exceptions.upper_bound(uuid); itException != endException; ++itException)
-	{
-		if (itException->second == eBehaviour)
-			return true;
-	}
-	return false;
-}
-
-// Checked:
-inline bool RlvHandler::isException(const std::string& strBehaviour, const LLUUID& uuid) const
+inline bool RlvHandler::isPermissive(ERlvBehaviour eBhvr) const
 {
-	return hasBehaviour(strBehaviour, uuid.asString());
+	return (RlvCommand::hasStrictVariant(eBhvr)) 
+		? !((hasBehaviour(RLV_BHVR_PERMISSIVE)) || (isException(RLV_BHVR_PERMISSIVE, eBhvr, RLV_CHECK_PERMISSIVE)))
+		: true;
 }
 
 // Checked: 2009-07-29 (RLVa-1.0.1b) | Added: RLVa-1.0.1b
@@ -428,19 +411,16 @@ inline bool RlvHandler::isRemovableExcept(EWearableType type, const LLUUID& idOb
 	}
 #endif // RLV_EXTENSION_FLAG_NOSTRIP
 
-// Checked: 2009-07-09 (RLVa-1.0.0f)
-inline void RlvHandler::removeException(ERlvBehaviour eBehaviour, const LLUUID &uuid)
+// Checked: 2009-10-04 (RLVa-1.0.4a) | Modified: RLVa-1.0.4a
+inline void RlvHandler::removeException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& varOption)
 {
-	if (!uuid.isNull())
+	for (rlv_exception_map_t::iterator itException = m_Exceptions.lower_bound(eBhvr), 
+			endException = m_Exceptions.upper_bound(eBhvr); itException != endException; ++itException)
 	{
-		for (rlv_exception_map_t::iterator itException = m_Exceptions.lower_bound(uuid), 
-				endException = m_Exceptions.upper_bound(uuid); itException != endException; ++itException)
+		if ( (itException->second.idObject == idObj) && (itException->second.varOption == varOption) )
 		{
-			if (itException->second == eBehaviour)
-			{
-				m_Exceptions.erase(itException);
-				break;
-			}
+			m_Exceptions.erase(itException);
+			break;
 		}
 	}
 }
diff --git a/linden/indra/newview/rlvhelper.cpp b/linden/indra/newview/rlvhelper.cpp
index f495384..c8d430d 100644
--- a/linden/indra/newview/rlvhelper.cpp
+++ b/linden/indra/newview/rlvhelper.cpp
@@ -23,7 +23,7 @@ RlvCommand::RlvBhvrTable RlvCommand::m_BhvrMap;
 
 // Checked: 2009-09-10 (RLVa-1.0.3a) | Modified: RLVa-1.0.3a
 RlvCommand::RlvCommand(const std::string& strCommand)
-	: m_eBehaviour(RLV_BHVR_UNKNOWN), m_eParamType(RLV_TYPE_UNKNOWN)
+	: m_eBehaviour(RLV_BHVR_UNKNOWN), m_fStrict(false), m_eParamType(RLV_TYPE_UNKNOWN)
 {
 	if ((m_fValid = parseCommand(strCommand, m_strBehaviour, m_strOption, m_strParam)))
 	{
@@ -51,8 +51,12 @@ RlvCommand::RlvCommand(const std::string& strCommand)
 		return;
 	}
 
-	RlvBhvrTable::const_iterator itBhvr = m_BhvrMap.find(m_strBehaviour);
-	if (itBhvr != m_BhvrMap.end())
+	// Check if this is the "strict" (aka "secure") variation of a behaviour
+	std::string::size_type idxStrict = m_strBehaviour.find("_sec");
+	m_fStrict = (std::string::npos != idxStrict) && (idxStrict + 4 == m_strBehaviour.length());
+
+	RlvBhvrTable::const_iterator itBhvr = m_BhvrMap.find( (!m_fStrict) ? m_strBehaviour : m_strBehaviour.substr(0, idxStrict));
+	if ( (itBhvr != m_BhvrMap.end()) && ((!m_fStrict) || (hasStrictVariant(itBhvr->second))) )
 		m_eBehaviour = itBhvr->second;
 }
 
@@ -104,8 +108,8 @@ void RlvCommand::initLookupTable()
 				"remoutfit", "getoutfit", "getattach", "showinv", "viewnote", "unsit", "sit", "sendchannel", "getstatus", "getstatusall",
 				"getinv", "getinvworn", "findfolder", "findfolders", "attach", "attachall", "detachall", "getpath", "attachthis",
 				"attachallthis", "detachthis", "detachallthis", "fartouch", "showworldmap", "showminimap", "showloc", "tpto", "accepttp",
-				"shownames", "fly", "getsitid", "setdebug", "setenv", "detachme", "showhovertextall", "showhovertextworld",
-				"showhovertexthud", "showhovertext", "notify"
+				"acceptpermission", "shownames", "fly", "getsitid", "setdebug", "setenv", "detachme", "showhovertextall", 
+				"showhovertextworld", "showhovertexthud", "showhovertext", "notify", "defaultwear", "versionnum", "permissive"
 			};
 
 		for (int idxBvhr = 0; idxBvhr < RLV_BHVR_COUNT; idxBvhr++)
@@ -133,12 +137,14 @@ bool RlvObject::addCommand(const RlvCommand& rlvCmd)
 		return false;
 
 	// Don't add duplicate commands for this object (ie @detach=n followed by another @detach=n later on)
-	bool fDuplicate =
-		(RLV_BHVR_UNKNOWN != rlvCmd.getBehaviourType())
-			? hasBehaviour(rlvCmd.getBehaviourType(), rlvCmd.getOption())
-			: hasBehaviour(rlvCmd.getBehaviour(), rlvCmd.getOption());
-	if (fDuplicate)
-		return false;
+	for (rlv_command_list_t::iterator itCmd = m_Commands.begin(); itCmd != m_Commands.end(); ++itCmd)
+	{
+		if ( (itCmd->getBehaviour() == rlvCmd.getBehaviour()) && (itCmd->getOption() == rlvCmd.getOption()) && 
+			 (itCmd->isStrict() == rlvCmd.isStrict() ) )
+		{
+			return false;
+		}
+	}
 
 	// Now that we know it's not a duplicate, add it to the end of the list
 	m_Commands.push_back(rlvCmd);
@@ -155,7 +161,8 @@ bool RlvObject::removeCommand(const RlvCommand& rlvCmd)
 	for (rlv_command_list_t::iterator itCmd = m_Commands.begin(); itCmd != m_Commands.end(); ++itCmd)
 	{
 		//if (*itCmd == rlvCmd) <- commands will never be equal since one is an add and the other is a remove *rolls eyes*
-		if ( (itCmd->getBehaviour() == rlvCmd.getBehaviour()) && (itCmd->getOption() == rlvCmd.getOption()) )
+		if ( (itCmd->getBehaviour() == rlvCmd.getBehaviour()) && (itCmd->getOption() == rlvCmd.getOption()) && 
+			 (itCmd->isStrict() == rlvCmd.isStrict() ) )
 		{
 			m_Commands.erase(itCmd);
 			return true;
@@ -164,34 +171,18 @@ bool RlvObject::removeCommand(const RlvCommand& rlvCmd)
 	return false;	// Command was never added so nothing to remove now
 }
 
-bool RlvObject::hasBehaviour(ERlvBehaviour eBehaviour) const
-{
-	for (rlv_command_list_t::const_iterator itCmd = m_Commands.begin(); itCmd != m_Commands.end(); ++itCmd)
-		if ( (itCmd->getBehaviourType() == eBehaviour) && (itCmd->getOption().empty()) )
-			return true;
-	return false;
-}
-
-bool RlvObject::hasBehaviour(const std::string& strBehaviour) const
-{
-	for (rlv_command_list_t::const_iterator itCmd = m_Commands.begin(); itCmd != m_Commands.end(); ++itCmd)
-		if ( (itCmd->getBehaviour() == strBehaviour) && (itCmd->getOption().empty()) )
-			return true;
-	return false;
-}
-
-bool RlvObject::hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption) const
+bool RlvObject::hasBehaviour(ERlvBehaviour eBehaviour, bool fStrictOnly) const
 {
 	for (rlv_command_list_t::const_iterator itCmd = m_Commands.begin(); itCmd != m_Commands.end(); ++itCmd)
-		if ( (itCmd->getBehaviourType() == eBehaviour) && (itCmd->getOption() == strOption) )
+		if ( (itCmd->getBehaviourType() == eBehaviour) && (itCmd->getOption().empty()) && ((!fStrictOnly) || (itCmd->isStrict())) )
 			return true;
 	return false;
 }
 
-bool RlvObject::hasBehaviour(const std::string& strBehaviour, const std::string& strOption) const
+bool RlvObject::hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption, bool fStrictOnly) const
 {
 	for (rlv_command_list_t::const_iterator itCmd = m_Commands.begin(); itCmd != m_Commands.end(); ++itCmd)
-		if ( (itCmd->getBehaviour() == strBehaviour) && (itCmd->getOption() == strOption) )
+		if ( (itCmd->getBehaviourType() == eBehaviour) && (itCmd->getOption() == strOption) && ((!fStrictOnly) || (itCmd->isStrict())) )
 			return true;
 	return false;
 }
@@ -361,6 +352,11 @@ RlvWLSnapshot* RlvWLSnapshot::takeSnapshot()
 
 BOOL RlvSettings::fShowNameTags = FALSE;
 
+BOOL RlvSettings::getEnableWear()
+{
+	return rlvGetSettingBOOL(RLV_SETTING_ENABLEWEAR, TRUE) && (!gRlvHandler.hasBehaviour(RLV_BHVR_DEFAULTWEAR));
+}
+
 #ifdef RLV_EXTENSION_STARTLOCATION
 	// Checked: 2009-07-08 (RLVa-1.0.0e) | Modified: RLVa-0.2.1d
 	void RlvSettings::updateLoginLastLocation()
@@ -475,6 +471,13 @@ bool rlvCanDeleteOrReturn()
 	return fIsAllowed;
 }
 
+// Checked: 2009-10-04 (RLVa-1.0.4b) | Modified: RLVa-1.0.4b
+BOOL rlvEnableWearEnabler(void* pParam)
+{
+	// Visually disables the "Enable Wear" option when restricted from toggling it
+	return (!gRlvHandler.hasBehaviour(RLV_BHVR_DEFAULTWEAR));
+}
+
 // Checked: 2009-05-26 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
 S32 rlvGetDirectDescendentsCount(const LLInventoryCategory* pFolder, LLAssetType::EType type)
 {
diff --git a/linden/indra/newview/rlvhelper.h b/linden/indra/newview/rlvhelper.h
index f36989b..42d10b8 100644
--- a/linden/indra/newview/rlvhelper.h
+++ b/linden/indra/newview/rlvhelper.h
@@ -9,6 +9,15 @@
 #include "llwlparamset.h"
 #include "rlvdefines.h"
 
+#ifdef LL_WINDOWS
+	#pragma warning (push)
+	#pragma warning (disable : 4702) // warning C4702: unreachable code
+#endif
+#include <boost/variant.hpp>
+#ifdef LL_WINDOWS
+	#pragma warning (pop)
+#endif
+
 // ============================================================================
 // RlvCommand
 //
@@ -28,10 +37,12 @@ public:
 	const std::string& getOption() const        { return m_strOption; }
 	const std::string& getParam() const         { return m_strParam; }
 	ERlvParamType      getParamType() const     { return m_eParamType; }
+	bool               isStrict() const			{ return m_fStrict; }
 	bool               isValid() const          { return m_fValid; }
 
 	static ERlvBehaviour      getBehaviourFromString(const std::string& strBhvr);
 	static const std::string& getStringFromBehaviour(ERlvBehaviour eBhvr);
+	static bool               hasStrictVariant(ERlvBehaviour eBhvr);
 
 	static void initLookupTable();
 protected:
@@ -50,6 +61,7 @@ protected:
 	bool         	m_fValid;
 	std::string  	m_strBehaviour;
 	ERlvBehaviour	m_eBehaviour;
+	bool            m_fStrict;
 	std::string  	m_strOption;
 	std::string  	m_strParam;
 	ERlvParamType	m_eParamType;
@@ -78,10 +90,8 @@ public:
 	bool removeCommand(const RlvCommand& rlvCmd);
 
 	std::string getStatusString(const std::string& strMatch) const;
-	bool        hasBehaviour(ERlvBehaviour eBehaviour) const;
-	bool        hasBehaviour(const std::string& strBehaviour) const;
-	bool        hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption) const;
-	bool        hasBehaviour(const std::string& strBehaviour, const std::string& strOption) const;
+	bool        hasBehaviour(ERlvBehaviour eBehaviour, bool fStrictOnly) const;
+	bool        hasBehaviour(ERlvBehaviour eBehaviour, const std::string& strOption, bool fStrictOnly) const;
 
 	const rlv_command_list_t* getCommandList() const { return &m_Commands; }
 
@@ -190,6 +200,24 @@ private:
 typedef std::list<RlvRetainedCommand> rlv_retained_list_t;
 
 // ============================================================================
+// RlvException
+//
+
+typedef boost::variant<std::string, LLUUID, S32, ERlvBehaviour> RlvExceptionOption;
+
+struct RlvException
+{
+public:
+	LLUUID				idObject;    // UUID of the object that added the exception
+	ERlvBehaviour		eBehaviour;  // Behaviour the exception applies to
+	RlvExceptionOption	varOption;   // Exception data (type is dependent on eBehaviour)
+
+	RlvException(const LLUUID& idObj, ERlvBehaviour eBhvr, const RlvExceptionOption& option) : idObject(idObj), eBehaviour(eBhvr), varOption(option) {}
+private:
+	RlvException();
+};
+
+// ============================================================================
 // RlvWLSnapshot
 //
 
@@ -225,7 +253,7 @@ public:
 	static BOOL getDebug()					{ return rlvGetSettingBOOL(RLV_SETTING_DEBUG, FALSE); }
 	static BOOL getForbidGiveToRLV()		{ return rlvGetSettingBOOL(RLV_SETTING_FORBIDGIVETORLV, TRUE); }
 
-	static BOOL getEnableWear()				{ return rlvGetSettingBOOL(RLV_SETTING_ENABLEWEAR, FALSE); }
+	static BOOL getEnableWear();
 	static BOOL getHideLockedLayers()		{ return rlvGetSettingBOOL(RLV_SETTING_HIDELOCKEDLAYER, FALSE); }		
 	static BOOL getHideLockedAttach()		{ return rlvGetSettingBOOL(RLV_SETTING_HIDELOCKEDATTACH, FALSE); }
 	static BOOL getHideLockedInventory()	{ return rlvGetSettingBOOL(RLV_SETTING_HIDELOCKEDINVENTORY, FALSE); }
@@ -290,6 +318,7 @@ struct RlvSelectIsSittingOn : public LLSelectedNodeFunctor
 
 BOOL rlvAttachToEnabler(void* pParam);
 bool rlvCanDeleteOrReturn();
+BOOL rlvEnableWearEnabler(void* pParam);
 S32  rlvGetDirectDescendentsCount(const LLInventoryCategory* pFolder, LLAssetType::EType type);
 bool rlvIsEmote(const std::string& strUTF8Text);
 bool rlvIsValidReplyChannel(S32 nChannel);
@@ -344,6 +373,22 @@ inline void RlvCurrentlyWorn::fetchItem(const LLUUID& idItem)
 	}
 }
 
+inline bool RlvCommand::hasStrictVariant(ERlvBehaviour eBhvr)
+{
+	switch (eBhvr)
+	{
+		case RLV_BHVR_RECVCHAT:
+		case RLV_BHVR_RECVEMOTE:
+		case RLV_BHVR_RECVIM:
+		case RLV_BHVR_SENDIM:
+		case RLV_BHVR_TPLURE:
+		case RLV_BHVR_SENDCHANNEL:
+			return true;
+		default:
+			return false;
+	}
+}
+
 // ============================================================================
 // Inlined helper functions
 //
-- 
cgit v1.1