--- /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 <logengdurations.h>
+#endif
+#include <msventry.h>
+#include <logcli.h>
+#include <logview.h>
+// socket error when switching to offline mode
+#include <es_sock.h>
+// Internet error when switching to offline mode
+#include <inet6err.h>
+#include <CoreApplicationUIsSDKCRKeys.h>
+#include <e32property.h> // pubsub
+#include <connect/sbdefs.h>
+#include <apgcli.h> // RApaLsSession
+#include <fileprotectionresolver.h>
+#include <mmsvattachmentmanager.h>
+
+#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<KMaxDataTypeLength> 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<TUint32>( 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<KMmsDateBufferLength> 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<KMmsDateBufferLength> 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