diff -r 000000000000 -r 094583676ce7 IMPSengine/engsrv/src/impssubsession.cpp --- /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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 myTid; + + // delete the request + // If there is no such opid then return an error code + TBool found( EFalse ); + TDblQueIter 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