diff -r 000000000000 -r 72b543305e3a mmsengine/mmsserver/src/mmsbaseoperation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmsengine/mmsserver/src/mmsbaseoperation.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,2123 @@ +/* +* Copyright (c) 2004-2007 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: base class for mms operations +* +*/ + + + + +// INCLUDE FILES +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif +#include +#include +#include +// socket error when switching to offline mode +#include +// Internet error when switching to offline mode +#include +#include +#include // pubsub +#include +#include // RApaLsSession +#include +#include + +#include "mmsbaseoperation.h" +#include "mmsservercommon.h" +#include "mmserrors.h" +#include "mmssettings.h" +#include "mmsserverentry.h" +#include "mmssession.h" +#include "mmsheaders.h" +#include "mmsdecode.h" +#include "mmsencode.h" +#include "mmsscheduledentry.h" +#include "mmslog.h" +#include "mmsconninit.h" +#include "MmsServerDebugLogging.h" +#include "MmsPhoneClient.H" +#include "mmscodecdatasupplier.h" +#include "mmscodecdatasink.h" +#include "mmsregisteredapplications.h" + +// EXTERNAL DATA STRUCTURES + +// EXTERNAL FUNCTION PROTOTYPES + +// CONSTANTS +const TInt KMmsBackupWait = 300000000; // wait for 5min, no more +const TInt KMmsServerReadyWait = 5000000; // wait for 5 sec for server ready after backup end + +// MACROS + +// LOCAL CONSTANTS AND MACROS + +// MODULE DATA STRUCTURES + +// LOCAL FUNCTION PROTOTYPES + +// FORWARD DECLARATIONS + +// ============================= LOCAL FUNCTIONS =============================== + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CMmsBaseOperation::CMmsBaseOperation +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CMmsBaseOperation::CMmsBaseOperation( RFs& aFs ):CMsgActive( KMmsActiveObjectPriority ), +iFs( aFs ) + { + // As this class is derived from CBase, all uninitialized variables are set to 0. + } + +// ----------------------------------------------------------------------------- +// CMmsBaseOperation::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CMmsBaseOperation::ConstructL( CMmsSettings* aMmsSettings ) + { + // Default critical state + // If nothing is stored, operations just fall through, and there is no error + iCriticalState = EMmsOperationEncodingPDU; + iMmsSettings = aMmsSettings; + + // connect to socket to keep connection open as long as we need it + TInt error = KErrNone; + error = iSocketServ.Connect(); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("BaseOperation: Connect Socket server returned error %d"), error ); + } +#endif + User::LeaveIfError( error ); + error = iConnection.Open( iSocketServ ); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("BaseOperation: RConnection::Open returned error %d"), error ); + } +#endif + User::LeaveIfError( error ); + iFailed = new( ELeave ) CMsvEntrySelection; + iSuccessful = new( ELeave ) CMsvEntrySelection; + iResponse = CMmsHeaders::NewL( iMmsSettings->MmsVersion() ); + iEncoder = CMmsEncode::NewL( iFs ); + iDecoder = CMmsDecode::NewL( iFs ); + // expand size is arbitrary. It is not used, we + // always allocate the amount we need + iEncodeBuffer = CBufFlat::NewL( 0x100 ); + iMmsSession = CMmsSession::NewL( KMmsActiveObjectPriority, iSocketServ, iConnection ); + iRemoteParties = new ( ELeave )CDesCArrayFlat( KMmsArrayAllocationNumber ); + // observe backup/restore event ends + iBackupStart = EFalse; + iBackupEnd = EFalse; + } + +// ----------------------------------------------------------------------------- +// CMmsBaseOperation::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CMmsBaseOperation* CMmsBaseOperation::NewL( RFs& aFs, CMmsSettings* aMmsSettings ) + { + CMmsBaseOperation* self = new( ELeave ) CMmsBaseOperation( aFs ); + + CleanupStack::PushL( self ); + self->ConstructL( aMmsSettings ); + CleanupStack::Pop( self ); + + return self; + } + + +// Destructor +CMmsBaseOperation::~CMmsBaseOperation() + { + // derived class must cancel. + Cancel(); // framework requirement + // iSelection, iServerEntry, and iMmsSettings + // are not deleted, because they belong to caller + +// start of ROAMING CHECK handling + delete iPhone; +// end ROAMING CHECK handling + if ( iRemoteParties ) + { + iRemoteParties->Reset(); + } + delete iRemoteParties; + delete iEncodeBuffer; + delete iFailed; + delete iSuccessful; + delete iEntryWrapper; + delete iUri; + delete iResponse; + delete iEncoder; + delete iDecoder; + delete iMmsLog; + delete iLogEvent; + delete iLogViewEvent; + delete iLogClient; + delete iMmsSession; + + // connection initiator should be NULL already... + delete iConnectionInitiator; + delete iIAPArray; + iConnection.Close(); + iSocketServ.Close(); + + } + +// --------------------------------------------------------- +// CMmsBaseOperation::NetworkOperationsAllowed() +// +// --------------------------------------------------------- +// +TBool CMmsBaseOperation::NetworkOperationsAllowed() + { + TBool networkAllowed = ETrue; // optimist + // If there is no such key, we will continue normally. + // This means that in a system where online/offline mode switching + // is not supported, we behave as we were always online + + CRepository* repository = NULL; + TInt error = KErrNone; + TInt value = ECoreAppUIsNetworkConnectionAllowed; + TRAP( error, repository = CRepository::NewL( KCRUidCoreApplicationUIs ) ); + if( error == KErrNone ) + { + repository->Get( KCoreAppUIsNetworkConnectionAllowed, value ); + delete repository; + repository = NULL; + if ( value == ECoreAppUIsNetworkConnectionNotAllowed ) + { + networkAllowed = EFalse; + } + } + + return networkAllowed; + } + +// --------------------------------------------------------- +// CMmsBaseOperation::AllocateTID +// +// --------------------------------------------------------- +// +TInt64 CMmsBaseOperation::AllocateTID() + { + TTime currentTime; + currentTime.UniversalTime(); + return currentTime.Int64(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::StartL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::StartL( + CMsvEntrySelection& aSelection, + CMsvServerEntry& aServerEntry, + TMsvId aService, + TRequestStatus& aStatus ) + { + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation starting")); +#endif + + InitializeL( aSelection, aServerEntry, aService ); + + if ( iCurrentMessageNo < 1 ) + { + // nothing to send. Give up immediately + aStatus = KRequestPending; + TRequestStatus* status = &aStatus; + User::RequestComplete( status, KErrNotFound ); + return; + } + + // The first thing to do is to encode the first message. + // We don't start connecting before we have something to send + // We want to minimize the connection time in order to minimize + // the probability of the connection being broken. + + // In the connect routine we must check that the connection + // exists, and open it if it doesn't + + // the last call in derived class StartL before SetActive() must be Queue. + // This will store the caller's status and set it to "request pending" state + Queue( aStatus ); + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::ContainsClosedContent +// +// --------------------------------------------------------- +// +TInt CMmsBaseOperation::CheckClosedContentL( CMsvServerEntry& aServerEntry, RFs& aFs ) + { + RApaLsSession apaLsSession; + TInt err = apaLsSession.Connect(); + + if ( err != KErrNone ) + { + return err; + } + CleanupClosePushL( apaLsSession ); + + CFileProtectionResolver* fileProt = CFileProtectionResolver::NewLC( aFs ); + + TDataRecognitionResult* dataType = new( ELeave )TDataRecognitionResult; + CleanupStack::PushL( dataType ); + + CMsvStore* store = aServerEntry.ReadStoreL(); + CleanupStack::PushL( store ); + + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + + TInt attaCount = attachMan.AttachmentCount(); + + TInt i = 0; + // we check all attachments, but if we find even one forbidden type, we give up. + for ( i = 0; i < attaCount && err == KErrNone; i++ ) + { + CMsvAttachment* attaInfo = attachMan.GetAttachmentInfoL( i ); + RFile file; + err = file.Open( aFs, attaInfo->FilePath(), EFileRead|EFileShareReadersOnly ); + delete attaInfo; + attaInfo = NULL; + + if ( err == KErrNone ) + { + // We continue only if we managed to open the file successfully + CleanupClosePushL( file ); + + err = apaLsSession.RecognizeData( file, *dataType ); +#ifndef _NO_MMSS_LOGGING_ + if ( err != KErrNone ) + { + TMmsLogger::Log(_L("- Error in recognization of data type"), err ); + // We assume that the file cannot be closed content, if its type + // is not recognized + err = KErrNotSupported; + } + else + { + HBufC16* buf16 = HBufC16::NewLC( KMaxFileName ); + TPtrC dummy; + TPtr ptr = buf16->Des(); + file.Name( ptr ); + // we cannot log indefinitely long strings. + // We get this long strings only if the message is corrupted. + dummy.Set( ptr.Left( KMmsMaxLogStringLength ) ); + TMmsLogger::Log( _L("- filename: %S"), &dummy ); + CleanupStack::PopAndDestroy( buf16 ); + + TBuf buffer; + buffer.Copy( dataType->iDataType.Des8() ); + TMmsLogger::Log(_L("- Data type recognizer result: %S"), &buffer ); + if ( dataType->iConfidence == CApaDataRecognizerType::ECertain ) + { + TMmsLogger::Log(_L("- Confidence level: Certain")); + } + else if ( dataType->iConfidence == CApaDataRecognizerType::EProbable ) + { + TMmsLogger::Log(_L("- Confidence level: Probable")); + } + else if ( dataType->iConfidence == CApaDataRecognizerType::EPossible ) + { + TMmsLogger::Log(_L("- Confidence level: Possible")); + } + else if ( dataType->iConfidence == CApaDataRecognizerType::EUnlikely ) + { + TMmsLogger::Log(_L("- Confidence level: Unlikely")); + } + else if ( dataType->iConfidence == CApaDataRecognizerType::ENotRecognized ) + { + TMmsLogger::Log(_L("- Confidence level: Not recognized")); + } + else + { + TMmsLogger::Log(_L("- Unknown confidence level")); + } + } +#endif + // We Must set limit to "possible". The recognition is rather uncertain + if ( err == KErrNone && dataType->iConfidence >= CApaDataRecognizerType::EPossible ) + { + TInt pos = 0; + file.Seek( ESeekStart, pos ); // rewind just in case + TInt prot = fileProt->ProtectionStatusL( file, dataType->iDataType ); + if ( ( prot & ( EFileProtForwardLocked | EFileProtClosedContent ) ) && + !( prot & EFileProtSuperDistributable ) ) + { + err = KMmsErrorProtectedContent; + } + } + CleanupStack::PopAndDestroy( &file ); //close file + } +#ifndef _NO_MMSS_LOGGING_ + else + { + TMmsLogger::Log(_L("- Error in opening file for recognization of data type"), err ); + } +#endif + } + + CleanupStack::PopAndDestroy( store ); + CleanupStack::PopAndDestroy( dataType ); + CleanupStack::PopAndDestroy( fileProt ); + + CleanupStack::PopAndDestroy( &apaLsSession ); // close apaSession + + if ( err == KErrNotSupported ) + { + // the content type was not recognized, we must assume it is not closed content + err = KErrNone; + } + + return err; + } + +// --------------------------------------------------------- +// CMmsBaseOperation::HandleBackupOperationEventL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::HandleBackupOperationEventL( + const TBackupOperationAttributes& aBackupOperationAttributes ) + { + switch ( aBackupOperationAttributes.iOperation ) + { + case EStart: + iBackupStart = ETrue; + iBackupEnd = EFalse; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log(_L("Backup/Restore Start")); +#endif + break; + case EEnd: + case EAbort: + iBackupStart = EFalse; + if ( iHaveTimer ) + { + iTimer.Cancel(); + // Set this to true only if we cancel timer because of backup end + iBackupEnd = ETrue; + // do not do it twice + iDoNotWaitForBackupEnd = ETrue; + iMustWait = ETrue; + } +#ifndef _NO_MMSS_LOGGING_ + if ( aBackupOperationAttributes.iOperation == EEnd ) + { + TMmsLogger::Log(_L("Backup/Restore End")); + } + else // if not end, then it was abort + { + TMmsLogger::Log(_L("Backup/Restore Abort")); + } +#endif + break; + default: + break; + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::RunL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::RunL() + { + // DoRunL() takes the AO through the states, queuing another + // asynch step as required. + // if DoRunL() detects the end of the cycle it returns + // without queuing another cycle. + + // If Run() would exit without queuing another cycle it reports + // completion to the client. + // In CMsgActive this is true if the asynch step or DoRunL fails, + // or the state cycle is complete + // However, we must keep our cycle going in spite of error. + // If any step reports an error, we must cleanup the current + // operation, and continue to the next entry in the selection. + // Only if we lose the connection, we complete the whole loop. + + // If we have completed ourselves, the error is already in iError. + // However, if an asynchronous operation completed us, the error + // comes back to us in iStatus. + // As we always complete ourselves by returning iError as the + // status, we get back either our original iError, or a new + // error value set by an asynchronous operation. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation RunL status %d, error %d"), + iStatus.Int(), iError ); +#endif + TInt status=iStatus.Int(); + + if ( iBackupEnd && ( status == KErrCancel || status == KErrTimedOut ) ) + { + // timer cancelled because of backup end - continue as if no error + status = KErrNone; + } + + // wait for 5 seconds after backup end + if ( iMustWait ) + { + // Timer is never active if iMustWait == ETrue, + // Actually it is not possible to check if iTimer is active. + // We must rely on iMustWait. + // iMustWait is set true only when the timer was just cancelled + // because of backup end, and we must wait a few seconds before continuing. + iMustWait = EFalse; + iTimer.After( iStatus, KMmsServerReadyWait ); + SetActive(); + return; + } + + // when we come here after wait, the timer can go. + if ( iHaveTimer ) + { + iTimer.Cancel(); + iTimer.Close(); + iHaveTimer = EFalse; + iDoNotWaitForBackupEnd = ETrue; + iBackupEnd = EFalse; + } + + if ( status <= (TInt) KMsvMediaUnavailable && + status >= (TInt) KMsvIndexRestore && + !iDoNotWaitForBackupEnd ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- must wait for backup end ") ); +#endif + iState = EMmsOperationWaitingForBackupEnd; + // This will take us back to RunL + WaitForBackupEnd(); + // iError has been set when the media unavailable error has occurred. + if ( iError == status ) + { + iError = KErrNone; + } + return; + } + + if ( iState == EMmsOperationCheckingRoaming ) + { + // the status is the result of the roaming query + iRegistrationStatus = status; + delete iPhone; + iPhone = NULL; + if ( status > 0 ) + { + // If the status is a successful registration state result, + // it must not be set into iError + // However, if something has gone wrong, the error should + // be propagated. + status = 0; + } + } + + // Don't override old error. + // Connection state returns index of successful connection in status, + // it must be handled separately. + // And if logging fails, the error is not stored. + // Logging is done on "best effort" basis + if ( iError == KErrNone && + iState != EMmsOperationConnectingToIAP && + iState != EMmsOperationLogging) + { + iError = status; + } + + // The connection may go wrong, which means there's no use continuing now. + // On the other hand, if one operation fails, the next one may be all right. + // We don't exit in the middle of the loop without deciding + // what cleanup work must be done. + // Only cancel can exit. + if ( status != KErrCancel ) + { + // iError contains the possible error from previous step. + // It may be needed to decide what to do with current message + // in next step. + TRAPD( error,DoRunL() ); // continue operations, may re-queue + // must not requeue in error situations + // If we want to continue the loop in spite of error, + // we must not leave, but return the error in iError. + // Symbian code may leave, so we must trap the leaves here, + // as we want to keep the loop going unless something + // is really fatally wrong (we are completely out of memory, + // or the system has crashed) +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log(_L("BaseOperatin DoRunL left with error %d"), + error ); + if ( IsActive() ) + { + TMmsLogger::Log(_L("- still active!")); + } + } +#endif + __ASSERT_DEBUG( error==KErrNone || !IsActive(), User::Invariant() ); + if ( IsActive() ) // requeued + { + return; + } + status=error; + if ( error == KErrNone ) + { + // If DoRunL did not leave, possible error is in iError + status = iError; + } + } + + Complete( status ); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::DoRunL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::DoRunL() + { + + // This routine takes the state machine through the states + // until an error is encountered or the cycle completes. + + if ( iState != EMmsOperationFinalizing ) + { + SelectNextState(); + // If appropriate, ChangeState makes us active again + ChangeStateL(); + } + else + { + iState = EMmsOperationIdle; + iStatus = iError; + // If we return from DoRunL without becoming active again, + // RunL completes. + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::DoComplete +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::DoComplete( TInt& aError ) + { + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation DoComplete")); +#endif + // We should get here if we are cancelled, or if + // the cycle has completed (with or without error) + + // Only final cleanup can be done here, nothing asychronous is allowed + + UnSetSendMask( *iFailed, aError ); + + } + +// --------------------------------------------------------- +// CMmsBaseOperation::DoCancel +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::DoCancel() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation DoCancel")); +#endif + // cancel anything that is still active + + if ( iPhone ) + { + iPhone->Cancel(); + } + if ( iDecoder ) + { + iDecoder->Cancel(); + } + if ( iEncoder ) + { + iEncoder->Cancel(); + } + if ( iConnectionInitiator ) + { + iConnectionInitiator->Cancel(); + } + if ( iMmsSession ) + { + iMmsSession->Cancel(); + } + if ( iHaveTimer ) + { + iTimer.Cancel(); + iTimer.Close(); + iHaveTimer = EFalse; + } + + CMsgActive::DoCancel(); + if ( iError == KErrNone ) + { + iError = KErrCancel; + } + + } + +// --------------------------------------------------------- +// CMmsBaseOperation::SelectNextState +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::SelectNextState() + { + + // If appropiate, the functions called within the switch statement + // will make us active again. If all is done, the asynchronous request + // will complete + + // iCritical state determines the state where the operation must be + // continued if progress is interrupted by an error caused by backup/restore. + // The critical state changes when enough information has been saved so + // that backtracking is no longer needed. The critical state is always a + // stat where information is read from disk or stored to disk, because + // disk operations may fail because of backup/restore. + // Continuation is usually possible only after backup, not after restore, + // but because available events do not specify which operation is in progress, + // every situation must be handled as if it would be possible to continue + // as soon as disk files can be accessed again. + + switch ( iState ) + { + case EMmsOperationIdle: + iState = EMmsOperationCheckingRoaming; + break; + case EMmsOperationCheckingRoaming: + // check what we got from the roaming query and adjust any + // roaming state dependent modes needed by the operation + GetRoamingState(); + iState = EMmsOperationEncodingPDU; + break; + case EMmsOperationEncodingPDU: + if ( iConnected ) + { + iState = EMmsOperationSubmittingTransaction; + } + else + { + iState = EMmsOperationConnectingToIAP; + } + iCriticalState = EMmsOperationCreatingEntry; + break; + case EMmsOperationConnectingToIAP: + CheckConnectionState(); + if ( iConnected ) + { + iState = EMmsOperationInitializingSession; + } + else + { + // if could not connect, give up + iState = EMmsOperationUpdatingEntryStatus; + } + break; + case EMmsOperationInitializingSession: + iState = EMmsOperationSubmittingTransaction; + break; + case EMmsOperationSubmittingTransaction: + iState = EMmsOperationCreatingEntry; + iCriticalState = EMmsOperationCreatingEntry; + break; + case EMmsOperationCreatingEntry: + iState = EMmsOperationDecodingResponse; + break; + case EMmsOperationDecodingResponse: + iState = EMmsOperationStoringResponseData; + iCriticalState = EMmsOperationStoringResponseData; + break; + case EMmsOperationStoringResponseData: + iState = EMmsOperationEncodingAck; + iCriticalState = EMmsOperationUpdatingEntryStatus; + break; + case EMmsOperationEncodingAck: + iState = EMmsOperationSendingAck; + break; + case EMmsOperationSendingAck: + iState = EMmsOperationUpdatingEntryStatus; + break; + case EMmsOperationUpdatingEntryStatus: + iState = EMmsOperationLogging; + iCriticalState = EMmsOperationMovingEntry; + break; + case EMmsOperationLogging: + iState = EMmsOperationMovingEntry; + break; + case EMmsOperationMovingEntry: + iCurrentMessageNo--; + if ( iCurrentMessageNo > 0 ) + { + iState = EMmsOperationEncodingPDU; + iCriticalState = EMmsOperationEncodingPDU; + } + else + { + iState = EMmsOperationFinalizing; + iCriticalState = EMmsOperationFinalizing; + } + break; + case EMmsOperationFinalizing: + // no more states + break; + case EMmsOperationWaitingForBackupEnd: + // return to failure point + iState = iCriticalState; + break; + default: + // If we are in an illegal state, we don't change it. + // The only way we can end here is if someone overwrites + // our stack, and in that case things are so terribly wrong + // that there is nothing to be done + // Change state will terminate the loop. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation in illegal state %d "), iState ); +#endif + break; + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::ChangeStateL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::ChangeStateL() + { + switch ( iState ) + { + case EMmsOperationCheckingRoaming: + RoamingCheck(); + break; + case EMmsOperationEncodingPDU: + EncodePDUL(); + break; + case EMmsOperationConnectingToIAP: + ConnectToIAPL(); + break; + case EMmsOperationInitializingSession: + InitializeSessionL(); + break; + case EMmsOperationSubmittingTransaction: + SubmitTransactionL(); + break; + case EMmsOperationCreatingEntry: + CreateEntryL(); + break; + case EMmsOperationDecodingResponse: + DecodeResponseL(); + break; + case EMmsOperationStoringResponseData: + StoreResponseL(); + break; + case EMmsOperationEncodingAck: + EncodeAckL(); + break; + case EMmsOperationSendingAck: + SendAckL(); + break; + case EMmsOperationUpdatingEntryStatus: + UpdateEntryStatusL(); + break; + case EMmsOperationLogging: + LogL(); + break; + case EMmsOperationMovingEntry: + MoveEntryL(); + break; + case EMmsOperationFinalizing: + FinalizeL(); + break; + case EMmsOperationWaitingForBackupEnd: + WaitForBackupEnd(); + break; + case EMmsOperationIdle: + // We should not become idle again. This is startup state + break; + default: + // Totally messed up. Someone overwrote the stack. + // If we return without becoming active again, the + // cycle completes. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation in illegal state %d "), iState ); +#endif + iError = KErrUnknown; + iStatus = iError; + break; + } + } + + +// --------------------------------------------------------- +// CMmsBaseOperation::FallThrough +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::FallThrough() + { + TRequestStatus* status = &iStatus; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, iError ); + } + + +// --------------------------------------------------------- +// CMmsBaseOperation::InitializeL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::InitializeL( + CMsvEntrySelection& aSelection, + CMsvServerEntry& aServerEntry, + TMsvId aService ) + { + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation Initialize")); +#endif + + iSelection = &aSelection; + iServerEntry = &aServerEntry; + iService = aService; + + iDoNotWaitForBackupEnd = EFalse; + iCriticalState = EMmsOperationEncodingPDU; + + delete iEntryWrapper; + iEntryWrapper = NULL; + iEntryWrapper = CMmsServerEntry::NewL( + iFs, + aServerEntry, + iService ); + + iFailed->Reset(); + iSuccessful->Reset(); + iResponse->Reset(); + + iCurrentMessageNo = iSelection->Count(); + iError = KErrNone; + + // clean these in case the class is reused + delete iUri; + iUri = NULL; + delete iIAPArray; + iIAPArray = NULL; + iConnectionIndex = 0; + iRegistrationStatus = 0; // hope for the best + + // Initialize everything into the failed list. + // Very pessimistic indeed. + + if ( iCurrentMessageNo > 0 ) + { + iFailed->AppendL( iSelection->Back( 0 ), iCurrentMessageNo ); + iSuccessful->SetReserveL( iCurrentMessageNo ); + } + + SetSendMask( *iFailed ); + + // + // NOTE: receive operation must override this setting to 'iLocalModeIn' + // + if ( iMmsSettings->LocalMode() ) + { + iFs.SetSessionPath( iMmsSettings->LocalModeOut() ); + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::EncodePDUL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::EncodePDUL() + { + // This state is quite variable. + // Each operation will need to know what to encode, + // no common implementation available. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation EncodePDUL - FallThrough")); +#endif + + // this always starts a new round. iError should be cleared. + FallThrough(); + + } + +// --------------------------------------------------------- +// CMmsBaseOperation::ConnectToIAPL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::ConnectToIAPL() + { + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation Connect to IAP")); +#endif + + if ( iError != KErrNone ) + { + FallThrough(); + return; + } + + // This operation should be identical for all derived classes. + TRequestStatus* status = &iStatus; + + // clean URI in case the class is used more than once + // Just at the last minute we check if network operations are allowed + if ( !NetworkOperationsAllowed() ) + { +#ifdef __WINS__ + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KMmsErrorOfflineMode ); + return; +#else + // if offline is on and global mode is on in HW, normal offline behaviour. + // if offline is on and local mode is on in HW, continue. + // The purpose of this is to allow fetching precreated messages even if + // network operations were not allowed during the first boot. + if ( !iMmsSettings->LocalMode() ) + { + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KMmsErrorOfflineMode ); + return; + } +#endif + } + + // Use WAP AP from Comms database table. + // This contains the home page URI, and is linked to the actual IAP. + TInt count = iMmsSettings->AccessPointCount(); + if( !iMmsSettings->LocalMode() ) + { + if ( count < 1 ) + { + User::Leave( KMmsErrorNoWAPAccessPoint ); + } + iIAPArray = new (ELeave) CArrayFixFlat( count ); + for( TInt index = 0; index < count; index++ ) + { + iIAPArray->AppendL( iMmsSettings->AccessPoint( index ) ); + } + + iConnectionInitiator = CMmsConnectionInitiator::NewL(); + // CMmsConnInit sets our status to "KRequestPending" + iConnectionInitiator->ConnectL( + iConnection, + *iIAPArray, + iStatus ); + + SetActive(); + } + else + { + // local mode has no real wap access point + iStatus = KRequestPending; + SetActive(); + // we return status 1 (as if we got connection to most + // preferred access point) + User::RequestComplete( status, 1 ); + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::InitializeSessionL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::InitializeSessionL() + { + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation InitializeSession")); +#endif + + if ( iError != KErrNone || iMmsSettings->LocalMode() ) + { + FallThrough(); + return; + } + +// When we are supporting only HTTP, there is no separate session connection stage. +// Session initialization only sets necessary headers. (User agent and UAProf) + delete iUri; + iUri = NULL; + TUint32 iap = 0; + TBool proxyUsed = EFalse; + HBufC8* proxyAddress = NULL; + HBufC* uri = NULL; + + // We are not supposed to be here, if we haven't succeeded + // in making connection to one of specified access points + CMmsConnectionInitiator::GetParametersL( + iIAPArray->At( iConnectionIndex - 1 ), + iap, + uri, + proxyUsed, + proxyAddress + ); + + // We must remember the uri, it will be used later. + iUri = uri; + uri = NULL; + + CleanupStack::PushL( proxyAddress ); + + iMmsSession->OpenL( + ( !proxyAddress )?TPtrC8():proxyAddress->Des(), + proxyUsed, + iMmsSettings->MaximumReceiveSize(), + iMmsSettings->MaximumSendSize(), + iStatus ); + + CleanupStack::PopAndDestroy( proxyAddress ); + SetActive(); + + } + +// --------------------------------------------------------- +// CMmsBaseOperation::SubmitTransactionL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::SubmitTransactionL() + { + // Standard transaction is sending a PDU to MMSC in global mode + // Local mode will need an override if supported (send and fetch only) + // Fetch will need an override, because it uses HTTP GET + // and not HTTP POST like other operations. + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation SubmitTransaction")); +#endif + + if ( !iConnected ) + { + if ( iError == KErrNone ) + { + iError = KErrCouldNotConnect; + } + } + + // This check is needed only when running tests in local mode + // if length of URI is 0, Symbian code will panic + if ( !iUri ) + { + iError = KMmsErrorNoURI1; + } + else if ( iUri->Des().Length() == 0 ) + { + iError = KMmsErrorNoURI1; + } + else + { + // keep LINT happy + } + + if ( iError != KErrNone ) + { + FallThrough(); + return; + } + + // Set entry + TInt error = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) ); + + // + // Last minute check to make sure entry has not been deleted or suspended + // + if( error == KErrNone ) + { + TMsvEntry entry = iServerEntry->Entry(); + if( entry.SendingState() == KMsvSendStateSuspended || entry.Deleted() ) + { + // The message has disappeared and we cannot do anything with it. + #ifndef _NO_MMSS_LOGGING_ + if ( entry.SendingState() == KMsvSendStateSuspended ) + { + TMmsLogger::Log( _L("- message in suspended") ); + } + if ( entry.Deleted() ) + { + TMmsLogger::Log( _L("- message deleted") ); + } + TMmsLogger::Log( _L("-> finished with this entry") ); + #endif + // delete entry from the list + iFailed->Delete( iCurrentMessageNo - 1 ); + + // + // Set error and fall through + // + iError = KErrNotFound; + FallThrough(); + return; + } + } + + // + // Update the sending state of our trigger + // + if( error == KErrNone ) + { + TMsvEntry entry = iServerEntry->Entry(); + entry.SetConnected( ETrue ); + entry.SetSendingState( KMsvSendStateSending ); + // if this fails, our sending state is wrong, but so what... + iServerEntry->ChangeEntry( entry ); + } + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + // + // Send + // + iMmsSession->SendMessageL( iUri->Des(), + *iEncodeBuffer, + *iEncoder, + *iDecoder, + iStatus ); + SetActive(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::CreateEntryL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::CreateEntryL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation CreateEntry - FallThrough")); +#endif + // When sending messages or command PDUs (forward) there is no entry to create. + // These fall through, only receive message and view MMBox have incoming data. + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::DecodeResponseL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::DecodeResponseL() + { + // Default action is to decode response into headers. + // The result will be analyzed in StoreResponse function + // There some data may be updated to original entry or just + // error code will be set. + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation DecodeResponse")); +#endif + + iResponse->Reset(); + if ( !( iError != KErrNone || iEncodeBuffer->Size() == 0 ) ) + { + // we have got a response - try to decode it + TRAP( iError, + { + // We don't need an actual entry here. + iEntryWrapper->SetCurrentEntry( KMsvNullIndexEntryId ); + iDecoder->DecodeHeadersL( + *iEntryWrapper, + *iResponse, + *iEncodeBuffer ); + }); + // Response status is mandatory in most responses, but not in quite all. + // Some operations may need an override. + if ( iResponse->ResponseStatus() != 0 ) + { + MapResponseStatus( *iResponse ); + } + } + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::StoreResponseL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::StoreResponseL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation StoreResponse - FallThrough")); +#endif + // Here the response from MMSC is stored. + // When receiving message, the results have already been stored while decoding. + // Here the message would become visible. + // However, if there is an error, the error text is saved here for all messages. + // Other information may also be saved to the trigger entry (for example message id). + // When a message is forwarded, the relevant information is stored in the notification. + + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::EncodeAckL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::EncodeAckL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation EncodeAck - FallThrough")); +#endif + // Only receive message has ack, others fall through + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::SendAckL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::SendAckL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation SendAck - FallThrough")); +#endif + // Only receive message sends ack, others fall through + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::UpdateEntryStatusL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::UpdateEntryStatusL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation UpdateEntryStatus - FallThrough")); +#endif + // This will update the status of the trigger as successful + // or unsuccessful + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::MoveEntryL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::MoveEntryL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation MoveEntry - FallThrough")); +#endif + // This will need an override + // Send message moves entry to sent folder + // Receive message checks the fate of a notification. + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::LogL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::LogL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation Log - FallThrough")); +#endif + // This will need an override - all operations do not log + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::FinalizeL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::FinalizeL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation Finalize")); +#endif + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::WaitForBackupEnd +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::WaitForBackupEnd() + { + LOG( _L("BaseOperation WaitForBackupEnd")); + TInt error = KErrNone; + TInt value = 0; + error = RProperty::Get( KUidSystemCategory, conn::KUidBackupRestoreKey, value ); + + if( value != conn::ENoBackup && error == KErrNone ) + { + // We are not going to wait forever: using a timer + LOG( _L(" - backup running ") ); + error = iTimer.CreateLocal(); + if( error == KErrNone ) + { + iHaveTimer = ETrue; + // The timer cannot be active as it was just created. + // It makes no sense to check. + // And besides it is impossible to check if RTimer is active. + iTimer.After( iStatus, KMmsBackupWait ); + SetActive(); + return; + } + } +#ifndef _NO_MMSS_LOGGING_ + else + { + LOG( _L(" - backup not running ") ); + } +#endif + + // No backup - media gone for other reason + iDoNotWaitForBackupEnd = ETrue; + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::SetSendMask +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::SetSendMask( const CMsvEntrySelection& aSelection ) + { + TInt index = aSelection.Count(); + TInt error = KErrNone; + while ( index-- ) + { + error = ( iServerEntry->SetEntry( aSelection[index] ) ); + if ( error == KErrNone ) + { + TMsvEntry tEntry = iServerEntry->Entry(); + // State is set to "sending" as late as possible to allow cancelling + // until the last possible moment + // Error in an entry is set to KErrNone to allow the error + // to be updated in case we fail halfway through a selection, and want to + // set error to unhandled entries. + tEntry.iError = KErrNone; + iServerEntry->ChangeEntry( tEntry ); + } + } + // Release the entry + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + } + +// --------------------------------------------------------- +// CMmsBaseOperation::UnSetSendMask +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::UnSetSendMask( CMsvEntrySelection& aSelection, TInt aError ) + { + TInt index = aSelection.Count(); + + // If we have a connection error because switching to offline mode + // has interrupted our sending operation, we suspend our failed messges + if ( !NetworkOperationsAllowed() ) + { + aError = KMmsErrorOfflineMode; + } + + while ( index-- ) + { + UnSetSendMask( aSelection[index], aError ); + // When the entry is in no list, it will not be touched by UpDateEntriesL + // function in CMmsServerMtm. + // When we switch back to "online" mode, these enries will automatically + // be rescheluded. + if ( aError == KMmsErrorOfflineMode ) + { + aSelection.Delete( index ); + } + } + // Release the entry + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::UnSetSendMask +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::UnSetSendMask( const TMsvId aId, TInt aError ) + { + if ( ( iServerEntry->SetEntry( aId ) ) != KErrNone ) + { + return; // no can do + } + TInt error = KErrNone; + + TMsvEntry tEntry = iServerEntry->Entry(); + tEntry.SetConnected( EFalse ); + tEntry.SetFailed( EFalse ); // optimistic + if ( tEntry.iError == KErrNone ) + { + tEntry.iError = aError; + } + + if ( aError == KMmsErrorOfflineMode ) + { + // special case: we did not necessarily fail, but we are not allowed to send now + // Set entry to "suspended" state, but do not update recipient so that this does + // not count as a send or receive attempt + tEntry.iError = aError; + // value for KMmsOffLineStae defined in mmsservercommon.h + tEntry.SetSendingState( KMmsOffLineState ); + + // if the notification is in inbox or in mmbox folder, its operation flags have to be marked + // that the operation has finished due to offline mode. + // In addition if notification in mmbox folder has a duplicate, + // the duplicate has to be marked as well. + TMsvId mmboxFolder = iMmsSettings->MMBoxFolder(); + if ( tEntry.Parent() == mmboxFolder ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" - CMmsBaseOperation:: duplicates has to be searched") ); +#endif + TInt err = KErrNone; + TRAP ( err, MarkDuplicateL( EMmsNotificationOperationFailed, *iServerEntry ) ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Trap failed: error %d "), err ); +#endif + } + // Mark original notification + if ( tEntry.Parent() == mmboxFolder || + tEntry.Parent() == KMsvGlobalInBoxIndexEntryIdValue ) + { + MarkNotificationOperationFailed( tEntry ); + tEntry.SetReadOnly( ETrue ); + } + } + else + { + // we must also update the recipient information + // in case it was not set already. + + // We trap all leaves. + // If we cannot update recipient because we are in very low memory situation, + // that cannot be helped. + // DoCancel and DoComplete cannot leave. + + CMmsScheduledEntry* mmsScheduledEntry = NULL; + TRAP( error, + { + mmsScheduledEntry = CMmsScheduledEntry::NewL( tEntry ); + CleanupStack::PushL( mmsScheduledEntry ); + CMsvStore * store = NULL; + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); + mmsScheduledEntry->RestoreL( *store ); + // if status is still NotYetSent, recipient has not been updated yet + if ( mmsScheduledEntry->MmsRecipient().Status() == CMsvRecipient::ENotYetSent ) + { + UpdateRecipient( tEntry.iError, *mmsScheduledEntry ); + } + mmsScheduledEntry->StoreL( *store ); + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + CleanupStack::PopAndDestroy( mmsScheduledEntry ); + };) + mmsScheduledEntry = NULL; + + // Don't override previous error + // We may have entries that have failed for different reasons + if ( tEntry.iError != KErrNone ) + { + tEntry.SetFailed( ETrue ); + } + } + error = iServerEntry->ChangeEntry( tEntry ); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::UpdateRecipient +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::UpdateRecipient( TInt aError, CMmsScheduledEntry& aMmsScheduledEntry ) + { + aMmsScheduledEntry.MmsRecipient().Time().UniversalTime(); + aMmsScheduledEntry.MmsRecipient().SetError( aError ); + if ( aError == KErrNone ) + { + aMmsScheduledEntry.MmsRecipient().SetStatus( CMsvRecipient::ESentSuccessfully ); + } + else + { + aMmsScheduledEntry.MmsRecipient().SetStatus( CMsvRecipient::EFailedToSend ); + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::MapResponseStatus +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::MapResponseStatus( CMmsHeaders& aResponse ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation MapResponseStatus")); +#endif + // response status was initialize to 0 + // KMmsStatusOk = 128 + + // This is common respose status mapping. Retrieve status is different, + // MMBox view confirmation uses this mapping. + + switch ( aResponse.ResponseStatus() ) + { + case KMmsStatusOk: + // No error, don't change iError + break; + case KMmsErrorPermanentSendingAddressUnresolved: + case KMmsErrorSendingAddressUnresolved: + iError = KMmsErrorStatusMessageAddressUnresolved; + break; + case KMmsErrorTransientSendingAddressUnresolved: + iError = KMmsErrorStatusTransientAddressUnresolved; + break; + case KMmsErrorPermanentMessageNotFound: + case KMmsErrorMessageNotFound: + iError = KMmsErrorStatusMessageNotFound; + break; + case KMmsErrorTransientMessageNotFound: + iError = KMmsErrorStatusTransientMessageNotFound; + break; + case KMmsErrorTransientNetworkProblem: + case KMmsErrorNetworkProblem: + iError = KMmsErrorStatusNetworkProblem; + break; + case KMmsErrorPermanentServiceDenied: + case KMmsErrorServiceDenied: + iError = KMmsErrorStatusServiceDenied; + break; + case KMmsErrorPermanentMessageFormatCorrupt: + case KMmsErrorMessageFormatCorrupt: + iError = KMmsErrorStatusMessageFormatCorrupt; + break; + case KMmsErrorPermanentContentNotAccepted: + case KMmsErrorNoContentAccepted: + iError = KMmsErrorStatusContentNotAccepted; + break; + case KMmsErrorPermanentReplyChargingLimitationsNotMet: + iError = KMmsErrorStatusReplyChargingLimitationsNotMet; + break; + case KMmsErrorPermanentReplyChargingRequestNotAccepted: + iError = KMmsErrorStatusReplyChargingRequestNotAccepted; + break; + case KMmsErrorPermanentReplyChargingForwardingDenied: + iError = KMmsErrorStatusReplyChargingForwardingDenied; + break; + case KMmsErrorPermanentReplyChargingNotSupported: + iError = KMmsErrorStatusReplyChargingNotSupported; + break; + case KMmsErrorTransientFailure: + iError = KMmsErrorStatusTransientFailure; + break; + case KMmsErrorUnspecified: + iError = KMmsErrorStatusUnspecified; + break; + case KMmsErrorPermanentFailure: + iError = KMmsErrorStatusPermanentFailure; + break; + case KMmsErrorUnsupportedMessage: + iError = KMmsErrorStatusUnsupportedMessage; + break; + default: + // No known status, but did not leave either + if ( iError == KErrNone ) + { + if ( ( iResponse->ResponseStatus() & KMmsErrorRangeMask ) == KMmsErrorTransient ) + { + iError = KMmsErrorStatusTransientFailure; + } + else + { + iError = KMmsErrorStatusPermanentFailure; + } + } + break; + } + } + +// --------------------------------------------------------- +// CMmsBaseOperation::InitializeLoggingL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::InitializeLoggingL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("BaseOperation InitializeLogging")); +#endif + if ( !iLogClient ) + { + iLogClient = CLogClient::NewL( iFs ); + } + if ( !iLogViewEvent ) + { + iLogViewEvent = CLogViewEvent::NewL( *iLogClient ); + } + if ( !iMmsLog ) + { + iMmsLog = CMmsLog::NewL( *iLogClient, *iLogViewEvent, iFs ); + } + if ( !iLogEvent ) + { + iLogEvent = CLogEvent::NewL(); + } + } + + +// --------------------------------------------------------- +// CMmsBaseOperation::CheckConnectionState +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::CheckConnectionState() + { + TInt status=iStatus.Int(); + // these can go now. + delete iConnectionInitiator; + iConnectionInitiator = NULL; + if ( status > 0 ) + { + iConnectionIndex = status; + // We say we are connected when we have the connection to IAP. + // When protocol is HTTP there is no separate session opening step. + iConnected = ETrue; + } + else + { + iError = status; + if ( status == KErrGeneral ) + { + iError = KErrCouldNotConnect; + } + } + } +// --------------------------------------------------------- +// CMmsBaseOperation::FindDuplicateNotificationL +// +// --------------------------------------------------------- +// +TInt CMmsBaseOperation::FindDuplicateNotificationL( + TMsvId aParent, CMmsHeaders& aHeaders, TMsvId& aDuplicate ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" CMmsBaseOperation::FindDuplicateNotificationL") ); +#endif + + TInt error = KErrNone; + aDuplicate = KMsvNullIndexEntryId; + if ( aParent == KMsvNullIndexEntryId ) + { + return KErrNotSupported; + } + + CMsvServerEntry* workingEntry = NULL; + TRAP( error, workingEntry = iServerEntry->NewEntryL( aParent ) ); + CleanupStack::PushL( workingEntry ); + + if ( error != KErrNone ) + { + CleanupStack::PopAndDestroy( workingEntry ); // workingEntry + return error; + } + + CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + + error = workingEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *selection ); + + TInt count = selection->Count(); + if ( count == 0 ) + { + error = KErrNotSupported; + } + + if ( error != KErrNone ) + { + CleanupStack::PopAndDestroy( selection ); + CleanupStack::PopAndDestroy( workingEntry ); + return error; + } + + CMmsHeaders* mmsHeaders = CMmsHeaders::NewL( iMmsSettings->MmsVersion() ); + CleanupStack::PushL( mmsHeaders ); + + for ( TInt i = count; i > 0 && ( aDuplicate == KMsvNullIndexEntryId ); i-- ) + { + if ( workingEntry->SetEntry( selection->At( i - 1 ) ) == KErrNone ) + { + CMsvStore* store = workingEntry->ReadStoreL(); + CleanupStack::PushL( store ); + mmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + + // content location must match + if ( mmsHeaders->ContentLocation().Compare( aHeaders.ContentLocation() ) == 0 ) + { + // Identical. This probably means that we have not sent a response yet, + // and MMSC has sent us a new notification. + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- content locations match") ); +#endif + aDuplicate = workingEntry->Entry().Id(); + } + } + } + + CleanupStack::PopAndDestroy( mmsHeaders ); + CleanupStack::PopAndDestroy( selection ); + CleanupStack::PopAndDestroy( workingEntry ); + + return error; + } + +// --------------------------------------------------------- +// CMmsBaseOperation::FreeNotification +// +// --------------------------------------------------------- +// +TBool CMmsBaseOperation::FreeNotification( TMsvEntry aEntry, const TUint32 aOperation ) + { + if ( aEntry.iMtmData2 & KMmsNewOperationForbidden || + ( aEntry.iMtmData2 & KMmsOperationFinished && + !( aEntry.iMtmData2 & KMmsOperationResult ) && + !( aEntry.iMtmData2 & KMmsStoredInMMBox ) && + ( aOperation == KMmsOperationFetch || aOperation == KMmsOperationForward ))) + { + return EFalse; + } + + return ETrue; + } + +// --------------------------------------------------------- +// CMmsBaseOperation::MarkNotificationOperationFailed +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::MarkNotificationOperationFailed( TMsvEntry& aEntry ) + { + aEntry.iMtmData2 |= KMmsOperationFinished; // finished + aEntry.iMtmData2 |= KMmsOperationResult; // failed + aEntry.iMtmData2 &= ~KMmsOperationOngoing; // operation not active + aEntry.iMtmData2 &= ~KMmsNewOperationForbidden; // new operation allowed + } + +// --------------------------------------------------------- +// CMmsBaseOperation::MarkNotificationDeletedFromMmbox +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::MarkNotificationDeletedFromMmbox( TMsvEntry& aEntry ) + { + aEntry.iMtmData2 &= ~KMmsStoredInMMBox; // not in mmbox + aEntry.iMtmData2 &= ~KMmsOperationOngoing; // operation not active + aEntry.iMtmData2 |= KMmsOperationFinished; // finished + aEntry.iMtmData2 &= ~KMmsOperationResult; // successfully + aEntry.iMtmData2 &= ~KMmsNewOperationForbidden; // new operation allowed + } + +// --------------------------------------------------------- +// CMmsBaseOperation::MarkDuplicateL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::MarkDuplicateL( TInt aState, CMsvServerEntry& aServerEntry ) + { + // iServerEntry must point to the original notification + // We can put default version here as the headers will be restored immediately anyway + // This is a static function and does not have access to iMmsSettings where the actual + // version is. + // Restoring the headers reads the correct version for the message in question + // Headers are used here only to clear the duplicate id from the headers. + // Other headers are not changed. + CMmsHeaders* mmsHeaders = CMmsHeaders::NewL( KMmsDefaultVersion ); + CleanupStack::PushL( mmsHeaders ); + CMsvStore* store = aServerEntry.ReadStoreL(); + CleanupStack::PushL( store ); + mmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + store = NULL; + + TMsvId duplicate = mmsHeaders->RelatedEntry(); + if ( duplicate != KMsvNullIndexEntryId ) + { + // Clear related entry from the original notification + store = aServerEntry.EditStoreL(); + CleanupStack::PushL( store ); + mmsHeaders->SetRelatedEntry( KMsvNullIndexEntryId ); + mmsHeaders->StoreL( *store ); + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + store = NULL; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L( "- related entry cleared" ) ); +#endif + + // Leaves if entry cannot be accessed. + // We are inside a trap already... + CMsvServerEntry* duplicateEntry = aServerEntry.NewEntryL( duplicate ); + CleanupStack::PushL( duplicateEntry ); + // Mark duplicate + TMsvEntry dupEntry = duplicateEntry->Entry(); + switch( aState ) + { + case EMmsNotificationOperationFailed: + MarkNotificationOperationFailed( dupEntry ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L( "- duplicate marked failed" ) ); +#endif + break; + case EMmsDeletedFromMmbox: + MarkNotificationDeletedFromMmbox( dupEntry ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L( "- duplicate marked deleted" ) ); +#endif + break; + default: + break; + } + duplicateEntry->ChangeEntry( dupEntry ); + CleanupStack::PopAndDestroy( duplicateEntry ); + + } + CleanupStack::PopAndDestroy( mmsHeaders ); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::MarkNotificationOperationReserved +// --------------------------------------------------------- +// +void CMmsBaseOperation::MarkNotificationOperationReserved( TMsvEntry& aEntry, + const TUint32 aOperation ) + { + aEntry.iMtmData2 &= ~KMmsOperationIdentifier; // clear possible old operation + aEntry.iMtmData2 |= KMmsNewOperationForbidden; // forbidden + aEntry.iMtmData2 |= KMmsOperationOngoing; // operation is active + aEntry.iMtmData2 |= aOperation; // operation + aEntry.iMtmData2 &= ~KMmsOperationFinished; // not finished + aEntry.iMtmData2 &= ~KMmsOperationResult; // not failed + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TBool CMmsBaseOperation::RegisteredL( const TDesC& aApplicationId ) + { + TBool registered = EFalse; + // check if application id is registered + CMmsRegisteredApplications* regAppId = CMmsRegisteredApplications::NewL(); + CleanupStack::PushL( regAppId ); + regAppId->LoadRegisteredApplicationsL(); + registered = regAppId->RegisteredL( aApplicationId ); + CleanupStack::PopAndDestroy( regAppId ); + return registered; + } + +// --------------------------------------------------------- +// CMmsBaseOperation::CommonLogEventInitializationL +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::CommonLogEventInitializationL( + CMmsHeaders& aMmsHeaders, + TMsvEntry& aEntry ) + { + if ( !iLogEvent || !iLogClient ) + { + // something wrong with log - cannot do logging + User::Leave( KErrGeneral ); + } + + iLogEvent->SetEventType( KLogMmsEventTypeUid ); + iLogClient->GetString( iLogString, R_LOG_DIR_OUT ); + iLogEvent->SetDirection( iLogString ); + iLogEvent->SetDurationType( KLogDurationNone ); + + iLogEvent->SetSubject( aEntry.iDescription ); + iLogEvent->SetLink( aEntry.Id() ); + + iRemoteParties->Reset(); + iNumberOfRemoteParties = 0; + + // Get some fields into log entry + TTime now; + // the dates in log must be in universal time, not local time + now.UniversalTime(); + iLogEvent->SetTime( now ); + // We only log if the sending has been successful. + // We don't generate a log entry if iError not equal to KErrNone + // Therefore the status is always "pending" + iLogClient->GetString( iLogString, R_LOG_DEL_PENDING ); + iLogEvent->SetStatus( iLogString ); + + // Generate remote party list (pure addresses only) + TInt i; + TPtrC dummy; + // To list + for (i = 0; i < aMmsHeaders.ToRecipients().MdcaCount(); i++) + { + dummy.Set( TMmsGenUtils::PureAddress( aMmsHeaders.ToRecipients().MdcaPoint( i ) ) ); + if ( iMmsSettings->LogEmailRecipients() || + ( dummy.Locate( '@' ) == KErrNotFound ) ) + { + iRemoteParties->AppendL( dummy ); + iNumberOfRemoteParties++; + } + } + // Cc list + for (i = 0; i < aMmsHeaders.CcRecipients().MdcaCount(); i++) + { + dummy.Set( TMmsGenUtils::PureAddress( aMmsHeaders.CcRecipients().MdcaPoint( i ) ) ); + if ( iMmsSettings->LogEmailRecipients() || + ( dummy.Locate( '@' ) == KErrNotFound ) ) + { + iRemoteParties->AppendL( dummy ); + iNumberOfRemoteParties++; + } + } + // Bcc list + for (i = 0; i < aMmsHeaders.BccRecipients().MdcaCount(); i++) + { + dummy.Set( TMmsGenUtils::PureAddress( aMmsHeaders.BccRecipients().MdcaPoint( i ) ) ); + if ( iMmsSettings->LogEmailRecipients() || + ( dummy.Locate( '@' ) == KErrNotFound ) ) + { + iRemoteParties->AppendL( dummy ); + iNumberOfRemoteParties++; + } + } + } + +// start of ROAMING CHECK handling +// --------------------------------------------------------- +// CMmsBaseOperation::RoamingCheck +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::RoamingCheck() + { + // base operation does nothing + // This is just a placeholder + FallThrough(); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::GetRoamingState +// +// --------------------------------------------------------- +// +void CMmsBaseOperation::GetRoamingState() + { + // Nothing needs to be done here + } + + + +#ifndef _NO_MMSS_LOGGING_ +// --------------------------------------------------------- +// CMmsBaseOperation::LogDateL +// +// --------------------------------------------------------- +void CMmsBaseOperation::LogDateL( const TTime& aDate ) + { + TBuf dateString; + aDate.FormatL(dateString,(_L("%*E%*D%X%*N%Y %1 %2 '%3"))); + TMmsLogger::Log( _L(" - date %S"), &dateString ); + aDate.FormatL(dateString,(_L("%-B%:0%J%:1%T%:2%S%:3%+B"))); + TMmsLogger::Log( _L(" - time %S"), &dateString ); + } + +// --------------------------------------------------------- +// CMmsBaseOperation::LogNetworkFormatDateL +// +// --------------------------------------------------------- +void CMmsBaseOperation::LogNetworkFormatDateL( const TInt64& aDateInSeconds ) + { + TBuf dateString; + TMmsLogger::Log( _L(" - %d seconds from 1.1.1970 (UTC)"), aDateInSeconds ); + TTime time = TTime( KMmsYear1970String ) + + TTimeIntervalMicroSeconds( aDateInSeconds * KMmsMillion ); + time.FormatL(dateString,(_L("%*E%*D%X%*N%Y %1 %2 '%3"))); + TMmsLogger::Log( _L(" - date %S"), &dateString ); + time.FormatL(dateString,(_L("%-B%:0%J%:1%T%:2%S%:3%+B"))); + TMmsLogger::Log( _L(" - time %S"), &dateString ); + } + +#else +// --------------------------------------------------------- +// CMmsBaseOperation::LogDateL +// +// --------------------------------------------------------- +void CMmsBaseOperation::LogDateL( const TTime& ) + { + } + +// --------------------------------------------------------- +// CMmsBaseOperation::LogNetworkFormatDateL +// +// --------------------------------------------------------- +void CMmsBaseOperation::LogNetworkFormatDateL( const TInt64& ) + { + } +#endif + + +// ========================== OTHER EXPORTED FUNCTIONS ========================= + +// End of File