realtimenetprots/sipfw/SIP/sipapi/src/sipdialogstate.cpp
changeset 0 307788aac0a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/realtimenetprots/sipfw/SIP/sipapi/src/sipdialogstate.cpp	Tue Feb 02 01:03:15 2010 +0200
@@ -0,0 +1,651 @@
+// Copyright (c) 2005-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          : sipdialogstate.cpp
+// Part of       : SIPAPI
+// Version       : SIP/4.0 
+//
+
+
+
+#include <uri8.h>
+#include "siperr.h"
+#include "sipinternalerr.h"
+#include "SipAssert.h"
+#include "sipfromheader.h"
+#include "siptoheader.h"
+#include "sipdialogstate.h"
+#include "SipConnectionImplementation.h"
+#include "sipclientconnection.h"
+#include "SipDialogImplementation.h"
+#include "SipDialogAssocImplementation.h"
+#include "siprefresh.h"
+#include "sipservertransaction.h"
+#include "sipinviteclienttransaction.h"
+#include "siprequestelements.h"
+#include "sipresponseelements.h"
+#include "sipcseqheader.h"
+#include "csipdialogresponsesender.h"
+#include "sipconnectioncallback.h"
+#include "sipinvitedialogassoc.h"
+#include "sipstrings.h"
+#include "sipstrconsts.h"
+
+#ifdef CPPUNIT_TEST
+#include "TestCleanupStack.h"
+#endif
+
+// -----------------------------------------------------------------------------
+// CDialogState::~CDialogState
+// -----------------------------------------------------------------------------
+//
+CDialogState::~CDialogState()
+	{
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendNonTargetRefreshRequestL
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction* CDialogState::SendNonTargetRefreshRequestL(
+								const CSIPDialogImplementation& /*aDialog*/,
+                                CSIPDialogAssocImplementation& /*aAssoc*/,
+                                RStringF /*aMethod*/,
+                                CSIPMessageElements* /*aElements*/) const
+    {
+  	return InvalidStateL();
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendInviteL
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendInviteL(const CSIPDialogImplementation& /*aDialog*/,
+                          CSIPInviteDialogAssoc& /*aAssoc*/,
+                          CSIPMessageElements* /*aElements*/) const
+	{
+	return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendPrackL
+// Application might try to send PRACK at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendPrackL(CSIPInviteDialogAssoc& /*aAssoc*/,
+                         CSIPMessageElements* /*aElements*/) const
+    {    
+    return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendUpdateL
+// Application might try to send UPDATE at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendUpdateL(CSIPInviteDialogAssoc& /*aAssoc*/,
+                          CSIPMessageElements* /*aElements*/) const
+    {
+    return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendAckL
+// -----------------------------------------------------------------------------
+//
+void CDialogState::SendAckL(CSIPInviteDialogAssoc& /*aAssoc*/,
+                            const CSIPClientTransaction& /*aTransaction*/,
+	                        CSIPMessageElements* /*aElements*/) const
+    {
+    InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendByeL
+// Application might try to send BYE at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendByeL(CSIPInviteDialogAssoc& /*aAssoc*/,
+                       CSIPMessageElements* /*aElements*/) const
+    {
+	return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendNotifyL
+// Application might try to send NOTIFY at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendNotifyL(CSIPNotifyDialogAssoc& /*aAssoc*/,
+                    	  CSIPMessageElements* /*aElements*/) const
+	{
+	return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendReferL
+// Application might try to send REFER at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendReferL(const CSIPDialogImplementation& /*aDialog*/,
+						 CSIPReferDialogAssoc& /*aAssoc*/,
+                    	 CSIPMessageElements* /*aElements*/) const
+	{
+	return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendResponseL
+// Application might try to send response at any time
+// -----------------------------------------------------------------------------
+//
+void CDialogState::SendResponseL(CSIPDialogImplementation& /*aDialog*/,
+                                 const CSIPResponseElements& /*aElements*/,
+						         TUint32 /*aRequestId*/,
+                                 TBool /*aAffectsDialogState*/,
+                                 TBool /*aTargetRefresh*/) const
+    {    
+    InvalidStateL();
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendSubscribeL
+// Application might try to send SUBSCRIBE at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendSubscribeL(const CSIPDialogImplementation& /*aDialog*/,
+                             CSIPSubscribeDialogAssoc& /*aAssoc*/,
+                             CSIPMessageElements* /*aElements*/,
+                             CSIPRefresh* /*aRefresh*/) const
+    {
+	return InvalidStateL();
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::UpdateL
+// Application might try to update SUBSCRIBE at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::UpdateL(const CSIPDialogImplementation& /*aDialog*/,
+                	  CSIPSubscribeDialogAssoc& /*aAssoc*/,
+                	  CSIPMessageElements* /*aElements*/) const
+	{
+    return InvalidStateL();
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendUnsubscribeL
+// Application might try to send (un)SUBSCRIBE at any time
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction*
+CDialogState::SendUnsubscribeL(CSIPSubscribeDialogAssoc& /*aAssoc*/,
+                               CSIPMessageElements* /*aElements*/) const
+    {    
+    return InvalidStateL();
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::IncomingResponseL
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::IncomingResponseL(CSIPDialogImplementation& /*aDialog*/,
+                                      CSIPResponseElements* /*aElements*/,
+		                              TUint32 /*aRequestId*/,
+                                      TUint32 /*aDialogId*/,
+                                      CConnectionCallback& /*aCallback*/) const
+    {
+	__SIP_ASSERT_LEAVE(EFalse, KErrSIPInvalidDialogState);
+    return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::IncomingResponseL
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::IncomingResponseL(CSIPDialogImplementation& /*aDialog*/,
+                                      CSIPResponseElements* /*aElements*/,
+		                              TUint32 /*aRequestId*/,
+                                      TUint32 /*aRefreshId*/,
+                                      TUint32 /*aDialogId*/,
+                                      CConnectionCallback& /*aCallback*/) const
+    {
+    __SIP_ASSERT_LEAVE(EFalse, KErrSIPInvalidDialogState);
+    return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::IncomingRequestL
+// If the received request is not handled by current state, ignore it.
+// -----------------------------------------------------------------------------
+//
+TBool
+CDialogState::IncomingRequestL(CSIPDialogImplementation& /*aDialog*/,
+                               CSIPServerTransaction* aTransaction,
+                               CConnectionCallback& /*aCallback*/) const                               
+    {
+    delete aTransaction;
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::ErrorOccured
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::ErrorOccured(CSIPDialogImplementation& /*aDialog*/,
+                                 TInt /*aError*/,
+                                 TUint32 /*aRequestId*/,
+                                 CConnectionCallback& /*aCallback*/) const
+    {
+	__ASSERT_DEBUG(EFalse,
+		User::Panic(_L("CDialogState::ErrorOccured() no event handler"),
+					KErrSIPInvalidDialogState));
+
+    return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CDialogState::ErrorOccured
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::ErrorOccured(CSIPDialogImplementation& /*aDialog*/,
+                                 TInt /*aError*/,
+                                 TUint32 /*aRefreshId*/,
+                                 TUint32 /*aRequestId*/,
+                                 CConnectionCallback& /*aCallback*/) const
+    {
+    __ASSERT_DEBUG(EFalse,
+		User::Panic(_L("CDialogState::ErrorOccured(refresh) no event handler"),
+					KErrSIPInvalidDialogState));
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::HandleErrorOccurred
+// If the transaction and association are not found, error is ignored.
+// If ACK wasn't received, the application must still be able to send a BYE to
+// terminate the session, so the dialog must not be terminated.
+// If dialog is confirmed, error won't cause it go into terminated state.
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::HandleErrorOccurred(CSIPDialogImplementation& aDialog,
+                                        TInt aError,
+                                        TUint32 aRequestId,
+                                        CConnectionCallback& aCallback,
+                                        CDialogState& aTerminated) const
+    {
+    __SIP_ASSERT_RETURN_VALUE(aError != KErrNone, EFalse);
+
+    CSIPTransactionBase* ta = NULL;
+    CSIPDialogAssocBase* assoc = NULL;
+
+	CConnectionCallback::TCallbackMethod callback =
+		CConnectionCallback::EErrorOccuredDialogTransaction;
+	if (!aDialog.FindTransactionAndAssoc(aRequestId, &ta, &assoc))	
+		{
+		CSIPConnection* conn = aDialog.Connection();
+		if (conn)
+			{
+			ta = conn->Implementation().FindTransaction(aRequestId);
+			if (ta)			
+				{		
+				callback = CConnectionCallback::EErrorOccuredTransaction;
+				}
+			}
+		}
+
+	if (ta)
+		{
+        if (ta->AffectsDialogState() && aError != KErrSIPNoAckReceived)
+            {
+			aDialog.ChangeState(&aTerminated);
+            }
+        ta->ChangeState(CSIPTransactionBase::ETerminated);
+
+    	if (aError == KInviteTransactionCancelled)
+        	{
+        	__SIP_ASSERT_RETURN_VALUE(!ta->IsSIPClientTransaction(), EFalse);
+        	callback = CConnectionCallback::EInviteCanceled;
+        	assoc = NULL;        	
+        	}
+
+        aCallback.Set(callback, ta, NULL, NULL, assoc, aError);
+        return ETrue;
+        }
+	return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::HandleRefreshErrorOccurred
+// INVITE can't be refreshed, so KInviteTransactionCancelled and
+// KErrSIPNoAckReceived don't need to be handled.
+// -----------------------------------------------------------------------------
+//
+TBool
+CDialogState::HandleRefreshErrorOccurred(CSIPDialogImplementation& aDialog,
+		                                 TInt aError,
+		                                 TUint32 aRefreshId,
+		                                 CConnectionCallback& aCallback,
+		                                 CDialogState& aTerminated) const
+    {
+ 	__SIP_ASSERT_RETURN_VALUE(aError != KErrNone, EFalse);
+
+    CSIPDialogAssocBase* assoc = NULL;
+    CSIPRefresh* refresh = NULL;
+    if (aDialog.FindAssocAndRefresh(aRefreshId, &assoc, &refresh))
+        {
+        CSIPClientTransaction* ta = refresh->Transaction();
+        if (ta)
+            {
+            if (ta->AffectsDialogState() &&
+            	aDialog.State() != CSIPDialog::EConfirmed)
+                {
+                aDialog.ChangeState(&aTerminated);
+                }
+            ta->ChangeState(CSIPTransactionBase::ETerminated);
+            }
+        refresh->ChangeState(CSIPRefresh::ETerminated);		
+        aCallback.Set(CConnectionCallback::EErrorOccuredDialog,
+                      NULL,
+                      NULL,
+                      NULL,
+                      assoc,
+                      aError);
+        return ETrue;
+        }
+
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::RequestReceivedL
+// Application must resolve the actual dialog association to which this request
+// belongs. No dialog association is created by SIP API, it is application's
+// responsibility to create the association.
+// Dialog-related sender is set, so that the response goes through the dialog.
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::RequestReceivedL(CSIPDialogImplementation& aDialog,
+                                     CSIPServerTransaction* aTransaction,                             
+                                     CConnectionCallback& aCallback) const
+    {
+    __SIP_ASSERT_LEAVE(aTransaction, KErrArgument);
+
+    aDialog.UpdateRemoteTargetL(aTransaction->Type(),
+						aTransaction->RequestElements()->MessageElements());
+
+    aTransaction->SetResponseSender(CSIPDialogResponseSender::NewL(aDialog));
+    aCallback.Set(CConnectionCallback::EIncomingRequest,
+                  aTransaction,
+                  NULL,
+                  &aDialog.Dialog());
+    return ETrue;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::SendResponseWithinDialogL
+// -----------------------------------------------------------------------------
+//
+void
+CDialogState::SendResponseWithinDialogL(CSIPDialogImplementation& aDialog,
+                                        const CSIPResponseElements& aElements,
+				    		            TUint32 aRequestId,
+                                        TBool aTargetRefresh) const
+    {
+    CSIPConnection* connection = aDialog.Connection();
+    __ASSERT_ALWAYS(connection, User::Leave(KErrSIPResourceNotAvailable));    
+    
+    connection->Implementation().ClientConnectionL().SendResponseWithinDialogL(
+    													aElements,
+                                                        aRequestId,
+                                                        aDialog.DialogId(),
+                                                        aTargetRefresh);    
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::IncomingResponseWithinDialogL
+// As response isn't refresh related, drop it if no matching transaction exists.
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::IncomingResponseWithinDialogL(
+									CSIPDialogImplementation& aDialog,
+                                    CSIPResponseElements* aElements,
+		                            TUint32 aRequestId,
+                                    TUint32 aDialogId,
+                                    CConnectionCallback& aCallback,
+                                    const CDialogState& aEarly,
+                                    const CDialogState& aConfirmed,
+                                    const CDialogState& aTerminated) const
+    {
+    __SIP_ASSERT_LEAVE(aElements, KErrArgument);
+
+    CSIPTransactionBase* ta = NULL;
+    CSIPDialogAssocBase* assoc = NULL;
+    if (!aDialog.FindTransactionAndAssoc(aRequestId, &ta, &assoc))
+        {
+        delete aElements;
+        return EFalse;
+        }
+
+    __SIP_ASSERT_LEAVE(ta->IsSIPClientTransaction(), KErrGeneral);
+    CSIPClientTransaction* clientTa = static_cast<CSIPClientTransaction*>(ta);
+    CSIPInviteDialogAssoc* newAssoc = CheckIfForkingL(aDialog,
+                                                      aDialogId,
+                                                      aElements,
+                                                      *clientTa);
+	CSIPDialogImplementation* dialog = &aDialog;
+    CConnectionCallback::TCallbackMethod callback =
+    	CConnectionCallback::EIncomingResponse;
+	if (newAssoc)
+		{
+		assoc = newAssoc;
+        dialog = &newAssoc->Dialog().Implementation();
+        callback = CConnectionCallback::EIncomingResponseNewAssoc;
+		}
+	else
+		{
+		aDialog.UpdateRemoteTargetL(ta->Type(), aElements->MessageElements());
+		}
+
+    //Ownership of aElements is taken when leave cannot occur
+    clientTa->SetResponseElements(aElements);
+
+    dialog->UpdateState(*clientTa, aEarly, aConfirmed, aTerminated);
+    aCallback.Set(callback, clientTa, NULL, NULL, assoc);
+
+    return ETrue;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::CheckIfForkingL
+// A forked 1xx or 2xx creates a new dialog and INVITE dialog association. No
+// need to give contact to the new CSIPInviteDialogAssoc. Even the original
+// dialog does not have iContact anymore.
+// -----------------------------------------------------------------------------
+//
+CSIPInviteDialogAssoc*
+CDialogState::CheckIfForkingL(CSIPDialogImplementation& aDialog,
+                              TUint32 aDialogId,
+                              CSIPResponseElements* aElements,
+                              CSIPClientTransaction& aTransaction) const
+    {
+    __SIP_ASSERT_LEAVE(aElements != NULL, KErrArgument);
+
+    CSIPInviteDialogAssoc* assoc = NULL;
+
+    if (aElements->StatusCode() < 300 &&
+        aTransaction.Type() == SIPStrings::StringF(SipStrConsts::EInvite) &&
+        aDialogId != 0 &&
+        aDialogId != aDialog.DialogId())
+        {
+        assoc = CreateForkedInviteAssocLC(aDialog, *aElements);
+
+        //Link transaction with the new assoc
+        static_cast<CSIPInviteClientTransaction&>(aTransaction).
+            AddAssociationL(assoc->Implementation());
+
+        assoc->Dialog().Implementation().SetDialogId(aDialogId);
+        CleanupStack::Pop(assoc);
+
+        assoc->Dialog().Implementation().InitialTransactionStarted(
+        	aTransaction.RequestId());
+        }
+    return assoc;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::CreateForkedInviteAssocLC
+// -----------------------------------------------------------------------------
+//
+CSIPInviteDialogAssoc*
+CDialogState::CreateForkedInviteAssocLC(CSIPDialogImplementation& aDialog,
+                                        CSIPResponseElements& aElements) const
+    {
+    __SIP_ASSERT_LEAVE(aElements.FromHeader() != NULL, KErrArgument);
+    __SIP_ASSERT_LEAVE(aElements.ToHeader() != NULL, KErrArgument);
+    __ASSERT_ALWAYS(aDialog.Connection(),
+        			User::Leave(KErrSIPResourceNotAvailable));
+
+    CSIPFromHeader* from = CSIPFromHeader::NewLC(*aElements.FromHeader());
+    CSIPToHeader* to = CSIPToHeader::NewLC(*aElements.ToHeader());
+
+	CUri8* remoteUri = aDialog.GetUriFromContactL(aElements.MessageElements());
+    if (remoteUri)
+    	{
+    	CleanupStack::PushL(remoteUri);
+    	}
+    else
+    	{
+    	remoteUri = CUri8::NewLC(aDialog.RemoteURI().Uri());
+    	}
+
+    CSIPInviteDialogAssoc* assoc = NULL;
+    if (aDialog.RegistrationContext())
+        {
+        assoc = CSIPInviteDialogAssoc::NewL(*aDialog.Connection(),
+        									remoteUri,
+                                            *aDialog.RegistrationContext(),
+                                            from,
+                                            to);
+        }
+    else
+        {
+        assoc = CSIPInviteDialogAssoc::NewL(*aDialog.Connection(),
+                                            from,
+                                            remoteUri,
+                                            to);
+        }
+    CleanupStack::Pop(3); //remoteUri, to, from
+    CleanupStack::PushL(assoc);        
+    assoc->Dialog().Implementation().CopyCallIdL(aDialog);
+
+    return assoc;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::ResponseToRefreshL
+// For 3xx-6xx to an existing refresh, the aRequestId is not recognized.
+// If ErrorOccured is sent to application, it can get the CSIPRefresh and from
+// it the client transaction and SIP response.
+// -----------------------------------------------------------------------------
+//
+TBool CDialogState::ResponseToRefreshL(CSIPDialogImplementation& aDialog,
+                                       CSIPResponseElements* aElements,
+                                       TUint32 aRequestId,
+                                       TUint32 aRefreshId,
+                                       CConnectionCallback& aCallback,
+                                       const CDialogState& aEarly,
+                                       const CDialogState& aConfirmed,
+                                       const CDialogState& aTerminated) const
+    {
+    __SIP_ASSERT_LEAVE(aElements != NULL, KErrArgument);
+
+    CSIPDialogAssocBase* assoc = NULL;
+    CSIPRefresh* refresh = NULL;
+    CSIPTransactionBase* ta = NULL;
+    if (aDialog.FindAssocAndRefreshL(aRequestId,
+                                     aRefreshId,
+                                     &assoc,
+                                     &refresh,
+                                     &ta))
+        {
+        aDialog.UpdateRemoteTargetL(aElements->CSeqHeader()->Method(),
+        							aElements->MessageElements());
+
+        CSIPClientTransaction* clientTa =
+            static_cast<CSIPClientTransaction*>(ta);
+        CBase* objToDelete = NULL;
+        if (ta)
+            {
+            __SIP_ASSERT_LEAVE(ta->IsSIPClientTransaction(), KErrGeneral);
+            }
+        else
+            {
+        	__SIP_ASSERT_LEAVE(aElements->CSeqHeader() != NULL, KErrArgument);
+            clientTa = aDialog.CreateClientTransactionL(
+					                aElements->CSeqHeader()->Method(),				             
+					                assoc->Implementation(),
+					                refresh);
+			clientTa->SetRequestId(aRequestId);
+			objToDelete = clientTa;
+            }
+
+        clientTa->SetResponseElements(aElements);
+        refresh->SetRefreshIdIfEmpty(aRefreshId);
+        refresh->UpdateRefreshState(aElements->StatusCode());
+        aDialog.UpdateState(*clientTa, aEarly, aConfirmed, aTerminated);
+
+        if (!ta && aElements->StatusCode() >= 300)
+            {
+            aCallback.Set(CConnectionCallback::EErrorOccuredDialog,
+                          NULL,
+                          NULL,
+                          NULL,
+                          assoc,
+                          KErrSIPTerminatedWithResponse,
+                          objToDelete);
+            }
+        else
+            {
+            aCallback.Set(CConnectionCallback::EIncomingResponse,
+                          clientTa,
+                          NULL,
+                          NULL,
+                          assoc,
+                          KErrNone,
+                          objToDelete);
+            }
+        return ETrue;
+        }
+
+    delete aElements;
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CDialogState::InvalidStateL
+// -----------------------------------------------------------------------------
+//
+CSIPClientTransaction* CDialogState::InvalidStateL() const
+	{
+	User::Leave(KErrSIPInvalidDialogState);
+    return NULL;
+	}