diff -r 000000000000 -r 307788aac0a8 realtimenetprots/sipfw/SIP/TransactionUser/src/UserAgentServer.cpp --- /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 iter = + aReq.Headers(SIPStrings::StringF(SipStrConsts::EMaxForwardsHeader)); + CSIPMaxForwardsHeader& maxForw = + static_cast(*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(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(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(); + } + } +