diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp
index 26b36ac23d64f298fa7c52d9fa44bd403c56318c..0715f488629c8ceb941f45b8d9b0d16fb2f5fe26 100644
--- a/indra/newview/rlvhandler.cpp
+++ b/indra/newview/rlvhandler.cpp
@@ -65,191 +65,6 @@ bool RlvCommandOptionParser<int>::parseOption(const std::string& strOption, int&
 	return LLStringUtil::convertToS32(strOption, nOption);
 }
 
-// ============================================================================
-// Command processing template specialization implmentation
-//
-
-ERlvCmdRet RlvBehaviourProcessorHelper::processBehaviourImpl(const RlvCommand& rlvCmd, RlvBhvrHandler* pHandlerFunc)
-{
-	bool fRefCount = false;
-	ERlvCmdRet eRet = (*pHandlerFunc)(rlvCmd, fRefCount);
-
-	// If this command represents a restriction that's been added/removed then we need to do some additional processing
-	if ( (RLV_RET_SUCCESS == eRet) && (fRefCount) )
-	{
-		ERlvBehaviour eBhvr = rlvCmd.getBehaviourType();
-		S16& refBhvr = gRlvHandler.m_Behaviours[eBhvr];
-		if (RLV_TYPE_ADD == rlvCmd.getParamType())
-		{
-			if (rlvCmd.isStrict())
-				gRlvHandler.addException(rlvCmd.getObjectID(), RLV_BHVR_PERMISSIVE, eBhvr);
-			refBhvr++;
-		}
-		else
-		{
-			if (rlvCmd.isStrict())
-				gRlvHandler.removeException(rlvCmd.getObjectID(), RLV_BHVR_PERMISSIVE, eBhvr);
-			refBhvr--;
-		}
-	}
-
-	return eRet;
-}
-
-// Handles: @bhvr=n|y
-ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_NONE>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
-{
-	// There should be no option
-	if (rlvCmd.hasOption())
-		return RLV_RET_FAILED_OPTION;
-
-	fRefCount = true;
-	return RLV_RET_SUCCESS;
-}
-
-// Handles: @bhvr:<uuid>=n|y
-ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_EXCEPTION>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
-{
-	// There should be an option and it should specify a valid UUID
-	LLUUID idException;
-	if (!RlvCommandOptionParser<LLUUID>::parseOption(rlvCmd.getOption(), idException))
-		return RLV_RET_FAILED_OPTION;
-
-	if (RLV_TYPE_ADD == rlvCmd.getParamType())
-		gRlvHandler.addException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
-	else
-		gRlvHandler.removeException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
-
-	fRefCount = true;
-	return RLV_RET_SUCCESS;
-}
-
-// Handles: @bhvr[:<uuid>]=n|y
-ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_NONE_OR_EXCEPTION>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
-{
-	// If there is an option then it should specify a valid UUID (but don't reference count)
-	if (rlvCmd.hasOption())
-	{
-		ERlvCmdRet eRet = RlvBehaviourGenericHandler<RLV_OPTION_EXCEPTION>::onCommand(rlvCmd, fRefCount);
-		fRefCount = false;
-		return eRet;
-	}
-	return RlvBehaviourGenericHandler<RLV_OPTION_NONE>::onCommand(rlvCmd, fRefCount);
-}
-
-// Handles: @sendim=n|y toggles
-void RlvBehaviourToggleHandler<RLV_BHVR_SENDIM>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr)
-{
-	gSavedPerAccountSettings.getControl("DoNotDisturbModeResponse")->setHiddenFromSettingsEditor(fHasBhvr);
-}
-
-
-// Handles: @sendchannel[:<channel>]=n|y
-ERlvCmdRet RlvBehaviourHandler<RLV_BHVR_SENDCHANNEL>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
-{
-	// If there's an option then it should be a valid (= positive and non-zero) chat channel
-	if (rlvCmd.hasOption())
-	{
-		S32 nChannel = 0;
-		if ( (!RlvCommandOptionParser<int>::parseOption(rlvCmd.getOption(), nChannel)) || (nChannel <= 0) )
-			return RLV_RET_FAILED_OPTION;
-
-		if (RLV_TYPE_ADD == rlvCmd.getParamType())
-			gRlvHandler.addException(rlvCmd.getObjectID(),  rlvCmd.getBehaviourType(), nChannel);
-		else
-			gRlvHandler.removeException(rlvCmd.getObjectID(),  rlvCmd.getBehaviourType(), nChannel);
-	} 
-	else
-	{
-		fRefCount = true;
-	}
-	return RLV_RET_SUCCESS;
-}
-
-// Handles: @showhovertext:<uuid>=n|y
-ERlvCmdRet RlvBehaviourHandler<RLV_BHVR_SHOWHOVERTEXT>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
-{
-	// There should be an option and it should specify a valid UUID
-	LLUUID idException;
-	if (!RlvCommandOptionParser<LLUUID>::parseOption(rlvCmd.getOption(), idException))
-		return RLV_RET_FAILED_OPTION;
-
-	if (RLV_TYPE_ADD == rlvCmd.getParamType())
-		gRlvHandler.addException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
-	else
-		gRlvHandler.removeException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
-
-	// Clear/restore the object's hover text as needed
-	LLViewerObject* pObj = gObjectList.findObject(idException);
-	if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
-		pObj->mText->setString( (RLV_TYPE_ADD == rlvCmd.getParamType()) ? "" : pObj->mText->getObjectText());
-
-	fRefCount = true;
-	return RLV_RET_SUCCESS;
-}
-
-// Handles: @setgroup:<uuid>=force
-ERlvCmdRet RlvCommandHandler<RLV_TYPE_FORCE, RLV_BHVR_SETGROUP>::onCommand(const RlvCommand& rlvCmd)
-{
-	if (gRlvHandler.hasBehaviourExcept(RLV_BHVR_SETGROUP, rlvCmd.getObjectID()))
-	{
-		return RLV_RET_FAILED_LOCK;
-	}
-
-	LLUUID idGroup; bool fValid = false;
-	if (idGroup.set(rlvCmd.getOption()))
-	{
-		fValid = (idGroup.isNull()) || (gAgent.isInGroup(idGroup, true));
-	}
-	else
-	{
-		for (S32 idxGroup = 0, cntGroup = gAgent.mGroups.size(); (idxGroup < cntGroup) && (idGroup.isNull()); idxGroup++)
-			if (boost::iequals(gAgent.mGroups.at(idxGroup).mName, rlvCmd.getOption()))
-				idGroup = gAgent.mGroups.at(idxGroup).mID;
-		fValid = (idGroup.notNull()) || ("none" == rlvCmd.getOption());
-	}
-
-	if (fValid)
-	{
-		gRlvHandler.m_idAgentGroup = idGroup;
-		LLGroupActions::activate(idGroup);
-	}
-
-	return (fValid) ? RLV_RET_SUCCESS : RLV_RET_FAILED_OPTION;
-}
-
-// Handles: @sit:<uuid>=force
-ERlvCmdRet RlvCommandHandler<RLV_TYPE_FORCE, RLV_BHVR_SIT>::onCommand(const RlvCommand& rlvCmd)
-{
-	LLViewerObject* pObj = NULL; LLUUID idTarget(rlvCmd.getOption());
-	// Sanity checking - we need to know about the object and it should identify a prim/linkset
-	if ( (idTarget.isNull()) || ((pObj = gObjectList.findObject(idTarget)) == NULL) || (LL_PCODE_VOLUME != pObj->getPCode()) )
-		return RLV_RET_FAILED_OPTION;
-
-	if (!gRlvHandler.canSit(pObj))
-		return RLV_RET_FAILED_LOCK;
-	else if ( (gRlvHandler.hasBehaviour(RLV_BHVR_STANDTP)) && (isAgentAvatarValid()) )
-	{
-		if (gAgentAvatarp->isSitting())
-			return RLV_RET_FAILED_LOCK;
-		gRlvHandler.m_posSitSource = gAgent.getPositionGlobal();
-	}
-
-	// Copy/paste from handle_sit_or_stand() [see http://wiki.secondlife.com/wiki/AgentRequestSit]
-	gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
-	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
-	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-	gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
-	gMessageSystem->addUUIDFast(_PREHASH_TargetID, pObj->mID);
-	// Offset: "a rough position in local coordinates for the edge to sit on"
-	// (we might not even be looking at the object so I don't think we can supply the offset to an edge)
-	gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3::zero);
-	pObj->getRegion()->sendReliableMessage();
-
-	return RLV_RET_SUCCESS;
-}
-
 // ============================================================================
 // Command specific helper functions
 //
@@ -1353,13 +1168,6 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd)
 	eRet = RLV_RET_SUCCESS; bool fRefCount = false; const std::string& strOption = rlvCmd.getOption();
 	switch (eBhvr)
 	{
-		case RLV_BHVR_DETACH:				// @detach[:<option>]=n|y
-			eRet = onAddRemDetach(rlvCmd, fRefCount);
-			break;
-		case RLV_BHVR_ADDATTACH:			// @addattach[:<option>]=n|y
-		case RLV_BHVR_REMATTACH:			// @addattach[:<option>]=n|y
-			eRet = onAddRemAttach(rlvCmd, fRefCount);
-			break;
 		case RLV_BHVR_ATTACHTHIS:			// @attachthis[:<option>]=n|y
 		case RLV_BHVR_DETACHTHIS:			// @detachthis[:<option>]=n|y
 			eRet = onAddRemFolderLock(rlvCmd, fRefCount);
@@ -1491,12 +1299,84 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd)
 	return eRet;
 }
 
-// Checked: 2010-03-03 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a
-ERlvCmdRet RlvHandler::onAddRemAttach(const RlvCommand& rlvCmd, bool& fRefCount)
+// Handles reference counting of behaviours and tracks strict exceptions for @permissive (all restriction handlers should call this function)
+ERlvCmdRet RlvBehaviourProcessorHelper::processBehaviourImpl(const RlvCommand& rlvCmd, RlvBhvrHandler* pHandlerFunc, RlvBhvrToggleHandler* pToggleHandlerFunc)
+{
+	ERlvBehaviour eBhvr = rlvCmd.getBehaviourType();
+	bool fRefCount = false, fHasBhvr = gRlvHandler.hasBehaviour(eBhvr);
+
+	ERlvCmdRet eRet = (*pHandlerFunc)(rlvCmd, fRefCount);
+
+	// If this command represents a restriction that's been added/removed then we need to do some additional processing
+	if ( (RLV_RET_SUCCESS == eRet) && (fRefCount) )
+	{
+		if (RLV_TYPE_ADD == rlvCmd.getParamType())
+		{
+			if (rlvCmd.isStrict())
+				gRlvHandler.addException(rlvCmd.getObjectID(), RLV_BHVR_PERMISSIVE, eBhvr);
+			gRlvHandler.m_Behaviours[eBhvr]++;
+		}
+		else
+		{
+			if (rlvCmd.isStrict())
+				gRlvHandler.removeException(rlvCmd.getObjectID(), RLV_BHVR_PERMISSIVE, eBhvr);
+			gRlvHandler.m_Behaviours[eBhvr]--;
+		}
+
+		if (fHasBhvr != gRlvHandler.hasBehaviour(eBhvr))
+		{
+			if (pToggleHandlerFunc)
+				(*pToggleHandlerFunc)(eBhvr, !fHasBhvr);
+			gRlvHandler.m_OnBehaviourToggle(eBhvr, rlvCmd.getParamType());
+		}
+	}
+
+	return eRet;
+}
+
+// Handles: @bhvr=n|y
+ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_NONE>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
+{
+	// There should be no option
+	if (rlvCmd.hasOption())
+		return RLV_RET_FAILED_OPTION;
+
+	fRefCount = true;
+	return RLV_RET_SUCCESS;
+}
+
+// Handles: @bhvr:<uuid>=n|y
+ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_EXCEPTION>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
+{
+	// There should be an option and it should specify a valid UUID
+	LLUUID idException;
+	if (!RlvCommandOptionParser<LLUUID>::parseOption(rlvCmd.getOption(), idException))
+		return RLV_RET_FAILED_OPTION;
+
+	if (RLV_TYPE_ADD == rlvCmd.getParamType())
+		gRlvHandler.addException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
+	else
+		gRlvHandler.removeException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
+
+	fRefCount = true;
+	return RLV_RET_SUCCESS;
+}
+
+// Handles: @bhvr[:<uuid>]=n|y
+ERlvCmdRet RlvBehaviourGenericHandler<RLV_OPTION_NONE_OR_EXCEPTION>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
 {
-	RLV_ASSERT( (RLV_TYPE_ADD == rlvCmd.getParamType()) || (RLV_TYPE_REMOVE == rlvCmd.getParamType()) );
-	RLV_ASSERT( (RLV_BHVR_ADDATTACH == rlvCmd.getBehaviourType()) || (RLV_BHVR_REMATTACH == rlvCmd.getBehaviourType()) );
+	// If there is an option then it should specify a valid UUID (but don't reference count)
+	if (rlvCmd.hasOption())
+	{
+		ERlvCmdRet eRet = RlvBehaviourGenericHandler<RLV_OPTION_EXCEPTION>::onCommand(rlvCmd, fRefCount);
+		fRefCount = false;
+		return eRet;
+	}
+	return RlvBehaviourGenericHandler<RLV_OPTION_NONE>::onCommand(rlvCmd, fRefCount);
+}
 
+ERlvCmdRet RlvBehaviourAddRemAttachHandler::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
+{
 	// Sanity check - if there's an option it should specify a valid attachment point name
 	S32 idxAttachPt = RlvAttachPtLookup::getAttachPointIndex(rlvCmd.getOption());
 	if ( (!idxAttachPt) && (!rlvCmd.getOption().empty())  )
@@ -1526,12 +1406,8 @@ ERlvCmdRet RlvHandler::onAddRemAttach(const RlvCommand& rlvCmd, bool& fRefCount)
 	return RLV_RET_SUCCESS;
 }
 
-// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a
-ERlvCmdRet RlvHandler::onAddRemDetach(const RlvCommand& rlvCmd, bool& fRefCount)
+ERlvCmdRet RlvBehaviourHandler<RLV_BHVR_DETACH>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
 {
-	RLV_ASSERT( (RLV_TYPE_ADD == rlvCmd.getParamType()) || (RLV_TYPE_REMOVE == rlvCmd.getParamType()) );
-	RLV_ASSERT(RLV_BHVR_DETACH == rlvCmd.getBehaviourType());
-
 	// We need to flush any queued force-wear commands before changing the restrictions
 	if (RlvForceWear::instanceExists())
 		RlvForceWear::instance().done();
@@ -1543,13 +1419,13 @@ ERlvCmdRet RlvHandler::onAddRemDetach(const RlvCommand& rlvCmd, bool& fRefCount)
 		//                - if it hasn't rezzed yet then it's a @detach=n from a non-attachment and RlvHandler::onAttach() takes care of it
 		//   * @detach=y: - if it ever rezzed as an attachment we'll have cached the UUID of its root
 		//                - if it never rezzed as an attachment there won't be a lock to remove
-		rlv_object_map_t::const_iterator itObj = m_Objects.find(rlvCmd.getObjectID());
-		if ( (itObj != m_Objects.end()) && (itObj->second.m_fLookup) && (itObj->second.m_idxAttachPt) )
+		RlvHandler::rlv_object_map_t::const_iterator itObj = gRlvHandler.m_Objects.find(rlvCmd.getObjectID());
+		if ( (itObj != gRlvHandler.m_Objects.end()) && (itObj->second.hasLookup()) && (itObj->second.getAttachPt()) )
 		{
 			if (RLV_TYPE_ADD == rlvCmd.getParamType())
-				gRlvAttachmentLocks.addAttachmentLock(itObj->second.m_idRoot, itObj->first);
+				gRlvAttachmentLocks.addAttachmentLock(itObj->second.getRootID(), itObj->first);
 			else
-				gRlvAttachmentLocks.removeAttachmentLock(itObj->second.m_idRoot, itObj->first);
+				gRlvAttachmentLocks.removeAttachmentLock(itObj->second.getRootID(), itObj->first);
 		}
 	}
 	else							// @detach:<attachpt>=n|y - RLV_LOCK_ADD and RLV_LOCK_REMOVE locks an attachment *point*
@@ -1640,6 +1516,56 @@ ERlvCmdRet RlvHandler::onAddRemFolderLockException(const RlvCommand& rlvCmd, boo
 	return RLV_RET_SUCCESS;
 }
 
+// Handles: @sendchannel[:<channel>]=n|y
+ERlvCmdRet RlvBehaviourHandler<RLV_BHVR_SENDCHANNEL>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
+{
+	// If there's an option then it should be a valid (= positive and non-zero) chat channel
+	if (rlvCmd.hasOption())
+	{
+		S32 nChannel = 0;
+		if ( (!RlvCommandOptionParser<int>::parseOption(rlvCmd.getOption(), nChannel)) || (nChannel <= 0) )
+			return RLV_RET_FAILED_OPTION;
+
+		if (RLV_TYPE_ADD == rlvCmd.getParamType())
+			gRlvHandler.addException(rlvCmd.getObjectID(),  rlvCmd.getBehaviourType(), nChannel);
+		else
+			gRlvHandler.removeException(rlvCmd.getObjectID(),  rlvCmd.getBehaviourType(), nChannel);
+	} 
+	else
+	{
+		fRefCount = true;
+	}
+	return RLV_RET_SUCCESS;
+}
+
+// Handles: @sendim=n|y toggles
+void RlvBehaviourToggleHandler<RLV_BHVR_SENDIM>::onCommandToggle(ERlvBehaviour eBhvr, bool fHasBhvr)
+{
+	gSavedPerAccountSettings.getControl("DoNotDisturbModeResponse")->setHiddenFromSettingsEditor(fHasBhvr);
+}
+
+// Handles: @showhovertext:<uuid>=n|y
+ERlvCmdRet RlvBehaviourHandler<RLV_BHVR_SHOWHOVERTEXT>::onCommand(const RlvCommand& rlvCmd, bool& fRefCount)
+{
+	// There should be an option and it should specify a valid UUID
+	LLUUID idException;
+	if (!RlvCommandOptionParser<LLUUID>::parseOption(rlvCmd.getOption(), idException))
+		return RLV_RET_FAILED_OPTION;
+
+	if (RLV_TYPE_ADD == rlvCmd.getParamType())
+		gRlvHandler.addException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
+	else
+		gRlvHandler.removeException(rlvCmd.getObjectID(), rlvCmd.getBehaviourType(), idException);
+
+	// Clear/restore the object's hover text as needed
+	LLViewerObject* pObj = gObjectList.findObject(idException);
+	if ( (pObj) && (pObj->mText.notNull()) && (!pObj->mText->getObjectText().empty()) )
+		pObj->mText->setString( (RLV_TYPE_ADD == rlvCmd.getParamType()) ? "" : pObj->mText->getObjectText());
+
+	fRefCount = true;
+	return RLV_RET_SUCCESS;
+}
+
 // ============================================================================
 // Command handlers (RLV_TYPE_FORCE)
 //
@@ -1834,6 +1760,66 @@ void RlvHandler::onForceWearCallback(const uuid_vec_t& idItems, U32 nFlags) cons
 	}
 }
 
+// Handles: @setgroup:<uuid|name>=force
+ERlvCmdRet RlvCommandHandler<RLV_TYPE_FORCE, RLV_BHVR_SETGROUP>::onCommand(const RlvCommand& rlvCmd)
+{
+	if (gRlvHandler.hasBehaviourExcept(RLV_BHVR_SETGROUP, rlvCmd.getObjectID()))
+	{
+		return RLV_RET_FAILED_LOCK;
+	}
+
+	LLUUID idGroup; bool fValid = false;
+	if (idGroup.set(rlvCmd.getOption()))
+	{
+		fValid = (idGroup.isNull()) || (gAgent.isInGroup(idGroup, true));
+	}
+	else
+	{
+		for (S32 idxGroup = 0, cntGroup = gAgent.mGroups.size(); (idxGroup < cntGroup) && (idGroup.isNull()); idxGroup++)
+			if (boost::iequals(gAgent.mGroups.at(idxGroup).mName, rlvCmd.getOption()))
+				idGroup = gAgent.mGroups.at(idxGroup).mID;
+		fValid = (idGroup.notNull()) || ("none" == rlvCmd.getOption());
+	}
+
+	if (fValid)
+	{
+		gRlvHandler.m_idAgentGroup = idGroup;
+		LLGroupActions::activate(idGroup);
+	}
+
+	return (fValid) ? RLV_RET_SUCCESS : RLV_RET_FAILED_OPTION;
+}
+
+// Handles: @sit:<uuid>=force
+ERlvCmdRet RlvCommandHandler<RLV_TYPE_FORCE, RLV_BHVR_SIT>::onCommand(const RlvCommand& rlvCmd)
+{
+	LLViewerObject* pObj = NULL; LLUUID idTarget(rlvCmd.getOption());
+	// Sanity checking - we need to know about the object and it should identify a prim/linkset
+	if ( (idTarget.isNull()) || ((pObj = gObjectList.findObject(idTarget)) == NULL) || (LL_PCODE_VOLUME != pObj->getPCode()) )
+		return RLV_RET_FAILED_OPTION;
+
+	if (!gRlvHandler.canSit(pObj))
+		return RLV_RET_FAILED_LOCK;
+	else if ( (gRlvHandler.hasBehaviour(RLV_BHVR_STANDTP)) && (isAgentAvatarValid()) )
+	{
+		if (gAgentAvatarp->isSitting())
+			return RLV_RET_FAILED_LOCK;
+		gRlvHandler.m_posSitSource = gAgent.getPositionGlobal();
+	}
+
+	// Copy/paste from handle_sit_or_stand()
+	gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
+	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+	gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
+	gMessageSystem->addUUIDFast(_PREHASH_TargetID, pObj->mID);
+	gMessageSystem->addVector3Fast(_PREHASH_Offset, LLVector3::zero);
+	pObj->getRegion()->sendReliableMessage();
+
+	return RLV_RET_SUCCESS;
+}
+
 // ============================================================================
 // Command handlers (RLV_TYPE_REPLY)
 //
diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h
index ac5f3c10cfc9315ec66783a249c6913b7938f5ea..9a438970bb0180e8d7ed112238dcc76ac8bdfedf 100644
--- a/indra/newview/rlvhandler.h
+++ b/indra/newview/rlvhandler.h
@@ -158,8 +158,6 @@ class RlvHandler : public LLOldEvents::LLSimpleListener
 
 	// Command handlers (RLV_TYPE_ADD and RLV_TYPE_CLEAR)
 	ERlvCmdRet processAddRemCommand(const RlvCommand& rlvCmd);
-	ERlvCmdRet onAddRemAttach(const RlvCommand& rlvCmd, bool& fRefCount);
-	ERlvCmdRet onAddRemDetach(const RlvCommand& rlvCmd, bool& fRefCount);
 	ERlvCmdRet onAddRemFolderLock(const RlvCommand& rlvCmd, bool& fRefCount);
 	ERlvCmdRet onAddRemFolderLockException(const RlvCommand& rlvCmd, bool& fRefCount);
 	// Command handlers (RLV_TYPE_FORCE)
diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp
index 4eb85c8920deff45670437f9958a9c47e24a8270..ff05df400cb9834f0838ee466ef700d0e9313cd7 100644
--- a/indra/newview/rlvhelper.cpp
+++ b/indra/newview/rlvhelper.cpp
@@ -40,7 +40,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary()
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("acceptpermission", RLV_BHVR_ACCEPTPERMISSION));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE_OR_EXCEPTION>("accepttp", RLV_BHVR_ACCEPTTP, RlvBehaviourInfo::BHVR_STRICT));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE_OR_EXCEPTION>("accepttprequest", RLV_BHVR_ACCEPTTPREQUEST, RlvBehaviourInfo::BHVR_STRICT | RlvBehaviourInfo::BHVR_EXTENDED));
-	addEntry(new RlvBehaviourInfo("addattach",				RLV_BHVR_ADDATTACH,				RLV_TYPE_ADDREM));
+	addEntry(new RlvBehaviourProcessor<RLV_BHVR_ADDATTACH, RlvBehaviourAddRemAttachHandler>("addattach"));
 	addEntry(new RlvBehaviourInfo("addoutfit",				RLV_BHVR_ADDOUTFIT,				RLV_TYPE_ADDREM));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("allowidle", RLV_BHVR_ALLOWIDLE, RlvBehaviourInfo::BHVR_EXPERIMENTAL));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("alwaysrun", RLV_BHVR_ALWAYSRUN));
@@ -51,7 +51,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary()
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("chatwhisper", RLV_BHVR_CHATWHISPER));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("chatnormal", RLV_BHVR_CHATNORMAL));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("chatshout", RLV_BHVR_CHATSHOUT));
-	addEntry(new RlvBehaviourInfo("detach",					RLV_BHVR_DETACH,				RLV_TYPE_ADDREM));
+	addEntry(new RlvBehaviourProcessor<RLV_BHVR_DETACH>("detach"));
 	addEntry(new RlvBehaviourInfo("detachthis",				RLV_BHVR_DETACHTHIS,			RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE));
 	addEntry(new RlvBehaviourInfo("detachallthis",			RLV_BHVR_DETACHTHIS,			RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_SUBTREE));
 	addEntry(new RlvBehaviourInfo("detachthis_except",		RLV_BHVR_DETACHTHISEXCEPT,		RLV_TYPE_ADDREM, RlvBehaviourInfo::FORCEWEAR_NODE));
@@ -72,7 +72,7 @@ RlvBehaviourDictionary::RlvBehaviourDictionary()
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_EXCEPTION>("recvimfrom", RLV_BHVR_RECVIMFROM, RlvBehaviourInfo::BHVR_STRICT));
 	addEntry(new RlvBehaviourInfo("redirchat",				RLV_BHVR_REDIRCHAT,				RLV_TYPE_ADDREM));
 	addEntry(new RlvBehaviourInfo("rediremote",				RLV_BHVR_REDIREMOTE,			RLV_TYPE_ADDREM));
-	addEntry(new RlvBehaviourInfo("remattach",				RLV_BHVR_REMATTACH,				RLV_TYPE_ADDREM));
+	addEntry(new RlvBehaviourProcessor<RLV_BHVR_REMATTACH, RlvBehaviourAddRemAttachHandler>("remattach"));
 	addEntry(new RlvBehaviourInfo("remoutfit",				RLV_BHVR_REMOUTFIT,				RLV_TYPE_ADDREM));
 	addEntry(new RlvBehaviourGenericProcessor<RLV_OPTION_NONE>("rez", RLV_BHVR_REZ));
 	addEntry(new RlvBehaviourProcessor<RLV_BHVR_SENDCHANNEL>("sendchannel", RlvBehaviourInfo::BHVR_STRICT));
diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h
index 5811b9f263f39931a8dc97f5f3f0cff7c194f6e4..78b22e2ae54a715a3c538643b973f9dacc1ce957 100644
--- a/indra/newview/rlvhelper.h
+++ b/indra/newview/rlvhelper.h
@@ -85,7 +85,8 @@ class RlvBehaviourProcessorHelper
 {
 public:
 	typedef ERlvCmdRet(RlvBhvrHandler)(const RlvCommand& rlvCmd, bool& fRefCount);
-	static ERlvCmdRet processBehaviourImpl(const RlvCommand& rlvCmd, RlvBhvrHandler* pHandlerFunc);
+	typedef void(RlvBhvrToggleHandler)(ERlvBehaviour eBhvr, bool fHasBhvr);
+	static ERlvCmdRet processBehaviourImpl(const RlvCommand& rlvCmd, RlvBhvrHandler* pHandlerFunc, RlvBhvrToggleHandler* pToggleHandlerFunc = nullptr);
 };
 
 template <RlvBehaviourOptionType optionType> struct RlvBehaviourGenericHandler { static ERlvCmdRet onCommand(const RlvCommand& rlvCmd, bool& fRefCount); };
@@ -109,17 +110,11 @@ template <ERlvBehaviour eBhvr, RlvBehaviourOptionType optionType, typename bhvrT
 {
 public:
 	RlvBehaviourToggleProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : RlvBehaviourInfo(strBhvr, eBhvr, RLV_TYPE_ADDREM, nBhvrFlags) {}
-	/*virtual*/ ERlvCmdRet processCommand(const RlvCommand& rlvCmd) const
-	{
-		bool fHasBhvr = gRlvHandler.hasBehaviour(eBhvr);
-
-		ERlvCmdRet eRet = RlvBehaviourProcessorHelper::processBehaviourImpl(rlvCmd, &RlvBehaviourGenericHandler<optionType>::onCommand);
-		if (fHasBhvr != gRlvHandler.hasBehaviour(eBhvr))
-			bhvrToggleHandler::onCommandToggle(eBhvr, !fHasBhvr);
-		return eRet;
-	}
+	/*virtual*/ ERlvCmdRet processCommand(const RlvCommand& rlvCmd) const { return RlvBehaviourProcessorHelper::processBehaviourImpl(rlvCmd, &RlvBehaviourGenericHandler<optionType>::onCommand, &bhvrToggleHandler::onCommandToggle); }
 };
 
+typedef RlvBehaviourHandler<RLV_BHVR_REMATTACH> RlvBehaviourAddRemAttachHandler;	// Shared between @addattach and @remattach
+
 // ============================================================================
 // RlvCommandHandler and related classes - Handles force/reply commands
 //
@@ -327,10 +322,16 @@ class RlvObject
 	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; }
 
-	const LLUUID&		getObjectID() const	{ return m_idObj; }
-	const LLUUID&		getRootID() const	{ return m_idRoot; }
+	/*
+	 * Accessors
+	 */
+public:
+	S32           getAttachPt() const	{ return m_idxAttachPt; }
+	const LLUUID& getObjectID() const	{ return m_idObj; }
+	const LLUUID& getRootID() const		{ return m_idRoot; }
+	bool          hasLookup() const     { return m_fLookup; }
+	const rlv_command_list_t* getCommandList() const { return &m_Commands; }
 
 	/*
 	 * Member variables