simpleengine/siputils/src/simplerequest.cpp
changeset 0 c8caa15ef882
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/simpleengine/siputils/src/simplerequest.cpp	Tue Feb 02 01:05:17 2010 +0200
@@ -0,0 +1,1056 @@
+/*
+* Copyright (c) 2006 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:    sip request
+*
+*/
+
+
+
+
+// INCLUDE FILES
+
+// sip api
+#include <sip.h>
+#include <sipconnection.h>
+#include <sipconnectionobserver.h>
+#include <sipresponseelements.h>
+#include <siprequestelements.h>
+#include <sipclienttransaction.h>
+#include <siprefresh.h>
+#include <sipdialog.h>
+#include <sipsubscribedialogassoc.h>
+
+// simple
+#include "simplesipconnection.h"
+#include "simplerefreshtimer.h"
+#include "simpleerrors.h"
+#include "simplecommon.h"
+
+#ifdef _DEBUG
+#include "simpledebugutils.h"
+#endif
+
+// time limits in seconds
+const TInt KRefreshLimit = 1200; // limit when KRefreshBefore is used
+const TInt KRefreshBefore = 600; // how much before expiration a refresh is done
+const TInt KRetryBefore = 300; // how much before expiration a retry is done
+const TInt KMinorDelay = 5; // garbage collection and retry delay
+const TInt KDeleteDelay = 1; // delay before deleting a subscription request
+const TInt KMinorExpiry = 65; // default expiration time for re-try 
+
+
+// ================= MEMBER FUNCTIONS =======================
+//
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::CSimpleRequest
+// -----------------------------------------------------------------------------
+
+CSimpleRequest::CSimpleRequest(
+    MSimpleSipConnCallback& aEngine,
+    MSimpleEngineRequest& aReq,
+    TSimpleSipReqType aType,
+    TUint aExpires )
+    : iEngine(aEngine), iReq(aReq),
+      iTrans(NULL), iSipRefresh(NULL), iType(aType),
+      iDialog(NULL), iStatus(KErrNone),
+      iState(ESimpleInit), iRetryAfter(0),
+      iReason(KErrNone), iExpires(aExpires),
+      iPendingState(ENoPending), iData(NULL),
+      iGivenETag( EFalse ), iSubscriptionState(MSimpleEngineRequest::ESimpleStateNone),
+      iRespCount(0)
+        {}
+
+CSimpleRequest::~CSimpleRequest()
+    {
+    StopExpiryTimer();
+    StopRefreshTimer();
+    delete iSipRefresh;
+    delete iRefreshTimer;
+    delete iExpiryTimer;
+    delete iETag;
+    delete iDialog;
+    delete iTrans;
+    delete iData;
+    delete iRecipientId;
+    delete iRequestContentType;
+    }
+
+// ----------------------------------------------------------
+// CSimpleRequest::NewL
+// ----------------------------------------------------------
+//
+CSimpleRequest* CSimpleRequest::NewL(
+    MSimpleSipConnCallback& aEngine,
+    MSimpleEngineRequest& aReq,
+    TSimpleSipReqType aType,
+    TUint aExpires )
+    {
+    CSimpleRequest* self = new (ELeave) CSimpleRequest(
+        aEngine,
+        aReq,
+        aType,
+        aExpires );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: NewL this=%d" ), (TInt)self );
+#endif
+    return self;
+    }
+
+// ----------------------------------------------------------
+// CSimpleRequest::ConstructL
+// ----------------------------------------------------------
+//
+void CSimpleRequest::ConstructL(  )
+    {
+    iExpiryTimer = new (ELeave) CSimpleExpiryTimer( iEngine, *this );
+    iRefreshTimer = new (ELeave) CSimpleRefreshTimer( iEngine, *this );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Destroy
+// -----------------------------------------------------------------------------
+void CSimpleRequest::Destroy()
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: DESTROY this=%d"), (TInt)this );
+#endif
+    iLink.Deque();
+    delete this;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::DestroyStart
+// -----------------------------------------------------------------------------
+void CSimpleRequest::DestroyStart()
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: DestroyStart this=%d"), (TInt)this );
+#endif
+    SetReqState( ESimpleDeleting );
+    // Yield control to active scheduler before deleting a request. The request may have
+    // called multiple callback methods, so it's safer to break the call.
+    StartExpiryTimer( KDeleteDelay );
+    StopRefreshTimer();
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Request
+// -----------------------------------------------------------------------------
+MSimpleEngineRequest& CSimpleRequest::Request()
+    {
+    return iReq;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Transaction
+// -----------------------------------------------------------------------------
+CSIPClientTransaction* CSimpleRequest::Transaction()
+    {
+    return iTrans;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetTransaction
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetTransaction( CSIPClientTransaction* aTrans )
+    {
+    delete iTrans;
+    iTrans = aTrans;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Refresh
+// -----------------------------------------------------------------------------
+CSIPRefresh* CSimpleRequest::Refresh()
+    {
+    return iSipRefresh;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Match
+// -----------------------------------------------------------------------------
+TBool CSimpleRequest::Match( CSIPClientTransaction& aTrans )
+    {
+    CSIPClientTransaction* tr = Transaction();
+    return ( tr && aTrans == *tr ? ETrue : EFalse );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Match
+// -----------------------------------------------------------------------------
+TBool CSimpleRequest::Match( TSimpleSipReqType aType ) const
+    {
+    return ( aType == iType ? ETrue : EFalse );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Match
+// -----------------------------------------------------------------------------
+TBool CSimpleRequest::Match( const CSIPDialog& aDialog ) const
+    {
+    return aDialog.IsAssociated( *iDialog );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Match
+// -----------------------------------------------------------------------------
+TBool CSimpleRequest::Match( MSimpleEngineRequest& aReq ) const
+    {
+    if ( aReq.OpId() == iReq.OpId() )
+        {
+        return ETrue;
+        }
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Complete
+// -----------------------------------------------------------------------------
+void CSimpleRequest::Complete( TInt aStatus )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: Complete %d this=%d"),aStatus, (TInt)this );
+#endif
+
+    if ( iState == ESimpleDeleting )
+        {
+        // Tell to other DLL that this can be deleted now.
+        iReq.Complete( aStatus );
+        return;
+        }
+    if (( aStatus == KErrDisconnected ) ||
+        ( aStatus == KErrTimedOut && iType != EReqPublish ))
+        {
+        // This is a serious network problem
+        SetRetryAfter( 0 );
+        StopExpiryTimer();
+        StopRefreshTimer();
+        SetReqState( ESimpleFailed );
+        iReq.Complete( aStatus );
+        return;
+        }
+
+    if ( iType == EReqRegister )
+        {
+        // stop expiry timer
+        StopExpiryTimer();
+        // Complete the client request
+        iReq.Complete( aStatus );
+        }
+    else if ( iType == EReqPublish )
+        {
+        DoCompletePublication( aStatus );
+        }
+    else if ( EReqSendIM == iType )
+        {
+        iReq.Complete( aStatus );
+        return;
+        }
+    else if ( EReqReceiveIM == iType )
+        {
+        TRAP_IGNORE( iReq.SetResponseDataL( Data() ) )
+        TRAP_IGNORE( iReq.SetRecipientL( RecipientL() ) )
+        iReq.Complete( aStatus );
+        return;
+        }
+    else if ( iType == EReqSubscribe || iType == EReqSubscribeList || iType == EReqSubscribeWinfo )
+        {
+        if ( Request().ResponseMethod( ) == MSimpleEngineRequest::ENotify )
+            {
+            DoCompleteNotification( aStatus );
+            }
+        else
+            {
+            DoCompleteSubscription( aStatus );
+            }
+        }
+    // EReqListEvents will go to CompleteEvent method.
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::CompleteEvent
+// -----------------------------------------------------------------------------
+void CSimpleRequest::CompleteEvent( )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: CompleteEvent this=%d"),(TInt)this );
+#endif
+    iReq.Complete( KErrNone );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetRefreshTime
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetRefreshTime( TUint aTime )
+    {
+    iRefreshTime = aTime;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::RefreshTime
+// -----------------------------------------------------------------------------
+TUint CSimpleRequest::RefreshTime( ) const
+    {
+    return iRefreshTime;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::StartRefreshTimer
+// -----------------------------------------------------------------------------
+void CSimpleRequest::StartRefreshTimer( TUint aTime )
+    {
+
+    // Refresh a little bit earlier, KRefreshBefore sec
+    // If expiration time is greater than 1200 seconds,
+    // the interval is set to 600 seconds lower than expiration time.
+    // Otherwise, interval is set to half of the expiration time.
+
+    if ( aTime )
+        {
+        // special time used for error recovery
+        iRefreshTimer->Start( aTime );
+        return;
+        }
+
+    // This time is for garbage collection
+    TUint myTime = iRefreshTime + KMinorDelay;
+
+    if ( iReq.IsRefresh() )
+        {
+        // This time is for regular PUBLISH refresh
+        if ( iRefreshTime > KRefreshLimit )
+            {
+            myTime = iRefreshTime - KRefreshBefore;
+            }
+        else
+            {
+            myTime = iRefreshTime / 2 ;
+            }
+        }
+
+    iRefreshTimer->Start( myTime );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::StopRefreshTimer
+// -----------------------------------------------------------------------------
+void CSimpleRequest::StopRefreshTimer()
+    {
+    if ( iRefreshTimer )
+        {
+        iRefreshTimer->Cancel();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::StartExpiryTimer
+// -----------------------------------------------------------------------------
+void CSimpleRequest::StartExpiryTimer( TUint aTime )
+    {
+    iExpiryTimer->Start( aTime );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::StopExpiryTimer
+// -----------------------------------------------------------------------------
+void CSimpleRequest::StopExpiryTimer()
+    {
+    if ( iExpiryTimer )
+        {
+        iExpiryTimer->Cancel();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::RetryTime
+// -----------------------------------------------------------------------------
+TUint CSimpleRequest::RetryTime()
+    {
+    // Retry (refresh) time in error recovery retry.
+
+    // If retry-after SIP header value is present then use it.
+
+    // If retry-after is not available:
+    // A quarter of expires value (=half of the current time until expires),
+    // maximum 300 seconds.
+
+    // This time policy is in line with SIP stack and presence refreshment policy.
+    // Too long time interval is checked outside this method,
+
+    TUint myTime(0);
+
+    if ( iRetryAfter )
+        {
+        // Retry-after header value
+        myTime = iRetryAfter;
+        }
+    else if ( iRefreshTime > KRefreshLimit )
+        {
+        myTime = KRetryBefore;
+        }
+    else
+        {
+        myTime = iRefreshTime / 4 ;
+        }
+    // Minun value is few seconds.
+    return ( myTime > KMinorDelay ? myTime : KMinorDelay );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::RetryExpiryTime
+// -----------------------------------------------------------------------------
+TInt CSimpleRequest::RetryExpiryTime()
+    {
+    // Expiry for retry in error cases, should be calculated at the same time as
+    // retry time above.
+    TInt myTime(0);
+
+    if ( iRefreshTime > KRefreshLimit )
+        {
+        myTime = KRefreshBefore;
+        }
+    else
+        {
+        myTime = iRefreshTime / 2 ;
+        }
+
+    return ( myTime > KMinorExpiry ? myTime : KMinorExpiry );
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetETag
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetETag( HBufC8* aTag )
+    {
+    // ownership is transferred
+    delete iETag;
+    iETag = aTag;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::ETag
+// -----------------------------------------------------------------------------
+TPtrC8 CSimpleRequest::ETag() const
+    {
+    return iETag ? iETag->Des(): TPtrC8();
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Status
+// -----------------------------------------------------------------------------
+TUint CSimpleRequest::Status() const
+    {
+    return iStatus;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetStatus
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetStatus( TUint aVal )
+    {
+    iStatus = aVal;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Dialog
+// -----------------------------------------------------------------------------
+CSIPSubscribeDialogAssoc* CSimpleRequest::Dialog( )
+    {
+    return iDialog;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetDialog
+// -----------------------------------------------------------------------------
+ void CSimpleRequest::SetDialog( CSIPSubscribeDialogAssoc* aDialog )
+    {
+    delete iDialog;
+    iDialog = NULL;
+    iDialog = aDialog;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::ReqState
+// -----------------------------------------------------------------------------
+CSimpleRequest::TSimpleReqState CSimpleRequest::ReqState() const
+    {
+    return iState;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetReqState
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetReqState( CSimpleRequest::TSimpleReqState aState )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: SetReqState %d->%d this=%d"),
+        iState, aState,(TInt)this );
+#endif
+    iState = aState;
+    if ( aState == ESimpleFailed || aState == ESimpleComplete || aState == ESimpleDeleting )
+        {
+        iSubscriptionState = MSimpleEngineRequest::ESimpleStateTerminated;
+        }        
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetThrottleTime
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetThrottleTime( TUint aSeconds )
+    {
+    TTime myTime;
+    myTime.HomeTime();
+    myTime += TTimeIntervalSeconds( aSeconds );
+    iThrottleTime = myTime;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::ThrottleTime
+// -----------------------------------------------------------------------------
+TTime CSimpleRequest::ThrottleTime( )
+    {
+    return iThrottleTime;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::RetryAfter
+// -----------------------------------------------------------------------------
+TUint CSimpleRequest::RetryAfter( )
+    {
+    return iRetryAfter;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetRetryAfter
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetRetryAfter( TUint aVal )
+    {
+    iRetryAfter = aVal;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetReason
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetReason( TInt aVal )
+    {
+    iReason = aVal;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Reason
+// -----------------------------------------------------------------------------
+TInt CSimpleRequest::Reason( )
+    {
+    return iReason;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::PlusErrNotify
+// -----------------------------------------------------------------------------
+void CSimpleRequest::PlusErrCount( )
+    {
+    ++iErrNotify;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::ResetErrNotify
+// -----------------------------------------------------------------------------
+void CSimpleRequest::ResetErrCount( )
+    {
+    iErrNotify = 0;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::ErrNotify
+// -----------------------------------------------------------------------------
+TInt CSimpleRequest::ErrCount( )
+    {
+    return iErrNotify;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::PendingState
+// -----------------------------------------------------------------------------
+CSimpleRequest::TSimplePendingSubState CSimpleRequest::PendingState( )
+    {
+    return iPendingState;
+    }
+    
+// -----------------------------------------------------------------------------
+// CSimpleRequest::AddPendingState
+// -----------------------------------------------------------------------------
+void CSimpleRequest::AddPendingState( CSimpleRequest::TSimplePendingSubState aVal )
+    {   
+    if (( aVal == EPendingModify && iPendingState == EPendingRefresh ) ||
+        ( aVal == EPendingRefresh && iPendingState == EPendingModify ))
+        {
+        SetPendingState( EPendingModifyAndRefresh );
+        }
+    else if ( aVal == EPendingModify && iPendingState == ENoPending )
+        {
+        SetPendingState( EPendingModify );
+        }   
+    else if ( aVal == EPendingRefresh && iPendingState == ENoPending )
+        {
+        SetPendingState( EPendingRefresh );
+        }                          
+    }    
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetPendingState
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetPendingState( CSimpleRequest::TSimplePendingSubState aVal )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: iPendingState=%d"), aVal );
+#endif    
+    iPendingState = aVal;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::Data
+// -----------------------------------------------------------------------------
+TPtrC8 CSimpleRequest::Data() const
+    {
+    return iData ? iData->Des() : TPtrC8();
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetRecipientL
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetRecipientL( const TDesC8& aRecipientId )
+    {
+    delete iRecipientId;
+    iRecipientId = NULL;
+    iRecipientId = aRecipientId.AllocL();
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::RecipientL
+// -----------------------------------------------------------------------------
+TPtrC8 CSimpleRequest::RecipientL() const
+    {
+    return iRecipientId ? iRecipientId->Des() : TPtrC8();
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetDataL
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetDataL( const TDesC8& aData )
+    {
+    delete iData;
+    iData = NULL;
+    iData = aData.AllocL();
+    }
+    
+// -----------------------------------------------------------------------------
+// CSimpleRequest::GivenETag
+// -----------------------------------------------------------------------------
+TBool CSimpleRequest::GivenETag() const
+    {
+    return iGivenETag;
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::SetGivenETag
+// -----------------------------------------------------------------------------
+void CSimpleRequest::SetGivenETag( TBool aVal )
+    {
+    iGivenETag = aVal;
+    }    
+    
+// ----------------------------------------------------------
+// CSimpleRequest::RequestContentType
+// ----------------------------------------------------------
+//
+TPtrC8 CSimpleRequest::RequestContentType()
+    {
+    return iRequestContentType ? iRequestContentType->Des() : TPtrC8();
+    }
+    
+// ----------------------------------------------------------
+// CSimpleRequest::SetRequestContentTypeL
+// ----------------------------------------------------------
+//
+void CSimpleRequest::SetRequestContentTypeL( const TDesC8& aData )
+    {
+    delete iRequestContentType;
+    iRequestContentType = NULL;
+    iRequestContentType = aData.AllocL(); 
+    }
+    
+// ----------------------------------------------------------
+// CSimpleRequest::SipSubscriptionState
+// ----------------------------------------------------------
+//
+MSimpleEngineRequest::TSimpleSipSubscriptionState CSimpleRequest::SipSubscriptionState()
+    {
+    return iSubscriptionState; 
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::DoCompleteSubscription
+// -----------------------------------------------------------------------------
+void CSimpleRequest::DoCompleteSubscription( TInt aStatus )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: DoCompleteSubscription %d"), aStatus);
+#endif
+
+    // This is a part of the the state machine for subscriptions.
+    // Mainly those cases that call API callback
+
+    TSimpleReqState orig = iState;
+
+    // Stop expiry timer always except a stop request is a special case, because of then
+    // both 200 and Notify are required, or an error code alone.
+    if ( orig != ESimpleStopping )
+        {
+        StopExpiryTimer();        
+        }
+    // Stop refresh timer used for garbage collection
+    if ( orig != ESimpleRunningInit )
+        {
+        StopRefreshTimer();
+        }
+
+    if ( orig == ESimpleRunningInit )
+        {
+        if ( aStatus == KErrNone || aStatus == KSimpleErrPending )
+            {
+            if ( iExpires )
+                {
+                // OK response [ expires != 0 ]
+                if ( aStatus == KSimpleErrPending )
+                    {
+                    iSubscriptionState = MSimpleEngineRequest::ESimpleStatePending;
+                    }
+                SetReqState( ESimpleActiveSubs );
+                }
+            else
+                {
+                // OK response [ expires == 0 ]
+                SetReqState( ESimpleComplete );
+                }
+            }
+        else
+            {
+            // error
+            SetReqState( ESimpleFailed );
+            }
+         // Complete API callback always in ESimpleRunningInit
+        iReq.Complete( aStatus );
+        }
+    else if ( orig == ESimpleActiveSubs )
+        {
+        // This happens if refresh has failed without response notify
+        TUint retryTime = RetryTime();
+        // If too long interval in the retry-after header then return an error
+        if ( retryTime > KRetryBefore )
+            {
+            SetReqState( ESimpleFailed );             
+            iReq.Complete( KErrCompletion );
+            return;
+            }
+        SetReqState( ESimpleReTry );          
+        StartRefreshTimer( retryTime );
+        }
+    else if ( orig == ESimpleStopping )
+        {
+        // Stop expiry timer always except a stop request is a special case, because of then
+        // both 200 and Notify are required, or an error code alone.        
+        ++iRespCount;
+        if (( aStatus != KErrNone && aStatus != KSimpleErrPending ) ||
+            ( iRespCount == 2 ) )
+            {
+            SetReqState( ESimpleComplete );  
+            StopExpiryTimer();                       
+            }
+        iReq.Complete( aStatus );
+        }
+    else if ( orig == ESimpleDialogReCre && aStatus == KErrNone )
+        {
+        // OK resp
+        SetReqState( ESimpleActiveSubs );
+        }
+    else
+        {
+        // expires or stop / callback
+        // error / callback
+        // Expires or stop /callbc, respectively
+        SetReqState( ESimpleFailed );
+        iReq.Complete( aStatus );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::DoCompleteNotification
+// -----------------------------------------------------------------------------
+void CSimpleRequest::DoCompleteNotification( TInt aStatus )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: DoCompleteNotification %d"), aStatus);
+#endif
+
+    // This is a part of the the state machine for subscriptions.
+    // Mainly those cases that call API callback
+
+    TSimpleReqState orig = iState;
+
+    if ( aStatus == KErrNone || aStatus == KSimpleErrPending )
+        {
+        if ( KSimpleErrPending == aStatus )
+            {
+            iSubscriptionState = MSimpleEngineRequest::ESimpleStatePending;
+            }
+        else
+            {
+            iSubscriptionState = MSimpleEngineRequest::ESimpleStateActive;             
+            }    
+           
+        if ( orig == ESimpleStopping && aStatus != KErrNone )
+            {
+            // A stop request is a special case, because of then
+            // both 200 and Notify are required, or an error code alone.             
+            ++iRespCount;
+            if ( iRespCount == 2 )
+                {
+                SetReqState( ESimpleComplete );  
+                StopExpiryTimer();                       
+                }
+            }
+                    
+        // Notification without error does not change the state,
+        // just pass the notification thru to the API
+        iReq.Complete( aStatus );
+        return;
+        }
+
+    if ( orig  == ESimpleActiveSubs )
+        {
+        // Ensure that Dialog refreshing will be stopped now.        
+        SetDialog( NULL );
+        if ( iReq.IsRefresh() && aStatus == KSimpleErrTemporary )
+            {
+            // terminated [ refresh ]
+            StopRefreshTimer();
+            TUint retryTime = RetryTime();
+            // If too long interval in the retry-after header then return an error
+            if ( retryTime > KRetryBefore )
+                {                                 
+                SetReqState( ESimpleFailed );
+                iReq.Complete( KErrCompletion );
+                return;
+                }
+            // Do not send the notification to the client API now, but
+            // go to retry-state and retry later dialog re-creation.
+            SetReqState( ESimpleReTry );
+            StartRefreshTimer( retryTime );
+            }
+        else
+            {
+            // Expires[ no refresh ]
+            // error [ no refresh ]
+            // Timer may stop the request if not refresh.
+            SetReqState( (aStatus==KErrTimedOut || aStatus==KErrCompletion) ?
+                ESimpleComplete : ESimpleFailed );                 
+            // Complete API callback
+            iReq.Complete( aStatus );
+            }
+        }
+    else
+        {         
+        iReq.Complete( aStatus );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CSimpleRequest::DoCompletePublication
+// -----------------------------------------------------------------------------
+void CSimpleRequest::DoCompletePublication( TInt aStatus )
+    {
+#ifdef _DEBUG
+    TSimpleLogger::Log(_L("SimpleRequest: DoCompletePublication %d"), aStatus);
+#endif
+
+    // This is a part of the the state machine for publications.
+    // Mainly those cases that call API callback
+    
+    // SIP error code 412, RFC3903
+    const TUint KMySip412 = 412;        
+    
+    TSimpleReqState orig = iState;
+    TSimplePendingSubState nextSub = ENoPending;
+    
+    // Set ETag that's received from network or reset 
+    // when an error has taken a place. If it fails, it is not very serious, 
+    // and it is very rare OOM situation.
+    TRAP_IGNORE( iReq.SetETagL( ETag() ));    
+
+    // Stop refresh timer and start only when needed.
+    if ( orig != ESimplePending )
+        {                
+        StopRefreshTimer();
+        }
+
+    if ( orig == ESimpleRunningInit )
+        {
+        // stop expiry timer
+        StopExpiryTimer();
+        if ( aStatus == KErrNone )
+            {
+            // OK response
+            SetReqState( ESimpleActive );
+            // Ensure API callback when terminated
+            StartRefreshTimer();
+            }
+        else
+            {
+            // error
+            SetReqState( ESimpleFailed );
+            }
+         // Complete API callback always in ESimpleRunningInit
+        iReq.Complete( aStatus );
+        }
+    else if ( orig == ESimpleRunning )
+        {
+        TBool isModify = Request().RequestType() == MSimpleEngineRequest::EPublishModify ? ETrue : EFalse;
+        // Handle pending/running modifify request situation first
+        if ( isModify )
+            {
+            SetReqState( ESimpleActive );
+            // Check SIP status and decide whether publication is terminated or
+            // the modify request failed only.
+            if ( PendingState() != ENoPending )
+                {
+                // The previous refresh request is completed and the next one is the
+                // pending modify request.
+                if ( aStatus )
+                    {
+                    // error[ callback ]
+                    // terminate the publication
+                    // stop expiry timer
+                    StopExpiryTimer();                    
+                    iReq.Complete( KErrCompletion );
+                    SetReqState( ESimpleFailed );
+                    }
+                else
+                    {
+                    StartRefreshTimer();
+                    // Send modification request
+                    TInt errx = iEngine.DoSendPendingPublish( *this );
+                    if ( errx )
+                        {
+                        // Modify request failed only
+                        // stop expiry timer
+                        StopExpiryTimer();
+                        iReq.Complete( errx );
+                        SetReqState( ESimpleActive );
+                        }
+                    else
+                        {
+                        // Just wait modify response, do not complete API request.
+                        // Do not stop or restart expiry timer
+                        }
+                    }
+                } // pending
+            else
+                {
+                // This is the actual modify request response.
+                TUint sipStatus = Status();
+                // check if not 412, see RFC3903
+                if ( ( !aStatus || aStatus == KSimpleErrTemporary ) && sipStatus != KMySip412 )
+                    {
+                    // Modify failed only
+                    StartRefreshTimer();
+                    // Complete modification in API callback
+                    iReq.Complete( aStatus );
+                    SetReqState( ESimpleActive );
+                    }
+                else
+                    {
+                    // The whole publication is terminated
+                    iReq.Complete( KErrCompletion );
+                    SetReqState( ESimpleFailed );
+                    }
+                // stop expiry timer
+                StopExpiryTimer();
+                } // not pending
+            } // modify
+        else
+            {
+            // stop expiry timer
+            StopExpiryTimer();
+            // re-freshing publication
+            if ( aStatus == KErrNone )
+                {
+                StartRefreshTimer();
+                SetReqState( ESimpleActive );
+                // Complete the request for ETag observers only
+                iReq.SetResponseMethod( MSimpleEngineRequest::EStatusETag );                
+                }
+            else
+                {
+                // error, no recovery
+                SetReqState( ESimpleFailed );                
+                iReq.Complete( aStatus );
+                }
+            }
+        }
+    else if ( orig  == ESimpleStopping )
+        {
+        // stop expiry timer
+        StopExpiryTimer();
+        // Any kind of response is OK for stopping,
+        // either timeout or OK response except with given ETag the real SIP response
+        // is sent to the client.
+        SetReqState( ESimpleComplete );
+        TInt retVal = GivenETag() ? aStatus : KErrNone;
+        iReq.Complete( retVal );
+        }
+    else if ( orig == ESimplePending && 
+             ( iPendingState==EPendingModify || iPendingState==EPendingModifyAndRefresh ) &&
+             aStatus != KErrCompletion )
+        {
+        // Check if just modify terminates or the entire publication is terminated
+        // stop expiry timer
+        StopExpiryTimer();
+        // Modify failed, automatic refresh continues
+        // Complete modification in API callback
+        iReq.Complete( aStatus );
+        SetReqState( ESimpleActive );
+        if ( iPendingState == EPendingModifyAndRefresh && aStatus )
+            {
+            // The publication may still be alive but automatic refresh is pending
+            SetReqState( ESimplePending );            
+            nextSub = EPendingRefresh;
+            }
+        }        
+    else // odds and ends like time-out
+        {
+        // stop expiry timer
+        StopExpiryTimer();
+        SetReqState( ESimpleFailed );        
+        iReq.Complete( aStatus );
+        }
+    // Reset the request body data
+    delete iData;
+    iData = NULL;
+    // Reset pendig modify request
+    SetPendingState( nextSub );
+    }