IMPSengine/engsrv/src/impssubsession.cpp
changeset 0 094583676ce7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IMPSengine/engsrv/src/impssubsession.cpp	Thu Dec 17 08:41:52 2009 +0200
@@ -0,0 +1,2366 @@
+/*
+* Copyright (c) 2003 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: Class for imps sub session.
+*
+*
+*/
+
+
+// INCLUDE FILES
+#include    "impsserver.h"
+#include    "impsutils.h"
+#include    "impssession.h"
+#include    "impsfields.h"
+#include    "impserrors.h"
+#include    "impstimer.h"
+#include    "impssdatautils.h"
+#include    "impspacked.h"
+#include    "impssubsession.h"
+#include    "impstdataaccessor.h"
+#include    "impsmessageinterpreterapi.h"
+#include    "impsliterals.h"
+#include    "ImpsVariantAPI.h"
+
+#ifdef _FAKE_RESPONSE
+#include   "impssrvtestutils.h"
+#endif
+
+// MACROS
+#ifndef _DEBUG
+#define _NO_IMPS_LOGGING_
+#endif
+
+
+
+
+// ================= MEMBER FUNCTIONS =======================
+
+// C++ default constructor can NOT contain any code, that
+// might leave.
+//
+CImpsSubSession::CImpsSubSession(  )
+        :   iEventList( _FOFF( CWvEvent, iLink ) ),  //lint !e413
+        iRequestList( _FOFF( CRequest, iLink ) ), //lint !e413
+        iStatusObserver( EFalse ),
+        iDetailedError( EFalse ),
+        iLastEvent( EImpsMessageNone ),
+        iExpiryTime( 0 )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: CREATE subses=%d" ), ( TInt )this );
+#endif
+    }
+
+// Destructor
+// Session destructor calls this
+CImpsSubSession::~CImpsSubSession()
+    {
+    DeleteAllRequests();
+    DeleteAllEvents();
+    delete iVariant;
+
+    // complete event msg
+    CompleteEventMsg( KErrCancel );
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DELETE subses=%d" ), ( TInt )this );
+#endif
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::NewL()
+// ---------------------------------------------------------
+CImpsSubSession* CImpsSubSession::NewL(
+    CImpsSession* aSession,
+    TImpsEventType aType,
+    const RMessage2 aMessage )
+    {
+    CImpsSubSession*  self = new ( ELeave ) CImpsSubSession();
+    // CleanupStack::PushL( self );
+    // Close method has to be called since this is derived from CObject.
+    CleanupClosePushL( *self );
+    self->ConstructL( aSession, aType, aMessage );
+    CleanupStack::Pop();
+    return self;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::ConstructL()
+// ---------------------------------------------------------
+void CImpsSubSession::ConstructL(
+    CImpsSession* aSession,
+    TImpsEventType aType,
+    const RMessage2 aMessage )
+    {
+    iServiceType = aType;
+    iSession = aSession;
+    TInt msg = aMessage.Function();
+
+    // get bit mask in an upper byte
+    TInt flags = ( msg >> 16 );
+
+    iOOMErr.Reset();
+
+#ifdef _FAKE_RESPONSE
+    // **********************************
+    // MODULE TEST for auxiliary classes
+
+    TImpsSrvTestUtils::TestOOMList();
+
+    // **********************************
+#endif
+
+    iHandleNew = ( flags & KImpsReqHandleNew ) ? ETrue : EFalse;
+    iVariant = CImpsVariant::NewLC( );
+    CleanupStack::Pop( ); // >> iVariant
+    // default value used ubtil client request to change it.
+    iExpiryTime = Server()->ExpirySeconds( iServiceType );
+    }
+
+// -----------------------------------------------------------------------------
+// CImpsSubSession::Unregister()
+// -----------------------------------------------------------------------------
+void CImpsSubSession::Unregister()
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: Unregister subses=%d h=%d type=%d" ),
+                            ( TInt )this, Handle(), ( TInt )iServiceType );
+#endif
+    iCanceled = ETrue;
+    DeleteAllRequests();
+
+    // Complete new message observer
+    CompleteEventMsg( KErrCancel );
+
+    ( void ) DeleteAllEvents();
+    }
+
+// -----------------------------------------------------------------------------
+// CImpsSubSession::DeleteSub()
+// -----------------------------------------------------------------------------
+void CImpsSubSession::DeleteSub()
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DeleteSub subses=%d type=%d" ), ( TInt )this, ( TInt )iServiceType );
+#endif
+    // Calculate new expiry timer value
+    Server()->ResetExpiryTimer( iExpiryTime );
+    // Delete this entity
+    RMessage2 myMsg( Message() );
+    iSession->DeleteSubSession( myMsg.Int3() );
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::AssignIdL()
+// ---------------------------------------------------------
+void CImpsSubSession::AssignIdL( TImpsSessIdent aCSP )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: AssignIdL subses=%d" ), ( TInt )this );
+#endif
+    // create an event for each orphan message
+    CImpsFields* msg = NULL;
+    msg = Server()->NextOrphanLC( iSession->ApplicationId(), iServiceType, aCSP );
+    while ( msg )
+        {
+        SendEvent( msg, 0, EImpsServNone, EImpsMessageNone );
+        CleanupStack::PopAndDestroy();
+        msg = Server()->NextOrphanLC( iSession->ApplicationId(), iServiceType, aCSP );
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::HandleAllOrphans()
+// ---------------------------------------------------------
+void CImpsSubSession::HandleAllOrphans( TImpsSessIdent aCSP )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: HandleAllOrphans subses=%d" ), ( TInt )this );
+#endif
+    TInt err = KErrNone;
+    TRAP( err, AssignIdL( aCSP ) );
+    }
+
+// -----------------------------------------------------------------------------
+// CImpsSubSession::DeleteAllEvents()
+// -----------------------------------------------------------------------------
+TInt CImpsSubSession::DeleteAllEvents(  )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DeleteAllEvents subses=%d" ), ( TInt )this );
+#endif
+
+    TInt ret = 0;
+
+    // Reset OOM_LIST too
+    iOOMErr.Reset();
+
+    // Delete all buffered events from this client,
+
+    TDblQueIter<CWvEvent> rIter( iEventList );
+
+    rIter.SetToFirst();
+
+    while ( rIter )
+        {
+        CWvEvent* event = rIter;
+        rIter++;
+        if ( event->iSent )
+            {
+            ret++;
+            }
+        event->Destroy();
+        }
+
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// CImpsSubSession::DeleteAllRequests()
+// -----------------------------------------------------------------------------
+void CImpsSubSession::DeleteAllRequests(  )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DeleteAllRequests" ) );
+#endif
+    // Delete all buffered requests from this client.
+
+    TDblQueIter<CRequest> rIter( iRequestList );
+
+    rIter.SetToFirst();
+
+    while ( rIter )
+        {
+        CRequest* requ = rIter;
+        rIter++;
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: DeleteAllRequests TID=%S" ), &( requ->iTID ) );
+#endif
+        requ->Destroy();
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::LoginL()
+// ---------------------------------------------------------
+void CImpsSubSession::LoginL( TBool aReactive )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: LoginL begins reactive=%d subses=%d" ),
+                            aReactive, ( TInt )this );
+#endif
+
+    // OPID
+    RMessage2 myMsg( Message() );
+    TInt opId = myMsg.Int1();
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: Login opid=%d" ), opId );
+#endif
+
+    // Read PACKED ARRAY
+    CDesCArrayFlat* tempArr = new ( ELeave )CDesCArrayFlat( 7 );
+    CleanupStack::PushL( tempArr );   // << tempArr
+
+    HBufC8* stream = *StreamBufAddr();
+    if ( iSession->ReadBuffer8L( 0, stream ) )
+        {
+        HBufC8** stream2 = StreamBufAddr();
+        *stream2 = stream;
+        }
+    TImpsPackedEntity packedMessage( stream );
+    const TUint8* pS = stream->Ptr();
+    packedMessage.DoUnpackArrayL( pS, tempArr );
+
+    __ASSERT_DEBUG(
+        tempArr->MdcaCount() == 7,
+        User::Panic( KImpsPanicCategory,
+                     EImpsCorrupted ) );
+    TPtrC tid;
+    TPtrC tempUser = tempArr->MdcaPoint( 0 );
+    TPtrC tempPassword = tempArr->MdcaPoint( 1 );
+    TPtrC tempCliId = tempArr->MdcaPoint( 2 );
+    TPtrC tempSAP = tempArr->MdcaPoint( 3 );
+    TPtrC key1 = tempArr->MdcaPoint( 4 );
+    TPtrC key2 = tempArr->MdcaPoint( 5 );
+    TPtrC ap = tempArr->MdcaPoint( 6 );
+
+    // convert AP back
+    TLex lex;
+    TUint32 myAp;
+    lex.Assign( ap );
+    lex.Val( myAp, EDecimal );
+
+    // Multi: update session's user-id and SAP.
+    iSession->SetUserIdL( tempUser );
+    iSession->SetSAPL( tempSAP );
+
+    TImpsSessIdent csp( KNullDesC, tempSAP, tempUser );
+    TPtrC p;
+    if ( Server()->IsLogged( csp, &p ) )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: CSP already logged ín subses=%d" ), ( TInt )this );
+#endif
+        // check password: if it does not match then return an error.
+        if ( p.CompareF( tempPassword ) )
+            {
+            User::Leave( KImpsErrorUnauthorized );
+            }
+
+        // Update session SID and other CSP identification data.
+        iSession->ModifySIDL( Server()->SID( csp ) );
+        iSession->SetSAPL( tempSAP );
+        iSession->SetUserIdL( tempUser );
+
+        // multi: Share existing CSP session
+        // Send callback reponse to a client
+        // Complete the open asyncronous request first and
+        // after that send login event (callback).
+        iSession->CompleteMe( KErrNone );
+        SendLoginEvent( KErrNone, opId );
+        if ( Server()->IsNegotiated( csp ) )
+            {
+            // On-line event too if CSP is negotiated.
+            // Must sent via Session to all subsessions
+            iSession->SendEvent( EInternal_ON_LINE );
+            // Check orphan messages because of IM subsessions
+            // may be registered before login request,
+            // Create an event for each orphan message
+            Server()->HandleAllOrphans();
+            }
+        tempArr->Reset();
+        CleanupStack::PopAndDestroy( 1 );   // >> tempArr
+        return;
+        }
+
+
+    // Save login expiry time and give it to CSP session too.
+    TTime loginExpiry = Server()->ExpiryTime( iExpiryTime );
+
+    tid.Set( Server()->LoginL( tempUser, tempPassword, tempCliId,
+                               tempSAP, myAp, key1, key2, this, loginExpiry, aReactive ) );
+
+    tempArr->Reset();
+    CleanupStack::PopAndDestroy( 1 );   // >> tempArr
+
+    // Update request queue
+    // Add request into queue
+    CRequest* request = new ( ELeave ) CRequest(
+        tid,    // tid
+        opId,   // op-id
+        EImpsServWVLogin,
+        loginExpiry,
+        EImpsLoginReq
+    );
+
+    iRequestList.AddLast( *request );
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: LoginL ends TID=%S" ), &tid );
+#endif
+
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::LogoutL()
+// ---------------------------------------------------------
+TPtrC CImpsSubSession::LogoutL( TImpsSessIdent aCSP, TBool aCancel )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: LogoutL begins subses=%d" ), ( TInt )this );
+#endif
+
+    RMessage2 myMsg( Message() );
+    TInt opId = myMsg.Int0();
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: Logout opid=%d" ), opId );
+#endif
+
+    TPtrC tid;
+
+    // Login-cancel.
+    if ( aCancel )
+        {
+        // Check that request still exists, if not then leave KErrNotFound
+        TBool found( EFalse );
+        TDblQueIter<CRequest> requestIter( iRequestList );
+        requestIter.SetToFirst();
+        while ( requestIter )
+            {
+            CRequest* request = requestIter;
+            requestIter++;
+            if ( request->iOpId == opId )
+                {
+                found = ETrue;
+                break;
+                }
+            }
+        if ( !found )
+            {
+            User::Leave( KErrNotFound );
+            }
+        }
+
+    // Send logout request forward to CSP session entity
+    TRAPD( errL, tid.Set( iSession->Server()->LogoutL( aCancel, aCSP ) ) );
+
+    // If canceling login operation then client needs to get only one callback method call,
+    // so do not save new CRequest entity then. The CSP session entity will call later
+    // SendEvent to respond to the cancelled login operation if there is something
+    // to cancel.
+    if ( aCancel )
+        {
+        if ( errL )
+            {
+            // If the previous request leaves then there is nothing to cancel.
+            // Therefore delete the login request entity now and verify that CSP entity
+            // clear the particular transaction data,
+            CancelTrans( myMsg, aCSP );
+            User::Leave( errL );
+            }
+        // CSPSession will call back later and delete the request and send a response
+        // event to cancel request when everyting is clear regarding cancel of login
+        // transaction..
+        return TPtrC();
+        }
+    if ( errL == KImpsErrorTerminalOffLine )
+        {
+        // Logout requested in such a situation where Logout request is
+        // not needed nor not possible to be sent to SAP server.
+        // We have to generate a response here
+        // because of Logout must not fail in client API.
+        SendLogoutEvent( KErrNone, opId );
+        // Send NOT_LOGGED events to subhandles
+        iSession->SendEvent( EInternal_NOT_LOGGED );
+        return TPtrC();
+        }
+    else if ( errL )
+        {
+        // Unexpeted error situation
+        User::Leave( errL );
+        }
+
+    // Add request into request queue
+    CRequest* request = new ( ELeave ) CRequest(
+        tid,
+        opId,
+        EImpsServWVLogout,
+        Server()->ExpiryTime( iExpiryTime ),
+        EImpsLogoutReq
+    );
+    iRequestList.AddLast( *request );
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: LogoutL ends with tid=%S" ), &tid );
+#endif
+    return tid;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::Message()
+// ---------------------------------------------------------
+// return message - get it from session
+const RMessagePtr2 CImpsSubSession::Message() const
+    {
+    return iSession->Message();
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::Server()
+// ---------------------------------------------------------
+// return message - get it from session
+CImpsServer* CImpsSubSession::Server()
+    {
+    return iSession->Server();
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoSendStatusEvent()
+// ---------------------------------------------------------
+void CImpsSubSession::DoSendStatusEvent( CWvEvent& aEvent )
+    {
+
+    // Status event is lighter than a regular event in a sense that
+    // it does not write data to client thread, but updates iStatus
+    // of the handler active object in a client thread only.
+    if ( iEventMsg.IsNull() )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: DoSendStatusEvent NULL MSG subses=%d" ),
+                                ( TInt )this );
+#endif
+        return;
+        }
+
+    TInt newStatus = ConvertStatusCode( ( EImpsInternalStatus )aEvent.iAux );
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DoSendStatusEvent stat=%d subses=%d" ),
+                            newStatus, ( TInt )this );
+#endif
+    CompleteEventMsg( newStatus );
+
+    aEvent.iSent = ETrue;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendEvent()
+// ---------------------------------------------------------
+void CImpsSubSession::SendEvent(
+    CImpsFields *aFields,
+    TInt aOpId,
+    TImpsServRequest aRequestType,
+    TImpsMessageType aReqMsgType )
+    {
+
+    TInt memErr = KErrNone;
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled  )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log(
+            _L( "SubSession: SendEvent cancelled subses=%d ****" ), ( TInt )this );
+#endif
+        return;
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendEvent begins" ) );
+#endif
+
+
+    TDblQueIter<CWvEvent> eventIter( iEventList );
+
+    // Check if too many events waiting
+    CWvEvent* event = NULL;
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+    // Split GroupChangeNotice up to three parts for
+    // HandleNewUsersL, HandleLeftUsersL and HandleGroupProperties
+    if ( aFields->MessageType() == EImpsGroupChangeNotice )
+        {
+        TRAP( memErr, DoSplitGroupChangeL( aOpId, aRequestType, aFields ) );
+        }
+    else
+        {
+        TRAP( memErr, DoCreateEventL( aOpId, aRequestType, aReqMsgType, aFields ) );
+        }
+
+    // Now the event is ready to be sent.
+    // Send the event to client thread if this is first one
+    if ( thisFirst && !memErr )
+        {
+        // This should be a new event just created
+        eventIter.SetToFirst();
+        event = eventIter;
+        __ASSERT_DEBUG(
+            !event->iSent,
+            User::Panic( KImpsPanicCategory,
+                         EImpsCorrupted ) );
+        // Select between base event and EventBody
+        DoSendBaseEvent( *event );
+        }
+    // Notice: CSP negotiation requests (client capabilities and service
+    // negotiaion) are internal for CSP class entity and they are not
+    // originated from client. Thus corresponding negotiation phase
+    // errors are not guaranted to be sent to a client in OOM situation.
+    // In those cases the aOpId is NULL.
+    //
+    else if ( memErr && aOpId )
+        {
+        // send/store OOM error message ( KErrNoMemory, aOpId )
+        iOOMErr.StoreOOM( aOpId );
+        if ( thisFirst )
+            {
+            ( void )SendOOMError();
+            }
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendEvent ends" ) );
+#endif
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendEvent()
+// Lighter version for engine status event
+//     Notice: CSP status changes are guaranted to
+//     to be sent to a client in OOM situation too.
+// ---------------------------------------------------------
+void CImpsSubSession::SendEvent(
+    EImpsInternalStatus aStatus )
+    {
+
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled || !iStatusObserver  )
+        {
+        return;
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendEvent (status) subses=%d" ), ( TInt )this );
+#endif
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+    // Prefer reqular event queue then OOM error queue in order
+    // to save the logical order of events.
+    TRAPD( memErr, DoCreateEventL( aStatus ) );
+
+    if ( thisFirst && !memErr )
+        {
+        TDblQueIter<CWvEvent> eventIter( iEventList );
+        CWvEvent* event = NULL;
+        // This should be a new event just created
+        eventIter.SetToFirst();
+        event = eventIter;
+        __ASSERT_DEBUG(
+            !event->iSent,
+            User::Panic( KImpsPanicCategory,
+                         EImpsCorrupted ) );
+        // Send the event immediately
+        DoSendStatusEvent( *event );
+        }
+
+    else if ( memErr )
+        {
+        // send/store OOM error message ( status, aOpId )
+        TInt retCode = ConvertStatusCode( aStatus );
+        iOOMErr.StoreOOM( retCode );
+        if ( thisFirst )
+            {
+            ( void )SendOOMError();
+            }
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendEvent ends" ) );
+#endif
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoSendBaseEvent()
+// ---------------------------------------------------------
+void CImpsSubSession::DoSendBaseEvent( CWvEvent& aEvent )
+    {
+    TInt bufSize = EventMsgBufSize();
+    TInt messageLength = aEvent.iPackedMessage ?
+                         aEvent.iPackedMessage->Length() : 0;
+    if ( bufSize > 0 && bufSize >= messageLength )
+        {
+        // Message body can be sent immediately.
+        // This sends message headers too.
+        DoSendEventBody( aEvent );
+        }
+    else
+        {
+        // No message body or client is asked to increase the buffer
+        DoSendEventHeader( aEvent );
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoSendEventHeader()
+// ---------------------------------------------------------
+void CImpsSubSession::DoSendEventHeader( CWvEvent& aEvent )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DoSendEventHeader type=%d subses=%d" ),
+                            aEvent.iType, ( TInt )this );
+#endif
+    RMessagePtr2 ptrMsg = EventMsg();
+    if ( ptrMsg.IsNull() )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: DoSendEventHeader NULL MSG subses=%d" ),
+                                ( TInt )this );
+#endif
+        return;
+        }
+    // Create the header information
+
+    TBuf < sizeof( SImpsEventData ) >
+    eventBuf( sizeof( SImpsEventData ) );
+
+    SImpsEventData* eventData =
+        ( SImpsEventData* )eventBuf.Ptr();
+
+    DoCreateEventIPC( aEvent, eventData );
+
+    // Write the message header to client thread via a message.
+    TInt err = KErrNone;
+
+
+    err = DoWriteEventHeaders( ptrMsg, eventBuf );
+
+    // We use NotifyHandler status parameter to pass the size of the
+    // packed message!
+    TInt messageLength = aEvent.iPackedMessage ?
+                         aEvent.iPackedMessage->Length() : 0;
+    if ( err == KErrNone )
+        {
+        err = messageLength;
+        }
+
+    // Signal event handler in a client thread
+    CompleteEventMsg( err );
+
+    aEvent.iSent = ETrue;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoSendEventBody()
+// ---------------------------------------------------------
+void CImpsSubSession::DoSendEventBody( CWvEvent& aEvent )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DoSendEventBody type=%d subses=%d" ),
+                            aEvent.iType, ( TInt )this );
+#endif
+
+    RMessagePtr2 ptrMsg = EventMsg();
+    if ( ptrMsg.IsNull() )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: DoSendEventBody NULL MSG subses=%d" ),
+                                ( TInt )this );
+#endif
+        return;
+        }
+
+    // This writes both headers and body.
+    // This increases the robustenss and thre client thread do not need
+    // to store data between these transactions, because of it receives
+    // all the necessary data at once.
+
+    TBuf < sizeof( SImpsEventData ) >
+    eventBuf( sizeof( SImpsEventData ) );
+
+    SImpsEventData* eventData =
+        ( SImpsEventData* )eventBuf.Ptr();
+
+    DoCreateEventIPC( aEvent, eventData );
+    eventData->iMessageBody = ( aEvent.iPackedMessage ? ETrue : EFalse );
+
+    // Write the message header to client thread via a message.
+    TInt err = KErrNone;
+    err = DoWriteEventHeaders( ptrMsg, eventBuf );
+
+    if ( !err )
+        {
+        // Write the message body
+        err = DoWriteEventBody( ptrMsg, aEvent );
+        // We use NotifyHandler status parameter to pass the size of the
+        // packed message!
+        TInt messageLength = aEvent.iPackedMessage ?
+                             aEvent.iPackedMessage->Length() : 0;
+        if ( err == KErrNone )
+            {
+            err = messageLength;
+            }
+        }
+
+    // Signal event handler in a client thread
+    CompleteEventMsg( err );
+
+    aEvent.iSent = ETrue;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendLogoutEvent
+// ---------------------------------------------------------
+void CImpsSubSession::SendLogoutEvent( TInt aRespStatus, TInt aOpId )
+    {
+    // transfer CSP error code if any
+    if ( aRespStatus > 0 )
+        {
+        aRespStatus = ErrorCode( aRespStatus );
+        }
+
+    // check session type first and filter sending
+    // of the same logout twice
+    if ( iServiceType != EImpsEventServerLogin ||
+         ( iLastEvent == EImpsDisconnect && aOpId == 0 ) )
+        {
+        return;
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendLogoutEvent in action subses=%d" ),
+                            ( TInt )this );
+#endif
+
+    TDblQueIter<CWvEvent> eventIter( iEventList );
+    CWvEvent* event = NULL;
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+    // Let this behave like real SAP initiated Disconnect primitive
+    TRAPD( memErr, DoCreateEventL( aOpId, aRespStatus,
+                                   EImpsMessageNone, EImpsDisconnect, EImpsServWVLogout ) );
+    // Since WV 1,2 logout messages may be either disconnect or Status,
+    // harmomize them here in order to avoid sending logout message
+    // with opid=actual_id and opid=0.
+    iLastEvent = EImpsDisconnect;
+
+    // Now the event is ready to be sent.
+    // Send the event to client thread if this is first one
+    if ( thisFirst && !memErr )
+        {
+        // This should be a new event just created
+        eventIter.SetToFirst();
+        event = eventIter;
+        __ASSERT_DEBUG(
+            !event->iSent,
+            User::Panic( KImpsPanicCategory,
+                         EImpsCorrupted ) );
+        // Logout event does not contain body in this case
+        DoSendEventHeader( *event );
+        }
+    else if ( memErr && aOpId )
+        {
+        // send/store OOM error message ( KErrNoMemory, aOpId )
+        iOOMErr.StoreOOM( aOpId );
+        if ( thisFirst )
+            {
+            ( void )SendOOMError();
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendLoginEvent
+// Multi: new method to generate events when CSP session
+// already exists
+// ---------------------------------------------------------
+void CImpsSubSession::SendLoginEvent( TInt aRespStatus, TInt aOpId )
+    {
+    // transfer CSP error code if any
+    if ( aRespStatus > 0 )
+        {
+        aRespStatus = ErrorCode( aRespStatus );
+        }
+
+    // check session type first and filter sending
+    // of the same logout twice
+    if ( iServiceType != EImpsEventServerLogin )
+        {
+        return;
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendLoginEvent in action subses=%d" ),
+                            ( TInt )this );
+#endif
+
+    TDblQueIter<CWvEvent> eventIter( iEventList );
+    CWvEvent* event = NULL;
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+    // Let this behave like real SAP initiated Disconnect primitive
+    TRAPD( memErr, DoCreateEventL( aOpId, aRespStatus,
+                                   EImpsMessageNone, EImpsLoginRes, EImpsServWVLogin ) );
+
+    // Now the event is ready to be sent.
+    // Send the event to client thread if this is first one
+    if ( thisFirst && !memErr )
+        {
+        // This should be a new event just created
+        eventIter.SetToFirst();
+        event = eventIter;
+        __ASSERT_DEBUG(
+            !event->iSent,
+            User::Panic( KImpsPanicCategory,
+                         EImpsCorrupted ) );
+        // Logout event does not contain body in this case
+        DoSendEventHeader( *event );
+        }
+    else if ( memErr && aOpId )
+        {
+        // send/store OOM error message ( KErrNoMemory, aOpId )
+        iOOMErr.StoreOOM( aOpId );
+        if ( thisFirst )
+            {
+            ( void )SendOOMError();
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendOOMError
+// ---------------------------------------------------------
+TBool CImpsSubSession::SendOOMError( )
+    {
+
+    if ( iEventMsg.IsNull() )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log(
+            _L( "SubSession: SendOOMError NULL MSG subses=%d" ), ( TInt )this );
+#endif
+        return EFalse;
+        }
+
+    TInt opId = iOOMErr.GetOOM();
+    if ( opId != 0 )
+        {
+        if ( opId > 0 )
+            {
+            CompleteEventMsg( KImpsOOMReply | opId );
+#ifndef _NO_IMPS_LOGGING_
+            CImpsClientLogger::Log(
+                _L( "SubSession: SendOOMError completes opid=%d ****" ), opId );
+#endif
+            }
+        else
+            {
+            // Status change special error code etc
+            CompleteEventMsg( opId );
+#ifndef _NO_IMPS_LOGGING_
+            CImpsClientLogger::Log(
+                _L( "SubSession: SendOOMError completes status=%d ****" ), opId );
+#endif
+            }
+        return ETrue;
+        }
+    else
+        {
+        return EFalse;
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendPrimitiveL()
+// ---------------------------------------------------------
+void CImpsSubSession::SendPrimitiveL( TInt aFunction, TImpsSessIdent aCSP )
+    {
+    RMessage2 myMsg( Message() );
+    TInt opId = myMsg.Int1();
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendPrimitiveL begins %d opid=%d" ),
+                            aFunction, opId );
+#endif
+
+    switch ( aFunction )
+        {
+        case EImpsServWVSendOnly:
+        case EImpsServBlock:
+            // Check availability of the service in general
+            TImpsSrvUtils::CheckServiceL( EIMFeat,
+                                          Server()->Services( aCSP ) );
+            break;
+        case EImpsServGroup:
+            TImpsSrvUtils::CheckServiceL( EGroupFeat,
+                                          Server()->Services( aCSP ) );
+            break;
+        case EImpsServPres:
+            TImpsSrvUtils::CheckServiceL( EPresenceFeat,
+                                          Server()->Services( aCSP ) );
+            break;
+        case EImpsServFundSearch:
+        case EImpsServFundInvite:
+            TImpsSrvUtils::CheckServiceL( EFundamentalFeat,
+                                          Server()->Services( aCSP ) );
+            break;
+        default:
+            break;
+        }
+    // Get data to iFields
+    GetWVDataL();
+
+    TImpsMessageType myMesType = ( TImpsMessageType ) ImpsFields()->MessageType();
+
+    // Check primitive if this is pure subsession
+    // Discard anything that does not belong to presence service
+    if ( iServiceType == EImpsEventPresencePure &&
+         impsService( iVariant, TImpsMessageType( myMesType ) ) != EImpsEventPresence )
+        {
+        User::Leave( KImpsErrorUnknownMessageType );
+        }
+
+    // Add sender if it is empty in new IM message
+    if ( aFunction == EImpsServWVSendOnly )
+        {
+        TPtrC sender;
+        TPtrC group;
+        CImpsKey* myKey = CImpsKey::NewLC();      // <<< myKey
+        TImpsSDataUtils::AddValuesFromArrayL(
+            myKey,
+            KSendMessageElements,
+            sizeof( KSendMessageElements ) /
+            sizeof( KSendMessageElements[0] ) );
+        myKey->AddL( CREATEKEY( EImpsKeyMessageInfo, 0 ) );
+        TImpsDataAccessor myAc( ImpsFields() );
+        TImpsSDataUtils::GetSenderL( myKey, &myAc, sender, group );
+        if ( sender.Length() == 0 )
+            {
+            TPtrC userId = iSession->UserId();
+            // Set Sender( User( UserID )) if sender not specified
+            // in client API
+            // Notice: Here we could add assigned clientID if wanted so.
+            TImpsSDataUtils::SetSenderL(
+                myKey,
+                &myAc,
+                &userId,
+                NULL );
+            }
+
+        TPtrC applicationID( iSession->ApplicationId() );
+        if ( applicationID.Length() != 0 )
+            {
+            // insert the ExtBlock
+            myKey->PopL( 3 );
+            myKey->AddL( CREATEKEY( EImpsKeyExtBlock, 0 ) );
+            myKey->AddL( CREATEKEY( EImpsKeyAPIClient, 0 ) );
+            myAc.StoreDescL( myKey, applicationID );
+            }
+
+
+        CleanupStack::PopAndDestroy( 1 );   // >>> myKey
+
+        }
+
+    TPtrC tid;
+
+    // Send data may in certain situation change iFields,
+    // so copy data address to safe.
+    tid.Set( Server()->SendDataL( this, aCSP ) );
+
+    // Add request into queue
+    CRequest* request = new ( ELeave ) CRequest(
+        tid,    // tid
+        opId,   // op-id
+        ( TImpsServRequest )aFunction,
+        Server()->ExpiryTime( iExpiryTime ),
+        myMesType );
+    iRequestList.AddLast( *request );
+
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendGetBlockedL()
+// This is for IM GetBlocked, only ID received from client
+// ---------------------------------------------------------
+void CImpsSubSession::SendGetBlockedL( TImpsSessIdent aCSP )
+    {
+
+    RMessage2 myMsg( Message() );
+    TInt opId = myMsg.Int1();
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendGetBlockedL  opid=%d" ), opId );
+#endif
+
+    // Check availability of the service in general
+    TImpsSrvUtils::CheckServiceL( EIMFeat, Server()->Services( aCSP ) );
+
+    // Generate the stuff. Server thread finalizes the message adding
+    // session-id etc
+
+    ImpsFields()->Reset();
+    ImpsFields()->SetMessageType( EImpsGetBlockedListReq );
+
+    CImpsDataAccessor* myAc = CImpsDataAccessor::NewL( ImpsFields() );
+    CleanupStack::PushL( myAc );        // <<< myAc
+    CImpsKey* myKey = CImpsKey::NewLC();    // <<< myKey
+
+    TImpsSDataUtils::AddValuesFromArrayL(
+        myKey,
+        KTransContentElements,
+        sizeof( KTransContentElements ) /
+        sizeof( KTransContentElements[0] ) );
+    myKey->AddL( CREATEKEY( EImpsKeyGetBlockedList_Request, 0 ) );
+    myAc->StoreEmptyL( myKey );
+
+    TPtrC tid;
+
+    // Send data may in certain situation change iFields,
+    // so copy data to safe.
+    tid.Set( Server()->SendDataL( this, aCSP ) );
+
+    // Update request queue
+    // Add request into queue
+    CRequest* request = new ( ELeave ) CRequest(
+        tid,            // tid
+        opId,           // op-id
+        EImpsServGetBlocked,
+        Server()->ExpiryTime( iExpiryTime ),
+        EImpsGetBlockedListReq );
+    iRequestList.AddLast( *request );
+
+    CleanupStack::PopAndDestroy( 2 );   // >>> myAc, myKey
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoNextEvent()
+// ---------------------------------------------------------
+void CImpsSubSession::DoNextEvent()
+    {
+
+    // check OOM_LIST first
+    iOOMErr.RemoveOOM();
+    TInt errId = SendOOMError();
+    if ( errId )
+        {
+        return;
+        }
+
+    // Delete the event that was sent and send next
+    TDblQueIter<CWvEvent> eventIter( iEventList );
+    eventIter.SetToFirst();
+
+    while ( eventIter )
+        {
+        CWvEvent* event = eventIter;
+        eventIter++;
+
+        if ( event->iSent )
+            {
+            event->Destroy();
+            }
+        else
+            {
+            if ( event->iRequestType != EImpsServStatusReg )
+                {
+                DoSendBaseEvent( *event );
+                }
+            else
+                {
+                DoSendStatusEvent( *event );
+                }
+            // Only one event at a time is possible
+            break;
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoEventBodyL()
+// ---------------------------------------------------------
+void CImpsSubSession::DoEventBodyL()
+    {
+    // Check that the first event (headers) in the queue
+    // is sent
+    TDblQueIter<CWvEvent> eventIter( iEventList );
+    eventIter.SetToFirst();
+    CWvEvent* event = eventIter;
+    if ( !event || !event->iSent )
+        {
+        // This ensures that if the synchronization between client-server
+        // is lost the next messages are handled because of the
+        // client asks next event after this error.
+        User::Leave( KImpsGeneralError );
+        }
+
+    // Write the body to the client thread
+    DoSendEventBody( *event );
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DiscardRequests()
+// Expiry detection
+// ---------------------------------------------------------
+void CImpsSubSession::DiscardRequests(
+    TTime aExpiryTime,
+    TImpsEventType aServiceType,
+    MImpsCSPSession* aSess )
+    {
+
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled )
+        {
+        return;
+        }
+
+    if ( !( aServiceType & iServiceType ) )
+        {
+        return;
+        }
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+    TDblQueIter<CRequest> requestIter( iRequestList );
+    requestIter.SetToFirst();
+    while ( requestIter )
+        {
+        CRequest* request = requestIter;
+        requestIter++;
+        if ( request->iExpiry < aExpiryTime )
+            {
+            // expired entry found
+            // Create error event for UI API
+#ifndef _NO_IMPS_LOGGING_
+            CImpsClientLogger::Log(
+                _L( "SubSession: Entry EXPIRED type=%d subses=%d" ),
+                request->iRequestType, ( TInt )this );
+#endif
+            // Delete the message from server queues if not send
+            Server()->CancelTrans( request->iTID, aSess );
+            // Create event for client
+            TRAPD( memErr, DoCreateEventL(
+                       request->iOpId,
+                       KErrTimedOut,
+                       request->iMessageType ) );
+
+            // store OOM error message if necessary ( KErrNoMemory, aOpId )
+            if ( memErr )
+                {
+                iOOMErr.StoreOOM( request->iOpId );
+                }
+
+            // Send an event if this is first
+            if ( thisFirst )
+                {
+                DoNextEvent();
+                thisFirst = EFalse;
+                }
+            // delete request from queue
+#ifndef _NO_IMPS_LOGGING_
+            CImpsClientLogger::Log(
+                _L( "SubSession: DiscardRequests TID=%S" ), &( request->iTID ) );  //lint !e525
+#endif
+            request->Destroy();
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DiscardRequests()
+// Discrad all requests
+// ---------------------------------------------------------
+void CImpsSubSession::DiscardRequests(
+    TInt aError,
+    TImpsEventType aServiceType,
+    MImpsCSPSession* aSess )
+    {
+
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled )
+        {
+        return;
+        }
+
+    if ( !( aServiceType & iServiceType ) )
+        {
+        return;
+        }
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+    TDblQueIter<CRequest> requestIter( iRequestList );
+    requestIter.SetToFirst();
+    while ( requestIter )
+        {
+        CRequest* request = requestIter;
+        requestIter++;
+
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log(
+            _L( "SubSession: Entry DISCARDED type=%d subses=%d" ),
+            request->iRequestType, ( TInt )this );
+#endif
+        // Delete the message from server queues if not send
+        Server()->CancelTrans( request->iTID, aSess );
+        // Create event for client
+        TRAPD( memErr, DoCreateEventL(
+                   request->iOpId,
+                   aError,
+                   request->iMessageType ) );
+
+        if ( memErr )
+            {
+            iOOMErr.StoreOOM( request->iOpId );
+            }
+
+        // Try to send event if this is first
+        if ( thisFirst )
+            {
+            DoNextEvent();
+            thisFirst = EFalse;
+            }
+        // delete request from queue
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log(
+            _L( "SubSession: DiscardRequests TID=%S" ), &( request->iTID ) ); //lint !e525
+#endif
+        request->Destroy();
+
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DiscardRequest()
+// Discard a single request
+// ---------------------------------------------------------
+TBool CImpsSubSession::DiscardRequest(
+    const TDesC& aTid,
+    TImpsEventType aServiceType,
+    TInt aCode )
+    {
+
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled )
+        {
+        return EFalse;
+        }
+
+    TBool ret( EFalse );
+    if ( aServiceType != iServiceType )
+        {
+        return ret;
+        }
+
+    // check if there is unhandled events for this session
+    TBool thisFirst = ( NbrEvents() > 0 || iOOMErr.Exists() ) ? EFalse : ETrue;
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DiscardRequest subses=%d" ), ( TInt )this );
+#endif
+
+    TDblQueIter<CRequest> requestIter( iRequestList );
+    requestIter.SetToFirst();
+    while ( requestIter )
+        {
+        CRequest* request = requestIter;
+        requestIter++;
+        if ( !request->iTID.Compare( aTid ) )
+            {
+            // entry found
+            // Create error event for UI API
+#ifndef _NO_IMPS_LOGGING_
+            CImpsClientLogger::Log( _L( "SubSession: entry DISCARDED" ) );
+#endif
+            TRAPD( memErr, DoCreateEventL( request->iOpId,
+                                           aCode,
+                                           request->iMessageType ) );
+
+            if ( memErr )
+                {
+                // store OOM error message ( KErrNoMemory, aOpId )
+                iOOMErr.StoreOOM( request->iOpId );
+                }
+
+            // The following is NOT called because of server has called this.
+            // Server()->CancelTrans( request->iTID );
+            // delete request from queue
+            request->Destroy();
+            ret = ETrue;
+            break;
+            }
+        }
+
+    // Send the new event only if no old events in queue
+    if ( ret && thisFirst )
+        {
+        DoNextEvent();
+        }
+
+    return ret;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::CheckNotification()
+// ---------------------------------------------------------
+TBool CImpsSubSession::CheckNotification( CImpsFields* aFields )
+    {
+
+    TBool ret ( EFalse );
+    TRAPD( errx, ( ret = DoCheckNotificationL( aFields ) ) );
+    if ( ret && !errx )
+        {
+        return ETrue;
+        }
+    else
+        {
+        return EFalse;
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoCheckNotificationL()
+// Notice: MessageNotifiction not supported.
+// ---------------------------------------------------------
+TBool CImpsSubSession::DoCheckNotificationL( CImpsFields* aFields )
+    {
+
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled )
+        {
+        return EFalse;
+        }
+
+    // Filter new messages out if necessary
+    if ( !iHandleNew )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: new msg FILTERED OUT" ) );
+#endif
+        return EFalse;
+        }
+
+    TImpsDataAccessor myAc( aFields );
+    TPtrC msgContentType;
+    TBool retVal = EFalse;
+    TInt messageType = aFields->MessageType();
+
+    // Handle SAP initiated transactions here.
+    //
+    switch ( this->iServiceType )
+        {
+        case EImpsEventMessage:
+            if ( messageType == EImpsNewMessage )
+                {
+                retVal = ETrue;
+                // Checking the ApplicationID
+                // Consider client-id routing if media type is ok
+                /*
+                 * 1) Get the ApplicationID from the message
+                 * 2) Get the ApplicationID from the corresponding session
+                 *    ownClientId = iSession->ApplicationID();
+                 * 3) if ownClientId == messageClientId -> retVal = ETrue,
+                 *    this includes the case when both are empty
+                 * 4) otherwise retVal = EFalse
+                */
+                TPtrC messageAppID; // The recipient's ApplicationID
+
+                TImpsSDataUtils::GetApplicationIDL( &myAc, messageAppID );
+
+                if ( !messageAppID.Compare( iSession->ApplicationId() ) )
+                    {
+                    // The ClientIDs are matching
+                    retVal = ETrue;
+                    }
+                else
+                    {
+                    retVal = EFalse;
+                    }
+                }
+            else if ( messageType == EImpsDeliveryReportReq )
+                {
+                retVal = ETrue;
+                }
+            break;
+        case EImpsEventServerLogin:
+            if ( messageType == EImpsDisconnect )
+                {
+                retVal = ETrue;
+                }
+            break;
+        case EImpsEventGroup:
+            if ( messageType == EImpsLeaveGroupRes ||
+                 messageType == EImpsGroupChangeNotice )
+                {
+                retVal = ETrue;
+                }
+            break;
+        case EImpsEventCommon:
+            if ( messageType == EImpsInviteUserReq ||
+                 messageType == EImpsInviteRes ||
+                 messageType == EImpsCancelInviteUserReq )
+                {
+                retVal = ETrue;
+                }
+            break;
+        case EImpsEventPresence:
+        case EImpsEventPresencePure:
+            if ( messageType == EImpsPresenceNotification ||
+                 messageType == EImpsPresenceAuthReq ||
+                 messageType == EImpsPureData )
+                {
+                retVal = ETrue;
+                }
+            break;
+        default:
+            retVal = EFalse;
+            break;
+        }
+
+    return retVal;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::CheckRequests()
+// ---------------------------------------------------------
+TBool CImpsSubSession::CheckRequests(
+    const TDesC& aTid,
+    TInt& aOpId,
+    TImpsServRequest& aRequestType,
+    TImpsMessageType& aReqMsgType )
+    {
+    // It is possible that the cancel request from client is not
+    // complete and this session entity is still waiting deletion.
+    if ( iCanceled )
+        {
+        return EFalse;
+        }
+
+    aRequestType = EImpsServNone;
+    aReqMsgType = EImpsMessageNone;
+
+    TDblQueIter<CRequest> requestIter( iRequestList );
+
+    requestIter.SetToFirst();
+
+    while ( requestIter )
+        {
+        CRequest* request = requestIter;
+        requestIter++;
+
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: CheckRequests subses=%d aTID=%S reg.TID=%S" ),
+                                ( TInt )this, &aTid, &request->iTID );
+#endif
+
+        if ( !request->iTID.Compare( aTid ) )
+            {
+            aOpId = request->iOpId;
+            aRequestType = request->iRequestType;
+            aReqMsgType = request->iMessageType;
+            return ETrue;
+            }
+        }
+
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// CImpsSubSession::DeleteRequest
+// -----------------------------------------------------------------------------
+TBool CImpsSubSession::DeleteRequest( TInt aOpId )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DeleteRequest opid=%d, subses=%d" ), aOpId, ( TInt )this );
+#endif
+    // Delete one buffered requests from this client,
+    TDblQueIter<CRequest> rIter( iRequestList );
+
+    rIter.SetToFirst();
+
+    while ( rIter )
+        {
+        CRequest* requ = rIter;
+        rIter++;
+        if ( requ->iOpId == aOpId )
+            {
+#ifndef _NO_IMPS_LOGGING_
+            CImpsClientLogger::Log( _L( "SubSession: DeleteRequest TID=%S" ), &( requ->iTID ) ); //lint !e525
+#endif
+            requ->Destroy();
+            return ETrue;
+            }
+        }
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: DeleteRequest failed" ) );
+#endif
+    return EFalse;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::NextEventL()
+// ---------------------------------------------------------
+void CImpsSubSession::NextEventL( const RMessage2& aMsg )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: NextEventL begins subses=%d" ), ( TInt )this );
+#endif
+    if ( iCanceled )
+        {
+        User::Leave( KErrCancel );
+        }
+    StoreEventMsg( aMsg );
+    DoNextEvent();
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SendEventBodyL()
+// ---------------------------------------------------------
+void CImpsSubSession::SendEventBodyL( const RMessage2& aMsg )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: SendEventBodyL subses=%d" ), ( TInt )this );
+#endif
+    if ( iCanceled )
+        {
+        User::Leave( KErrCancel );
+        }
+
+    StoreEventMsg( aMsg );
+    DoEventBodyL();
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoCreateEventL
+// ---------------------------------------------------------
+void CImpsSubSession::DoCreateEventL(
+    TInt aOpId,
+    TImpsServRequest aRequestType,
+    TImpsMessageType aReqMesType,
+    CImpsFields* aFields )
+    {
+
+#ifdef _DEBUG
+    TInt rate = 0;
+    if ( !rate )
+        {
+        __UHEAP_RESET;
+        }
+    else
+        {
+        __UHEAP_FAILNEXT( rate );
+        }
+#endif
+
+    // Create an event
+    CWvEvent* myEvent = new ( ELeave ) CWvEvent( iServiceType );
+    CleanupStack::PushL( myEvent );     // <<< myEvent
+    myEvent->iOpCode = aOpId;
+    myEvent->iStatus = aFields->Status();
+    myEvent->iRequestType = aRequestType;
+    myEvent->iMessageType = ( TImpsMessageType )aFields->MessageType();
+    myEvent->iReqMesType = aReqMesType;
+
+    // convert CSP error code if any
+    TInt32 resstatus = aFields->ResponseStatusL();
+    if ( resstatus != KImpsStatusOk &&
+         resstatus != KErrNone )
+        {
+        myEvent->iStatus = ErrorCode( resstatus );
+        }
+
+    TImpsMessageType msgType = ( TImpsMessageType )myEvent->iMessageType;
+
+    // *********************************************************
+    // Handle pure client responses in different way
+    // In that case XML-formatted response needed in a client
+    if ( iServiceType == EImpsEventPresencePure )
+        {
+        if ( msgType == EImpsStatus ||
+             impsService( iVariant, TImpsMessageType( msgType ) ) == EImpsEventPresence )
+            {
+            myEvent->iMessageType = EImpsPureData;
+            msgType = EImpsPureData;
+            }
+        }
+    //*********************************************************/
+
+    // List of responses having serialized CImpsFields
+    // Notice: Check this list if new methods in API presented
+    if ( // im client
+        msgType == EImpsNewMessage ||
+        msgType == EImpsGetBlockedListRes ||
+        msgType == EImpsSendMessageRes ||
+        msgType == EImpsDeliveryReportReq ||
+        // group client
+        msgType == EImpsJoinGroupRes ||
+        msgType == EImpsLeaveGroupRes ||
+        msgType == EImpsGroupMembersRes ||
+        msgType == EImpsGroupPropertiesRes ||
+        msgType == EImpsGroupRejectListRes ||
+        msgType == EImpsNewUsers ||
+        msgType == EImpsLeftUsers ||
+        msgType == EImpsGroupChangeNotice ||
+        // Pure data
+        msgType == EImpsPureData ||
+        // fundamental
+        impsService( iVariant, TImpsMessageType( msgType ) ) == EImpsEventCommon ||
+        // presence
+        impsService( iVariant, TImpsMessageType( msgType ) ) == EImpsEventPresence )
+        // access service has nothing to pack in responses
+        {
+        HBufC8* dataBuffer = NULL;
+        TRAPD( errx, ( dataBuffer = HBufC8::NewL( aFields->Size() ) ) );
+        if ( errx )
+            {
+            myEvent->iStatus = errx;
+            myEvent->iPackedMessage = NULL;
+            }
+        else
+            {
+            TImpsPackedEntity packed( dataBuffer );
+            // Check data length if enough
+            User::LeaveIfError ( packed.PackEntity( *aFields ) );
+            myEvent->iPackedMessage = dataBuffer;
+            }
+        }
+
+    // detailed error data code in special format
+    // notice: avoid to pack in vain
+    if ( msgType == EImpsStatus && iDetailedError )
+        {
+        HBufC8* dataBuffer = HBufC8::NewLC( aFields->Size() );
+        TImpsPackedEntity packed( dataBuffer );
+        // Check data length if enough
+        User::LeaveIfError ( packed.PackEntity( *aFields ) );
+        myEvent->iPackedMessage = dataBuffer;
+        CleanupStack::Pop();    // dataBuffer
+        }
+
+    // list of responses having one byte response
+    if ( aFields->MessageType() == EImpsSubsGroupNoticeRes )
+        {
+        CImpsKey* myKey = CImpsKey::NewLC();    // <<< myKey
+        CImpsDataAccessor* myAc = CImpsDataAccessor::NewL( aFields );
+        CleanupStack::PushL( myAc );            // <<< myAc
+        TBool groupNotice = TImpsSDataUtils::SubGroupNoticeResponseL(
+                                myKey,
+                                myAc );
+        myEvent->iAux = groupNotice;
+        CleanupStack::PopAndDestroy( 2 );   // >>> myKey, myAc
+        }
+
+    // New event created succesfully
+    CleanupStack::Pop( 1 ); // >>> myEvent
+    // Since WV 1,2 logout messages may be either disconnect or Status,
+    // harmomize them here in order to avoid sending logout message
+    // with opid=actual_id and opid=0.
+    if ( aRequestType == EImpsServWVLogout )
+        {
+        iLastEvent = EImpsDisconnect;
+        }
+    else
+        {
+        iLastEvent = myEvent->iMessageType;
+        }
+    iEventList.AddLast( *myEvent );
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoCreateEventL
+// This creates wv engine online status event
+// ---------------------------------------------------------
+void CImpsSubSession::DoCreateEventL(
+    EImpsInternalStatus aStatus )
+    {
+#ifdef _DEBUG
+    TInt rate = 0;
+    if ( !rate )
+        {
+        __UHEAP_RESET;
+        }
+    else
+        {
+        __UHEAP_FAILNEXT( rate );
+        }
+#endif
+    // Create an event
+    CWvEvent* myEvent = new ( ELeave ) CWvEvent( iServiceType );
+    // No need for CleanupStack, no leave before inserted to queue
+    myEvent->iOpCode = 0;
+    myEvent->iStatus = KErrNone;
+    myEvent->iRequestType = EImpsServStatusReg;
+    myEvent->iMessageType = EImpsMessageNone;
+    myEvent->iReqMesType = 0;
+    myEvent->iAux = aStatus;
+
+    // New event created succesfully
+    iEventList.AddLast( *myEvent );
+
+    // Notice: this does not update iLastEvent
+    }
+
+// ---------------------------------------------------------
+// CImpSsubSession::DoCreateEventL
+// This creates wv engine expiry events and other internal messages
+// ---------------------------------------------------------
+void CImpsSubSession::DoCreateEventL(
+    TInt aOpCode,
+    TInt aStatus,
+    TImpsMessageType aReqMesType,
+    TImpsMessageType aRcvMesType,
+    TImpsServRequest aReqType )
+    {
+#ifdef _DEBUG
+    TInt rate = 0;
+    if ( !rate )
+        {
+        __UHEAP_RESET;
+        }
+    else
+        {
+        __UHEAP_FAILNEXT( rate );
+        }
+#endif
+    // Create an event
+    CWvEvent* myEvent = new ( ELeave ) CWvEvent( iServiceType );
+    // No need for CleanupStack, no leave before inserted to queue
+    myEvent->iOpCode = aOpCode;
+    myEvent->iStatus = aStatus;
+    myEvent->iMessageType = aRcvMesType;
+    myEvent->iReqMesType = aReqMesType;
+    myEvent->iRequestType = aReqType;
+
+    // New event created succesfully
+    iLastEvent = myEvent->iMessageType;
+    iEventList.AddLast( *myEvent );
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoSplitGroupChangeL
+// ---------------------------------------------------------
+void CImpsSubSession::DoSplitGroupChangeL(
+    TInt aOpId,
+    TImpsServRequest aRequestType,
+    CImpsFields* aFields )
+    {
+
+    CImpsKey* myKey = CImpsKey::NewLC();    // <<< myKey
+    CImpsDataAccessor* myAc = CImpsDataAccessor::NewL( aFields );
+    CleanupStack::PushL( myAc );        // myAc
+
+    TImpsSDataUtils::AddValuesFromArrayL(
+        myKey,
+        KTransContentElements,
+        sizeof( KTransContentElements ) /
+        sizeof( KTransContentElements[0] ) );
+    myKey->AddL( CREATEKEY( EImpsKeyGroupChangeNotice, 0 ) );
+
+    // First get GroupId
+    TDesC* pId;  // for GroupID
+    myKey->AddL( CREATEKEY( EImpsKeyGroupID, 0 ) );
+    if ( myAc->RestoreDescL( myKey, pId ) )
+        {
+        myKey->PopL();
+        }
+    else
+        {
+        // This means illegal data from transport.
+        User::Leave( EImpsCorrupted );
+        }
+
+    // Search if new users
+    myKey->AddL( CREATEKEY( EImpsKeyJoined, 0 ) );
+    if ( myAc->CheckBranchExistenceL( myKey ) )
+        {
+        CImpsFields* newFields = CImpsFields::NewL();
+        CleanupStack::PushL( newFields );   // <<< newFields
+
+        // Copy message type etc important
+        newFields->SetStatus( aFields->Status( ) );
+
+        // set special internal message type
+        newFields->SetMessageType( EImpsNewUsers );
+
+        // Copy data, only Joined users list
+        CImpsDataAccessor* myAc2 = CImpsDataAccessor::NewLC( newFields );
+        TImpsDataUtils::CopyNewUsersL( *myAc, *myAc2 );
+
+        // Add Group Id
+        myKey->ReplaceLastL( CREATEKEY( EImpsKeyGroupID, 0 ) );
+        myAc2->StoreDescL( myKey, *pId );
+        // Make a request entity into queue
+        DoCreateEventL( aOpId, aRequestType, EImpsMessageNone, newFields );
+
+        CleanupStack::PopAndDestroy( 2 );   // >>> myAc2, newFields
+        }
+
+    // Search if LeftUsers
+    myKey->ReplaceLastL( CREATEKEY( EImpsKeyLeft, 0 ) );
+    if ( myAc->CheckBranchExistenceL( myKey ) )
+        {
+        CImpsFields* newFields = CImpsFields::NewL();
+        CleanupStack::PushL( newFields );   // <<< newFields
+
+        // Copy message type etc important
+        newFields->SetStatus( aFields->Status( ) );
+
+        // set special internal message type
+        newFields->SetMessageType( EImpsLeftUsers );
+
+        // Copy data, only Left users list
+        CImpsDataAccessor* myAc2 = CImpsDataAccessor::NewLC( newFields );
+        TImpsDataUtils::CopyLeftUsersL( *myAc, *myAc2 );
+
+        // Add Group Id
+        myKey->ReplaceLastL( CREATEKEY( EImpsKeyGroupID, 0 ) );
+        myAc2->StoreDescL( myKey, *pId );
+
+        // Make a request entity into queue
+        DoCreateEventL( aOpId, aRequestType, EImpsMessageNone, newFields );
+
+        CleanupStack::PopAndDestroy( 2 );   // >>> myAc2, newFields
+        }
+
+    CImpsFields* newFields = CImpsFields::NewL();
+    CleanupStack::PushL( newFields );   // <<< newFields
+
+    // Copy message type etc important
+    newFields->SetStatus( aFields->Status( ) );
+
+    // set special internal message type
+    newFields->SetMessageType( EImpsGroupChangeNotice );
+
+    // Search if GroupProperties
+    CImpsDataAccessor* myAc2 = CImpsDataAccessor::NewLC( newFields );
+    myKey->ReplaceLastL( CREATEKEY( EImpsKeyGroupProperties, 0 ) );
+    if ( myAc->CheckBranchExistenceL( myKey ) )
+        {
+        TImpsDataUtils::CopyGroupPropertiesL( *myAc, *myAc2 );
+        }
+    // Search if ownproperties
+    myKey->ReplaceLastL( CREATEKEY( EImpsKeyOwnProperties, 0 ) );
+    if ( myAc->CheckBranchExistenceL( myKey ) )
+        {
+        TImpsDataUtils::CopyOwnPropertiesL( *myAc, *myAc2 );
+        }
+
+    // Add Group Id
+    myKey->ReplaceLastL( CREATEKEY( EImpsKeyGroupID, 0 ) );
+    myAc2->StoreDescL( myKey, *pId );
+
+    // Make a request entity into queue
+    DoCreateEventL( aOpId, aRequestType, EImpsMessageNone, newFields );
+
+    CleanupStack::PopAndDestroy( 4 );     // >>> myAc2, newFields
+    // myKey, myAc
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::NbrEvents
+// ---------------------------------------------------------
+TInt CImpsSubSession::NbrEvents()
+    {
+    TDblQueIter<CWvEvent> eventIter( iEventList );
+    CWvEvent* event = NULL;
+    TInt counter = 0;
+
+    eventIter.SetToFirst();
+    event = eventIter;
+    while ( event )
+        {
+        counter++;
+        eventIter++;
+        event = eventIter;
+        }
+
+    return counter;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::GetWVDataL()
+// Generic data converter from packed data to internal data structure.
+// ---------------------------------------------------------
+void CImpsSubSession::GetWVDataL()
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: GetWVDataL begins" ) );
+#endif
+
+    // serialized message is always in ptr0
+    HBufC8* stream = *StreamBufAddr();
+    if ( iSession->ReadBuffer8L( 0, stream ) )
+        {
+        HBufC8** stream2 = StreamBufAddr();
+        *stream2 = stream;
+        }
+    ImpsFields()->Reset();
+    TImpsPackedEntity packedMessage( *StreamBufAddr() );
+    packedMessage.UnpackEntityL( *ImpsFields() );
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "Session: GetWVDataL ends" ) );
+#endif
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::ErrorCode
+// ---------------------------------------------------------
+TInt CImpsSubSession::ErrorCode( TInt aCSPError )
+    {
+
+    if ( aCSPError >= 100 && aCSPError <= 999 )
+        {
+        // Convert the error code if valid input value
+        return KImpsGeneralError - aCSPError;
+        }
+    // This means illagel data from transport.
+    return KImpsErrorValidate;
+
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoCreateEventIPC
+// ---------------------------------------------------------
+void CImpsSubSession::DoCreateEventIPC(
+    CWvEvent& aEvent, SImpsEventData* aData )
+    {
+    // Set pointers to the data
+    aData->iOpCode = aEvent.iOpCode;
+    aData->iMessageType = aEvent.iMessageType;
+    aData->iRequestType = ( TInt ) aEvent.iRequestType;
+    aData->iStatus = aEvent.iStatus;
+    aData->iMessageBody = EFalse;
+    aData->iReqMesType = aEvent.iReqMesType;
+    // this is not used in all cases but let's write it every time
+    aData->iAux = aEvent.iAux;
+
+    // Make a special change for Logout SAP errors to hide the error code
+    if ( iServiceType == EImpsEventServerLogin &&
+         aEvent.iRequestType == EImpsServWVLogout &&
+         aEvent.iStatus < ( Imps_ERROR_BASE - 200 ) )
+        {
+        aData->iStatus = KErrNone;
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L
+                                ( "SubSession: DoCreateEventIPC converts LOGOUT code" ) );
+#endif
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoWriteEventHeaders
+// ---------------------------------------------------------
+TInt CImpsSubSession::DoWriteEventHeaders(
+    RMessagePtr2 aMsg, const TDes& aData )
+    {
+
+    TInt err = KErrNone;
+
+    TPtrC ptr( aData );
+
+    err = aMsg.Write( 0, ptr );
+
+    __ASSERT_DEBUG( err != KErrBadDescriptor,
+                    aMsg.Panic( KImpsPanicCategory, EImpsCorrupted ) );
+
+#ifndef _NO_IMPS_LOGGING_
+    SImpsEventData* eventData = ( SImpsEventData* )aData.Ptr();
+    CImpsClientLogger::Log( _L(
+                                "SubSession: DoWriteEventHeaders opid=%d msg=0x%x status=%d err=%d subses=%d" ),
+                            eventData->iOpCode, eventData->iMessageType, eventData->iStatus, err, ( TInt )this );
+#endif
+
+    return err;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::DoWriteEventBody
+// ---------------------------------------------------------
+TInt CImpsSubSession::DoWriteEventBody(
+    RMessagePtr2 aMsg, CWvEvent& aEvent )
+    {
+    // Write message body
+    TPtrC8 ptr8( KNullDesC8 );
+    if ( aEvent.iPackedMessage )
+        {
+        ptr8.Set( aEvent.iPackedMessage->Des() );
+        }
+    TInt mLen = aMsg.GetDesMaxLength( 1 );
+    __ASSERT_DEBUG( mLen != KErrBadDescriptor,
+                    aMsg.Panic( KImpsPanicCategory, EImpsCorrupted ) );
+    __ASSERT_DEBUG( mLen >= ptr8.Length(),
+                    aMsg.Panic( KImpsPanicCategory, EImpsCorrupted ) );
+    if ( mLen < ptr8.Length() )
+        {
+        // This is an error situation in a client side
+        return KErrBadHandle;
+        }
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: EventMsg.Write2 subses=%d" ), ( TInt )this );
+    TTime testTime1;
+    testTime1.HomeTime();
+#endif
+
+    TInt err = aMsg.Write( 1, ptr8 );
+    __ASSERT_DEBUG( err != KErrBadDescriptor,
+                    aMsg.Panic( KImpsPanicCategory, EImpsCorrupted ) );
+
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L(
+                                "SubSession: DoWriteEventBody opid=%d msg=0x%x status=%d err=%d" ),
+                            aEvent.iOpCode, aEvent.iMessageType, aEvent.iStatus, err );
+#endif
+
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::StoreEventMsg
+// ---------------------------------------------------------
+void CImpsSubSession::StoreEventMsg( RMessagePtr2 aMsg )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    RMessage2 myMsg( aMsg );
+    CImpsClientLogger::Log( _L( "SubSession: StoreEventMsg subses=%d bufSize=%d" ), ( TInt )this, myMsg.Int2() );
+#endif
+    iEventMsg = aMsg;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::CompleteEventMsg
+// ---------------------------------------------------------
+void CImpsSubSession::CompleteEventMsg( TInt aStatus )
+    {
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: CompleteEventMsg size=%d subses=%d" ), aStatus, ( TInt )this );
+#endif
+    if ( iEventMsg.IsNull() )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: CompleteEventMsg NULL MSG subses=%d" ), ( TInt )this );
+#endif
+        return;
+        }
+    iEventMsg.Complete( aStatus );
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::EventMsgBufSize
+// ---------------------------------------------------------
+TInt CImpsSubSession::EventMsgBufSize( ) const
+    {
+    if ( iEventMsg.IsNull() )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: EventMsgBufSize NULL MSG subses=%d" ), ( TInt )this );
+#endif
+        return 0;
+        }
+    RMessage2 myMsg( iEventMsg );
+    return myMsg.Int2();
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::ConvertStatusCode
+// ---------------------------------------------------------
+TInt CImpsSubSession::ConvertStatusCode( EImpsInternalStatus aStatus ) const
+    {
+    TInt newStatus = KImpsOnlineStatus;
+    if ( aStatus == EInternal_OFF_LINE )
+        {
+        // This is not supported in client API anymore, so this is an error code.
+        newStatus = KImpsOfflineStatus;
+        }
+    else if ( aStatus == EInternal_NOT_LOGGED )
+        {
+        newStatus = KImpsNotLoggedStatus;
+        }
+    else if ( aStatus == EInternal_NO_IAP )
+        {
+        newStatus = KImpsNoIapStatus;
+        }
+    else if ( aStatus == EInternal_SHUTTING_DOWN )
+        {
+        newStatus = KImpsErrorShuttingDown;
+        }
+    return newStatus;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::CancelTrans
+// ---------------------------------------------------------
+TBool CImpsSubSession::CancelTrans(
+    RMessagePtr2 aMsg, TImpsSessIdent aCSP )
+    {
+    // Get operation id from client request messages
+    RMessage2 myMsg( aMsg );
+    TInt opid = myMsg.Int0();
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: CancelTransL opid=%d subses=%d" ), opid, ( TInt )this );
+#endif
+
+    TBuf<KImpsMaxTID> myTid;
+
+    // delete the request
+    // If there is no such opid then return an error code
+    TBool found( EFalse );
+    TDblQueIter<CRequest> requestIter( iRequestList );
+    requestIter.SetToFirst();
+    while ( requestIter )
+        {
+        CRequest* request = requestIter;
+        requestIter++;
+        if ( request->iOpId == opid )
+            {
+            myTid = request->iTID;
+            // entry found and deleted
+            request->Destroy();
+            found = ETrue;
+            break;
+            }
+        }
+#ifndef _NO_IMPS_LOGGING_
+    CImpsClientLogger::Log( _L( "SubSession: CancelTransL returns %d" ), found );
+#endif
+    if ( found )
+        {
+        // ok, the session cancels the transport and completes the request
+        Server()->CancelTrans( myTid, aCSP );
+        }
+    return found;
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SetExpiryTime
+// ---------------------------------------------------------
+void CImpsSubSession::SetExpiryTime( RMessagePtr2 aMsg )
+    {
+    RMessage2 myMsg( aMsg );
+    TInt time = myMsg.Int0();
+    SetExpiryTime( time );
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::SetExpiryTime
+// ---------------------------------------------------------
+void CImpsSubSession::SetExpiryTime( TInt aVal )
+    {
+    if ( aVal > 0 )
+        {
+#ifndef _NO_IMPS_LOGGING_
+        CImpsClientLogger::Log( _L( "SubSession: SetExpiryTime %d" ), aVal );
+#endif
+        iExpiryTime = aVal;
+        Server()->SetExpiryTimer( aVal, EFalse );
+        }
+    }
+
+// ---------------------------------------------------------
+// CImpsSubSession::ExpiryTime
+// ---------------------------------------------------------
+TInt CImpsSubSession::ExpiryTime( )
+    {
+    return iExpiryTime;
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMErrors::StoreOOM
+// ---------------------------------------------------------
+void TImpsOOMErrors::StoreOOM( TInt aOpCode )
+    {
+    // FIFO, a ring buffer
+    iOOMList[iW].SetOpId( aOpCode );
+    TInt newW = ( iW + 1 ) % KImpsMaxBuffered;
+    if ( newW != iR )
+        {
+        // overflow, don't increase the index.
+        // the last free write cell is re-used as needed
+        iW = newW;
+        }
+    }
+
+// ---------------------------------------------------------
+// ImpsOOMErrors::GetOOM
+// ---------------------------------------------------------
+TInt TImpsOOMErrors::GetOOM()
+    {
+    if ( iR != iW )
+        {
+        iOOMList[iR].SetAsSent();
+        return iOOMList[iR].OpId();
+        }
+    else
+        {
+        return 0;
+        }
+    }
+
+// ---------------------------------------------------------
+// ImpsOOMErrors::RemoveOOM
+// ---------------------------------------------------------
+TInt TImpsOOMErrors::RemoveOOM()
+    {
+    if ( iR != iW && iOOMList[iR].IsSent() )
+        {
+        TInt ret = iOOMList[iR].OpId();
+        iR = ( ++iR ) % KImpsMaxBuffered;
+        return ret;
+        }
+    else
+        {
+        return 0;
+        }
+    }
+
+// ---------------------------------------------------------
+// ImpsOOMErrors::Exists
+// ---------------------------------------------------------
+TBool TImpsOOMErrors::Exists()
+    {
+    if ( iR != iW /*&& !iOOMList[iR].IsSent()*/ )
+        {
+        return ETrue;
+        }
+    else
+        {
+        return EFalse;
+        }
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMErrors::Reset
+// ---------------------------------------------------------
+void TImpsOOMErrors::Reset()
+    {
+    iR = 0;
+    iW = 0;
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMError::TImpsOOMError
+// ---------------------------------------------------------
+TImpsOOMError::TImpsOOMError()
+    {
+    iOpId = 0;
+    iSent = EFalse;
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMError::SetOpId
+// ---------------------------------------------------------
+void TImpsOOMError::SetOpId( TInt aOpId )
+    {
+    iOpId = aOpId;
+    iSent = EFalse;
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMError::OpId
+// ---------------------------------------------------------
+TInt TImpsOOMError::OpId()
+    {
+    return iOpId;
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMError::SetAsSent
+// ---------------------------------------------------------
+void TImpsOOMError::SetAsSent( )
+    {
+    iSent = ETrue;
+    }
+
+// ---------------------------------------------------------
+// TImpsOOMError::IsSent
+// ---------------------------------------------------------
+TBool TImpsOOMError::IsSent( )
+    {
+    return iSent;
+    }
+
+
+//  End of File