realtimenetprots/sipfw/SIP/sipapi/src/SipDialogImplementation.h
author Petteri Saari <petteri.saari@digia.com>
Thu, 25 Nov 2010 13:59:42 +0200
branchMSRP_FrameWork
changeset 58 cdb720e67852
parent 0 307788aac0a8
permissions -rw-r--r--
This release addresses the following issues: 1. The crash bug fix when receiving file 2. Now the sending is based on MSRP messages, there is no longer file receiving or sending. Client sends data as MSRP was designed. 3. Soma MSRP stack was created so that the client told the correct session-id, Symbian stack generated it by itself. This is not allowed, it was changed so that clients tell the session-id (same as used in SIP INVITE). 4. Unnecessary division of data to chunks removed when there is no need to interrupt sending. The message is sent in as few chunks as possible. 5. Stack can now receive files and chunks with ?unlimited? size. Old stack wrote the incoming data to memory and did not utilize disk space until the end of chunk was reached (large chunks from another client crashed it). 6. Now when writing the incoming data to file, it will take into account the byte-range header values. So, this complies with the RFC4975 requirements that stack must be able to handle chunks that come in any sequence. 7. Some buffering changes to outgoing/incoming data. 8. The outgoing data is now checked that it does not contain the created transaction-id before sending the data. 9. MSRP success reports are now implemented and tested against servers. 10. Progress report system fixed so progress is now visible on client (all the way to 100%). 11. Message Cancel receiving / Cancel sending now corrected and made to work as rfc4975 requires. (termination from sender and error code from receiver when cancelling). 12. Bug correction related to messages received not belonging to any session, old stack implementation did send error response, but after response was written it did give the buffer to client anyway. Now corrected.

/*
* 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        : SipDialogImplementation.h
* Part of     : SIP Client
* Interface   : SDK API, SIP Client API
* Version     : 1.0
*
*/




/**
 @internalComponent
*/


#ifndef CSIPDIALOGIMPLEMENTATION_H
#define CSIPDIALOGIMPLEMENTATION_H

// INCLUDES
#include <e32base.h>
#include <stringpool.h>
#include "sipdialog.h"
#include "siptransactionbase.h"
#include "_sipcodecdefs.h"

// FORWARD DECLARATIONS
class CSIPConnection;
class CSIPConnectionImplementation;
class MSIPRegistrationContext;
class CSIPDialogAssocBase;
class CSIPDialogAssocImplementation;
class CSIPInviteDialogAssoc;
class CSIPSubscribeDialogAssoc;
class CSIPNotifyDialogAssoc;
class CSIPReferDialogAssoc;
class CSIPMessageElements;
class CSIPRefresh;
class CSIPClientTransaction;
class CSIPServerTransaction;
class CDialogState;
class CSIPFromToHeaderBase;
class CSIPFromHeader;
class CSIPToHeader;
class CUri8;
class CSIPContactHeader;
class CSIPCallIDHeader;
class CConnectionCallback;

// CLASS DECLARATION

/**
*  Implementation class for managing SIP dialogs.
*/
class CSIPDialogImplementation: public CBase
	{	
	public: // Constructors and destructor

		/**
        * Creates a new CSIPDialogImplementation, and pushes it to cleanupstack.
        * @param aConnImplementation Connection implementation to use with the
        *	new dialog.
        * @param aContext Registration context whose outbound proxy and other
		*        parameters are to be used. If NULL, context isn't used.
		*		 Ownership is not transferred.
        * @return New object, ownership is transferred.
        */
		static CSIPDialogImplementation*
			NewLC(CSIPConnectionImplementation& aConnImplementation,
				  const MSIPRegistrationContext* aContext=0);
						
		/**
		* Two-phased constructor. 
		* This constructor should be used if the user has received
		* SIP request that creates a SIP dialog association.
        *
        * @param aDialog Dialog which creates this CSIPDialogImplementation.
        *	Ownership is transferred.
        * @param aConnImplementation Implementation of the used SIP connection
        * @return New object, ownership is transferred.
		*/
		static CSIPDialogImplementation*
			NewL(CSIPDialog* aDialog,
				 CSIPConnectionImplementation& aConnImplementation);		

		/**
		* Two-phased constructor
		* This constructor should be used if the user has received
		* SIP request that creates a SIP dialog association.
        *
        * @param aDialog Dialog which creates this CSIPDialogImplementation.
        *	Ownership is transferred.
        * @param aConnImplementation Implementation of the used SIP connection
		* @param aContext Registration context whose outbound proxy and other
		*        parameters are to be used.
        * @return New object, ownership is transferred.
		*/
		static CSIPDialogImplementation*
			NewL(CSIPDialog* aDialog,
				 CSIPConnectionImplementation& aConnImplementation,
                 const MSIPRegistrationContext& aContext);		

		/**
		* Destructor
		*/
		~CSIPDialogImplementation();

	public: // New functions

		/**
		* Gets dialog state		
		* @return dialog state
		*/
        CSIPDialog::TState State() const;

		/**
		* Gets all dialog associations. 		
		* @return All dialog associations. Ownership of the array or the items
        *   inside it, is not transferred.
		*/
		const RPointerArray<CSIPDialogAssocBase>& SIPDialogAssociations() const;

        /**
        * Gets used registration context for this dialog
        * @return associated registration or 0-pointer otherwise.
        *   Ownership is not transferred.
        */
        const MSIPRegistrationContext* RegistrationContext() const;
	
        /**
		* Checks if the dialog association belongs to this dialog
		* @param aAssoc a dialog association
		* @return ETrue if belongs, EFalse otherwise
		*/
		TBool IsAssociated(const CSIPDialogAssocBase& aAssoc) const;

        /**
		* Gets the SIP connection used for this dialog
		* @return SIP connection used for the dialog, or 0-pointer if the
        *   connection has been deleted. Ownership isn't transferred.
		*/
        CSIPConnection* Connection();

        /**
		* Gets the SIP connection used for this dialog.
		* @return SIP connection used for the dialog, or 0-pointer if the
        *   connection has been deleted. Ownership isn't transferred.
		*/
        const CSIPConnection* Connection() const;

        /**
		* Gets originator's address
		* @return originator's address (From-header)
		*/
		const CSIPFromHeader& FromHeader() const;

		/**
		* Gets recipient's address
		* @return recipient's address (To-header)
		*/
		const CSIPToHeader& ToHeader() const;

		/**
		* Gets remote-uri used during dialog creation
		* @return Remote target uri
		*/
		const CUri8& RemoteURI() const;
		
		/**
		* Gets the Call-ID of the dialog.
		* @return Call-ID
		*/
		const CSIPCallIDHeader& CallIdL() const;

		/**
		* Get the Call-ID from SIP Client and store it.		
		*/
		void StoreCallIdL();

		/**
		* Copies Call-ID from aDialog.
		* @pre iCallID == NULL
		* @param aDialog CSIPDialogImplementation where the Call-ID is copied
		*	from.		
		*/
		void CopyCallIdL(const CSIPDialogImplementation& aDialog);

        /**
		* Compares this object to another object
		* @param aDialog CSIPDialogImplementation object to compare
		* @returns ETrue if the objects are equal, otherwise EFalse
		*/
        TBool operator==(const CSIPDialogImplementation& aDialog) const;
        
        TInt ReuseInitialRequestData();
        
        CSIPDialog& Dialog();

        TBool IncomingResponseL(CSIPResponseElements* aElements,
		                        TUint32 aRequestId,
					            TUint32 aDialogId,
                                CConnectionCallback& aCallback);

        TBool IncomingResponseL(CSIPResponseElements* aElements,
		                        TUint32 aRequestId,
                                TUint32 aRefreshId,
					            TUint32 aDialogId,
                                CConnectionCallback& aCallback);

        TBool ResponseToRefreshL(CSIPResponseElements* aElements,
                                 TUint32 aRequestId,
                                 TUint32 aRefreshId,
                                 CConnectionCallback& aCallback,
                                 const CDialogState& aEarly,
                                 const CDialogState& aConfirmed,
                                 const CDialogState& aTerminated,
                                 TBool aIsConfirmed=EFalse);

		/**
		* Handle an incoming SIP request.
        *
        * @param aTransaction Server transaction, ownership is transferred.
        * @param aCallback For selecting a callback function and its parameters
        * @return ETrue if the caller should make a callback function call,
    	*   EFalse otherwise
		*/
        TBool IncomingRequestL(CSIPServerTransaction* aTransaction,
                               CConnectionCallback& aCallback);                              

        TBool ErrorOccured(TInt aError,
                           TUint32 aRequestId,
                           CConnectionCallback& aCallback);
        
        TBool ErrorOccured(TInt aError,             
                           TUint32 aRefreshId,
                           TUint32 aRequestId,
                           CConnectionCallback& aCallback);

		/**
		* Obtains the dialog id.
        *        
        * @return TSIPDialogId
		*/
        TUint32 DialogId() const;

		/**
		* Set the dialog id.
        *
        * @param aDialogId Dialog id        
		*/
        void SetDialogId(TUint32 aDialogId);

		/**
		* Searches for a transaction having the specified id.
        *
        * @param aRequestId Identifier used for searching the transaction.        
        * @return CSIPTransactionBase The found transaction or NULL if
        *	transaction wasn't found. Ownership is not transferred.
		*/
        CSIPTransactionBase* FindTransaction(TUint32 aRequestId) const;

		/**
		* Searches for a transaction having the specified id.
        *
        * @param aRequestId Identifier used for searching the transaction
        * @param aTransaction OUT: pointer is set to the found transaction
        * @param aAssoc OUT: pointer is set to the dialog association related
        *	to the found transaction
        * @return ETrue if found. EFalse if not found, in this case aTransaction
        *	and aAssoc haven't been set.
		*/
        TBool FindTransactionAndAssoc(TUint32 aRequestId,
                                    CSIPTransactionBase** aTransaction,
                                    CSIPDialogAssocBase** aAssoc) const;

        /**
		* Searches for the dialog association and refresh by the aRefreshId.
        *
        * @param aRefreshId RefreshId
        * @param OUT: Dialog association, if found
        * @param OUT: aRefresh CSIPRefresh, if found
        * @return ETrue if a matching refresh and dialog association were
        *   found, EFalse otherwise
		*/
        TBool FindAssocAndRefresh(TUint32 aRefreshId,
                                  CSIPDialogAssocBase** aAssoc,
                                  CSIPRefresh** aRefresh) const;

        /**
		* Searches for the dialog association, refresh and transaction.
        * Both aRequestId and aRefreshId are used for search, because either
        * of them might not yet be stored in the SIP API.        
        *
        * @param aRequestId RequestId
        * @param aRefreshId RefreshId
        * @param aAssoc OUT: Dialog association, if found
        * @param aRefresh OUT: CSIPRefresh, if found
        * @param aTransaction OUT: Transaction, if found
        * @return ETrue if a matching dialog association and refresh were
        *   found, EFalse otherwise
		*/
        TBool FindAssocAndRefreshL(TUint32 aRequestId,
                                   TUint32 aRefreshId,
                                   CSIPDialogAssocBase** aAssoc,
                                   CSIPRefresh** aRefresh,
                                   CSIPTransactionBase** aTransaction) const;
        /**
		* Finds a refresh by first the given request ID and the the refresh ID. 
		* All dialog associations are searched through.
        *
        * @param aRequestId RequestId associated with the refresh instance
        * @param aRefreshId RefreshId associated with the refresh instance
        * 
        * @return refresh or NULL if not found, ownership is not transferred.
		*/
        CSIPRefresh* FindRefresh(TUint32 aRequestId,
                                 TUint32 aRefreshId) const;

        /**
		* Changes the dialog's current state.
        *
		* @param aNewState State which dialog enters		
		*/
        void ChangeState(const CDialogState* aNewState);

        /**
		* Connection state is no longer available.        		
		*/
        void ConnectionLost();

        CSIPClientTransaction*
            SendNonTargetRefreshRequestL(CSIPDialogAssocImplementation& aAssoc,
                                         RStringF aMethod,
                                         CSIPMessageElements* aElements) const;

        CSIPClientTransaction*
            SendInviteL(CSIPInviteDialogAssoc& aAssoc,                                            
                        CSIPMessageElements* aElements) const;

        CSIPClientTransaction*
            SendPrackL(CSIPInviteDialogAssoc& aAssoc,                                            
                       CSIPMessageElements* aElements) const;

        CSIPClientTransaction*
            SendUpdateL(CSIPInviteDialogAssoc& aAssoc,
                        CSIPMessageElements* aElements) const;

        void SendAckL(CSIPInviteDialogAssoc& aAssoc,
                      const CSIPClientTransaction& aTransaction,
                      CSIPMessageElements* aElements) const;
        
        CSIPClientTransaction*
            SendSubscribeL(CSIPSubscribeDialogAssoc& aAssoc,
                           CSIPMessageElements* aElements,
                           CSIPRefresh* aRefresh) const;

		CSIPClientTransaction*
			UpdateL(CSIPSubscribeDialogAssoc& aAssoc,
			        CSIPMessageElements* aElements) const;
			                      
        CSIPClientTransaction*
            SendUnsubscribeL(CSIPSubscribeDialogAssoc& aAssoc,
                             CSIPMessageElements* aElements) const;

		/**
		* Send a SIP response to network. Application can't send a 100 response.
		* That has already been checked by CSIPResponseElements.
		* @param aElements Response elements
		* @param aRequestId Identifies the request to which this response is for
		* @param aAffectsDialogState Tells if the response can cause dialog to
		*		 enter another state.
		* @param aTargetRefresh Tells if this is a response to a target refresh
		*		 request.
		*/
        void SendResponseL(const CSIPResponseElements& aElements,
						   TUint32 aRequestId,
                           TBool aAffectsDialogState,
                           TBool aTargetRefresh=EFalse);

        CSIPClientTransaction* SendByeL(CSIPInviteDialogAssoc& aAssoc,                                        
                                        CSIPMessageElements* aElements) const;

        CSIPClientTransaction* SendCancelL(TUint32 aRequestId) const;

		CSIPClientTransaction*
			SendNotifyL(CSIPNotifyDialogAssoc& aAssoc,
                        CSIPMessageElements* aElements) const;
                        
        CSIPClientTransaction*
			SendReferL(CSIPReferDialogAssoc& aAssoc,
                       CSIPMessageElements* aElements) const;

		/**
		* Add a dialog association to this dialog. A dialog can have only one
		* INVITE association at a time.
		* @param aAssoc Dialog association
		* @param aType Type of the dialog association
		* @leave KErrAlreadyExists If aAssoc is already associated with this
		*		 dialog or if attempting to add another INVITE dialog
		*		 association.
		*/
        void AddAssocL(CSIPDialogAssocBase& aAssoc, RStringF aType);

		/**
		* Remove a dialog association from this dialog.
		* When there are no associations left, the dialog is deleted.		 
		* @param aAssoc Dialog association		
		*/
        void RemoveAssoc(const CSIPDialogAssocBase& aAssoc);

        /**
		* Informs CSIPDialogImplementation that the associated CSIPConnection
		* has been deleted. After this the CSIPDialogImplementation object can't
		* be used anymore.
		*/
        void ConnectionDeleted();

        void CheckNoTransactionExistsL() const;        

		/**
		* Send a request that creates the dialog.
        *
        * @param aAssoc Dialog association which sends the request.
        * @param aElements Message elements, can be NULL. Ownership is
        *	transferred.
        * @param aRefresh Refresh object, if the request is to be refreshed.
        *	Ownership isn't transferred.
        * @return Client transaction. Ownership is transferred.
		*/
		CSIPClientTransaction*
			SendDialogCreatingRequestL(CSIPDialogAssocImplementation& aAssoc,
									   CSIPMessageElements* aElements,
									   CSIPRefresh* aRefresh=0);
									  
		/**
		* Sends a SIP request described by aElements withing dialog and creates
		* a client transaction representing the request.
        *
        * @pre aMethod.Length() > 0
        *
        * @param aAssoc Dialog association with which the transaction will be
        *	associated
        * @param aMethod SIP request method
        * @param aElements Optional SIP message headers and body, can be NULL.
        *	Ownership is transferred.
        * @return CSIPClientTransaction new transaction, ownership is
        *	transferred
		*/
        CSIPClientTransaction*
        	SendRequestInDialogL(CSIPDialogAssocImplementation& aAssoc,
                                 RStringF aMethod,
                                 CSIPMessageElements* aElements) const;

		/**
		* Creates a client transaction.
        *
        * @param aType Transaction type        
        * @param aAssoc Dialog association with which the transaction will be
        *	associated
        * @param aRefresh If transaction is refreshed, this points to a
        *   CSIPRefresh, otherwise this is NULL. Ownership is not transferred.
        * @return CSIPClientTransaction new transaction, ownership is
        *	transferred
		*/
        CSIPClientTransaction*
            CreateClientTransactionL(RStringF aType,                                     
                                     CSIPDialogAssocImplementation& aAssoc,
                                     CSIPRefresh* aRefresh) const;

        /**
		* Sets the dialog related headers when a SIP request that creates a SIP
		* dialog association, is sent. Headers can be set only once.
		* If aTo is NULL, aRemoteUri is used as To-header.
        *
        * @pre aRemoteUri != NULL &&
        *	   iRemoteUri == NULL &&
        *      iFrom == NULL &&
        *      iTo == NULL &&
        *      iContact == NULL
        * @param aFrom Originator's address, can be NULL. Ownership is
        *   transferred.
        * @param aTo Recipient's address, can be NULL. Ownership is transferred
        * @param aRemoteURI Remote target URI. ownership is transferred.
        * @param aContact Contact to be used in dialog creation, can be NULL.
        *		 Must be given only if user intends to re-direct future
        *		 requests. Ownership is transferred.
		*/
        void SetHeadersL(CSIPFromHeader* aFrom,
                         CSIPToHeader* aTo,
                         CUri8* aRemoteUri,
                         CSIPContactHeader* aContact);

        /**
		* Stores the To and From headers of the server transaction and marks
        * the transaction to affect the dialog state.
        *
        * @pre
        * @param aTransaction Server transaction which causes a dialog to be
        *   created.
		*/		
        void InitialTransactionReceivedL(CSIPServerTransaction& aTransaction);

		const CSIPContactHeader* ContactHeader() const;

		/**
		* The dialog creating transaction has been initiated.
		*
		* @pre aRequestId != 0
		* @pre iInitialRequestId == 0
		* @post iInitialRequestId == aRequestId
		*
		* @param aRequestId RequestId of the transaction that creates the dialog
		*/
        void InitialTransactionStarted(TUint32 aRequestId);

		/**
		* Determine if an incoming NOTIFY causes dialog to enter confirmed
		* state.
		*
		* @return ETrue Dialog should enter confirmed state at the reception of
		*	NOTIFY. EFalse otherwise.		
		*/
		TBool DoesNotifyConfirmDialog() const;

		/**
		* Searches if there are dialog related requests, that are being
		* refreshed. If found, those that are in state CSIPRefresh::EInactive
		* are moved into CSIPRefresh::EActive state.		
		*/
		void ChangeRefreshesToActive() const;
		
		/**
		* If aMethod is a target refresh request, and aElements contains a
		* Contact-header, the remote target URI of the dialog is updated.
		*
		* @param aMethod SIP request method
		* @param aElements Message elements
		*/
		void UpdateRemoteTargetL(RStringF aMethod,
								 const CSIPMessageElements& aElements);

		/**
		* Creates a copy of the URI in Contact-header, if aElements contains
		* exactly one Contact-header.
		*		
		* @param aElements Message elements
		* @return Copy of the URI in Contact, or NULL if aElements doesn't have
		*   one Contact header. Ownership is transferred.
		*/
		CUri8* GetUriFromContactL(const CSIPMessageElements& aElements) const;

		/**
		* Based on the received response, dialog may enter another state.
		* A 100 response should not have a To-tag, so dialog won't
		* enter Early-state when a 100 is received.
        *
        * @param aTransaction Client transaction carrying the received response
        * @param aEarly Early-state of the dialog state machine
        * @param aConfirmed Confirmed-state of the dialog state machine
        * @param aTerminated Terminated-state of the dialog state machine
		*/		
        void UpdateState(const CSIPClientTransaction& aTransaction,
                         const CDialogState& aEarly,
                         const CDialogState& aConfirmed,
                         const CDialogState& aTerminated);

		/**
		* If the local tag is not yet known, obtain it from the SIP server via
		* an ITC operation.
		*
		* @param aClientInitiatedDialog
		*	ETrue if the dialog creating request was sent by the local endpoint
		*	EFalse otherwise
		*/
        void FillLocalTagL(TBool aClientInitiatedDialog) const;

        /**
		* If aToHeader has a tag parameter, the tag is stored as a remote-tag
		* in the To-header of the dialog.
		*
		* @param aToHeader To-header of a response from remote endpoint.		
		*/
        void FillRemoteTagL(const CSIPToHeader& aToHeader) const;
        
        TUint32 RegistrationId() const;

    private: // Constructors, for internal use

        CSIPDialogImplementation(
        	CSIPConnectionImplementation& aConnImplementation);

        CSIPDialogImplementation(
        	CSIPConnectionImplementation& aConnImplementation,
            const MSIPRegistrationContext& aContext);

        void ConstructL(CSIPDialog* aDialog);

	private: // New functions, for internal use

        /**
		* Checks that CSIPConnection is available for use (not NULL). If
        * iConnection is NULL, it means user has deleted a resource needed by
        * CSIPDialogImplementation, and this function leaves.
		*/
        void CheckConnectionL() const;

		CSIPConnection* GetConnection() const;

        /**
		* Returns the current dialog state. If state handler is not available,
        * meaning user has deleted a resource needed by
        * CSIPDialogImplementation, this function leaves.        
		*/
        const CDialogState& StateL() const;        

        /**
		* Checks whether the dialog has an INVITE assocation.
        *
        * @return ETrue There is an INVITE association, EFalse otherwise
		*/
        TBool HasInviteAssoc() const;

		/**
		* The dialog creating transaction has been created. The SIP responses
		* to aTransaction will affect the state of dialog.
		*
		* @pre iInitialRequestId == 0
		*
		* @param aTransaction Transaction that creates the dialog.
		*/
        void InitialTransactionStarted(CSIPTransactionBase& aTransaction);

	private: // Data

		//Dialog instance, Owned.
		CSIPDialog* iDialog;

        //DialogId received from SIP client
        TUint32 iDialogId;

        //Current dialog state. Not owned.
        const CDialogState* iState;

        //Initially zero. Is the requestId of the transaction that creates the
        //dialog. RequestId is kept even after the transaction has been deleted.
        TUint32 iInitialRequestId;

        //Connection implementation associated with the dialog. Not owned.
        //If NULL, application has deleted the CSIPConnection associated with
        //the CSIPConnectionImplementation, and this CSIPDialogImplementation
        //is useless as it can't use the CSIPConnection anymore.
        CSIPConnectionImplementation* iConnection;

        //Associated registration, can be NULL. Not owned.
        const MSIPRegistrationContext* iRegistration;

        //Dialog associations. Not owned.
        RPointerArray<CSIPDialogAssocBase> iDialogAssocs;

        //From header of the request that created the dialog. Owned.
        CSIPFromHeader* iFrom;
        //To header of the request that created the dialog. Owned.
        CSIPToHeader* iTo;

        //Remote target, can be NULL. Owned.
        CUri8* iRemoteUri;

        //Contact given by application. Will be deleted after the dialog
        //creating request has been sent. Owned.
        CSIPContactHeader* iContact;

        //Call-ID of the dialog. Owned.
        CSIPCallIDHeader* iCallID;

		TBool iStringPoolOpened;
                
	private: // For testing purposes

	    UNIT_TEST(CSIP_Test)
        UNIT_TEST(CSIPInviteDialogAssoc_Test)
        UNIT_TEST(CSIPSubscribeDialogAssoc_Test)
        UNIT_TEST(CSIPReferDialogAssoc_Test)
        
		void __DbgTestInvariant() const;

	};

#endif