realtimenetprots/sipfw/SIP/TransactionUser/src/UserAgentServer.cpp
changeset 0 307788aac0a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/realtimenetprots/sipfw/SIP/TransactionUser/src/UserAgentServer.cpp	Tue Feb 02 01:03:15 2010 +0200
@@ -0,0 +1,674 @@
+// Copyright (c) 2006-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          : UserAgentServer.cpp
+// Part of       : TransactionUser
+// Version       : SIP/5.0
+//
+
+
+
+#include "SipAssert.h"
+#include "siperr.h"
+#include "siprequest.h"
+#include "sipresponse.h"
+#include "siptoheader.h"
+#include "sipmaxforwardsheader.h"
+#include "uricontainer.h"
+#include "sipstrings.h"
+#include "sipstrconsts.h"
+#include "MSipRegistrations.h"
+#include "MSipDialogs.h"
+#include "MSIPRequestRouter.h"
+#include "Transmitter.h"
+
+#include "UserAgentServer.h"
+#include "UserAgentState.h"
+#include "UserAgentTimer.h"
+#include "TimerValues.h"
+#include "CTransactionStore.h"
+#include "SIPMessageUtility.h"
+
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::CUserAgentServer
+// -----------------------------------------------------------------------------
+//
+CUserAgentServer::CUserAgentServer(CUserAgentCreateParams& aParams,
+                                   MSipDialogs& aDialogs,
+                                   MSIPRequestRouter& aRouter) :
+	CUserAgent(aParams),
+    iDialogs(aDialogs),
+    iRouter(aRouter)
+	{
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::~CUserAgentServer
+// If transaction exists or InviteUAS sent a 2xx, inform ConnectionMgr to stop
+// using the responses.
+// -----------------------------------------------------------------------------
+//
+CUserAgentServer::~CUserAgentServer()
+	{
+	CancelGetOwnerRequest();
+	iToTag.Close();
+
+	if (iTransmitter)
+		{
+        iTransmitter->CancelSendResponses(CUserAgent::TransactionId(), ETrue);        
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::IsUAS
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgentServer::IsUAS() const
+	{
+	__TEST_INVARIANT;
+	return ETrue;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::RegistrationId
+// UAS doesn't have a registration ID.
+// -----------------------------------------------------------------------------
+//
+TRegistrationId CUserAgentServer::RegistrationId() const
+	{
+	__TEST_INVARIANT;
+
+	return KEmptyRegistrationId;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::OwnerFoundL
+// -----------------------------------------------------------------------------
+//
+void
+CUserAgentServer::OwnerFoundL(TUint32 aRequestId, MTransactionOwner* aOwner)
+	{
+	__TEST_INVARIANT;
+    __SIP_ASSERT_LEAVE(aOwner && aRequestId == iRouterRequestId, KErrArgument);
+
+	if (!HasStopped())
+		{				
+		State().OwnerFoundL(*this, aOwner);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::OwnerNotFoundL
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::OwnerNotFoundL(TUint32 aRequestId, CSIPResponse* aResp)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aResp && aRequestId == iRouterRequestId, KErrArgument);
+	
+	if (HasStopped())
+		{
+		delete aResp;
+		}
+	else
+		{
+		State().OwnerNotFoundL(*this, aResp);
+		}	
+
+	__TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::ErrorOccurred
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::ErrorOccurred(TUint32 aRequestId, TInt aError)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aRequestId == iRouterRequestId, KErrArgument);
+
+	StopTimerOwnerResolver();
+	if (aError == KErrNoMemory)
+		{
+		Stop(aError);
+		}
+	else
+		{
+		TRAPD(err, RequestRouterErrorL());
+		if (err != KErrNone)
+			{
+			Stop(aError);
+			}
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::RequestRouterErrorL
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::RequestRouterErrorL()
+    {
+    __TEST_INVARIANT;
+
+	if (!HasStopped())
+		{
+		State().RequestRouterErrorL(*this);
+		}
+
+	__TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::InitialRequestReceivedL
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::InitialRequestReceivedL(CSIPRequest* aReq,
+									  const CUserAgentState& aGetTxOwner,
+									  const CUserAgentState& aWaitRespFromApp,
+									  const CUserAgentState& aErrorRespSent)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aReq, KErrArgument);
+
+	if (!UpdateTransportProtocol(*aReq))
+		{
+		delete aReq;
+		Stop(KErrNone);
+		return;
+		}
+
+    StoreToTag(*aReq);
+
+	TInt responseCode = 0;
+	RStringF reasonPhrase;
+	if (CheckReceivedRequest(*aReq, responseCode, reasonPhrase))
+		{
+		iTransactionStore.StoreContactHeadersL(TransactionId(), *aReq);        
+
+		if (DoesDialogExistForRequestL(*aReq))
+			{
+			//Enter next state before passing request. If upper layer sends
+			//response synchronously in PassMsgToTransactionOwnerL, UAS is in a
+			//correct state to handle it.
+			ChangeState(aWaitRespFromApp);
+			PassMsgToTransactionOwnerL(aReq);
+			}
+		else
+			{
+			RequestOutsideDialogL(aReq, aGetTxOwner, aErrorRespSent);
+			}
+		}
+	else
+		{
+		SendErrorResponseL(responseCode, reasonPhrase, aErrorRespSent);
+		delete aReq;
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::CheckReceivedRequest
+// If no To-tag, compare request to ongoing transactions to see if it is a
+// merged request.
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgentServer::CheckReceivedRequest(CSIPRequest& aReq,
+											 TInt& aResponseCode,
+											 RStringF& aReasonPhrase) const
+	{
+	__TEST_INVARIANT;
+    __ASSERT_ALWAYS(aReq.To() != NULL,
+					User::Panic(_L("UAS:ChkRcvReq to"), KErrArgument));
+
+	if (aReq.HasHeader(SIPStrings::StringF(SipStrConsts::EMaxForwardsHeader)))
+		{
+		TSglQueIter<CSIPHeaderBase> iter =
+            aReq.Headers(SIPStrings::StringF(SipStrConsts::EMaxForwardsHeader));
+		CSIPMaxForwardsHeader& maxForw =
+            static_cast<CSIPMaxForwardsHeader&>(*iter);
+
+		if (maxForw.Value() == 0)
+			{
+			aReasonPhrase =
+				SIPStrings::StringF(SipStrConsts::EPhraseTooManyHops);
+			aResponseCode = 483;
+			return EFalse;
+			}
+        }
+
+	if (!aReq.To()->HasParam(SIPStrings::StringF(SipStrConsts::ETag)) &&
+		iTransactionStore.IsMergedRequest(*this, aReq))
+		{
+		aReasonPhrase =
+				SIPStrings::StringF(SipStrConsts::EPhraseLoopDetected);		
+		aResponseCode = 482;
+		return EFalse;
+		}
+
+	return ETrue;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::DoesDialogExistForRequestL
+// -----------------------------------------------------------------------------
+//
+TBool CUserAgentServer::DoesDialogExistForRequestL(CSIPRequest& aReq)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(!iObserver && !iOutgoingMsg, KErrAlreadyExists);
+
+	CSIPResponse* resp = NULL;
+	iObserver = iDialogs.TransactionOwnerL(aReq, &resp);
+
+    if (!iObserver && resp)
+		{
+		iOutgoingMsg = resp;
+		}
+
+    __TEST_INVARIANT;
+    return iObserver != NULL;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::RequestOutsideDialogL
+// If registration subsystem does not recognize the Request-URI, a 404 is sent.
+// Record-Routes are only stored from requests outside dialog
+// -----------------------------------------------------------------------------
+//
+void
+CUserAgentServer::RequestOutsideDialogL(CSIPRequest* aReq,
+										const CUserAgentState& aGetTxOwner,
+										const CUserAgentState& aErrorRespSent)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aReq, KErrArgument);
+
+	if (iOutgoingMsg)
+		{
+		HandleResponseFromDialogsL(aErrorRespSent);
+		}
+	else
+		{		
+		CURIContainer* uri = aReq->RequestURI();			
+		if (uri && iRegistrations.CheckRequestURI(*uri))
+			{
+			TRAPD(err, iRouterRequestId =
+		        iRouter.TransactionOwnerL(*aReq,
+										  TransportParams().IapId(),
+									      *this));
+			if (err == KErrNone)
+				{
+	            StartTimerOwnerResolverL();
+				iTransactionStore.StoreRecordRouteHeadersL(TransactionId(),
+	                	                                   *aReq);
+				__SIP_ASSERT_LEAVE(!iIncomingMsg, KErrAlreadyExists);
+				iIncomingMsg = aReq;
+
+				ChangeState(aGetTxOwner);
+				return; //aReq is not deleted
+				}
+
+			SendErrorResponseL(603,
+						   SIPStrings::StringF(SipStrConsts::EPhraseDecline),
+						   aErrorRespSent);
+			}
+		else
+			{
+			SendErrorResponseL(404,
+							SIPStrings::StringF(SipStrConsts::EPhraseNotFound),
+							aErrorRespSent);
+			}
+	    }
+
+	delete aReq;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::SendErrorResponseL
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::SendErrorResponseL(TInt aResponseCode,
+										  RStringF aReasonPhrase,
+										  const CUserAgentState& aErrorRespSent)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aResponseCode >= 300, KErrArgument);
+
+	CSIPResponse* resp = CSIPResponse::NewLC(aResponseCode, aReasonPhrase);
+	FillResponseL(*resp);
+
+	ChangeState(aErrorRespSent);
+	SendResponseToTransactionL(resp);
+	CleanupStack::Pop(resp);
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::CheckResponseL
+// Via headers should not be present yet. UAS copies them from the request.
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::CheckResponseL(const CSIPResponse& aResp) const
+	{
+	__TEST_INVARIANT;
+
+    if (aResp.HasHeader(SIPStrings::StringF(SipStrConsts::EViaHeader)))
+        {
+        User::Leave(KErrSIPMalformedMessage);
+        }
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::FillResponseL
+// If the To-header doesn't yet have a tag, it's added unless the response is
+// 100. If a tag has already been generated, it is used for all responses.
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::FillResponseL(CSIPResponse& aResp)
+	{
+	__TEST_INVARIANT;
+
+	iTransactionStore.CopyHeadersToResponseL(TransactionId(), aResp);
+    CheckContactHeadersL(aResp, aResp.To());
+
+	RStringF tag = SIPStrings::StringF(SipStrConsts::ETag);
+
+	if (aResp.ResponseCode() > 100)
+		{
+        CSIPToHeader* to = aResp.To();
+        __SIP_ASSERT_LEAVE(to != NULL, KErrArgument);
+
+        if (iToTag.DesC().Length() > 0)
+            {
+            if (!to->HasParam(tag))
+                {
+                to->SetParamL(tag, iToTag);
+                }
+            }
+        else
+            {
+            if (!to->HasParam(tag))
+                {
+                //Generate the tag after CopyHeadersToResponseL so SIP headers
+                //can be used as random input for tag.
+				AddTagL(*to);
+                }
+
+			StoreToTag(aResp);
+
+			//Put the tag to store. Routing of an ACK needs it.
+			iTransactionStore.UpdateToTagL(TransactionId(), iToTag);
+            }
+		}
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::StoreToTag
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::StoreToTag(CSIPMessage& aMsg)
+    {
+    __TEST_INVARIANT;
+    __ASSERT_ALWAYS(iToTag.DesC().Length() == 0,
+    				User::Panic(_L("StoreToTag exist"), KErrAlreadyExists));
+
+    CSIPToHeader* to = aMsg.To();
+    __ASSERT_ALWAYS(to != NULL,
+    				User::Panic(_L("StoreToTag !to"), KErrArgument));
+
+    if (to->HasParam(SIPStrings::StringF(SipStrConsts::ETag)))
+        {
+        iToTag.Close();
+		iToTag = to->ParamValue(SIPStrings::StringF(SipStrConsts::ETag)).Copy();
+        }
+
+    __TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::SendResponseToTransactionL
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::SendResponseToTransactionL(CSIPResponse* aResp) const
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(iTransaction, KErrNotFound);
+	__SIP_ASSERT_LEAVE(aResp, KErrArgument);
+
+	iTransaction->SendResponseL(aResp, iTransportProtocol, TransportParams());
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::HandleResponseFromDialogsL
+// -----------------------------------------------------------------------------
+//
+void
+CUserAgentServer::HandleResponseFromDialogsL(const CUserAgentState& aNextState)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(iOutgoingMsg, KErrNotFound);
+
+	CSIPResponse* resp = static_cast<CSIPResponse*>(iOutgoingMsg);
+    __SIP_ASSERT_LEAVE(resp->IsErrorResponse(), KErrSIPMalformedMessage);
+
+	FillResponseL(*resp);
+
+	ChangeState(aNextState);
+	SendResponseToTransactionL(resp);
+	iOutgoingMsg = NULL;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::HandleOwnerFoundL
+// Enter next state before passing request to MTransactionOwner. If upper layer
+// sends response synchronously in MTransactionOwner::ReceiveL, UAS must be in a
+// correct state to handle it.
+// -----------------------------------------------------------------------------
+//
+void
+CUserAgentServer::HandleOwnerFoundL(MTransactionOwner* aOwner,
+								    const CUserAgentState& aWaitRespFromApp)
+	{
+	__TEST_INVARIANT;
+	//Observer can be set only once by MSIPRequestRouter. But when application
+	//sends a response, it can change MTransactionOwner with every SendL call.
+	__SIP_ASSERT_LEAVE(!iObserver, KErrAlreadyExists);
+	__SIP_ASSERT_LEAVE(aOwner, KErrArgument);
+
+	iObserver = aOwner;
+
+    StopTimerOwnerResolver();
+
+	ChangeState(aWaitRespFromApp);
+	PassStoredRequestToTransactionOwnerL();
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::HandleOwnerNotFoundL
+// Response can not be 2xx for INVITE since it has different state in the
+// InviteUAS than the error responses.
+// -----------------------------------------------------------------------------
+//
+void
+CUserAgentServer::HandleOwnerNotFoundL(CSIPResponse* aResp,
+ 									   const CUserAgentState& aFinalRespSent)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(iIncomingMsg, KErrNotFound);	
+	__SIP_ASSERT_LEAVE(aResp && aResp->ResponseCode() >= 200, KErrArgument);
+
+	if (static_cast<CSIPRequest*>(iIncomingMsg)->Method() ==
+		SIPStrings::StringF(SipStrConsts::EInvite))
+		{
+		__SIP_ASSERT_LEAVE(aResp->ResponseCode() >= 300, KErrArgument);
+		}
+
+    StopTimerOwnerResolver();
+	CheckResponseL(*aResp);
+	FillResponseL(*aResp);
+	ChangeState(aFinalRespSent);
+	SendResponseToTransactionL(aResp);
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::HandleRequestRouterErrorL
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::HandleRequestRouterErrorL(
+									const CUserAgentState& aErrorRespSent)
+    {
+    __TEST_INVARIANT;    
+
+    CancelGetOwnerRequest();
+    SendErrorResponseL(603,
+    				   SIPStrings::StringF(SipStrConsts::EPhraseDecline),
+    				   aErrorRespSent);
+    __TEST_INVARIANT;
+    }
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::HandleSendResponseL
+// Free stored Record-Route and Contact headers when a final response is sent.
+// Always keep the TransportId from original TSIPTransportParams.
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::HandleSendResponseL(CSIPResponse* aResp,
+										   const TSIPTransportParams& aParams)
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(aResp, KErrArgument);
+
+	CheckResponseL(*aResp);
+	FillResponseL(*aResp);
+
+	if (CSIPMessageUtility::IsFinalResponse(*aResp))
+        {
+	    iTransactionStore.FreeRecordRouteHeaders(TransactionId());
+	    iTransactionStore.FreeContactHeaders(TransactionId());
+        }
+
+	TSIPTransportParams newParams(aParams);
+	newParams.SetTransportId(iTransportParams.TransportId());
+	iTransportParams = newParams;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::StartTimerOwnerResolverL
+// Timer duration is selected to be shorter than timer F2, so if this timer
+// expires, the server transaction still exists and a response can be sent.
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::StartTimerOwnerResolverL()
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(!iTimerOwnerResolver, KErrAlreadyExists);
+
+	iTimerOwnerResolver =
+        CTimerOwnerResolver::NewL(iTimers,
+								  this,
+                                  (iTimerValues.Duration64xT1() / 10) * 9);
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::StopTimerOwnerResolver
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::StopTimerOwnerResolver()
+	{
+	__TEST_INVARIANT;
+
+	delete iTimerOwnerResolver;
+	iTimerOwnerResolver = NULL;
+
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::CancelGetOwnerRequest
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::CancelGetOwnerRequest() const
+	{
+	__TEST_INVARIANT;    
+
+	iRouter.Cancel(iRouterRequestId);
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::PassStoredRequestToTransactionOwnerL
+// iIncomingMsg is not passed as parameter to PassMsgToTransactionOwnerL(),
+// since iIncomingMsg would be set to NULL only after PassMsgToTransactionOwnerL
+// returns. If upper layer deletes the request, accessing iIncomingMsg causes
+// crash in TU's invariants.
+// -----------------------------------------------------------------------------
+//
+void CUserAgentServer::PassStoredRequestToTransactionOwnerL()
+	{
+	__TEST_INVARIANT;
+	__SIP_ASSERT_LEAVE(iIncomingMsg, KErrNotFound);
+
+	CSIPMessage* msg = iIncomingMsg;
+	iIncomingMsg = NULL;
+	CleanupStack::PushL(msg);
+	PassMsgToTransactionOwnerL(msg);
+	CleanupStack::Pop(msg);
+	
+	__TEST_INVARIANT;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::ToTag
+// -----------------------------------------------------------------------------
+//
+RStringF CUserAgentServer::ToTag() const
+	{
+	__TEST_INVARIANT;
+	return iToTag;
+	}
+
+// -----------------------------------------------------------------------------
+// CUserAgentServer::__DbgTestInvariant
+// -----------------------------------------------------------------------------
+//
+
+void CUserAgentServer::__DbgTestInvariant() const
+	{
+	//For CCancelUAS, iOwnerResolver is always NULL
+	if ((iIncomingMsg && !iIncomingMsg->IsRequest()) ||
+		(iOutgoingMsg && iOutgoingMsg->IsRequest()) ||
+		!iOwnsOutgoingMsg)
+		{
+		User::Invariant();
+		}
+	}
+