realtimenetprots/sipfw/SIP/TransactionUser/src/UserAgent.cpp
changeset 0 307788aac0a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/realtimenetprots/sipfw/SIP/TransactionUser/src/UserAgent.cpp	Tue Feb 02 01:03:15 2010 +0200
@@ -0,0 +1,786 @@
+// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Name          : UserAgent.cpp
+// Part of       : TransactionUser
+// Version       : SIP/6.0 
+//
+
+
+
+#include "siperr.h"
+#include "SipAssert.h"
+#include "siprequest.h"
+#include "sipresponse.h"
+#include "sipuri.h"
+#include "uricontainer.h"
+#include "siphostport.h"
+#include "sipaddress.h"
+#include "sipfromtoheaderbase.h"
+#include "sipcontactheader.h"
+#include "sipstrings.h"
+#include "sipstrconsts.h"
+#include "SipLogs.h"
+#include "DeleteMgr.h"
+#include "Transmitter.h"
+
+#include "MSipRegistrationContact.h"
+#include "MTransactionOwner.h"
+#include "CUserAgent.h"
+#include "UserAgentState.h"
+#include "UserAgentCreateParams.h"
+#include "CTransactionStore.h"
+#include "SIPMessageUtility.h"
+#include "MTransactionUser.h"
+
+
+// -----------------------------------------------------------------------------
+// CUserAgent::CUserAgent
+// aObserver is NULL for UAS
+// -----------------------------------------------------------------------------
+//
+CUserAgent::CUserAgent(const CUserAgentCreateParams& aParams) :	
+	iObserver(aParams.iObserver),	
+	iTimers(aParams.iTimers),
+	iRegistrations(aParams.iRegistrations),
+    iRegistrationContact(aParams.iRegistrationContact),
+    iSigComp(aParams.iSigComp),
+	iTransactionStore(aParams.iTransactionStore),
+	iTransactionMgr(aParams.iTransactionMgr),	
+	iTransportProtocol(SIPStrings::StringF(SipStrConsts::EUDP)),
+	iTransportParams(aParams.iTransportParams),
+	iTimerValues(aParams.iTimerValues),
+	iOwnsOutgoingMsg(aParams.iOwnsOutgoingMsg),
+	iDeleteMgr(aParams.iDeleteMgr),
+	iSIPMsgUtility(aParams.iSIPMsgUtility),    
+	iState(aParams.iInitialUaState),
+	iTransactionId(aParams.iTransactionId)
+	{
+	__SIP_ASSERT_RETURN(iTransactionId != KEmptyTransactionId, KErrArgument);
+    __SIP_ASSERT_RETURN(iState != NULL, KErrArgument);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::ConstructL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::ConstructL(MSipConnectionMgr& aConnectionMgr)
+	{
+	iTransmitter = CTransmitter::NewL(aConnectionMgr);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::~CUserAgent
+// -----------------------------------------------------------------------------
+//
+CUserAgent::~CUserAgent()
+	{
+	delete iTransmitter;
+	delete iTransaction;
+
+	if (iOwnsOutgoingMsg)
+		{
+		delete iOutgoingMsg;
+		}
+
+	delete iIncomingMsg;
+	iTransportProtocol.Close();	
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::TransportParams
+// -----------------------------------------------------------------------------
+//
+const TSIPTransportParams& CUserAgent::TransportParams() const
+    {
+    __TEST_INVARIANT;
+    return iTransportParams;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgent::CleanupSilently
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::CleanupSilently(TAny* aUserAgent)
+	{
+	__SIP_ASSERT_RETURN(aUserAgent, KErrArgument);
+
+	CUserAgent* ua = reinterpret_cast<CUserAgent*>(aUserAgent);
+	ua->ClearTransactionOwner(); // Prevent MTransactionOwner::TransactionEnded
+	ua->Stop(KErrGeneral); 		 // End silently, error code doesn't matter
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::SendRequestL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::SendRequestL(CSIPRequest* aReq,
+							  TRegistrationId aRegisterId,
+							  const CURIContainer& aRemoteTarget)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aReq, KErrArgument);
+    __ASSERT_ALWAYS(!iStopped, User::Leave(KErrSIPInvalidTransactionState));
+
+	iState->SendRequestL(*this, aReq, aRegisterId, aRemoteTarget);
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::SendResponseL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::SendResponseL(CSIPResponse* aResp,
+							   const TSIPTransportParams& aParams)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aResp, KErrArgument);
+    __ASSERT_ALWAYS(!iStopped, User::Leave(KErrSIPInvalidTransactionState));
+	
+    iState->SendResponseL(*this, aResp, aParams);	
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::SendCancelL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::SendCancelL(TTransactionId aInviteTaId)
+	{
+	__TEST_INVARIANT;
+    __ASSERT_ALWAYS(!iStopped, User::Leave(KErrSIPInvalidTransactionState));
+
+	iState->SendCancelL(*this, aInviteTaId);
+	
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::TransactionOwner
+// -----------------------------------------------------------------------------
+//
+const MTransactionOwner* CUserAgent::TransactionOwner() const
+	{
+	__TEST_INVARIANT;
+	return iObserver;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::ClearTransactionOwner
+// Detach iOutgoingMsg unless owned, as the upper layer can delete it.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::ClearTransactionOwner()
+	{
+	__TEST_INVARIANT;
+
+	iObserver = NULL;
+
+	if (!iOwnsOutgoingMsg)
+        {
+        DetachOutgoingMsg();
+        }
+
+	if (iState->ShouldUASStop())
+		{
+		Stop(KErrNone);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::DetachOutgoingMsg
+// No action.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::DetachOutgoingMsg()
+	{
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::ReceiveL
+// Write request to log, if it came directly from ConnectionMgr.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::ReceiveL(CSIPRequest* aRequest)
+    {
+    __TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aRequest, KErrArgument);
+
+	if (iStopped)
+		{
+		delete aRequest;
+		}
+	else
+        {
+		if (!iTransaction)
+			{
+			__SIP_MESSAGE_LOG("TransactionUser", *aRequest)
+			}
+
+		iState->ReceiveL(*this, aRequest);
+		}
+
+	__TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgent::ReceiveL
+// Write response to log, if it came directly from ConnectionMgr.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::ReceiveL(CSIPResponse* aResponse)
+    {
+    __TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aResponse, KErrArgument);
+
+	if (iStopped)
+		{
+		delete aResponse;
+		}
+	else
+		{
+		if (!iTransaction)
+			{
+			__SIP_MESSAGE_LOG("TransactionUser", *aResponse)
+			}
+
+		iState->ReceiveL(*this, aResponse);
+		}
+
+	__TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgent::LeaveOccurred
+// If Dialog drops a response, it leaves with KErrSIPInvalidDialogResponse.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::LeaveOccurred(TInt aReason)
+	{
+	__TEST_INVARIANT;
+	
+	__SIP_INT_LOG1( "CUser Agent :: LeaveOccured with reason", 
+   			                aReason )
+
+	if (aReason != KErrSIPInvalidDialogResponse)
+		{
+		Stop(aReason);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::SendCompleteL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::SendCompleteL()
+	{
+	__TEST_INVARIANT;
+
+	if (!iStopped)
+		{
+		iState->SendCompleteL(*this);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::SendFailedL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::SendFailedL(TInt aError)
+	{
+	__TEST_INVARIANT;
+	__SIP_INT_LOG1( "CUserAgent Sending failed with Error",
+					aError )
+
+	if (!iStopped)
+        {
+        iState->SendFailedL(*this, aError);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::LeaveFromTransmitter
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::LeaveFromTransmitter(TInt aReason)
+	{
+	__TEST_INVARIANT;
+	__ASSERT_DEBUG(aReason != KErrNone,
+				   User::Panic(_L("UA:LeaveFromTransmitter"), KErrArgument));
+	Stop(aReason);
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::TimerExpiredL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::TimerExpiredL(TTimerId aTimerId, TAny* aTimerParam)
+	{
+	__TEST_INVARIANT;
+
+	if (!iStopped)
+		{
+		iState->TimerExpiredL(*this, aTimerId, aTimerParam);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::DeleteTimer
+// The concrete UA must implement DeleteTimer.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::DeleteTimer(const CUserAgentTimer& /*aTimer*/) 
+	{
+	__TEST_INVARIANT;
+    __SIP_ASSERT_RETURN(EFalse, KErrGeneral);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::StoreOutgoingMsg
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::StoreOutgoingMsg(CSIPMessage* aMsg)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_RETURN(aMsg, KErrArgument);
+
+	if (iOwnsOutgoingMsg)
+		{
+		delete iOutgoingMsg;
+		}
+
+	iOutgoingMsg = aMsg;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::IsInviteUAC
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgent::IsInviteUAC() const
+	{
+	__TEST_INVARIANT;
+	return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::IsSubInviteUAC
+// Don't call __TEST_INVARIANT here to avoid recursion
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgent::IsSubInviteUAC() const
+	{
+	return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::State
+// -----------------------------------------------------------------------------
+//
+const CUserAgentState& CUserAgent::State() const
+    {
+    __TEST_INVARIANT;
+    return *iState;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgent::ChangeState
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::ChangeState(const CUserAgentState& aNewState)
+	{
+	__TEST_INVARIANT;
+
+	iState = &aNewState;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::IsResolving
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgent::IsResolving() const
+	{
+	__TEST_INVARIANT;
+	return iState->IsResolving(); 
+	}
+	
+// -----------------------------------------------------------------------------
+// CUserAgent::UpdateTransactionOwner
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::UpdateTransactionOwner(MTransactionOwner* aNewObserver)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_RETURN(aNewObserver, KErrArgument);
+
+	iObserver = aNewObserver;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::TransactionEndsL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::TransactionEndsL(TInt aReason)
+	{
+	__TEST_INVARIANT;
+
+	iState->TransactionEndsL(*this, aReason);
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::PassMsgToTransactionOwnerL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::PassMsgToTransactionOwnerL(CSIPMessage* aMsg) const
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aMsg, KErrArgument);
+
+	if (iObserver)
+		{
+		__SIP_INT_LOG1( "TU passes SIP message to owner, transaction ID", 
+		                 iTransactionId )
+
+		if (aMsg->IsRequest())
+			{
+			CSIPRequest* request = static_cast<CSIPRequest*>(aMsg);
+
+			__SIP_DES8_LOG( "TU SIP request method", 
+			                request->Method().DesC() )
+			iObserver->ReceiveL(iTransportParams.IapId(),
+                                iTransactionId,
+								request);
+			}
+		else
+			{
+			CSIPResponse* response = static_cast<CSIPResponse*>(aMsg);
+
+			__SIP_INT_LOG1( "TU SIP response code", 
+			                response->ResponseCode() )
+			iObserver->ReceiveL(iTransactionId, response);
+			}
+		}
+	else
+        {
+    	__SIP_INT_LOG1(
+    	    "TU msg not passed to owner, iObserver==NULL, transaction ID",
+    	    iTransactionId )
+
+		delete aMsg;
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::UpdateTransportProtocol
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgent::UpdateTransportProtocol(CSIPMessage& aMsg)
+	{
+	__TEST_INVARIANT;
+	return CSIPMessageUtility::TransportProtocol(aMsg, iTransportProtocol);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::AddTagL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::AddTagL(CSIPFromToHeaderBase& aToFromHeader) const
+	{
+	__TEST_INVARIANT;
+
+	TBuf8<MTransactionUser::KTagLength> tagBuf;
+	AddRandomStringL(tagBuf, MTransactionUser::KTagLength, EFalse);
+	
+	RStringF tag = SIPStrings::Pool().OpenFStringL(tagBuf);
+	CleanupClosePushL(tag);
+	aToFromHeader.SetParamL(SIPStrings::StringF(SipStrConsts::ETag), tag);
+	CleanupStack::PopAndDestroy(); // tag
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::AddRandomStringL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::AddRandomStringL(TDes8& aBuf,
+								  TInt aLength,
+								  TBool aCaseSensitive) const
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aLength <= aBuf.MaxLength() - aBuf.Length(),
+                       KErrArgument);
+
+	iSIPMsgUtility.AddRandomStringL(aBuf,
+									aLength,
+									aCaseSensitive,
+									iOutgoingMsg,
+									iTransactionId,
+									this);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::IcmpErrorL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::IcmpErrorL(const TInetAddr& aAddress,
+							CSipConnectionMgr::TICMPError aError)
+	{
+	__TEST_INVARIANT;
+
+	if (!iStopped)
+		{
+		iState->IcmpErrorL(*this, aAddress, aError);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::CheckContactHeadersL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::CheckContactHeadersL(CSIPMessage& aMsg,
+                              const CSIPFromToHeaderBase* aFromToHeader) const
+	{
+	__TEST_INVARIANT;
+    __SIP_ASSERT_LEAVE(aFromToHeader != NULL, KErrArgument);
+
+	const RStringF KContact = SIPStrings::StringF(SipStrConsts::EContactHeader);
+	if (aMsg.HasHeader(KContact))
+		{
+		TSglQueIter<CSIPHeaderBase> iter = aMsg.Headers(KContact);
+		for (CSIPHeaderBase* header = iter++; header; header = iter++)
+			{
+			CSIPContactHeader* contact =
+				static_cast<CSIPContactHeader*>(header);
+			if (!contact->Star())
+                {
+                __SIP_ASSERT_LEAVE(contact->SIPAddress(), KErrArgument);
+				if (contact->SIPAddress()->URI().IsSIPURI())
+					{
+					CSIPURI* sipUri = contact->SIPAddress()->URI().SIPURI();
+					if (sipUri->HostPort().Host().CompareF(
+						SIPStrings::StringF(SipStrConsts::ELocalHost).DesC())
+						== 0)
+						{
+						iRegistrationContact.ContactL(iTransportParams.IapId(),
+                                    RegistrationId(),
+                                    aFromToHeader,
+						            static_cast<CSIPContactHeader&>(*header));
+						}
+	                CheckSigCompL(contact->SIPAddress()->URI());
+					}
+				}
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::RemoveFromStore
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::RemoveFromStore() const
+	{
+	__TEST_INVARIANT;
+
+	iTransactionStore.Remove(iTransactionId);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::RequestDeletionOfTransactionL
+// Leave if AddDeleteRequest fails. Don't change TransactionId, to avoid using
+// RequestDeletionOfUserAgent after changing id. Changing id twice fails causing
+// "delete this" and call stack likely has UA/transaction code, causing a crash.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::RequestDeletionOfTransactionL()
+    {
+    __TEST_INVARIANT;
+
+	if (iTransaction)
+		{
+		User::LeaveIfError(iDeleteMgr.AddDeleteRequest(iTransaction));		
+		iTransaction = NULL;
+		iTransactionStore.ClearTransaction(iTransactionId);
+		}
+
+    __TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgent::RequestDeletionOfUserAgent
+// -----------------------------------------------------------------------------
+//
+TInt CUserAgent::RequestDeletionOfUserAgent()
+	{
+	__TEST_INVARIANT;
+
+    TInt status = iDeleteMgr.AddDeleteRequest(this);
+	if (status == KErrNone)
+        {
+        // DeleteMgr took UA's ownership
+		RemoveFromStore();
+		}
+	else
+		{
+        // Other errors mean e.g. trying to delete object twice
+        if (status == KErrNoMemory)
+            {
+			// Set id empty, to be freed later. Fails if UA isn't stored, but it
+			// occurs only in CTransactionUser::SendL/SendAndGetHeadersL/
+			// SendCancelL so UA isn't in call stack, and can "delete this".
+			status = iTransactionStore.UpdateTransactionId(iTransactionId,
+				            						       KEmptyTransactionId);
+            if (status != KErrNone)
+                {
+                delete this;
+                return KErrDied; // Skip __TEST_INVARIANT
+                }
+            }
+        }
+
+	__TEST_INVARIANT;
+	return status;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::Stop
+// "sub InviteUAC" doesn't use observer or request its own deletion.
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::Stop(TInt aReason)
+	{
+	__TEST_INVARIANT;
+
+	if (!iStopped)
+		{
+		iStopped = ETrue;
+
+		if (!IsSubInviteUAC())
+			{
+			if (iObserver)
+				{
+				if (IsInviteUAC() && IsCanceled() && aReason == KErrTimedOut)
+					{
+					aReason = KErrCancel;
+					}
+				__SIP_INT_LOG2( "TU TransactionEnded (reason, transaction ID)",
+				                aReason, iTransactionId )
+				iObserver->TransactionEnded(iTransportParams.IapId(), 
+				                            iTransactionId,
+				                            aReason);
+				}
+			else
+				{
+				__SIP_INT_LOG1( "TU UA:Stop(), iObserver==NULL, transaction ID",
+				                iTransactionId )
+				}
+
+            if (RequestDeletionOfUserAgent() == KErrDied)
+                {
+                // Skip __TEST_INVARIANT as UA was deleted synchronously
+                return;
+                }
+			}
+		}
+
+    __TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::TransactionId
+// -----------------------------------------------------------------------------
+//
+TTransactionId CUserAgent::TransactionId() const
+	{
+    __TEST_INVARIANT;
+	return iTransactionId;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::Transaction
+// -----------------------------------------------------------------------------
+//
+CTransactionBase* CUserAgent::Transaction()
+	{
+    __TEST_INVARIANT;
+	return iTransaction;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::CheckSigCompL
+// -----------------------------------------------------------------------------
+//
+void CUserAgent::CheckSigCompL(const CURIContainer& aUri) const
+    {
+    __TEST_INVARIANT;
+	
+	if (CSIPMessageUtility::HasSigCompParam(aUri) && !iSigComp.IsSupported())
+        {
+        User::Leave(KErrNotSupported);
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgent::HasStopped
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgent::HasStopped() const
+	{
+	__TEST_INVARIANT;
+	return iStopped;	
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::IsCanceled
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgent::IsCanceled() const
+	{
+	__TEST_INVARIANT;
+	return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgent::__DbgTestInvariant
+// -----------------------------------------------------------------------------
+//
+
+void CUserAgent::__DbgTestInvariant() const
+	{
+	if (!iState || !iTransmitter || (IsUAS() && !iOwnsOutgoingMsg))
+		{	
+		User::Invariant();
+		}
+	}
+