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