IMPSengine/engsrv/src/impssubsession.cpp
author Fionntina Carville <fionntinac@symbian.org>
Mon, 15 Nov 2010 11:27:49 +0000
branchRCL_3
changeset 22 df9aab66f73e
parent 0 094583676ce7
permissions -rw-r--r--
Bug 3539. Update localisation mappings.

/*
* 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