diff -r 6a20128ce557 -r ebfee66fde93 mmsengine/mmsserver/src/mmsserver.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmsengine/mmsserver/src/mmsserver.cpp Fri Jun 04 10:25:39 2010 +0100 @@ -0,0 +1,5557 @@ +/* +* Copyright (c) 2002-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: +* Server Mtm +* +*/ + + + +// INCLUDE FILES +#include +#include +#include +#include +#include +#include +#include // for notification generation +#include +#include +#include +#include +#include +#include +#include +#include "LogsApiConsts.h" +#include + +// MMS specific +#include "mmsconst.h" +#include "mmserrors.h" +#include "mmsmmboxmessageheaders.h" +#include "mmsservercommon.h" +#include "mmscmds.h" +#include "mmssettings.h" +#include "mmsaccount.h" +#include "mmsserver.h" +#include "mmssendoperation.h" +#include "mmsreceivemessage.h" +#include "mmsforwardoperation.h" +#include "mmsdeleteoperation.h" +#include "mmsmmboxlist.h" +#include "mmsdecode.h" +#include "mmsencode.h" +#include "mmsheaders.h" +#include "mmsschedulesend.h" +#include "mmsscheduledentry.h" +#include "mmslog.h" +#include "mmsgenutils.h" +#include "mmsserverentry.h" +#include "MmsEnginePrivateCRKeys.h" +#include "mmsreadreport.h" + +// LOCAL CONSTANTS AND MACROS +const TInt KMmsGarbageCollectionDelay = 30; // 30s delay +const TInt KMmsSanityInterval = 96; // 96 hours, 4 days +const TInt KMmsScheduleAllowance = 10; +const TInt KMmsScheduleDelay = 5; + +// ================= MEMBER FUNCTIONS ======================= + +// --------------------------------------------------------- +// C++ default constructor can NOT contain any code, that +// might leave. +// --------------------------------------------------------- +// +CMmsServerMtm::CMmsServerMtm( + CRegisteredMtmDll& aRegisteredMtmDll, + CMsvServerEntry* aInitialEntry ) + : CScheduleBaseServerMtm( aRegisteredMtmDll, aInitialEntry ), + iNotification ( KMsvNullIndexEntryId ), + iOOMState ( EFalse ), + iDeliveryStatus (EFalse) + { + // Everything not mentioned gets initialized to NULL + // Save our initial entry id + // It is either default service or service specified in Entry Selection + iServiceEntryId = aInitialEntry->Entry().Id(); + // We use the file session offered by initial entry. + // Documentation says that it is expensive to open new file sessions + // We offer the same session to all classes we create so that everybody + // is using the same session. + // In the final version file session is needed for attachment access + // only, in the preliminary version we have a fake MMSC set up in a + // directory on disk, and we need the file session to access that. + iFs = aInitialEntry->FileSession(); + CActiveScheduler::Add( this ); + } + +// --------------------------------------------------------- +// Symbian OS default constructor can leave. +// +// --------------------------------------------------------- +// +void CMmsServerMtm::ConstructL() + { + + CScheduleBaseServerMtm::ConstructL(); + iScheduleSend = CMmsScheduleSend::NewL( *iServerEntry ); + iMsvSelection = new( ELeave ) CMsvEntrySelection; + iMmsSettings = CMmsSettings::NewL(); + // don't load settings yet in case someone else is trying + // to change the settings. + iMmsHeaders = CMmsHeaders::NewL( iMmsSettings->MmsVersion() ); + iMessageDrive = EDriveC; + // see if message server knows better + iMessageDrive = MessageServer::CurrentDriveL( iFs ); + } + +// --------------------------------------------------------- +// Factory function +// +// --------------------------------------------------------- +// +EXPORT_C CMmsServerMtm* CMmsServerMtm::NewL( + CRegisteredMtmDll& aRegisteredMtmDll, + CMsvServerEntry* aInitialEntry ) + { + + CleanupStack::PushL( aInitialEntry ); // Take ownership of aInitialEntry + CMmsServerMtm* self = new( ELeave ) CMmsServerMtm( + aRegisteredMtmDll, aInitialEntry ); + CleanupStack::Pop( aInitialEntry ); // Entry now safely stored in member + + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + return self; + } + + +// --------------------------------------------------------- +// Destructor +// +// --------------------------------------------------------- +// +CMmsServerMtm::~CMmsServerMtm() + { + // We don't close the file session anymore, as we + // obtained if from the initial entry, and it is not ours to close... + Cancel(); // cancel anything that may be pending... + if ( iRemoteParties ) + { + iRemoteParties->Reset(); + } + delete iEntryWrapper; + delete iRemoteParties; + delete iReadReport; + delete iSendOperation; + delete iReceiveMessage; + delete iForwardOperation; + delete iDeleteOperation; + delete iUpdateMmboxList; + delete iMsvSelection; + delete iMmsSettings; + delete iScheduleSend; + delete iDecoder; + delete iMmsHeaders; + delete iEncodeBuffer; + delete iMmsLog; + delete iLogEvent; + delete iLogViewEvent; + delete iLogClient; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MMSServer destructor, done") ); +#endif + } + + +// --------------------------------------------------------- +// CMmsServerMtm::CopyToLocalL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CopyToLocalL( + const CMsvEntrySelection& /*aSelection*/, + TMsvId /*aDestination*/, + TRequestStatus& aStatus ) + { + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNotSupported ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::CopyFromLocalL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CopyFromLocalL( + const CMsvEntrySelection& aSelection, + TMsvId /*aDestination*/, + TRequestStatus& aStatus ) + { + + // test code: copy from local means send. + // sent folder is handled separately + TCommandParameters parameters; + parameters.iInitialDelay = 0; + TCommandParametersBuf paramPack( parameters ); + + CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection; + CleanupStack::PushL(selection); + if ( aSelection.Count() > 0 ) + { + selection->AppendL( aSelection.Back( 0 ), aSelection.Count() ); + } + StartCommandL( *selection, EMmsScheduledSend, paramPack, aStatus ); + CleanupStack::PopAndDestroy( selection ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::CopyWithinServiceL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CopyWithinServiceL( + const CMsvEntrySelection& /*aSelection*/, + TMsvId /*aDestination*/, + TRequestStatus& aStatus ) + { + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNotSupported ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::MoveToLocalL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::MoveToLocalL( + const CMsvEntrySelection& /*aSelection*/, + TMsvId /*aDestination*/, + TRequestStatus& aStatus ) + { + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNotSupported ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::MoveFromLocalL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::MoveFromLocalL( + const CMsvEntrySelection& /*aSelection*/, + TMsvId /*aDestination*/, + TRequestStatus& aStatus ) + { + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNotSupported ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::MoveWithinServiceL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::MoveWithinServiceL( + const CMsvEntrySelection& /*aSelection*/, + TMsvId /*aDestination*/, + TRequestStatus& aStatus ) + { + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNotSupported ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::DeleteAllL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::DeleteAllL( + const CMsvEntrySelection& aSelection, + TRequestStatus& aStatus ) + { + + // this is implemented for scheduled send + // It needs to change entry asynchronously, + // and needs help on server mtm + TInt error; + // we are always called with a selection that has at least one member. + // we cannot be called with an empty selection (because then the server + // does not know whom to call) + if ( aSelection.Count() == 0 ) + { + User::Leave( KErrNotFound ); + } + User::LeaveIfError( iServerEntry->SetEntry( aSelection[0] ) ); + User::LeaveIfError( iServerEntry->SetEntry( iServerEntry->Entry().Parent() ) ); + CMsvEntrySelection* sel = aSelection.CopyL(); + error = iServerEntry->DeleteEntries( *sel ); + if ( error == KErrNotFound ) + { + error = KErrNone; // if not found, deleted already. + } + delete sel; + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, error ); + } + +// --------------------------------------------------------- +// CMmsServerMtm::CreateL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CreateL( + TMsvEntry /*aNewEntry*/, + TRequestStatus& aStatus ) + { + + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNotSupported ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::ChangeL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::ChangeL( + TMsvEntry aNewEntry, + TRequestStatus& aStatus ) + { + + // this is implemented for scheduled send + // It needs to change entry asynchronously, + // and needs help on server mtm + User::LeaveIfError( iServerEntry->SetEntry( aNewEntry.Id() )); + User::LeaveIfError( iServerEntry->ChangeEntry( aNewEntry ) ); + TRequestStatus* status = &aStatus; + aStatus = KRequestPending; + User::RequestComplete( status, KErrNone ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::StartCommandL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::StartCommandL( + CMsvEntrySelection& aSelection, + TInt aCommand, + const TDesC8& aParameter, + TRequestStatus& aStatus ) + { + + TInt error = KErrNone; + TMsvEntry entry; // This will be used a lot later to access the index data + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MMSServer Start Command %d"), aCommand ); +#endif // _NO_MMSS_LOGGING_ + // log the code for debugging + LogCommandCode( aCommand ); + + // The content of the parameter depends on command. + // For EMmsDecodePushedMessage it will be TWatcherParametersBuf structure, + // but the content is currently ignored because it has become impossible + // to copy the data between processes in the protected environment. + // For EMmsScheduledSend, EMmsScheduledReceive, and EMmsScheduledReceiveForced + // it will be TCommandParametersBuf. + // For EMmsGarbageCollection it will be TMMSGarbageCollectionParametersBuf + // containing the reason for the garbage collection. + // For others the parameter will be ignored. + // The parameter should be unpackaged only when the contents are known. + + // The default service entry is always used. + + // Because of the restriction that only one mtm + // per service can be open at any time, the scheduling calls + // cheat and offer the stuff here using local service instead + // of mms service. As we would like to load our settings + // anyway, we try to find out the real service. + + if ( iServiceEntryId == KMsvLocalServiceIndexEntryId ) + { + // we have been cheated +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- local service id") ); +#endif + // Get the actual service id from a message entry + GetRealServiceId( aSelection ); + } + + // free whatever entry we are holding + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + // + // Load the service settings. + // In case call fails, loading will be retried + // + TInt loadServiceError = KErrNone; + TRAP( loadServiceError, LoadSettingsL( aCommand ) ); + +#ifndef _NO_MMSS_LOGGING_ + if ( loadServiceError != KErrNone ) + { + TMmsLogger::Log( _L("-ERROR loading settings: %d"), loadServiceError ); + } + else + { + TMmsLogger::Log( _L("- settings loaded successfully") ); + } +#endif + + iCurrentCommand = aCommand; + if ( aCommand != EMmsRetryServiceLoading ) + { + iCommand = aCommand; + iParameter = aParameter; + iRequestStatus = &aStatus; + + // We remove the service entry from the selection, as we don't need it anymore + iMsvSelection->Reset(); + if ( aSelection.Count() > 0 ) + { + iMsvSelection->AppendL( aSelection.Back( 0 ), aSelection.Count() ); + } + } + else + { + // iRequestStatus was set on an earlier round. + // EMmsRetryServiceLoading is never the first command + iCurrentCommand = iCommand; // orginal command was stored here + } + + // If service loading has failed, we loop through RunL to retry. + // Actually only EMmsScheduledReceive and EMmsScheduledReceiveForced + // need this service. + // Other callers can handle error situations gracefully. + // We have saved all our parameters. + // All we have to do now is to change iCurrent command and complete ourselves + // In order to get to our RunL. + // RunL will route us to DoRunL where we can continue + + if ( loadServiceError != KErrNone ) + { + // Actually we should no longer get load service error as the settings + // are now in central repository, no longer saved into the service entry + HandleLoadServiceError( loadServiceError ); + // we cannot continue. HandleLoadServiceError has set completion status + // as required. + return; + } + + // If we have loaded the service, we can discard the service entry + // if it still is in our selection + if ( iMsvSelection->Count() > 0 && iMsvSelection->At( 0 ) == iServiceEntryId ) + { + iMsvSelection->Delete( 0 ); + } + +#ifndef _NO_MMSS_LOGGING_ + // log the parent folder of the selection (needed for debugging) + LogEntryParent(); +#endif + + // we do not move the entries anywhere. + // it is the client's responsibility to move them to the right place + // we just make them visible because some applications left invisible + // entries to outbox. + if ( iCurrentCommand == EMmsSend || + iCurrentCommand == EMmsScheduledSend || + iCurrentCommand == EMmsDeleteSchedule ) + { + RestoreVisibilityAndService(); + } + + // + // Following switch handles all the different requests + // + switch( iCurrentCommand ) + { + // scheduled operations can only use default service. + case EMmsScheduledSend: + if ( iMsvSelection->Count() > 0 ) + { + iCommand = EMmsSend; + // This will complete our caller. + // If no error, task scheduler will complete the caller. + // If error, the subroutine will complete + error = ScheduleSelectionL(); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("MmsServer Schedule send status %d"), error ); + } +#endif + } + else + { + // nothing to send - successfully sent nothing + // ("You must have keen eyes to see nobody coming") + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + break; + // + // Handle push message (i.e. notification or delivery report) + // + case EMmsDecodePushedMessage: + { + // + // First read pushed data from dummy entries stream store, + // and then delete it. + // If there is no entry, HandleDummyEntryL() leaves + // + HandleDummyEntryL(); + // + // Decode received databuffer into message + // + TInt err = KErrNone; + TRAP( err, DecodePushedMessageL() ); + // DecodePushedMessageL might set iError, don't override it + if ( iError == KErrNone ) + { + iError = err; + } + + delete iEncodeBuffer; + iEncodeBuffer = NULL; + // The resulting id is now in iNotification + + if ( iNotification != KMsvNullIndexEntryId ) + { + iMsvSelection->AppendL( iNotification ); + if ( iServerEntry->SetEntry( iNotification ) == KErrNone ) + { + entry = iServerEntry->Entry(); + } + // Unrecognized PDUs are handled as notifications. + // we send a response back to MMSC + if ( ( ( entry.iMtmData1 & KMmsMessageTypeMask ) == KMmsMessageDeliveryInd ) || + ( ( entry.iMtmData1 & KMmsMessageTypeMask ) == KMmsMessageReadOrigInd ) ) + { + // Delivery report or PDU read report + HandleDeliveryReportL(); + } + else + { + // Everything else. Handle as notification. + // If not a notification, send back response "unrecognized" + // Any PDU with wrong type will fall here besides the actual notifications. + HandleNotificationL(); + } + } + else + { + // something has gone wrong... + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, iError ); + } + break; + } + case EMmsScheduledReceiveForced: + iCommand = EMmsReceiveForced; + iMmsSettings->SetFetchOverride( ETrue ); + // if we do forced fetch, we clean up old schedules first + if ( iMsvSelection->Count() > 0 ) + { + CleanSchedulesL( *iMsvSelection ); + } + // fall through on purpose + case EMmsScheduledReceive: + if ( iCurrentCommand != EMmsScheduledReceiveForced ) + { + iCommand = EMmsReceive; + } + if ( iMsvSelection->Count() < 1 ) + { + TRAP( iError, CreateNotificationsL() ); + } + else // iMsvSelection->Count() > 0 + { + // notifications are not checked if the fetch is forced + if ( iCurrentCommand == EMmsScheduledReceive ) + { + CheckNotificationsL( *iMsvSelection ); + } + } + + if ( iMsvSelection->Count() > 0 ) + { + // This will complete our caller. + // If no error, task scheduler will complete the caller. + // If error, the subroutine will complete + error = ScheduleSelectionL(); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("MmsServer Schedule receive status %d"), error ); + } +#endif + } + else + { + // Nothing to be done, complete. + if ( iError != KErrNoMemory && iError != KErrDiskFull ) + { + iError = KErrNone; + } + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, iError ); + } + break; + case EMmsSend: + // send messages in current selection + // we cannot do this, if we don't have settings. + // Our access point is defined in settings. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Number of entries to send %d"), iMsvSelection->Count() ); +#endif + if ( iMsvSelection->Count() == 0 ) + { + // nothing in the selection... + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + else + { + iCommand = EMmsSend; + SendToMmscL(); + } + break; + case EMmsReceive: + // fetch message to inbox + // This is a troublesome case. + // we cannot fetch if we did not manage to load + // our settings. + // And we cannot leave, because schsend has an assert + // that forbids rescheduling entries that are not children + // of local service. And our notifications are children of + // the MMS service itself. + iCommand = EMmsReceive; + FetchFromMmscL(); + break; + case EMmsReceiveForced: + iCommand = EMmsReceiveForced; + iMmsSettings->SetFetchOverride( ETrue ); + FetchFromMmscL(); + break; + case EMmsLogDeliveryReport: + iCommand = EMmsLogDeliveryReport; + // delivery reports should appear one by one for handling + LogDeliveryReportL(); + break; + case EMmsDeleteSchedule: + iScheduleSend->DeleteScheduleL( aSelection ); + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + break; + case EMmsDeleteEntries: + if ( iMsvSelection->Count() > 0 ) + { + error = iServerEntry->SetEntry( iMsvSelection->At(0) ); + if ( error == KErrNone ) + { + error = iServerEntry->SetEntry( iServerEntry->Entry().Parent() ); + if ( error == KErrNone ) + { + error = iServerEntry->DeleteEntries( *iMsvSelection ); + } + } + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, error ); + } + else + { + // if nothing to delete, then done already + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + break; + case EMmsGarbageCollection: + TRAP(error, GarbageCollectionL()); + // This returns at least KMmsErrorOfflineMode + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, error ); + break; + case EMmsMessageGeneration: + iCommand = EMmsReceiveForced; + iMmsSettings->SetLocalModeIn( KMmsMessageVariationDirectory() ); + iMmsSettings->SetFetchOverride( ETrue ); + // set local mode on the fly - not stored anywhere. + iMmsSettings->SetLocalMode( ETrue ); + iMmsSettings->SetAcceptAnonymousMessages( ETrue ); // variated messages are anonymous + FetchFromMmscL(); + break; + case EMmsDeleteExpiredNotifications: + // not implemented + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNotSupported ); + break; + + case EMmsScheduledForward: + // Make sure there is something to schedule + if ( iMsvSelection->Count() > 0 ) + { + iCommand = EMmsForward; + // ScheduleSelectionL completes the caller. + // If no error, task scheduler will complete the caller. + // If error, the subroutine will complete + error = ScheduleSelectionL(); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("MmsServer EMmsScheduledForward status %d"), error ); + } +#endif + } + else + { + // Nothing to send + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + break; + + case EMmsForward: + // Sends the current selection (containing forward requests) + // we cannot do this, if we don't have settings. + // Our access point is defined in settings. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Number of forward requests to send %d"), iMsvSelection->Count() ); +#endif + if ( iMsvSelection->Count() == 0 ) + { + // Nothing in the selection + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + else + { + iCommand = EMmsForward; + SendForwardRequestsToMmscL(); + } + break; + + // + // Handles scheduling of notification deletion + // + case EMmsScheduledNotificationDelete: + // Make sure there is something to schedule + if ( iMsvSelection->Count() > 0 ) + { + iCommand = EMmsNotificationDelete; + // ScheduleSelectionL completes the caller. + // If no error, task scheduler will complete the caller. + // If error, the subroutine will complete + error = ScheduleSelectionL(); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("MmsServer EMmsScheduledNotificationDelete status %d"), error ); + } +#endif + } + else + { + // Nothing to schedule + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + break; + + // + // Deletes selection of notifications + // + case EMmsNotificationDelete: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Number of notifications to delete %d"), iMsvSelection->Count() ); +#endif + if ( iMsvSelection->Count() == 0 ) + { + // Nothing in the selection + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + else + { + iCommand = EMmsNotificationDelete; + + // Dig out delete type + TCommandParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + + // + // Create a CMmsDeleteOperation instance and start it + // + delete iDeleteOperation; + iDeleteOperation = NULL; + iDeleteOperation = CMmsDeleteOperation::NewL( iFs, iMmsSettings ); + iDeleteOperation->StartL( + (TMmsDeleteOperationType)paramPack().iError, + *iMsvSelection, + *iServerEntry, + iServiceEntryId, + iStatus ); + *iRequestStatus = KRequestPending; + SetActive(); + } + break; + // update mmbox list + case EMmsUpdateMmboxList: + iCommand = EMmsUpdateMmboxList; + delete iUpdateMmboxList; + iUpdateMmboxList = NULL; + iUpdateMmboxList = CMmsMmboxList::NewL( iFs, iMmsSettings ); + iUpdateMmboxList->StartL( + *iMsvSelection, + *iServerEntry, + iServiceEntryId, + iStatus ); + *iRequestStatus = KRequestPending; + SetActive(); + break; + case EMmsSendReadReport: + SendReadReportL(); + break; + case EMmsScheduledReadReport: + if ( iMsvSelection->Count() > 0 ) + { + iCommand = EMmsSendReadReport; + // ScheduleSelectionL completes the caller. + // If no error, task scheduler will complete the caller. + // If error, the subroutine will complete + error = ScheduleSelectionL(); +#ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("MmsServer EMmsScheduledReadReport status %d"), error ); + } +#endif + } + else + { + // Nothing to schedule + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNone ); + } + break; + default: + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNotSupported ); + break; + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::CommandExpected +// +// --------------------------------------------------------- +// +TBool CMmsServerMtm::CommandExpected() + { + // so far we don't expect anything + + return EFalse; + + } + +// --------------------------------------------------------- +// CMmsServerMtm::Progress +// +// --------------------------------------------------------- +// +const TDesC8& CMmsServerMtm::Progress() + { + // should load in latest progress, if something is going on + + return iProgressBuffer; + + } + +// --------------------------------------------------------- +// CMmsServerMtm::LoadResourceFile +// +// --------------------------------------------------------- +// +void CMmsServerMtm::LoadResourceFileL() + { + // THIS IS NO LONGER NEEDED, BECAUSE THERE IS NO RESOURCE FILE ANY MORE. + } + +// --------------------------------------------------------- +// CMmsServerMtm::PopulateSchedulePackage +// --------------------------------------------------------- +// +void CMmsServerMtm::PopulateSchedulePackage( const TDesC8& aParameter, + const TBool /*aMove*/, TMsvSchedulePackage& aPackage ) const + { + aPackage.iParameter = aParameter; + // We have a member telling what we are supposed to do. + // We can schedule both sending and receiving. + aPackage.iCommandId = iCommand; + } + +// --------------------------------------------------------- +// CMmsServerMtm::RestoreScheduleSettingsL +// --------------------------------------------------------- +// +void CMmsServerMtm::RestoreScheduleSettingsL( + TBool /*aRestoreErrorsFromResource*/, + TInt /*aErrorsResourceId*/ ) + { + // EMPTY IMPLEMENTATION + } + +// --------------------------------------------------------- +// CMmsServerMtm::DoCancel +// +// --------------------------------------------------------- +// +void CMmsServerMtm::DoCancel() + { + + // first cancel whatever operation is active + if ( iSendOperation ) + { + iSendOperation->Cancel(); + } + + if ( iReceiveMessage ) + { + iReceiveMessage->Cancel(); + } + + if ( iMmsLog ) + { + iMmsLog->Cancel(); + } + + if( iDeleteOperation ) + { + iDeleteOperation->Cancel(); + } + + if( iForwardOperation ) + { + iForwardOperation->Cancel(); + } + + if ( iUpdateMmboxList ) + { + iUpdateMmboxList->Cancel(); + } + + if ( iReadReport ) + { + iReadReport->Cancel(); + } + + DoComplete( KErrCancel ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::DoRunL +// Active object completion +// Run is used in this object to clean up after operations have finished. +// +// --------------------------------------------------------- +// +void CMmsServerMtm::DoRunL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" MmsServer DoRunL status %d"), iStatus.Int() ); +#endif + + if ( iCurrentCommand == EMmsRetryServiceLoading ) + { + StartCommandL( + *iMsvSelection, + iCurrentCommand, + iParameter, + *iRequestStatus); + return; + } + + TInt error = KErrNone; + + if ( iOOMState ) // out of memory. + { + error = KErrNoMemory; + } + + if ( iCurrentCommand == EMmsUpdateMmboxList ) + { + error = iStatus.Int(); + } + + // When we come here, we must see, if everything + // was sent - or received, or if some items need resceduling + DoComplete( error ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::DoComplete +// +// Active object complete +// --------------------------------------------------------- +// +void CMmsServerMtm::DoComplete( TInt aError ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" MmsServer DoComplete with status %d"), aError ); +#endif + + // free whatever entry we were holding + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + // iSendOperation and iReceiveMessage tell us if + // we were sending or receiving. The one that is non-null + // has done all the work + + // Now we must check if all went fine, or do some entries + // need rescheduling. + + // In the case of sending iMsvSelection holds Ids of the + // messages to be sent. In the case of receiving iMsvSelection + // holds Ids of the notifications corresponding to messages + // to be fetched. + + TRAPD( error, UpdateEntriesL() ); + +#ifdef __WINS__ + if ( iSendOperation ) + { + User::InfoPrint(_L("MMS sending complete")); + } + if ( iReceiveMessage ) + { + User::InfoPrint(_L("MMS receiving complete")); + } +#endif + + // restore original entry + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + if ( iError == KErrNone ) + { + // The error may be changed in UpdateEntriesL + iError = aError; + } + + if ( iError == KMmsErrorApplicationDiskFull ) + { + // this was handled by restarting the fetch if possible + iError = KErrNone; + } + + if ( iError == KErrNone ) + { + // Pass on the error from UpDateEntriesL if it is relevant + // to the caller. + // First all errors caused by simultaneous backup/restore + // are passed on. + // If the entries were successfully reschedules we should + // have no error here. + if ( error >= KMsvMediaUnavailable && error <= KMsvIndexRestore ) + { + iError = error; + } + } +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer returns error: %d to caller" ), iError ); +#endif + if ( !( IsActive() && iStatus.Int() == KRequestPending ) ) + { + // If CMmsServer has become active again it means that it has restarted + // the fetch after deleting some application messages + // The restarted fetch will complete caller when finished + + // However, if any ongoing operation is cancelled prematurely, + // we may still be in active state but our own status is "cancelled" + + User::RequestComplete( iRequestStatus, iError ); + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::LoadSettingsL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::LoadSettingsL( TInt aCommand ) + { + // Load settings + iMmsSettings->LoadSettingsL(); + + // Save service entry id + iServiceEntryId = iMmsSettings->Service(); + + // Make sure localmode related paths exist + if( iMmsSettings->LocalMode() ) + { + iFs.MkDirAll( iMmsSettings->LocalModeIn() ); + iFs.MkDirAll( iMmsSettings->LocalModeOut() ); + } + + // Load schedule settings + ((CMmsScheduleSend*)iScheduleSend)->LoadSettingsL( aCommand ); + } + +// --------------------------------------------------------- +// CMmsServerMtm::SendToMmscL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::SendToMmscL() + { + // We call our new excellent state machine + delete iSendOperation; + iSendOperation = NULL; + iSendOperation = CMmsSendOperation::NewL( iFs, iMmsSettings ); + iSendOperation->StartL( *iMsvSelection, *iServerEntry, + iServiceEntryId, iStatus ); + *iRequestStatus = KRequestPending; + SetActive(); + } + +// --------------------------------------------------------- +// CMmsServerMtm::SendForwardRequestsToMmscL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::SendForwardRequestsToMmscL() + { + // + // Create a CMmsForwardOperation instance and start it + // + delete iForwardOperation; + iForwardOperation = NULL; + iForwardOperation = CMmsForwardOperation::NewL( iFs, iMmsSettings ); + iForwardOperation->StartL( + *iMsvSelection, + *iServerEntry, + iServiceEntryId, + iStatus ); + *iRequestStatus = KRequestPending; + SetActive(); + } + +// --------------------------------------------------------- +// CMmsServerMtm::FetchFromMmscL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::FetchFromMmscL() + { + // We must put notifications into the selection. + // The notifications will be have the same format + // as message entries, but they will contain only + // MMS headers. + + // The test version will use only the URI (Content-Location) + // from the notification. + // The Content-Location will contain the path and filename - + // as the first approximation. + + // For test purposes we create a fake selection by scanning + // the directory specified in the settings, and storing the + // filenames as messages under current service. + + // If we have rescheduled entries, we don't create a new notification + // list, as we have one already + if ( iMsvSelection->Count() < 1 ) + { + CreateNotificationsL(); + } + + // If anything was left, we fetch them + if ( iMsvSelection->Count() > 0 ) + { + // We call our new excellent state machine + delete iReceiveMessage; + iReceiveMessage = NULL; + iReceiveMessage = CMmsReceiveMessage::NewL( iFs, iMmsSettings ); + + iReceiveMessage->StartL( *iMsvSelection, *iServerEntry, + iServiceEntryId, iStatus ); + if ( iRequestStatus->Int() != KRequestPending ) + { + *iRequestStatus = KRequestPending; + } + SetActive(); + } + else + { + // We say we are done without error, if we pruned everything. + // The original notifications that caused the pruning should + // still be hanging around. + // There is a danger of fetching failing so often that + // we cannot even manage to send a response to MMSC, and + // it sends us a duplicate notification because it thinks + // the original one has got lost. + // We must carefully test the failure conditions and try to + // determine reasonable amount of retries that should be done + // to avoid such situation. + // The other possibility would be to always remove the old + // notification if a new one arrives with identical TID and + // Content location, but then we would be in danger of + // deleting an entry that is currently being used to fetch + // a message. + // The actual fetching code in CMmsReceiveMessage class + // tries to test that the notifications are accessible, + // and it tries not to trap, if the notifications have + // disappeared from its lists, + if ( iError != KErrNoMemory && + iError != KErrDiskFull && iError != KMmsErrorApplicationDiskFull ) + { + iError = KErrNone; + } + if ( iRequestStatus->Int() != KRequestPending ) + { + *iRequestStatus = KRequestPending; + } + if ( iError == KMmsErrorApplicationDiskFull ) + { + // DoComplete will complete caller + iError = KErrNone; + } + else + { + User::RequestComplete( iRequestStatus, iError ); + } + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::UpdateEntriesL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::UpdateEntriesL() + { + // if something goes fatally wrong, this error will be + // returned to the caller in hope the caller may be able + // to do something to fix the problem + TInt fatalError = KErrNone; + TMsvEntry entry; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("UpdateEntriesL" )); +#endif + + // Tell scheduler about entries that were successfully sent + // or received + + // Rescedule failed entries + // We have a member that tells which command to use + // (Send or receive) + + // The central repository file contains a list of hopeless cases. + // These are not rescheduled + + TMsvSchedulePackage* schedulePackage = new( ELeave ) TMsvSchedulePackage; + CleanupStack::PushL( schedulePackage ); + PopulateSchedulePackage( iParameter, ETrue, *schedulePackage ); + + // Failed forward entries + if( iForwardOperation && iForwardOperation->Failed().Count() > 0 ) + { + HandleFailedForwardsL( *schedulePackage ); + } + + // Successfully sent entries + if( iSendOperation && iSendOperation->Sent().Count() > 0 ) + { + HandleSuccessfulSendsL(); + } + + // Entries that failed to be sent + if ( iSendOperation && iSendOperation->Failed().Count() > 0 ) + { + HandleFailedSendsL( *schedulePackage ); + } + + // Message generation (branding messages) + if ( iReceiveMessage && iCurrentCommand == EMmsMessageGeneration ) + { + CleanupAfterMessageGenerationL(); + } + + // Mark entries that failed to be fetched + // If there is some error connected to this, it must be returned to caller + // as fetching is automatic. + TBool restartFetch = EFalse; + if ( iReceiveMessage && iReceiveMessage->Failed().Count() > 0 ) + { + restartFetch = HandleFailedReceivesL( *schedulePackage, fatalError ); + } + + // Successfully received entries + if ( iReceiveMessage && iReceiveMessage->Received().Count() > 0 ) + { + HandleSuccessfulReceivesL(); + } + + // Remove bad notifications from task scheduler + if ( iReceiveMessage && iReceiveMessage->BadNotifications().Count() > 0 ) + { + HandleBadNotificationsL(); + } + + if ( restartFetch ) + { + // We did find some failed notifications that were in inbox or MMBox folder + // These are not normally rescheduled + // But if the error was due to lack of disk space and we have been able to + // free the disk space by deleting some older messages belonging to a + // lazy application, we can retry the fetch immediately + + // We must clear the receiver class to start with a clean slate. + delete iReceiveMessage; + iReceiveMessage = NULL; + FetchFromMmscL(); + iError = KMmsErrorApplicationDiskFull; + } + + // update delivery status counts and reschedule possible extra delivcery reports + if ( iCommand == EMmsLogDeliveryReport ) + { + UpdateDeliveryReportsL( *schedulePackage ); + } + + // handle failed read report entries + if ( iReadReport && iReadReport->Failed().Count() > 0 ) + { + HandleFailedReadReports(); + } + + CleanupStack::PopAndDestroy( schedulePackage ); + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("UpdateEntriesL done, error: %d" ), iError ); +#endif + if ( fatalError != KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("UpdateEntriesL, fatal error: %d" ), fatalError ); +#endif + // we catch this + User::Leave( fatalError ); + // return not needed as User::Leave returns + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleFailedForwardsL( TMsvSchedulePackage& aPackage ) + { + TInt count = 0; + TMsvEntry entry; + TInt error = KErrNone; + + count = iForwardOperation->Failed().Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- %d failed (not sent) forward entries"), count ); + TInt rescheduled = count; // rescheduled needed only for logging +#endif + // Loop selection and make them reschedulable (readonly == false) + while ( count-- ) + { + if ( iServerEntry->SetEntry( iForwardOperation->Failed().At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + entry.SetReadOnly( EFalse ); + iServerEntry->ChangeEntry( entry ); // ignore error + } + } + + // + // Now reschedule. + // + iScheduleSend->ReScheduleL( iForwardOperation->Failed(), aPackage ); + + // Mark entries that failed to be rescheduled + count = iForwardOperation->Failed().Count(); + while ( count-- ) + { + if ( iServerEntry->SetEntry( iForwardOperation->Failed().At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + if ( entry.SendingState() != KMsvSendStateResend ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- forward entry failed to reschedule, setting state to failed") ); +#endif + entry.SetSendingState( KMsvSendStateFailed ); + error = iServerEntry->ChangeEntry( entry ); // ignore error +#ifndef _NO_MMSS_LOGGING_ + if( error != KErrNone ) + { + TMmsLogger::Log( _L("- ERROR: changing entry failed") ); + } +#endif + // Clear related notification from Inbox: + // Get the related notification id + CMsvStore* store = NULL; + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); // *** + iMmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + TMsvId relatedEntryId = iMmsHeaders->RelatedEntry(); + iMmsHeaders->Reset(); // headers not needed any more + + if( relatedEntryId != KMsvNullIndexEntryId ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- related notification-entry exists, clearing it") ); +#endif + // Set context (iServerEntry and entry) to notification and clear it + error = iServerEntry->SetEntry( relatedEntryId ); +#ifndef _NO_MMSS_LOGGING_ + if( error != KErrNone ) + { + TMmsLogger::Log( _L("- ERROR: Could not set entry") ); + } +#endif + entry = iServerEntry->Entry(); + entry.iMtmData2 &= ~KMmsNewOperationForbidden; // not forbidden + entry.iMtmData2 &= ~KMmsOperationOngoing; // not ongoing + entry.iMtmData2 |= KMmsOperationFinished; // finished + entry.iMtmData2 |= KMmsOperationResult; // NOK + entry.SetReadOnly( ETrue ); + error = iServerEntry->ChangeEntry( entry ); +#ifndef _NO_MMSS_LOGGING_ + if( error != KErrNone ) + { + TMmsLogger::Log( _L("- ERROR: Could not change related entry") ); + } + TMmsLogger::Log( _L("- Clear the related-entry link itself") ); +#endif + + // Clear related-id link from forward entry + if ( iMsvSelection->Count() > count ) + { + error = iServerEntry->SetEntry( iMsvSelection->At( count ) ); + } + else + { + // We should never get this, + // we check count only to keep CodeScanner happy + error = KErrNotFound; + } + if ( error == KErrNone ) + { + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); // *** + iMmsHeaders->RestoreL( *store ); + iMmsHeaders->SetRelatedEntry( KMsvNullIndexEntryId ); + iMmsHeaders->StoreL( *store ); + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + iMmsHeaders->Reset(); // headers not needed any more +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Related-entry and the link cleared") ); +#endif + } + } + + // let go of the entry + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + } + else + { + // + // Delete successfully rescheduled entries + // +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- notification is in resend, as it should") ); +#endif + iForwardOperation->Failed().Delete( count ); + } + } +#ifndef _NO_MMSS_LOGGING_ + else + { + TMmsLogger::Log( _L("- ERROR: could not access entry %d"), count ); + } +#endif + } + +#ifndef _NO_MMSS_LOGGING_ + count = iForwardOperation->Failed().Count(); + rescheduled = rescheduled - count; + if ( rescheduled > 0 ) + { + TMmsLogger::Log( _L("- %d rescheduled forward entries"), rescheduled ); + } + if ( count > 0 ) + { + TMmsLogger::Log( _L("- %d not rescheduled forward entries"), count ); + } +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleSuccessfulSendsL() + { + TInt count = 0; + TMsvEntry entry; + + // Delete schedule should not be needed in the case of successfully + // sent entries. + count = iSendOperation->Sent().Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- %d Sent entries"), count ); +#endif + iScheduleSend->DeleteScheduleL( iSendOperation->Sent() ); + while ( count-- ) + { + if ( iServerEntry->SetEntry( iSendOperation->Sent().At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + entry.SetSendingState( KMsvSendStateSent ); + if ( entry.Parent() == KMsvSentEntryIdValue ) + { + // if we have not managed to move this entry away from outbox, + // it must not be set to read only state + entry.SetReadOnly( ETrue ); + } + // We don't want to leave here, we want to continue. + // The next message may succeed. + // We don't consider this fatal: If the message + // has been successfully sent, it should already + // be moved to sent folder. + // If the user tries to send messages during backup/restore, + // it is his own fault if the messages are sent more than once. + iServerEntry->ChangeEntry( entry ); + } + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleFailedSendsL( TMsvSchedulePackage& aPackage ) + { + TInt count = 0; + TMsvEntry entry; + + count = iSendOperation->Failed().Count(); +#ifndef _NO_MMSS_LOGGING_ + TInt rescheduled = count; + TMmsLogger::Log( _L("- %d failed (not sent) entries"), count ); +#endif + while ( count-- ) + { + if ( iServerEntry->SetEntry( iSendOperation->Failed().At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + // we set the entry into failed state after we have + // checked if it still can be rescheduled. + entry.SetReadOnly( EFalse ); + iServerEntry->ChangeEntry( entry ); // ignore error + if ( entry.SendingState() == KMsvSendStateSuspended && + entry.iError != KMmsErrorOfflineMode ) + { + // suspended by user, not offline mode + // We remove this just in case. + // If everything has gone well, the error is "KErrNotFound" + // and the entry would not be rescheduled, but this is an + // extra precaution. + iSendOperation->Failed().Delete( count ); + } + } + } + + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + count = iSendOperation->Failed().Count(); + + if ( count > 0 ) + { + // Check needed to avoid a stupid ASSERT_DEBUG + iScheduleSend->ReScheduleL( iSendOperation->Failed(), aPackage ); + } + + // Mark entries that failed to be rescheduled + count = iSendOperation->Failed().Count(); + + while ( count-- ) + { + if ( iServerEntry->SetEntry( iSendOperation->Failed().At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + if ( entry.SendingState() != KMsvSendStateResend ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- setting state to failed") ); +#endif + entry.SetSendingState( KMsvSendStateFailed ); + iServerEntry->ChangeEntry( entry ); // ignore error + // let go of the entry + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + } + else + { + iSendOperation->Failed().Delete( count ); + } + } +#ifndef _NO_MMSS_LOGGING_ + else + { + TMmsLogger::Log( _L("- could not access entry %d"), count ); + } +#endif + } + + count = iSendOperation->Failed().Count(); +#ifndef _NO_MMSS_LOGGING_ + rescheduled = rescheduled - count; + if ( rescheduled > 0 ) + { + TMmsLogger::Log( _L("- %d rescheduled for sending"), rescheduled ); + } + if ( count > 0 ) + { + TMmsLogger::Log( _L("- %d hopeless, not rescheduled for sending"), count ); + } +#endif + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CleanupAfterMessageGenerationL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Finished message generation") ); +#endif + // we try only once. + // The whole selection must be in the same place. + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + + // everything must go. + if ( iReceiveMessage->BadNotifications().Count() > 0 ) + { + selection->AppendL( iReceiveMessage->BadNotifications().Back( 0 ), + iReceiveMessage->BadNotifications().Count() ); + iReceiveMessage->BadNotifications().Reset(); + } + + if ( iReceiveMessage->Failed().Count() > 0 ) + { + selection->AppendL( iReceiveMessage->Failed().Back( 0 ), + iReceiveMessage->Failed().Count() ); + iReceiveMessage->Failed().Reset(); + } + + if ( iReceiveMessage->Received().Count() > 0 ) + { + selection->AppendL( iReceiveMessage->Received().Back( 0 ), + iReceiveMessage->Received().Count() ); + iReceiveMessage->Received().Reset(); + } + + // If the next loop fails, we don't care: + // The files will be deleted ayway, and the notifications will be orphaned + // If there is something weird going on during the boot, + // the phone won't probably work after this anyway. + if ( selection->Count() > 0 ) + { + if ( iServerEntry->SetEntry( selection->At( 0 ) ) == KErrNone ) + { + if ( iServerEntry->SetEntry( iServerEntry->Entry().Parent() ) == KErrNone ) + { + iServerEntry->DeleteEntries( *selection ); + } + } + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + } + CleanupStack::PopAndDestroy( selection ); + + // Then we delete the files in the directory + // (if anything left) + CFileMan* fileMan = CFileMan::NewL( iFs ); + CleanupStack::PushL( fileMan ); + // Best effort - ignore error + fileMan->RmDir( KMmsMessageVariationDirectory ); + CleanupStack::PopAndDestroy( fileMan ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TBool CMmsServerMtm::HandleFailedReceivesL( TMsvSchedulePackage& aPackage, TInt& aFatalError ) + { + TInt count = 0; + TMsvEntry entry; + TInt error = KErrNone; + TBool restartFetch = EFalse; + + iMsvSelection->Reset(); + count = iReceiveMessage->Failed().Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- %d failed (not received) entries"), count ); +#endif + // Check if the current receive mode is manual + TBool manual = EFalse; + if( iReceiveMessage->InForeignNetwork() ) + { + if( iMmsSettings->ReceivingModeForeign() == EMmsReceivingManual ) + { + manual = ETrue; + } + } + else + { + if( iMmsSettings->ReceivingModeHome() == EMmsReceivingManual ) + { + manual = ETrue; + } + } + + TBool inInbox = EFalse; + TBool inMmsFolder = EFalse; + TBool inMmboxFolder = EFalse; + + while ( count-- ) + { + error = iServerEntry->SetEntry( iReceiveMessage->Failed().At( count ) ); + if ( error == KErrNone ) + { + entry = iServerEntry->Entry(); + + if ( entry.iMtmData2 & KMmsDoNotMoveToInbox ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- do not move entry to inbox ")); +#endif + // We do not reschedule this. It will potentially cause problems at mode change + // if it has not been handled by then. + iReceiveMessage->Failed().Delete( count ); + // it is put into bad list and deleted later + iReceiveMessage->BadNotifications().AppendL( entry.Id() ); + } + else + { + if ( entry.Parent() == FindMMSFolderL() ) + { + inMmsFolder = ETrue; + } + else if ( entry.Parent() == KMsvGlobalInBoxIndexEntryIdValue ) + { + inInbox = ETrue; + // inbox entries will not be rescheduled + if ( entry.iError == KMmsErrorApplicationDiskFull ) + { + // This is best effort only - error ignored + iMsvSelection->AppendL( iReceiveMessage->Failed().At( count ) ); + restartFetch = ETrue; + } + iReceiveMessage->Failed().Delete( count ); + } + else + { + inMmboxFolder = ETrue; + if ( entry.iError == KMmsErrorApplicationDiskFull ) + { + // This is best effort only - error ignored + iMsvSelection->AppendL( iReceiveMessage->Failed().At( count ) ); + restartFetch = ETrue; + } + // mmbox entries will not be rescheduled + iReceiveMessage->Failed().Delete( count ); + manual = EFalse; // manual mode must not affect the mmbox notification + } + // We don't have a separate receiving state. + // We just use sending state instead. + // We are the only one that uses scheduled receiving. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- marking sending state as failed ")); +#endif + entry.SetSendingState( KMsvSendStateFailed ); + + if ( manual && inMmsFolder ) + { + // Change the iMtm from KUidMsgTypeMultimedia to + // KUidMsgMMSNotification + entry.iMtm.iUid = KUidMsgMMSNotification.iUid; // this is a notification + + entry.iMtmData2 &= ~KMmsOperationFinished; // clear flag just in case + entry.iMtmData2 &= ~KMmsOperationResult; // clear flag just in case + entry.iMtmData2 &= ~KMmsOperationOngoing; // Fetch operation is not active anymore + entry.iMtmData2 &= ~KMmsNewOperationForbidden; // New operation can be started + entry.iMtmData1 |= KMmsMessageMobileTerminated; + entry.SetReadOnly( ETrue ); + entry.iError = KErrNone; + entry.SetSendingState( KMsvSendStateUnknown ); + } + else if ( inInbox ) + { + // Mark original notification + CMmsBaseOperation::MarkNotificationOperationFailed( entry ); + if ( entry.iError != KMmsErrorApplicationDiskFull ) + { + entry.SetReadOnly( ETrue ); + } + } + else if ( inMmboxFolder ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- in mmbox folder")); +#endif + TRAP( error, CMmsBaseOperation::MarkDuplicateL( + CMmsBaseOperation::EMmsNotificationOperationFailed, + *iServerEntry ) ); + error = KErrNone; // ignore error from trap + entry = iServerEntry->Entry(); + CMmsBaseOperation::MarkNotificationOperationFailed( entry ); + if ( entry.iError != KMmsErrorApplicationDiskFull ) + { + entry.SetReadOnly( ETrue ); + } +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- marking original notif failed")); +#endif + } + else + { + // keep LINT happy + } + error = iServerEntry->ChangeEntry( entry ); + + // move the entry from mms folder to the inbox in manual mode. + if ( manual && inMmsFolder ) + { + iError = iServerEntry->SetEntry( entry.Parent() ); + if ( iError == KErrNone ) + { + error = iServerEntry->MoveEntryWithinService( + entry.Id(), KMsvGlobalInBoxIndexEntryIdValue ); + } + else + { + error = iError; + } + if ( error == KErrNone ) + { + inInbox = ETrue; + // This should not be rescheduled as it has moved to inbox + iReceiveMessage->Failed().Delete( count ); + } + } + } + if ( aFatalError == KErrNone ) + { + // if we canot access the entry, tell the caller + aFatalError = error; + } + // let go of the entry + iServerEntry->SetEntry( KMsvNullIndexEntryId ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("UpdateEntriesL: entry #%d, sending state = failed"), count ); + TMmsLogger::Log( _L("UpdateEntriesL: entry #%d, error = %d"), count, entry.iError ); + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("UpdateEntriesL: ChangeEntry failed, error %d"), error ); + } +#endif + } +#ifndef _NO_MMSS_LOGGING_ + else + { + // Not found is not fatal. What is gone is gone. + if ( error != KErrNotFound ) + { + if ( aFatalError == KErrNone ) + { + aFatalError = error; + } + } + TMmsLogger::Log( _L("UpdateEntriesL: could not access entry #%d"), count ); + } +#endif + } + + // only if the notification is in mmsfolder, reschedule the notification + // if the notification is in Inbox or in mmbox folder, do not rescedule. + // We have removed each notification from failed list if it was originally + // in inbox or in MMBox folder or if it was successfully moved to inbox + if( iReceiveMessage->Failed().Count() > 0 ) + { + iScheduleSend->ReScheduleL( iReceiveMessage->Failed(), aPackage ); + } + // The messages that could not be rescheduled anymore, are either deleted, or moved + // to Inbox to be handled manually + count = iReceiveMessage->Failed().Count(); + // we do not delete hopeless entries: + // There is a separate error watcher that decides + // what to do with them. + // For example: If there is no access point, receiving + // fails. The error watcher gives notice to user, and + // when the user has entered the access point, the + // fetch is restarted by the error watcher. +#ifndef _NO_MMSS_LOGGING_ + TInt hopeless = count; +#endif + while ( count-- ) + { + TMsvId failedEntry = iReceiveMessage->Failed().At( count ); + if ( iServerEntry->SetEntry( failedEntry ) == KErrNone ) + { + entry = iServerEntry->Entry(); + // update retry count + TRAP( error, + { + CMsvStore* store = NULL; + store = iServerEntry->ReadStoreL(); + CleanupStack::PushL( store ); + CMmsScheduledEntry* mmsScheduledEntry = + CMmsScheduledEntry::NewL( iServerEntry->Entry() ); + CleanupStack::PushL( mmsScheduledEntry ); + mmsScheduledEntry->RestoreL( *store ); + entry.iMtmData3 &= ~KMmsRetryCountMask; + entry.iMtmData3 |= mmsScheduledEntry->MmsRecipient().Retries(); + iServerEntry->ChangeEntry( entry ); + CleanupStack::PopAndDestroy( mmsScheduledEntry ); + CleanupStack::PopAndDestroy( store ); + } + ); + + if ( entry.SendingState() == KMsvSendStateFailed ) + { + // remove from list just to see what was left over + iReceiveMessage->Failed().Delete( count ); + } + } + } +#ifndef _NO_MMSS_LOGGING_ + count = iReceiveMessage->Failed().Count(); + hopeless = hopeless - count; + if ( count > 0 ) + { + TMmsLogger::Log( _L("- %d rescheduled for receiving"), count ); + } + if ( hopeless > 0 ) + { + TMmsLogger::Log( _L("- %d hopeless, not rescheduled for receiving"), hopeless ); + } +#endif + + return restartFetch; + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleSuccessfulReceivesL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- %d received entries"), iReceiveMessage->Received().Count() ); +#endif + TMsvId parent = KMsvNullIndexEntryId; + // The whole selection must be in the same place. + if ( iServerEntry->SetEntry( iReceiveMessage->Received().At( 0 ) ) == KErrNone ) + { + parent = iServerEntry->Entry().Parent(); + } + + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + selection->AppendL( iReceiveMessage->Received().Back( 0 ), + iReceiveMessage->Received().Count() ); + + // if entry is in inbox or in MMBoxfolder, + // check possible duplicate and mark it "fetched from server" + if ( parent == KMsvGlobalInBoxIndexEntryIdValue || parent == iMmsSettings->MMBoxFolder() ) + { + for ( TInt i = selection->Count(); i > 0; i-- ) + { + if ( iServerEntry->SetEntry( selection->At( i - 1 )) == KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- check possible duplicate" )); +#endif + CMsvStore* store = iServerEntry->ReadStoreL(); + CleanupStack::PushL( store ); + iMmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + store = NULL; + TMsvId duplicate = KMsvNullIndexEntryId; + + if ( parent == KMsvGlobalInBoxIndexEntryIdValue ) + { + TMsvId mmboxFolder = iMmsSettings->MMBoxFolder(); + if ( mmboxFolder != KMsvNullIndexEntryId ) + { + FindDuplicateNotificationL( mmboxFolder, *iMmsHeaders, duplicate ); + } + } + else // parent is mmbox folder + { + duplicate = iMmsHeaders->RelatedEntry(); + } + if ( duplicate != KMsvNullIndexEntryId ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- duplicate found")); +#endif + if ( iServerEntry->SetEntry( duplicate ) == KErrNone ) + { + // Mark duplicate + TMsvEntry dupEntry = iServerEntry->Entry(); + CMmsBaseOperation::MarkNotificationDeletedFromMmbox( dupEntry ); + iServerEntry->ChangeEntry( dupEntry ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- duplicate marked as fetched from mmbox")); +#endif + } + } + } + } + } + if ( parent != KMsvNullIndexEntryId && iServerEntry->SetEntry( parent ) == KErrNone ) + { + iServerEntry->DeleteEntries( *selection ); + } + CleanupStack::PopAndDestroy( selection ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleBadNotificationsL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- %d bad notification entries"), iReceiveMessage->BadNotifications().Count() ); +#endif + // The whole selection must be in the same place. + if ( iServerEntry->SetEntry( iReceiveMessage->BadNotifications().At( 0 ) ) == KErrNone ) + { + if ( iServerEntry->SetEntry( iServerEntry->Entry().Parent() ) == KErrNone ) + { + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + selection->AppendL( iReceiveMessage->BadNotifications().Back( 0 ), + iReceiveMessage->BadNotifications().Count() ); + iServerEntry->DeleteEntries( *selection ); + CleanupStack::PopAndDestroy( selection ); + } + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::UpdateDeliveryReportsL( TMsvSchedulePackage& aPackage ) + { + TInt error = KErrNone; + TMsvEntry entry; + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- logged delivery report" )); +#endif + if ( iError == KErrNone ) + { + // first entry in selection was handled + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + selection->AppendL( iMsvSelection->At( 0 ), 1 ); + if ( selection->Count() > 0 ) + { + error = iServerEntry->SetEntry( selection->At( 0 ) ); + } + else + { + // this will never happen - we are just keeping codescanner happy + error = KErrNotFound; + } + if ( error == KErrNone ) + { + if ( iServerEntry->SetEntry( iServerEntry->Entry().Parent() ) == KErrNone ) + { + iServerEntry->DeleteEntries( *selection ); + } + } + CleanupStack::PopAndDestroy( selection ); + iMsvSelection->Delete( 0 ); + } + + //Now let's set delivery report bits in MtmData + TMsvId link = 0; + if ( iMmsLog ) + { + link = iMmsLog->GetLink(); + } + if (link != 0) + { + error = iServerEntry->SetEntry(link); + if ( error == KErrNone ) + { + entry = iServerEntry->Entry(); + } + } + + // Even if the link exists, the original message may have disappeared already + // (if only 20 messages are saved in outbox, they may start disappearing quite fast) + if ( link != 0 && error == KErrNone && ( entry.iMtmData2 & KMmsDeliveryStatusMask ) != + KMmsDeliveryStatusNotRequested ) + { + TUint temp(0); + TUint total(0); + if (iDeliveryStatus) //successfully delivered + { + total = ( entry.iMtmData3 & KMmsSentItemTotalRecipientsMask ) >> + KMmsSentItemTotalRecipientsShift; + + temp = ( entry.iMtmData3 & KMmsSentItemSuccessfullyDeliveredMask ) + >> KMmsSentItemSuccessfullyDeliveredShift; + temp++; + entry.iMtmData3 &= ~KMmsSentItemSuccessfullyDeliveredMask; + entry.iMtmData3 |= temp << KMmsSentItemSuccessfullyDeliveredShift; + + // must make sure that if even one send has been failed the delivery status + // is always failed + if ( temp == total && ( entry.iMtmData2 & KMmsDeliveryStatusMask ) + != KMmsDeliveryStatysFailed ) + { + entry.iMtmData2 &= ~KMmsDeliveryStatusMask; + entry.iMtmData2 |= KMmsDeliveryStatysDelivered; + } + else if (temp < total && ( entry.iMtmData2 & KMmsDeliveryStatusMask ) + != KMmsDeliveryStatysFailed ) + { + entry.iMtmData2 &= ~KMmsDeliveryStatusMask; + entry.iMtmData2 |= KMmsDeliveryStatusPartial; + } + else + { + // keep LINT happy + } + + } + else + { + temp = (entry.iMtmData3 & KMmsSentItemFailedDeliveryMask) >> + KMmsSentItemFailedDeliveryShift; + temp++; + entry.iMtmData3 &= ~KMmsSentItemFailedDeliveryMask; + entry.iMtmData3 |= temp << KMmsSentItemFailedDeliveryShift; + entry.iMtmData2 &= ~KMmsDeliveryStatusMask; + entry.iMtmData2 |= KMmsDeliveryStatysFailed; + + + } + // If we successfully accessed the entry, iServerEntry must still be pointing to the link. + iServerEntry->ChangeEntry(entry); + } + + iError = KErrNone; // we don't care about the error. + // We should normally never be here, delivery reports come + // one at a time... + if ( iMsvSelection->Count() > 0 ) + { + iScheduleSend->ReScheduleL( *iMsvSelection, aPackage ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleFailedReadReports() + { + TInt error = KErrNone; + if ( iReadReport->Failed().Count() > 0 ) + { + error = iServerEntry->SetEntry( iReadReport->Failed().At( 0 ) ); + } + else + { + error = KErrNotFound; + } + if ( error == KErrNone ) + { + error = iServerEntry->SetEntry( iServerEntry->Entry().Parent() ); + } + if ( error == KErrNone ) + { + TInt i; + for ( i = iReadReport->Failed().Count() - 1; i >= 0; i-- ) + { + iServerEntry->DeleteEntry( iReadReport->Failed().At( i ) ); + } + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::MakeDatesIdenticalL( + CMsvEntrySelection& aSelection, + TTimeIntervalSeconds aInterval, + TBool aClearError /* = EFalse */ ) + { + + TInt count = aSelection.Count(); + TTime curTime; + curTime.UniversalTime(); + if ( aInterval > TTimeIntervalSeconds( KMmsDelayInSeconds ) ) + { + curTime += aInterval; + } + else + { + curTime += TTimeIntervalSeconds( KMmsDelayInSeconds ); + } + + while ( count-- ) + { + if ( iServerEntry->SetEntry( aSelection.At( count ) ) == KErrNone ) + { + TMsvEntry entry = iServerEntry->Entry(); + if ( entry.Id() != aSelection.At( count ) || entry.Id() == KMsvNullIndexEntryId ) + { + // The entry is garbage + aSelection.Delete( count, 1 ); + } + else + { + entry.iDate = curTime; + if ( aClearError ) + { + entry.iError = KErrNone; + } + iServerEntry->ChangeEntry( entry ); + } + } + } + + } + +// --------------------------------------------------------- +// CMmsServerMtm::DecodePushedMessageL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::DecodePushedMessageL() + { + + // Decode the pushed content to see, if it was + // a notification or a delivery report + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer decoding pushed message") ); +#endif + iNotification = KMsvNullIndexEntryId; + iMmsHeaders->Reset(); + // the possible error from this is not important as the settings are + // now stored in central repository + iServerEntry->SetEntry( iServiceEntryId ); + + // We decode first, and create the entry afterwards. + if ( !iDecoder ) + { + iDecoder = CMmsDecode::NewL( iFs ); + } + + if ( !iEntryWrapper ) + { + iEntryWrapper = CMmsServerEntry::NewL( + iFs, + *iServerEntry, + iServiceEntryId ); + } + + TRAP ( iError, iDecoder->DecodeHeadersL( *iEntryWrapper, *iMmsHeaders, *iEncodeBuffer ) ); + + // If we could not allocate memory to decode, we must try again later. + if ( iError == KErrNoMemory ) + { + return; + } + + if ( iError == KErrCorrupt || + iError == KErrTooBig ) + { + // if the notification has illegal content, + // it is just discarded, we must not tell the + // mmswatcher to resend it. + iError = KErrNone; + return; + } + + // Even if we encounter an error when decoding, we must save + // the message for handling, because we must be able to send + // "unrecognized" status back, if we get an unknown PDU + // If we get a message with different major version number, it + // may be so incompatible that we get an error while decoding. + + // If we get a notification, we make some sanity checks so that + // we can reject malicious notifications right away. + + // mark if this is a notification of a delivery report + // send requests or retrieve confirmations are not pushed + // They are equivalent to unrecognized type + + TUint32 messageType = 0; + switch ( iMmsHeaders->MessageType() ) + { + case KMmsMessageTypeMNotificationInd: + messageType = KMmsMessageMNotificationInd; + break; + case KMmsMessageTypeDeliveryInd: + messageType = KMmsMessageDeliveryInd; + break; + case KMmsMessageTypeReadOrigInd: + messageType = KMmsMessageReadOrigInd; + break; + default: + // unrecognized type. + // We must send response to MMSC + // This includes types that should never be pushed to us. + messageType = KMmsMessageUnrecognized; + break; + } + + // If this is an extended notification, it may already contain the whole message + TBool completeMessage = ( iMmsHeaders->MessageComplete() == KMmsExtendedMessageComplete ); + + TBool passedChecks = ETrue; // we are optimistic + + // If we have a notification, we must do a couple of special tricks. + + if ( messageType == KMmsMessageMNotificationInd ) + { + // Some handling has been moved here from CMmsDecode, + // because it is better to have all notification logic in on place. + + // Expiry interval must be changed to absolute time, + // otherwise it makes no sense. + if ( iMmsHeaders->ExpiryDate() == 0 ) + { + TTime time; + // handle expiry in universal time in case user changes location + time.UniversalTime(); + time += TTimeIntervalSeconds( iMmsHeaders->ExpiryInterval() ); + // we can't use "seconds from" as it only returns a + // 32 bit signed integer. If fails in 2038. + // "microseconds from" returns a 64 bit signed integer + // expiry date in iMmsHeaders in in seconds from 1.1.1970. + // interval must be changed back to seconds + // This way the result may still be a 64bit integer -> + // no overflow in 2038 + iMmsHeaders->SetExpiryDate( + ( time.MicroSecondsFrom( TTime( KMmsYear1970String ) ).Int64() ) / KMmsMillion ); + } + + // Then we must check if the message type is acceptable. + // Rejection based on message type overrides the status + // set above. For example, if fetching is deferred, but + // we get an advertisement, and we don't accept advertisements + // we change the message status from deferred to rejected. + + switch ( iMmsHeaders->MessageClass() ) + { + case EMmsClassPersonal: + if ( iMmsSettings->AcceptPersonalMessages() == EFalse ) + { + iMmsHeaders->SetStatus( KMmsMessageStatusRejected ); + } + break; + case EMmsClassAdvertisement: + if ( iMmsSettings->AcceptAdvertisementMessages() == EFalse ) + { + iMmsHeaders->SetStatus( KMmsMessageStatusRejected ); + } + break; + case EMmsClassInformational: + if ( iMmsSettings->AcceptInformationalMessages() == EFalse ) + { + iMmsHeaders->SetStatus( KMmsMessageStatusRejected ); + } + break; + case EMmsClassAuto: + // We accept automatic messages. The only automatic message we + // know about is a text mode read report. + // As we now handle read reports, we must accept automatic messages + // in case some server has converted a read report into a text message + // (possible if the server does not recognize the phone and know its + // capabilities). + break; + default: + // if we cannot determine the message type, + // we reject it. + // Message class header is mandatory in notification + //iMmsHeaders->SetStatus( KMmsMessageStatusRejected ); + break; + } + // check if we accept anonymous messages + if ( iMmsSettings->AcceptAnonymousMessages() == EFalse && + iMmsHeaders->Sender().Length() == 0 ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Anonymous message rejected") ); +#endif + iMmsHeaders->SetStatus( KMmsMessageStatusRejected ); + } + + // If the encapsulation version is 1.3, the notification may already + // contain the application id header. + // If the application has not been registered, we reject the message + // (at least until more complex support for the applicatiom message + // handling has been implemented) + if ( iMmsHeaders->ApplicId().Length() > 0 ) + { + if ( !CMmsBaseOperation::RegisteredL( iMmsHeaders->ApplicId() ) ) + { + iMmsHeaders->SetStatus( KMmsMessageStatusRejected ); + } + } + + // if the extended notification contains the whole message + // we must mark it as "retrieved" + // If there is an extended message addressed to an application, + // it is something that cannot be reasonably handled. + // I hope that is an illegal case anyway. + + if ( completeMessage ) + { + iMmsHeaders->SetStatus( 0 ); // not applicable + } + + // legality checks: + // TID is mandatory + if ( iMmsHeaders->Tid().Length() == 0 ) + { + passedChecks = EFalse; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- invalid TID: length = %d"), iMmsHeaders->Tid().Length() ); +#endif + } + // message class is mandatory + if ( iMmsHeaders->MessageClass() == 0 ) + { + passedChecks = EFalse; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- message class not defined") ); +#endif + } + + // Messages with zero length must be accepted if message size may be + // just the size of payload and subject (both may have zero length) + // If headers are not included in calculation, zero length is acceptable here. + + // expiry is mandatory - but it cannot be 0 because we just set it. + // If expiry is not given, the notification expires NOW + + // content location is mandatory + if ( iMmsHeaders->ContentLocation().Length() == 0 ) + { + passedChecks = EFalse; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- no content location") ); +#endif + } + } + + // if we have notification that does not fill our + // criteria, we just throw it away. The purpose of this + // is to discard possible malignant notifications that + // would cause us to contact some unknown server and + // do strange damage - or at least cause unncessary + // network traffic. + + // If the message is complete, we keep it anyway + + if ( passedChecks == EFalse && !completeMessage ) + { + iError = KErrNone; + return; + } + + TMsvEntry tEntry; + tEntry.iType = KUidMsvMessageEntry; + // This may be different for notifications in manual modes + // This is ok, as all notifications are stored first to the internal MMS folder + tEntry.iMtm = KUidMsgTypeMultimedia; + + // use the iRelatedId to bypass queue. + tEntry.iServiceId = KMsvLocalServiceIndexEntryId; + tEntry.iRelatedId = iServiceEntryId; + tEntry.SetUnread( ETrue ); + tEntry.SetNew( ETrue ); + tEntry.SetVisible( EFalse ); + tEntry.SetComplete( EFalse ); + tEntry.SetInPreparation( ETrue ); + tEntry.SetReadOnly( EFalse ); + tEntry.iSize = iMmsHeaders->Size(); + + // + // Setting StoredInMMBox flag correctly + // + HandleMMBoxFlagL( tEntry ); + + // Notifications are always originally children of MMS Folder + // We have decoded our headers already. + // We know if this is a notification or a delivery report, + // and we can decide what to do. + + TMsvId parent = KMsvNullIndexEntryId; + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + TMsvId mmsFolder = FindMMSFolderL(); + if ( mmsFolder == KMsvNullIndexEntryId ) + { + // If the id for our notification folder is 0, we are really in a mess + // and cannot continue + iError = KErrNotFound; + return; + } + parent = mmsFolder; + + if ( messageType != KMmsMessageDeliveryInd && + messageType != KMmsMessageReadOrigInd && + messageType != KMmsMessageReadRecInd ) + { + // Check duplicates from parent folder, from Inbox and from mmbox folder + // no mater which receiving mode is on. + TMsvId mmboxFolder = iMmsSettings->MMBoxFolder(); + if ( PruneDuplicateNotificationsL( parent, *iMmsHeaders ) || + PruneDuplicateNotificationsL( KMsvGlobalInBoxIndexEntryIdValue, *iMmsHeaders ) || + PruneDuplicateNotificationsL( mmboxFolder, *iMmsHeaders )) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- duplicate - not stored") ); +#endif + iError = KErrNone; + return; + } + } + + // If this is an extended notification that contains the whole message + // the entry goes to inbox as a message, not a notification. + // The fetching must not be scheduled in this case as we already have the + // message. + // However, if we are in home network, we must send acknowledge back to MMSC + // Besides storing the notification as message entry, we must create another + // entry to serve as base for sending the acknowledgement. Therefore we have + // marked our status as "KMmsMessageStatusRetrieved" + + // this is the size of the attachment if we have an extended notification + TInt attaSize = 0; + + if ( completeMessage ) + { + parent = KMsvGlobalInBoxIndexEntryIdValue; + tEntry.iMtm = KUidMsgTypeMultimedia; + // indicate complete message + iMmsHeaders->SetMessageType( KMmsMessageTypeMRetrieveConf ); + } + + // If we can't access the parent where the notification is to be stored, + // we can do nothing + iError = iServerEntry->SetEntry( parent ); + if ( iError != KErrNone ) + { + return; // cannot continue + } + + // Query about disk space. KMmsIndexEntryExtra is extra for TMsvEntry + // Make one query, we assume no one takes away the disk space this fast. + if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( + &iFs, iMmsHeaders->Size() + KMmsIndexEntryExtra + iMmsHeaders->Size(), iMessageDrive ) ) + { + // we use standard error code here + iError = KErrDiskFull; + return; // cannot continue + } + + iError = iServerEntry->CreateEntry( tEntry ); + + if ( iError == KErrNone ) + { + iError = iServerEntry->SetEntry( tEntry.Id() ); + iNotification = tEntry.Id(); + } + + CMsvStore* store = NULL; + HBufC* buffer = NULL; // we need this to generate a description for the entry + TBool attachmentAdded = EFalse; // to track if the extended notification text has been added to the message store or not. + + if ( iError == KErrNone ) + { + // if this is a whole message, create an attachment from the text + if ( completeMessage && iNotification != KMsvNullIndexEntryId ) + { + tEntry = iServerEntry->Entry(); // save the settings we have so far + if ( iMmsHeaders->Subject().Length() > 0 ) + { + tEntry.iDescription.Set( iMmsHeaders->Subject() ); + } + else + { + // Save text as description if we have no subject + TPtrC temp = iMmsHeaders->ExtendedNotification().Left( KMmsMaxDescription ); + buffer = HBufC::NewL( temp.Length() ); + // no need to put buffer onto cleanup stack - we don't leave before we are done + buffer->Des().Copy( temp ); + TPtr pDescription = buffer->Des(); + TMmsGenUtils::ReplaceCRLFAndTrim( pDescription ); + tEntry.iDescription.Set( pDescription ); + } + iServerEntry->ChangeEntry( tEntry ); + // If we have not allocated the buffer, it is NULL, and it is safe to delete. + delete buffer; + buffer = NULL; + + if ( iMmsHeaders->ExtendedNotification().Length() > 0 ) + { + TMsvAttachmentId attachmentId = 0; + CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL(); + CleanupStack::PushL( mimeHeaders ); + TPtrC8 temp; + temp.Set( KMmsTextPlain ); + mimeHeaders->SetContentTypeL( temp.Left( temp.Find( KMmsSlash8 ) ) ); + mimeHeaders->SetContentSubTypeL( temp.Mid( temp.Find( KMmsSlash8 ) + 1 ) ); + mimeHeaders->SetMimeCharset( KMmsUtf8 ); // text saved as utf-8 + _LIT( KRelated, "att1.txt"); + mimeHeaders->SetSuggestedFilenameL( KRelated ); + // attaData must point to the text + // buffer long enough for conversion + const TInt KMmsConversionMultiplier = 5; + HBufC8* dataContent = HBufC8::NewL( + iMmsHeaders->ExtendedNotification().Length() * KMmsConversionMultiplier ); + CleanupStack::PushL( dataContent ); + TPtr8 attaData = dataContent->Des(); + CnvUtfConverter::ConvertFromUnicodeToUtf8( + attaData, iMmsHeaders->ExtendedNotification() ); + // set parent + iError = iEntryWrapper->SetCurrentEntry( iNotification ); + + if ( iError == KErrNone ) + { + store = iEntryWrapper->EditStoreL(); + CleanupStack::PushL( store ); + TInt32 drmFlags = 0; //ignored + iError = iEntryWrapper->CreateFileAttachmentL( + *store, + KRelated, + attaData, + *mimeHeaders, + attachmentId, + attaSize, + drmFlags); + //If attachment is added to Message store successfully then attachmentAdded is set + if ( iError == KErrNone ) + { + attachmentAdded = ETrue; + } + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + store = NULL; + } + iMmsHeaders->SetExtendedNotificationL( TPtrC() ); + + CleanupStack::PopAndDestroy( dataContent ); + CleanupStack::PopAndDestroy( mimeHeaders ); + } + } + if ( iServerEntry->SetEntry( iNotification ) == KErrNone && + iNotification != KMsvNullIndexEntryId ) + { + store = iServerEntry->EditStoreL(); + CleanupStack::PushL(store); + iMmsHeaders->StoreL(*store); + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + store = NULL; + } + } + + + if ( ( iError == KErrNone || iMmsHeaders->MessageType() != 0 ) + && iNotification != KMsvNullIndexEntryId ) + { + iError = iServerEntry->SetEntry( iNotification ); + if ( iError != KErrNone ) + { + // If we have an error here, there is something + // seriously wrong with the system + if ( iServerEntry->SetEntry( parent ) == KErrNone ) + { + iServerEntry->DeleteEntry( iNotification ); + } + iNotification = KMsvNullIndexEntryId; + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + return; // cannot continue + } + } + else + { + if ( iServerEntry->SetEntry( parent ) == KErrNone && + iNotification != KMsvNullIndexEntryId ) + { + // We managed to create an entry, but not to decode + // the buffer contents into the entry + iServerEntry->DeleteEntry( iNotification ); + } + iNotification = KMsvNullIndexEntryId; + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + return; + } + + // finish the details, and the notification entry is ready to be used + tEntry = iServerEntry->Entry(); + + if ( messageType == KMmsMessageMNotificationInd ) + { + // If the sender is a phone number, add alias. + // We don't add alias for email addresses here, as the contact + // database search for email addresses is very slow + // if there are lots of contacts. + buffer = HBufC::NewL( KMmsMaxDescription ); + CleanupStack::PushL( buffer ); + + TPtr pBuffer = buffer->Des(); + + if ( TMmsGenUtils::GenerateDetails( iMmsHeaders->Sender(), + pBuffer, KMmsMaxDescription, iFs ) == KErrNone ) + { + tEntry.iDetails.Set( pBuffer ); + } + else + { + // We come here only if there was an fatal error in GenerateDetails. + // Even if we don't find the alias, we have something in the string + tEntry.iDetails.Set( iMmsHeaders->Sender() ); + } + + // set subject if available + if ( iMmsHeaders->Subject().Length() > 0 ) + { + tEntry.iDescription.Set( iMmsHeaders->Subject() ); + } + CleanupStack::Pop( buffer ); + + } + else if ( messageType == KMmsMessageDeliveryInd && + iMmsHeaders->ToRecipients().MdcaCount() > 0 ) + { + tEntry.iDetails.Set( iMmsHeaders->ToRecipients().MdcaPoint( 0 ) ); + } + else + { + // keep LINT happy + } + + tEntry.iMtmData1 &= ~KMmsMessageTypeMask; + tEntry.iMtmData1 |= messageType; + if ( iMmsHeaders->MessageClass() == EMmsClassAdvertisement ) + { + tEntry.iMtmData1 |= KMmsMessageAdvertisement; + } + else if ( iMmsHeaders->MessageClass() == EMmsClassInformational ) + { + tEntry.iMtmData1 |= KMmsMessageInformational; + } + else + { + // keep LINT happy + } + + switch ( iMmsHeaders->MessagePriority() ) + { + case KMmsPriorityNormal: + tEntry.SetPriority( EMsvMediumPriority ); + break; + case KMmsPriorityLow: + tEntry.SetPriority( EMsvLowPriority ); + break; + case KMmsPriorityHigh: + tEntry.SetPriority( EMsvHighPriority ); + break; + default: + // if not defined default is normal + tEntry.SetPriority( EMsvMediumPriority ); + break; + } + + tEntry.SetVisible( ETrue ); + tEntry.SetComplete( ETrue ); + tEntry.SetInPreparation( EFalse ); + tEntry.SetReadOnly( EFalse ); + tEntry.iDate.UniversalTime(); // This is arrival time + tEntry.iSize = iMmsHeaders->Size() + attaSize; // add attachment data + mime headers + + // Set values to correspond to a new message if this was an extended notification + // containing full text. + + if ( completeMessage ) + { + tEntry.iMtmData1 &= ~KMmsMessageTypeMask; + // We override message type. + tEntry.iMtmData1 |= KMmsMessageMRetrieveConf | KMmsMessageMobileTerminated; + tEntry.SetReadOnly( ETrue ); + tEntry.SetSendingState( KMsvSendStateSent ); + tEntry.iServiceId = iServiceEntryId; + if ( attachmentAdded ) + { + tEntry.SetAttachment(ETrue); + } + } + + // we mark delivery reports as already sent so that + // we get rid of them at next garbage collection, if the phone + // boots or crashes before they are handled. + // Delivery reports are lost if phone crashes. + if ( messageType == KMmsMessageDeliveryInd || + messageType == KMmsMessageReadOrigInd ) + { + tEntry.SetSendingState( KMsvSendStateSent ); + } + + iServerEntry->ChangeEntry( tEntry ); // ignore any error + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + delete buffer; + buffer = NULL; + + // If the message was complete, we have put our notification to inbox, and it is ready to be used. + // However, we still need an entry for sending back an acknowledgement to the server. + // We must create a new new notification entry for that purpose. + + if ( completeMessage ) + { + // don't schedule the entry that went into inbox + iNotification = KMsvNullIndexEntryId; + TInt error = KErrNone; + tEntry.iType = KUidMsvMessageEntry; // This will never go to inbox + tEntry.iMtm = KUidMsgTypeMultimedia; + tEntry.iServiceId = iServiceEntryId; + tEntry.iRelatedId = iServiceEntryId; + tEntry.SetUnread( ETrue ); + tEntry.SetNew( ETrue ); + tEntry.SetVisible( EFalse ); + tEntry.SetComplete( EFalse ); + tEntry.SetInPreparation( ETrue ); + tEntry.SetReadOnly( EFalse ); + tEntry.iSize = iMmsHeaders->Size(); + tEntry.SetSendingState( KMsvSendStateUnknown ); + tEntry.iDescription.Set( iMmsHeaders->Subject() ); + tEntry.iMtmData1 &= ~KMmsMessageTypeMask; + tEntry.iMtmData1 |= KMmsMessageMNotificationInd; + // even if sending ack fails, this must not be moved to inbox + // because the corresponding message already is there. + tEntry.iMtmData2 |= KMmsDoNotMoveToInbox; + parent = mmsFolder; + error = iServerEntry->SetEntry( parent ); + if ( error == KErrNone ) + { + error = iServerEntry->CreateEntry( tEntry ); + } + if ( error == KErrNone ) + { + iError = iServerEntry->SetEntry( tEntry.Id() ); + if ( iError == KErrNone ) + { + // Now we have a new entry that will be scheduled for sending the acknowledgement + iNotification = tEntry.Id(); + iMmsHeaders->SetStatus( KMmsMessageStatusRetrieved ); + iMmsHeaders->SetMessageType( KMmsMessageTypeMNotificationInd ); + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); + iMmsHeaders->StoreL( *store ); + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + store = NULL; + tEntry.SetVisible( ETrue ); + tEntry.SetComplete( ETrue ); + tEntry.SetInPreparation( EFalse ); + iServerEntry->ChangeEntry( tEntry ); // ignore any error + } + } + } + +#ifndef _NO_MMSS_LOGGING_ + // log a little bit of something + if ( messageType == KMmsMessageDeliveryInd ) + { + TMmsLogger::Log( _L("- delivery report received") ); + } + else if ( messageType == KMmsMessageMNotificationInd ) + { + TMmsLogger::Log( _L("- notification received") ); + } + else + { + TMmsLogger::Log( _L("- pushed message of type %d received"), messageType ); + } +#endif + + } + +// --------------------------------------------------------- +// CMmsServerMtm::HandleNotificationL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleNotificationL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer HandleNotification, %d items"), iMsvSelection->Count() ); +#endif + if ( iMsvSelection->Count() < 1 ) + { + // nothing to do, give up + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, iError ); + return; + } + + // If both receiving modes in home and foreign network are "reject". + // The notification is deleted. + if( iMmsSettings->ReceivingModeHome() == EMmsReceivingReject && + iMmsSettings->ReceivingModeForeign() == EMmsReceivingReject ) + { + // we play possum and delete the notification without + // sending any response to MMSC. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- playing possum, deleting notifications") ); +#endif + TInt count = iMsvSelection->Count(); + TMsvEntry entry; + TMsvId parent = KMsvNullIndexEntryId; + if ( iServerEntry->SetEntry( iMsvSelection->At( count - 1 ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + parent = entry.Parent(); + } + while ( count-- && parent != KMsvNullIndexEntryId ) + { + if ( iServerEntry->SetEntry( iMsvSelection->At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + parent = entry.Parent(); + } + + if ( iServerEntry->SetEntry( parent ) == KErrNone ) + { + iServerEntry->DeleteEntry( iMsvSelection->At( count ) ); + } + } + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, iError ); + return; + } + + // When the notificatio arrived, it was checked, and if it was + // a duplicate of an earlier one, it was not saved on disk. + + iCommand = EMmsReceive; + + // ScheduleL completes our caller + // we want to get back to out RunL to check the error + // Query about disk space. + // Subroutine knows how much must be checked for task scheduler + TInt error = KErrNone; + if ( DiskSpaceBelowCriticalForSchedulingL( &iFs, 0, iMessageDrive ) ) + { + // we use standard error code here + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("HandleNotificationL.. Disk Full") ); + #endif + error = KErrDiskFull; + TInt count = iMsvSelection->Count(); + TMsvEntry entry; + if ( iServerEntry->SetEntry( iMsvSelection->At( count - 1 ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + entry.iError = error; + iServerEntry->ChangeEntry( entry ); + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("HandleNotificationL.. Setting ierror to Entry") ); + #endif + } + + } + else + { + // We must set the caller's status to KRequest Pending because + // CScheduleBaseServerMtm::ScheduleL does not do it. + *iRequestStatus = KRequestPending; + TRAP( error, ScheduleL( *iMsvSelection, EFalse, KNullDesC8, *iRequestStatus ) ); + // ScheduleL would complete our caller, but if it leaves, + // we must complete. We don't want to leave... + } + if ( error != KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer HandleNotification status %d"), error ); +#endif + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, error ); + } + + } + +// --------------------------------------------------------- +// CMmsServerMtm::HandleDeliveryReportL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleDeliveryReportL() + { + + if ( iMsvSelection->Count() < 1 ) + { + // nothing to do, give up + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, iError ); + return; + } + + iCommand = EMmsLogDeliveryReport; + + // Query about disk space. + // Subroutine knows how much must be checked for task scheduler + TInt error = KErrNone; + if ( DiskSpaceBelowCriticalForSchedulingL( &iFs, 0, iMessageDrive ) ) + { + // we use standard error code here + error = KErrDiskFull; + } + else + { + // ScheduleL completes our caller + // We must set the caller's status to KRequest Pending because + // CScheduleBaseServerMtm::ScheduleL does not do it. + *iRequestStatus = KRequestPending; + TRAP( error, ScheduleL( *iMsvSelection, EFalse, KNullDesC8, *iRequestStatus ) ); + // ScheduleL would complete our caller, but if it leaves, + // we must complete. We don't want to leave... + } + if ( error != KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer HandleDeliveryReport status %d"), error ); +#endif + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, error ); + } + } + + +// --------------------------------------------------------- +// CMmsServerMtm::LogDeliveryReportL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::LogDeliveryReportL() + { + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer Logging delivery report") ); +#endif + // this subroutine does not do critical disk space level check + // normally we just update an existing entry changing only status + // which does not change disk space usage. + // Only if our previous entry has been deleted, we add something. + // We don't check that. The amount of disk space needed should + // be less than 100 bytes anyway. Large messages are more critical + // than small log entries. + + if ( iMsvSelection->Count() < 1 ) + { + // nothing to do, give up + // we complete our caller + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, iError ); + return; + } + + iCommand = EMmsLogDeliveryReport; + TRequestStatus* status = &iStatus; + TBool readReport = EFalse; // first guess - delivery report + + iError = iServerEntry->SetEntry( iMsvSelection->At( 0 ) ); + if ( iError == KErrNotFound ) + { + // The entry we are supposed to handle has disappeared. + // We complete the active object. + // RunL will call UpdateEntriesL, and that function + // will delete the entry from the list, and try the next + // one, if it exists. + // We must set the error to "none" to delete the entry + // instead of retrying. + iError = KErrNone; + // get back to our own RunL, it will complete caller + *iRequestStatus = KRequestPending; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, iError ); + return; + } + + TMsvEntry entry; + if ( iError == KErrNone ) + { + entry = iServerEntry->Entry(); + } + else + { + // cannot access entry. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer could not access delivery report") ); +#endif + // Retry later. + // now our error is not KErrNone, so we will reschedule + // in UpdateEntriesL + *iRequestStatus = KRequestPending; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, iError ); + } + + if ( ( entry.iMtmData1 & KMmsMessageTypeMask ) == KMmsMessageReadOrigInd ) + { + // The remote party is in "From" field because that's the recipient + // that is sending the read report + readReport = ETrue; + } + else if ( ( entry.iMtmData1 & KMmsMessageTypeMask ) != KMmsMessageDeliveryInd ) + { + iError = KErrNone; + // get back to our own RunL + *iRequestStatus = KRequestPending; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, iError ); + return; + } + + if ( !iMmsLog ) + { + iLogClient = CLogClient::NewL( iFs ); + iLogViewEvent = CLogViewEvent::NewL( *iLogClient ); + iMmsLog = CMmsLog::NewL( *iLogClient, *iLogViewEvent, iFs ); + } + + if ( !iLogEvent ) + { + iLogEvent = CLogEvent::NewL(); + } + + if ( !iRemoteParties ) + { + // We only handle one delivery report at a time + iRemoteParties = new ( ELeave )CDesCArrayFlat( 1 ); + } + else + { + iRemoteParties->Reset(); + } + + // save the items that are the same for all our events + iLogEvent->SetEventType( KLogMmsEventTypeUid ); + iLogClient->GetString( iLogString, R_LOG_DIR_OUT ); + iLogEvent->SetDirection( iLogString ); + iLogEvent->SetDurationType( KLogDurationNone ); + // This should never stay + iLogClient->GetString( iLogString, R_LOG_DEL_SENT ); + iLogEvent->SetStatus( iLogString ); + + CMsvStore* store = NULL; + + TRAP( iError, store = iServerEntry->ReadStoreL(); ) + + if ( iError == KErrNone ) + { + CleanupStack::PushL( store ); + iMmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + iLogEvent->SetDataL( iMmsHeaders->MessageId() ); + // Use delivery time from delivery report + iLogEvent->SetTime( TTime( KMmsYear1970String ) + + TTimeIntervalMicroSeconds( iMmsHeaders->Date() * KMmsMillion ) ); + + switch ( iMmsHeaders->Status() ) + { + case KMmsMessageStatusRetrieved: + case KMmsMessageStatusForwarded: // forwarded is delivered from our point of view + iDeliveryStatus = ETrue; + iLogClient->GetString( iLogString, R_LOG_DEL_DONE ); + break; + case KMmsMessageStatusExpired: + case KMmsMessageStatusRejected: + case KMmsMessageStatusUnreachable: + iDeliveryStatus = EFalse; + iLogClient->GetString( iLogString, R_LOG_DEL_FAILED ); + break; + default: + if ( !readReport ) + { + // if status cannot be mapped, it is just "pending" + // KMmsMessageStatusDeferred, KMmsMessageStatusUnrecognized, and KMmsMessageStatusIndeterminate + // map to "pending" state + iLogClient->GetString( iLogString, R_LOG_DEL_PENDING ); + } + else + { + // read report can only have status "read" or + // "deleted without being read" + // We need some string mapping for those... + if ( iMmsHeaders->ReadStatus() == KMmsReadStatusRead ) + { + // read + iLogString.Copy( KLogsMsgReadText ); + } + else + { + // deleted without being read + // This does not change "delivered" status + // But in case the delivery report has not arrived, + // This ensures that the information of delivery gets stored. + // If the user deleted the message without reading it, + // it must have been delivered first. + iLogClient->GetString( iLogString, R_LOG_DEL_DONE ); + } + } + break; + } + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer delivery status code %d"), iMmsHeaders->Status() ); + TMmsLogger::Log( _L(" - delivery status %S"), &iLogString ); + TMmsLogger::Log( _L(" - delivery datetime:") ); + CMmsBaseOperation::LogDateL( iLogEvent->Time() ); +#endif + iLogEvent->SetStatus( iLogString ); + if ( iMmsHeaders->ToRecipients().MdcaCount() == 0 && + iMmsHeaders->Sender().Length() == 0 ) + { + // No recipient, cannot log + // no use retrying either + *iRequestStatus = KRequestPending; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KErrNone ); + return; + } + if ( !readReport ) + { + iRemoteParties->AppendL( TMmsGenUtils::PureAddress( + iMmsHeaders->ToRecipients().MdcaPoint( 0 ) ) ); + } + else + { + // If we have a read report, the remote party is the sender + iRemoteParties->AppendL( TMmsGenUtils::PureAddress( + iMmsHeaders->Sender() ) ); + } + +// CMmsLog is responsible for setting our status to "KRequestPending" +// If the status or the entry is already "read" it must not be changed +// back to "delivered" +// CMmsLog must take care of that because it is the component that +// finds the corresponding entry from the log database + iMmsLog->StartL( *iLogEvent, *iRemoteParties, iStatus); + *iRequestStatus = KRequestPending; + SetActive(); + } + else + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer could not access delivery report") ); +#endif + // Retry later. + // now our error is not KErrNone, so we will reschedule + // in UpdateEntriesL + *iRequestStatus = KRequestPending; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, iError ); + } + + } + +// --------------------------------------------------------- +// CMmsServerMtm::PruneDuplicateNotificationsL +// +// --------------------------------------------------------- +// +TBool CMmsServerMtm::PruneDuplicateNotificationsL( TMsvId aParent, CMmsHeaders& aNotification ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MmsServer Pruning duplicate notifications") ); +#endif + TInt error = KErrNone; + TBool pruned = EFalse; + TMsvId mmsFolder = FindMMSFolderL(); + + if ( aParent == KMsvNullIndexEntryId ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- no proper parent") ); +#endif + return EFalse; + } + + error = iServerEntry->SetEntry( aParent ); + if ( error != KErrNone ) + { + // cannot access parent, cannot prune. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- cannot access notification parent, error %d "), error ); +#endif + return EFalse; + } + + // show invisible entries + TMsvSelectionOrdering ordering = + TMsvSelectionOrdering( KMsvNoGrouping, EMsvSortByNone, ETrue ); + iServerEntry->SetSort( ordering ); + + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + if ( aParent == mmsFolder ) + { + error = iServerEntry->GetChildrenWithType( KUidMsvMessageEntry, *selection ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- from MMS Folder") ); +#endif + } + else + { + error = iServerEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *selection ); +#ifndef _NO_MMSS_LOGGING_ + if ( aParent == KMsvGlobalInBoxIndexEntryIdValue ) + { + TMmsLogger::Log( _L("- from Inbox") ); + } + else + { + TMmsLogger::Log( _L("- from mmbox folder") ); + } +#endif + } + + TInt count = selection->Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- %d notifications on disk"), count ); +#endif + if ( error != KErrNone || count == 0 ) + { + // cannot check or no old notifications found, anything goes + CleanupStack::PopAndDestroy( selection ); // selection + return EFalse; + } + + TInt i; + CMmsHeaders* notification = CMmsHeaders::NewL( iMmsSettings->MmsVersion() ); + CleanupStack::PushL( notification ); + CMsvStore* store = NULL; + for ( i = count; i > 0 && !pruned; i-- ) + { + error = iServerEntry->SetEntry( selection->At( i - 1 ) ); + if ( error != KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- cannot access notification") ); +#endif + // cannot handle this entry. + continue; + } + if ( ( ( iServerEntry->Entry().iMtmData1 != 0 ) && + ( ( iServerEntry->Entry().iMtmData1 & KMmsMessageTypeMask ) != + KMmsMessageMNotificationInd ) ) || + ( iServerEntry->Entry().iMtmData2 & KMmsNotificationBinary ) ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- not a notification") ); +#endif + continue; + } + + // binary notifications (empty entries) were already handled, so we can delete the rest + if ( iServerEntry->Entry().iMtmData1 == 0 ) + { + // remove garbage. + error = iServerEntry->SetEntry( aParent ); + if ( error == KErrNone ) + { + // never mind the error - we are just doing our best + iServerEntry->DeleteEntry( selection->At( i - 1 ) ); + } + continue; + } + + error = KErrNone; + TRAP( error, + { + store = iServerEntry->ReadStoreL(); + CleanupStack::PushL( store ); + notification->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); // store + }) + store = NULL; + + if ( error != KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- cannot access notification") ); +#endif + // cannot handle this entry. + continue; + } + + // content location is used to identify notifications + // referring to the same message. + // Content location is the only information given back to + // MMSC when fetching a message, therefore it must be unique + if ( notification->ContentLocation().Compare( aNotification.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 + pruned = ETrue; + } + } + + CleanupStack::PopAndDestroy( notification ); + CleanupStack::PopAndDestroy( selection ); + error = iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + return pruned; + } + +// --------------------------------------------------------- +// CMmsServerMtm::CheckNotificationsL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CheckNotificationsL( CMsvEntrySelection& aSelection ) + { + TInt error = KErrNone; + TMsvId parent = KMsvNullIndexEntryId; + TMsvId mmsFolder = FindMMSFolderL(); + if ( mmsFolder == KMsvNullIndexEntryId ) + { + // no folder... + return; + } + + TInt i; + + // Don't mix notifications from mms folder and inbox, + // in debug mode Symbian scheduler panics. + if ( aSelection.Count() > 0 ) + { + i = aSelection.Count() - 1; + while ( i >= 0 && parent == KMsvNullIndexEntryId ) + { + error = iServerEntry->SetEntry( aSelection.At( 0 ) ); + if ( error == KErrNone ) + { + parent = iServerEntry->Entry().Parent(); + } + else if ( error == KErrNotFound ) + { + // The entry has disappeared already. + aSelection.Delete( i ); + } + else + { + // keep LINT happy + } + i--; + } + } + + if ( parent == KMsvNullIndexEntryId ) + { + parent = mmsFolder; + } + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CheckNotificationsL, got %d entries"), aSelection.Count() ); +#endif + + error = iServerEntry->SetEntry( parent ); + User::LeaveIfError( error ); + + // If no notifications input find notifications from mms folder + if ( parent == mmsFolder && aSelection.Count() == 0 ) + { + error = iServerEntry->GetChildrenWithType( KUidMsvMessageEntry, aSelection ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Notifications found in mms folder %d "), aSelection.Count() ); +#endif + if ( error != KErrNone ) + { + aSelection.Reset(); // make sure we return no garbage + User::Leave( error ); + } + } + + // notifications in inbox + // If we have a selection, we don't generate new ones, + // An empty selection only lists notifications from MMS folder, we cannot + // touch inbox stuff otherwise. + // MMS Client MTM lists notifications from inbox when mode is changed + // from manual to automatic. + + if ( parent == KMsvGlobalInBoxIndexEntryId ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Notifications found in inbox %d "), aSelection.Count() ); +#endif + TInt j; + for ( j = aSelection.Count() - 1; j >= 0; j-- ) + { + error = iServerEntry->SetEntry( aSelection.At( j ) ); + TMsvEntry entry; + if ( error == KErrNone ) + { + entry = iServerEntry->Entry(); + } + + // Drop notification, if operationForbidden flag is on OR + // if messageexpired flag is on OR + // if notification is not stored in mmbox and forward operation has been succussfull OR + // if notification is deleted succuessfully from mmbox OR + // if notification is fetched successfully from mmbox. + // Or if notification cannot be accessed (used by someone else) + if ( error != KErrNone || + entry.iMtmData2 & KMmsNewOperationForbidden || + entry.iMtmData2 & KMmsMessageExpired || + ( entry.iMtmData2 & KMmsOperationFinished && + !( entry.iMtmData2 & KMmsOperationResult) && + !(entry.iMtmData2 & KMmsStoredInMMBox) ) ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" Dropped " )); + aSelection.Delete( j ); +#endif + } + else + { + // mark this that others can't start do an operation + entry.iMtmData2 |= KMmsNewOperationForbidden; + + // readonly is needed to set EFalse for scheduling + entry.SetReadOnly( EFalse ); + iServerEntry->ChangeEntry( entry ); + // We keep this entry in our selection + } + } + aSelection.Compress(); + } + + error = iServerEntry->SetEntry( parent ); + + // Check the list and see what we got. + // The list may contain both notifications and delivery reports + + TInt count = aSelection.Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("Found %d entries altogether"), count ); +#endif + TMsvEntry tEntry; + CMsvEntrySelection* thisNotification = NULL; + TTime now; + TTime entryTime; + + for ( i = count; i > 0; i-- ) + { + error = iServerEntry->SetEntry( aSelection.At( i - 1 ) ); + if ( error == KErrNoMemory ) + { + User::Leave( error ); + } + else if ( error != KErrNone ) + { + continue; + } + else + { + // keep LINT happy + } + tEntry = iServerEntry->Entry(); + parent = tEntry.Parent(); + // We keep only notifications - the rest are removed from the list. + // If we are doing garbage collection, all non-notifications are + // deleted from the disk (because they will never expire). + // See if a notification or dummy entry + if ( iServerEntry->Entry().iMtmData2 & KMmsNotificationBinary && + iCurrentCommand == EMmsGarbageCollection ) + { + // get rid of these as these are empty +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- dummy entry - deleting")); +#endif + error = iServerEntry->SetEntry( parent ); + if ( error == KErrNone ) + { + iServerEntry->DeleteEntry( aSelection.At( i - 1 ) ); + } + aSelection.Delete( i - 1 ); + } + else if ( ( tEntry.iMtmData1 & KMmsMessageTypeMask ) != KMmsMessageMNotificationInd ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- not a notification")); +#endif + if ( iCurrentCommand == EMmsGarbageCollection ) + { + // If we are doing garbage collection, these are deleted, too + // If any delivery reports or other non-notification stuff + // is around at garbage collection time, it has become garbage. + + // Garbage collection is called with MMS Service, not with + // local service, so it means that garbage collection cannot + // be called when some other operation is ongoing. + // It should not be possible to delete an entry that is being + // handled already because access to MMS Server MTM is sequential + + // However, a delivery report or read report might just have been + // scheduled for handling. Check that they won't get deleted too soon + + thisNotification = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( thisNotification ); + thisNotification->AppendL( aSelection.At( i - 1 ) ); + iScheduleSend->CheckScheduleL( *thisNotification ); + CleanupStack::PopAndDestroy( thisNotification ); + thisNotification = NULL; + + TBool alreadyScheduled = EFalse; + + if ( iServerEntry->Entry().Scheduled() ) + { + // Already scheduled - check, if schedule is valid + // Leave the schedule, if it is in the future - but not too much + now.UniversalTime(); + now += TTimeIntervalSeconds( KMmsScheduleDelay ); + entryTime = iServerEntry->Entry().iDate; + if ( ( ( entryTime - TTimeIntervalHours( KMmsSanityInterval ) ) <= now ) && + ( entryTime > now ) ) + { + // scheduled in the future, we don't touch it + alreadyScheduled = ETrue; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- already scheduled")); +#endif + } + } + + error = iServerEntry->SetEntry( parent ); + if ( error == KErrNone && !alreadyScheduled ) + { + // we delete extra entries - but if they seem to have + // legal schedule we leave them, the scheduling will + // handle them in due time + iServerEntry->DeleteEntry( aSelection.At( i - 1 ) ); + } + } + // We always clear the non-notifications from our list + // Either they were already legally scheduled and were left alone + // or they were gagbage and were deleted + aSelection.Delete( i - 1 ); + } + else + { + // Check if scheduled + + thisNotification = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( thisNotification ); + thisNotification->AppendL( aSelection.At( i - 1 ) ); + if ( iCommand != EMmsReceiveForced ) + { + iScheduleSend->CheckScheduleL( *thisNotification ); + } + else + // if we do forced fetch, we clean up old schedules first + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- cleaning schedules")); +#endif + CleanSchedulesL( *thisNotification ); + tEntry = iServerEntry->Entry(); + } + + if ( iServerEntry->Entry().Scheduled() ) + { + // Already scheduled - check, if schedule is valid + // Reschedule this, if it was scheduled in the past + // The entry is left into the selection list. + // If the list is rescheduled, this entry will + // be rescheduled to a later time. + // Leave the schedule, if it is in the future - but not too much + now.UniversalTime(); + now += TTimeIntervalSeconds( KMmsScheduleDelay ); + entryTime = iServerEntry->Entry().iDate; + if ( ( ( entryTime - TTimeIntervalHours( KMmsSanityInterval ) ) <= now ) && + ( entryTime > now ) ) + { + // scheduled in the future, we don't touch it +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- already scheduled")); +#endif + aSelection.Delete( i - 1 ); + } + } + else + { + // Check expiration + CMsvStore * store = NULL; + store = iServerEntry->ReadStoreL(); + CleanupStack::PushL( store ); + iMmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + TTime now; + now.UniversalTime(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MMSserver checking notifications") ); + TMmsLogger::Log( _L("MMS terminal universal datetime: ") ); + CMmsBaseOperation::LogDateL( now ); + TMmsLogger::Log( _L("MMS message expiry datetime:") ); + CMmsBaseOperation::LogNetworkFormatDateL( iMmsHeaders->ExpiryDate() ); +#endif + if ( ( ( iMmsHeaders->ExpiryDate() + iMmsSettings->ExpiryOvershoot() ) * + KMmsMillion ) < now.MicroSecondsFrom( TTime( KMmsYear1970String ) ).Int64() ) + { + // expired + // remove schedule, + iScheduleSend->DeleteScheduleL( *thisNotification ); + + // if the entry is in inbox or in mmbox folder, let it go + if ( parent == KMsvGlobalInBoxIndexEntryIdValue || + parent == iMmsSettings->MMBoxFolder() ) + { + tEntry.SetReadOnly( ETrue ); + // not forbidden for a new operation + tEntry.iMtmData2 &= ~KMmsNewOperationForbidden; + iServerEntry->ChangeEntry( tEntry ); + + } + else // delete the whole entry if it is in mms folder + { + error = iServerEntry->SetEntry( parent ); + if ( error == KErrNone ) + { + iServerEntry->DeleteEntry( aSelection.At( i - 1 ) ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- whole entry deleted")); +#endif + } + } +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- already expired")); +#endif + aSelection.Delete( i - 1 ); + } + } + CleanupStack::PopAndDestroy( thisNotification ); + thisNotification = NULL; + } + } + aSelection.Compress(); + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CheckNotificationsL, %d entries left"), aSelection.Count() ); +#endif + + } + +// --------------------------------------------------------- +// CMmsServerMtm::CreateNotificationsL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CreateNotificationsL() + { + // This function is for testing and message variation + + TInt err = KErrNone; + TInt i; + TInt count = 0; + TUint size = 0; + // Include old notifications into the selection + // Except when doing message generation at boot time - + // Then only new notifications are handled + if ( iCurrentCommand != EMmsMessageGeneration ) + { + CheckNotificationsL( *iMsvSelection ); + } + count = iMsvSelection->Count(); + + // If there are notifications to be handled, we don't + // create new ones, because new notification creation + // should be used for testing only + if ( !iMmsSettings->LocalMode() && count > 0 ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CreateNotificationsL - Global mode") ); + TMmsLogger::Log( _L("- %d old notifications, not generating new ones"), count ); +#endif + return; + } + + RFs fs; // We need a separate session to be able to set the session path + err = fs.Connect(); + if ( err != KErrNone ) + { + return; + } + CleanupClosePushL( fs ); + + HBufC8* bufferPointer = HBufC8::NewL( KMaxFileName ); + CleanupStack::PushL( bufferPointer ); + TPtr8 buffer = bufferPointer->Des(); + + CDir* fileList = NULL; + + // create notifications for local messages: + if ( iMmsSettings->LocalMode() ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CreateNotificationsL - Local mode")); +#endif + TEntry* entry = new( ELeave ) TEntry; // allocated from heap to save stack space + CleanupStack::PushL( entry ); + HBufC* filename = HBufC::NewL( iMmsSettings->LocalModeIn().Length() ); + CleanupStack::PushL( filename ); + TPtr fileNamePtr = filename->Des(); + fileNamePtr.Copy( iMmsSettings->LocalModeIn() ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("local mode dir %S "), &fileNamePtr ); +#endif + err = fs.SetSessionPath( fileNamePtr ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("set session path returned: %d"), err ); + HBufC* temp = HBufC::NewL( KMaxPath ); + CleanupStack::PushL( temp ); + TPtr tempPtr = temp->Des(); + fs.SessionPath( tempPtr ); + TMmsLogger::Log( _L("Session path: %S"), &tempPtr ); + CleanupStack::PopAndDestroy( temp ); + temp = NULL; +#endif + err = fs.Entry( fileNamePtr, *entry ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("get path properties returned: %d"), err ); +#endif + if ( err == KErrNone ) + { + TFindFile* finder = new( ELeave ) TFindFile( fs ); // allocated from heap to save stack space + CleanupStack::PushL( finder ); + _LIT( KWild, "*" ); + + err = finder->FindWildByPath( KWild, &fileNamePtr, fileList ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("find files returned: %d"), err ); +#endif + CleanupStack::PopAndDestroy( finder ); + + if ( fileList ) + { + CleanupStack::PushL( fileList ); + } + + if ( err == KErrNone ) + { + count = fileList->Count(); + } + else + { + count = 0; + } + } + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("found %d files in %S"), count, &fileNamePtr ); +#endif + for (i = 0; i < count; i++ ) + { + iParse.Set( ( ( *fileList )[i] ).iName, &fileNamePtr, NULL ); + buffer.Copy( iParse.FullName() ); + + size = fs.Entry( iParse.FullName(), *entry ); + size = entry->iSize; + + iNotification = KMsvNullIndexEntryId; + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("notification size %d"), size ); +#endif + if ( size > 0 ) + { + if ( !iEncodeBuffer ) + { + iEncodeBuffer = CBufFlat::NewL( 0x400 ); + } + else + { + iEncodeBuffer->Reset(); + } + + CreateNotificationL( buffer, size ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("notification created") ); +#endif + + DecodePushedMessageL(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("notification decoded") ); +#endif + delete iEncodeBuffer; + iEncodeBuffer = NULL; + } + else + { + fs.Delete( iParse.FullName() ); + } + + if ( iNotification != KMsvNullIndexEntryId ) + { + iMsvSelection->AppendL( iNotification ); + } + + } + if ( fileList ) + { + CleanupStack::PopAndDestroy( fileList ); + } + + CleanupStack::PopAndDestroy( filename ); + CleanupStack::PopAndDestroy( entry ); + } + + CleanupStack::PopAndDestroy( bufferPointer ); + CleanupStack::PopAndDestroy( &fs ); // close fs + iServerEntry->SetEntry( iServiceEntryId ); + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CreateNotificationsL - %d notifications"), iMsvSelection->Count() ); +#endif + } + +// --------------------------------------------------------- +// CMmsServerMtm::CreateNotificationL() +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CreateNotificationL( TDesC8& aUrl, TInt aSize ) + { + // for test purposes aUrl will contain the filename. + + // Reset sets the default encapsulation version + // The default version has been set from MmsSettings in NewL + iMmsHeaders->Reset(); + + // construct the notification into iMmsHeaders, and call encode + + iMmsHeaders->SetMessageType( KMmsMessageTypeMNotificationInd ); + + TTime currentTime; + currentTime.UniversalTime(); + currentTime.Int64(); + + TBufC8 tid; + tid.Des().NumUC( currentTime.Int64(), EHex ); + iMmsHeaders->SetTidL( tid ); + + iMmsHeaders->SetMessageClass( EMmsClassPersonal ); + iMmsHeaders->SetMessageSize( aSize ); + const TInt KTenHours = 10 * 60 * 60; // 10 hours relative expiry + iMmsHeaders->SetExpiryInterval( KTenHours ); + iMmsHeaders->SetContentLocationL( aUrl ); + + CMmsEncode* encoder = CMmsEncode::NewL( iFs ); + CleanupStack::PushL( encoder ); + encoder->EncodeHeadersL( *iMmsHeaders, *iEncodeBuffer ); + CleanupStack::PopAndDestroy( encoder ); // encoder + + } + +// --------------------------------------------------------- +// CMmsServerMtm::GarbageCollectionL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GarbageCollectionL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MMSServer doing garbage collection") ); +#endif + + // are we in offline mode... + // We use CMmsSendOperation to allow change in the name of the + // base opeation class. The function is in the base class anyway. + if ( !CMmsSendOperation::NetworkOperationsAllowed() ) + { + // not allowed to send or receive anything + User::Leave( KMmsErrorOfflineMode ); + } + + // MMS watcher sends us reason codes with garbage collection parameters. + // paramPack().iReasonFlags will contain the reason flags. + // paramPack().iMediaUnavailableTime tells when the memory card was removed + TMMSGarbageCollectionParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- reason code:") ); + if ( paramPack().iReasonFlags & KMmsReasonBoot ) + { + TMmsLogger::Log( _L(" -- Boot") ); + } + if ( paramPack().iReasonFlags & KMmsReasonMessageStoreChanged ) + { + TMmsLogger::Log( _L(" -- MessageStoreChanged") ); + } + if ( paramPack().iReasonFlags & KMmsReasonNetworkAllowed ) + { + TMmsLogger::Log( _L(" -- Network operations allowed") ); + } + if ( paramPack().iReasonFlags & KMmsReasonBackupEnded ) + { + TMmsLogger::Log( _L(" -- Backup ended") ); + } + if ( paramPack().iReasonFlags & KMmsReasonHotswap ) + { + TMmsLogger::Log( _L(" -- Hotswap") ); + } + if ( paramPack().iReasonFlags & KMmsReasonEnvironmentTimeChanged ) + { + TMmsLogger::Log( _L(" -- Environment time change") ); + } +#endif + + // + // Forward entries left in outbox + // + GcOutBoxNotificationsL(); + + // + // Message entries left in outbox + // + GcOutBoxMessagesL(); + + // + // Notifications in MMSFolder (automatic fetch) + // + GcMmsFolderNotificationsL(); + + // + // Notifications from inbox + // + GcInboxNotifications(); + + // + // Notifications in mmbox folder + // + GcMmboxFolderNotifications(); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::CleanSchedulesL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::CleanSchedulesL( CMsvEntrySelection& aSelection ) + { + // delete old schedules and reset scheduled entry. + // we trap each entry separately, so that we can clean as + // many as possible even if there are errors + + TMsvId oldEntry = iServerEntry->Entry().Id(); + TInt i; + CMsvEntrySelection* thisNotification = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( thisNotification ); + TInt error = KErrNone; + + for ( i = 0; i < aSelection.Count(); i++ ) + { + TRAP ( error, + { + error = iServerEntry->SetEntry( aSelection.At( i ) ); + if ( error == KErrNone ) + { + CMmsScheduledEntry* mmsScheduledEntry = + CMmsScheduledEntry::NewL( iServerEntry->Entry() ); + CleanupStack::PushL( mmsScheduledEntry ); + thisNotification->Reset(); + thisNotification->AppendL( aSelection.At( i ) ); + iScheduleSend->DeleteScheduleL( *thisNotification ); + + CMsvStore* store = NULL; + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); + // We clean up the old scheduled entry data + // by storing a new clean scheduled entry + mmsScheduledEntry->StoreL( *store ); + store->CommitL(); + + CleanupStack::PopAndDestroy( store ); + CleanupStack::PopAndDestroy( mmsScheduledEntry ); + } + } + ); + } + CleanupStack::PopAndDestroy( thisNotification ); + // restore the entry we were pointing at + // if this fails, something is seriously wrong. We did not delete anything. + iServerEntry->SetEntry( oldEntry ); + + } + +// --------------------------------------------------------- +// CMmsServerMtm::FindMMSFolderL +// +// --------------------------------------------------------- +// +TMsvId CMmsServerMtm::FindMMSFolderL() + { + // Actually the value in iMmsSettings should be correct + // but we try to really search for the folder in case + // the message store has been moved or something + TMsvId mmsFolderId = iMmsSettings->NotificationFolder(); + TInt error; + + // get a new entry, don't mess up with the original entry. + CMsvServerEntry* workingEntry = NULL; + TRAP( error, workingEntry = iServerEntry->NewEntryL( KMsvLocalServiceIndexEntryId ) ); + CleanupStack::PushL( workingEntry ); + + if ( error == KErrNoMemory ) + { + CleanupStack::PopAndDestroy( workingEntry ); + User::Leave( error ); + } + + if ( error != KErrNone ) + { + // no can do + CleanupStack::PopAndDestroy( workingEntry ); + return mmsFolderId; + } + + // Get List of services + // show invisible entries + TMsvSelectionOrdering ordering = + TMsvSelectionOrdering( KMsvNoGrouping, EMsvSortByIdReverse, ETrue ); + workingEntry->SetSort( ordering ); + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + error = workingEntry->GetChildrenWithType( KUidMsvFolderEntry, *selection ); + if ( error != KErrNone ) + { + // no can do + delete selection; + if ( error == KErrNoMemory ) + { + CleanupStack::PopAndDestroy( workingEntry ); + User::Leave( error ); + } + else + { + CleanupStack::PopAndDestroy( workingEntry ); + return mmsFolderId; + } + } + CleanupStack::PushL( selection ); + + // Now we should have a list of all local folders. + // prune away the standard folders. + // They should be at the end of the list + + TInt count = selection->Count(); + TInt i; + + for ( i = count - 1; i >= 0; i-- ) + { + if ( selection->At( i ) <= KMsvDeletedEntryFolderEntryId ) + { + // Anything below this should not be ours + selection->Delete( i ); + } + } + + // anything left... + count = selection->Count(); + + for ( i = 0; i < count && mmsFolderId == KMsvNullIndexEntryId ; i++ ) + { + error = workingEntry->SetEntry( selection->At( i ) ); + if ( error == KErrNoMemory ) + { + CleanupStack::PopAndDestroy( selection ); + CleanupStack::PopAndDestroy( workingEntry ); + User::Leave( error ); + } + // must be exact match + if ( error == KErrNone && + workingEntry->Entry().iDetails.Compare( KMMSNotificationFolder ) == 0 ) + { + mmsFolderId = selection->At( i ); + } + } + CleanupStack::PopAndDestroy( selection ); + CleanupStack::PopAndDestroy( workingEntry ); + + return mmsFolderId; + } + +// --------------------------------------------------------- +// CMmsServerMtm::DiskSpaceBelowCriticalForScheduling +// +// --------------------------------------------------------- +// +TBool CMmsServerMtm::DiskSpaceBelowCriticalForSchedulingL( RFs* aFs, + TInt aBytesToWrite, TInt aMessageDrive) + { + TBool belowCritical = EFalse; // optimistic + // The amount of memory needed depends on the number of messages to handle + belowCritical = TMmsGenUtils::DiskSpaceBelowCriticalLevelL( + aFs, KMmsTaskSpace * iMsvSelection->Count(), EDriveC ); + if ( !belowCritical ) + { + belowCritical = TMmsGenUtils::DiskSpaceBelowCriticalLevelL( + aFs, aBytesToWrite, aMessageDrive ); + } + return belowCritical; + } + +// --------------------------------------------------------- +// CMmsServerMtm::ScheduleSelectionL +// +// --------------------------------------------------------- +// +TInt CMmsServerMtm::ScheduleSelectionL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::ScheduleSelectionL") ); +#endif + + TInt error = KErrNone; + TInt i; + if ( iMsvSelection->Count() > 0 ) + { + TCommandParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + + MakeDatesIdenticalL( *iMsvSelection, paramPack().iInitialDelay, ETrue ); + // Query about disk space. + // Subroutine knows how much must be checked for task scheduler + if ( DiskSpaceBelowCriticalForSchedulingL( &iFs, 0, iMessageDrive ) ) + { + // we use standard error code here + error = KErrDiskFull; + } + else + { + // We must set the caller's status to KRequest Pending because + // CScheduleBaseServerMtm::ScheduleL does not do it. + *iRequestStatus = KRequestPending; + TRAP( error, ScheduleL( *iMsvSelection, EFalse, iParameter, *iRequestStatus ) ); + // ScheduleL would complete our caller, but if it leaves, + // we must complete. We don't want to leave without cleaning up first. + #ifndef _NO_MMSS_LOGGING_ + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("- ScheduleL left with error %d"), error ); + } + #endif + } + if ( error != KErrNone ) + { + // Put the entries into failed state because we could not schedule them. + for ( i = 0; i < iMsvSelection->Count(); i++ ) + { + if ( iServerEntry->SetEntry( iMsvSelection->At( i ) ) == KErrNone ) + { + TMsvEntry entry = iServerEntry->Entry(); + TMsvId parent = entry.Parent(); + + TMsvId mmboxFolder = iMmsSettings->MMBoxFolder(); + + if ( entry.iMtm == KUidMsgMMSNotification && + ( parent == KMsvGlobalInBoxIndexEntryIdValue || + parent == mmboxFolder ) ) + { + // if the notification is in mmbox folder and duplicate exists, + // the duplicate has to be marked too. + if ( parent == mmboxFolder ) + { + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- parent is mmbox folder") ); + #endif + TRAP_IGNORE( CMmsBaseOperation::MarkDuplicateL( + CMmsBaseOperation::EMmsNotificationOperationFailed, + *iServerEntry ) ); + } + + // Mark original notification + entry = iServerEntry->Entry(); + CMmsBaseOperation::MarkNotificationOperationFailed( entry ); + entry.SetSendingState( KMsvSendStateFailed ); + entry.iError = error; + entry.SetReadOnly( ETrue ); + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- marked original notification as failed") ); + #endif + } + iServerEntry->ChangeEntry( entry ); + } + } + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, error ); + } + } + return error; + } + +// --------------------------------------------------------- +// CMmsServerMtm::HandleDummyEntryL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleDummyEntryL() + { + // + // Get access to the streamstore of the entry + // +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("HandleDummyEntryL()") ); +#endif + if ( iMsvSelection->Count() == 0 ) + { + User::Leave( KErrNotFound ); + } + + User::LeaveIfError( iServerEntry->SetEntry( iMsvSelection->At( 0 ) ) ); + CMsvStore* store = iServerEntry->ReadStoreL(); + CleanupStack::PushL( store ); // *** + RMsvReadStream ins; + ins.OpenLC( *store, KUidBinaryNotificationStream ); // *** + + // + // Read first 32 bits into integer. It will tell the length of the data + // + TInt datalength = ins.ReadUint32L(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" - Streamed %d bytes from dummy-entry's store"), datalength ); +#endif + + // + // Reserve correct size buffer + // + if ( !iEncodeBuffer ) + { + iEncodeBuffer = CBufFlat::NewL( datalength ); + iEncodeBuffer->ResizeL( datalength ); + } + else + { + // Throw away old stuff and resize + iEncodeBuffer->Reset(); + iEncodeBuffer->ResizeL( datalength ); + } + TPtr8 pBuf = iEncodeBuffer->Ptr( 0 ); + + // + // Read the data into buffer + // + ins.ReadL( pBuf ); + CleanupStack::PopAndDestroy( &ins ); // close in + CleanupStack::PopAndDestroy( store ); + + // + // Clean up: the dummy entry is deleted + // + if ( iServerEntry->SetEntry( iServerEntry->Entry().Parent() ) == KErrNone ) + { + iServerEntry->DeleteEntry( iMsvSelection->At( 0 ) ); + } + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + iMsvSelection->Reset(); + } + +// --------------------------------------------------------- +// CMmsServerMtm::HandleMMBoxFlagL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleMMBoxFlagL( TMsvEntry& aEntry ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("HandleMMBoxFlagL()") ); +#endif + // Is there an mmbox field in the notification PDU... + if( iMmsHeaders->MMBoxMessageHeadersL().MmsStored() == 0 ) + { + TInt value = 0; + TInt error = KErrNone; + CRepository* repository = NULL; + TRAPD( error2, + // Notification does not contain explicit field, so checking cenrep + repository = CRepository::NewL( KUidMmsServerMtm ) ); // *** + if ( error2 == KErrNone ) + { + CleanupStack::PushL( repository ); + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- repository created") ); + #endif + error = repository->Get( KMmsEnginePseudoMMBox, value ); + if( error == KErrNone && value == 1 ) + { + // Assuming MMBox storage in this case + aEntry.iMtmData2 |= KMmsStoredInMMBox; + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Setting MMBox flag based on cenrep data!") ); + #endif + } + else + { + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Key not found from cenrep data, or value != 1") ); + #endif + } + CleanupStack::PopAndDestroy( repository ); + } + } + else if( iMmsHeaders->MMBoxMessageHeadersL().MmsStored() == KMmsYes ) + { + // Notification specifies the flag as 'yes' + #ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Notification contains stored in mmbox flag!") ); + #endif + aEntry.iMtmData2 |= KMmsStoredInMMBox; + } + else + { + // Keep LINT happy + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::GcOutBoxMessagesL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GcOutBoxMessagesL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::GcOutBoxMessagesL") ); +#endif + // + // MMS watcher sends us reason codes with garbage collection parameters. + // paramPack().iReasonFlags will contain the reason flags. + // paramPack().iMediaUnavailableTime tells when the memory card was removed + // + TMMSGarbageCollectionParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + + TMsvEntry entry; + + iMsvSelection->Reset(); + TInt err = iServerEntry->SetEntry( KMsvGlobalOutBoxIndexEntryId ); + // Get entries of type MMS Message + if ( err == KErrNone ) + { + err = iServerEntry->GetChildrenWithMtm( KUidMsgTypeMultimedia, *iMsvSelection ); + } + TInt count = iMsvSelection->Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- found %d message entries from outbox"), count ); +#endif + + if ( err == KErrNone && count > 0 ) + { + // iSendOperation should not be around in this context. + // To avoid possible memory leaks, we clean it away anyway + delete iSendOperation; + iSendOperation = NULL; + iSendOperation = CMmsSendOperation::NewL( iFs, iMmsSettings ); + + iSendOperation->Failed().AppendL( iMsvSelection->Back( 0 ), count ); + iSendOperation->Sent().SetReserveL( count ); + + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + while ( count-- ) + { + // The detailed handling depends both on the state of the entry + // and the event that triggered the garbage collection. + // Some states are handled differently depending on the event, + // some states are always hanldled the same way. + if ( iServerEntry->SetEntry( iMsvSelection->At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + selection->Reset(); + selection->AppendL( iMsvSelection->At( count ) ); + switch ( entry.SendingState() ) + { + case KMsvSendStateSent: + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- already sent")); +#endif + // Sent entries are always moved to sent folder (or deleted) + // regardless of trigger event + // A sent message should not be in outbox. It should have already been + // moved to sent folder. However, as it is not moved, we must do it. + if ( iServerEntry->SetEntry( KMsvGlobalOutBoxIndexEntryId ) == KErrNone ) + { + if ( iMmsSettings->MoveToSent() ) + { + // Move entry from Outbox to Sent Folder + // count has originally been set to iMsvSelection->Count(). + iServerEntry->MoveEntryWithinService( + iMsvSelection->At( count ), KMsvSentEntryIdValue ); + // This will not fail + if ( iSendOperation->Failed().Count() > count ) + { + iSendOperation->Sent().AppendL( + iSendOperation->Failed().At( count ) ); + } + } + else + { + // Move entry to Message Heaven + iServerEntry->DeleteEntry( iMsvSelection->At( count ) ); + } + } + iSendOperation->Failed().Delete( count ); + break; + } + case KMsvSendStateWaiting: + case KMsvSendStateSending: +#ifndef _NO_MMSS_LOGGING_ + if ( entry.SendingState() == KMsvSendStateWaiting ) + { + TMmsLogger::Log( _L("- waiting")); + } + else + { + TMmsLogger::Log( _L("- sending")); + } +#endif + if ( paramPack().iReasonFlags & + ( KMmsReasonHotswap | KMmsReasonBackupEnded ) ) + { + // reschedule if last time accessed earlier that media unavailable time stamp + if ( iServerEntry->Entry().iDate > paramPack().iMediaUnavailableTime ) + { + // Access time is after media unavailable, + // It means that something has been done to this entry already. + // Better leave it as is. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" -- accessed after media unavailable - leave as is")); +#endif + iSendOperation->Failed().Delete( count ); + } + } + else + { + iSendOperation->Failed().Delete( count ); + } + break; + case KMsvSendStateUponRequest: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- upon request")); +#endif + // Entries in "upon request" state are not rescheduled + iSendOperation->Failed().Delete( count ); + break; + case KMsvSendStateSuspended: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- suspended")); +#endif + // entries in "upon request" or "suspended" state are not rescheduled + // unless we have switched from offline mode back to online mode + if ( !( ( paramPack().iReasonFlags & KMmsReasonNetworkAllowed ) && + ( entry.SendingState() == KMmsOffLineState ) && + ( entry.iError == KMmsErrorOfflineMode ) ) ) + { + iSendOperation->Failed().Delete( count ); + } + break; + case KMsvSendStateScheduled: + case KMsvSendStateResend: + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- already scheduled")); +#endif + // These are either rescheduled or scheduled for the first time + // reschedule if the schedule is in the past + // If the scedule is in the past, CheckSchedule moves it forward. + // However, if it is too much in the past, it is moved forward by a year. + // We must do some sanity chaecking about the amount of change + + // we set flag to indicate that our shedule had passed + TInt oldSchedule = EFalse; + TTime now; + now.UniversalTime(); + + if ( iServerEntry->Entry().iDate < now ) + { + oldSchedule = ETrue; + } + + iScheduleSend->CheckScheduleL( *selection ); + if ( iServerEntry->Entry().Scheduled() ) + { + // already scheduled - check, if schedule is valid + TTime scheduleTime = iServerEntry->Entry().iDate; + now += TTimeIntervalSeconds( 1 ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("MMS terminal universal datetime: ") ); + CMmsBaseOperation::LogDateL( now ); + TMmsLogger::Log( _L("Scheduled datetime:") ); + CMmsBaseOperation::LogDateL( scheduleTime ); +#endif + // leave the schedule, if it is in the future - but not too much + TTimeIntervalMinutes allowance = + TTimeIntervalMinutes( KMmsScheduleAllowance ); + if ( ( ( ( scheduleTime - allowance ) <= now ) && ( scheduleTime > now ) ) + || !oldSchedule ) + { + // scheduled in the future, we don't touch it + // or the schedule has been originally set into the future, + // and we don't want to change it + iSendOperation->Failed().Delete( count ); + } +#ifndef _NO_MMSS_LOGGING_ + else + { + TMmsLogger::Log( _L("- bad schedule")); + } +#endif + } + break; + } + case KMsvSendStateNotApplicable: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- not applicable")); +#endif + break; + case KMsvSendStateUnknown: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- unknown")); +#endif + break; + case KMsvSendStateFailed: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- failed")); +#endif + break; + default: + // all entries that are left in the array, are rescheduled in the end + break; + } + } + } + CleanupStack::PopAndDestroy( selection ); + iCommand = EMmsSend; + // whatever was left gets rescheduled + MakeDatesIdenticalL( iSendOperation->Failed(), KMmsGarbageCollectionDelay ); + TRAP( err, UpdateEntriesL() ); + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::GcMmsFolderNotificationsL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GcMmsFolderNotificationsL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::GcMmsFolderNotificationsL") ); +#endif + // + // MMS watcher sends us reason codes with garbage collection parameters. + // paramPack().iReasonFlags will contain the reason flags. + // paramPack().iMediaUnavailableTime tells when the memory card was removed + // + TMMSGarbageCollectionParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + + TMsvEntry entry; + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + TMsvId parent = FindMMSFolderL(); + + TInt err = iServerEntry->SetEntry( parent ); + iMsvSelection->Reset(); + // All entries in MMSFolder have the same Uid as messages + // There may be notifications, delivery reports and read reports. + // If the whole notification folder has disappeared we cannot have messages either... + if ( err == KErrNone && parent != KMsvNullIndexEntryId ) + { + err = iServerEntry->GetChildrenWithType( KUidMsvMessageEntry, *iMsvSelection ); + } + TInt count = iMsvSelection->Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" - found %d notification entries"), iMsvSelection->Count() ); +#endif + + if ( err == KErrNone && count > 0 ) + { + // iReceiveMessage should not be around in this context. + // To avoid possible memory leaks, we clean it away anyway + delete iReceiveMessage; + iReceiveMessage = NULL; + iReceiveMessage = CMmsReceiveMessage::NewL( iFs, iMmsSettings ); + + iReceiveMessage->Failed().AppendL( iMsvSelection->Back( 0 ), count ); + iReceiveMessage->Received().SetReserveL( count ); + + while ( count-- ) + { + if ( iServerEntry->SetEntry( iMsvSelection->At( count ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + if ( entry.SendingState() == KMsvSendStateSent ) + { + // successful entry, delete it + // This will not fail + // Read reports are never marked as "sent" so they should not + // disturb the logic here + if ( iReceiveMessage->Failed().Count() > count ) + { + iReceiveMessage->Received().AppendL( + iReceiveMessage->Failed().At( count ) ); + iReceiveMessage->Failed().Delete( count ); + } + } + else if ( ( ( entry.SendingState() == KMsvSendStateWaiting ) || + ( entry.SendingState() == KMsvSendStateSending ) ) ) + { + if ( paramPack().iReasonFlags & ( KMmsReasonHotswap | KMmsReasonBackupEnded ) ) + { + // These will be rescheduled only if they were accessed the last time + // before the media unavailable event. + // Otherwise they might be a new operation. +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- hotswap or backup trigger")); +#endif + if ( entry.iDate > paramPack().iMediaUnavailableTime ) + { + iReceiveMessage->Failed().Delete( count ); + } + } + } + else if ( entry.iError == KErrNone && entry.iMtmData2 & KMmsNotifyResponseSent ) + { + // If this is legally deferred, it will not be rescheduled + CMsvStore* store = iServerEntry->ReadStoreL(); + CleanupStack::PushL( store ); + iMmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + if ( iMmsHeaders->Status() == KMmsMessageStatusDeferred ) + { + // this has been deferred legally - do not reschedule + iReceiveMessage->Failed().Delete( count ); + } + } + else + { + // Keep LINT happy + } + } + // Here could be a branch that prevents automatic sending of + // messages that have been suspended by user. However, user can suspend + // fetching only in manual mode, and then the notifications are in inbox + } + // Check notifications will remove expired notifications + if ( iReceiveMessage->Failed().Count() > 0 ) + { + // only check if there was anything left + CheckNotificationsL( iReceiveMessage->Failed() ); + } + iCommand = EMmsReceive; + MakeDatesIdenticalL( iReceiveMessage->Failed(), KMmsGarbageCollectionDelay ); + TRAP( err, UpdateEntriesL() ); + err = KErrNone; // we don't care about the error, we just do our best + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::GcMmsFolderNotificationsL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GcInboxNotifications() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::GcInboxNotifications") ); +#endif + // + // MMS watcher sends us reason codes with garbage collection parameters. + // paramPack().iReasonFlags will contain the reason flags. + // paramPack().iMediaUnavailableTime tells when the memory card was removed + // + TMMSGarbageCollectionParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + + TMsvEntry entry; + + TInt err = iServerEntry->SetEntry( KMsvGlobalInBoxIndexEntryId ); + iMsvSelection->Reset(); + if ( err == KErrNone ) + { + err = iServerEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *iMsvSelection ); + } + TInt count = iMsvSelection->Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" - found %d notifications in inbox"), iMsvSelection->Count() ); +#endif + TInt i; + if ( err == KErrNone && count > 0 ) + { + for ( i = 0; i < count; i++ ) + { + if ( iServerEntry->SetEntry( iMsvSelection->At( i ) ) == KErrNone ) + { + entry = iServerEntry->Entry(); + + // + // If booting and notif is not allowed to start a new operation -> something wrong + // + if( ( paramPack().iReasonFlags & ( KMmsReasonBoot ) ) && + ( entry.iMtmData2 & KMmsNewOperationForbidden ) ) + { + entry.iError = KMmsGeneralError; + CMmsBaseOperation::MarkNotificationOperationFailed( entry ); + entry.SetReadOnly( ETrue ); + entry.iMtmData2 &= ~KMmsOperationIdentifier; + iServerEntry->ChangeEntry( entry ); + } + } + + } + } + } + +// --------------------------------------------------------- +// CMmsServerMtm::GcOutBoxNotificationsL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GcOutBoxNotificationsL() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::GcOutBoxNotificationsL") ); +#endif + // + // MMS watcher sends us reason codes with garbage collection parameters. + // paramPack().iReasonFlags will contain the reason flags. + // paramPack().iMediaUnavailableTime tells when the memory card was removed + // + TMMSGarbageCollectionParameters param; + TPckgC paramPack( param ); + paramPack.Set( iParameter ); + + // + // Get selection of notifications from Outbox + // + TInt err = iServerEntry->SetEntry( KMsvGlobalOutBoxIndexEntryId ); + iMsvSelection->Reset(); + if ( err == KErrNone ) + { + err = iServerEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *iMsvSelection ); + } + TInt count = iMsvSelection->Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- found %d notification entries from Outbox"), count ); +#endif + if( err != KErrNone || count <= 0 ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- no entries to clean up"), count ); +#endif + return; + } + + // + // Creating iForwardOperation that will handle resends + // + delete iForwardOperation; + iForwardOperation = NULL; + iForwardOperation = CMmsForwardOperation::NewL( iFs, iMmsSettings ); + iForwardOperation->Failed().AppendL( iMsvSelection->Back( 0 ), count ); + + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + + // + // Loop through the found notifications (forward entries) + // + while( count-- ) + { + // + // The detailed handling depends both on the state of the entry + // and the event that triggered the garbage collection. + // + TMsvEntry tEntry; + err = iServerEntry->SetEntry( iMsvSelection->At( count ) ); + if( err != KErrNone ) + { + // If entry not accessible, start a new round + continue; + } + tEntry = iServerEntry->Entry(); + selection->Reset(); + selection->AppendL( iMsvSelection->At( count ) ); + + // + // Switch through based on notification entry's state + // + switch ( tEntry.SendingState() ) + { + case KMsvSendStateSuspended: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- KMsvSendStateSuspended")); +#endif + if( tEntry.iError == KMmsErrorOfflineMode ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- entry error == offline")); +#endif + if( paramPack().iReasonFlags & KMmsReasonNetworkAllowed ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- GCreason == back-from-offline: rescheduling entry")); +#endif + // Reschedule entry + // i.e. nothing done here + } + else // GC reason is not "back from offline" + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- GCreason != back-from-offline: leaving entry suspended")); +#endif + // Leave entry suspended (applies to both entry types) + // i.e. take entryId out from to-be-scheduled list + iForwardOperation->Failed().Delete( count ); + } + } + else // entry.iError not equal to KMmsErrorOfflineMode + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- entry error != offline, set forward entry as Failed") ); +#endif + // Set forward entry's send-state to failed + tEntry.SetFailed( ETrue ); + + // Clear related notification from Inbox: + // Get the related notification id + CMsvStore* store = NULL; + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); // *** + iMmsHeaders->RestoreL( *store ); + CleanupStack::PopAndDestroy( store ); + TMsvId relatedEntryId = iMmsHeaders->RelatedEntry(); + iMmsHeaders->Reset(); // headers not needed any more + + if( relatedEntryId != KMsvNullIndexEntryId ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- related notification-entry exists, clearing it") ); +#endif + // Set context (iServerEntry and entry) to notification and clear it + err = iServerEntry->SetEntry( relatedEntryId ); +#ifndef _NO_MMSS_LOGGING_ + if( err != KErrNone ) + { + TMmsLogger::Log( _L("- ERROR: Could not set entry") ); + } +#endif + if ( err == KErrNone ) + { + tEntry = iServerEntry->Entry(); + tEntry.iMtmData2 &= ~KMmsNewOperationForbidden; // not forbidden + tEntry.iMtmData2 &= ~KMmsOperationOngoing; // not ongoing + tEntry.iMtmData2 |= KMmsOperationFinished; // finished + tEntry.iMtmData2 |= KMmsOperationResult; // NOK + tEntry.SetReadOnly( ETrue ); + err = iServerEntry->ChangeEntry( tEntry ); + } +#ifndef _NO_MMSS_LOGGING_ + if( err != KErrNone ) + { + TMmsLogger::Log( _L("- ERROR: Could not change related entry") ); + } + TMmsLogger::Log( _L("- Clear the related-entry link itself") ); +#endif + + // Clear related-id link from forward entry + err = iServerEntry->SetEntry( iMsvSelection->At( count ) ); + if ( err == KErrNone ) + { + store = iServerEntry->EditStoreL(); + CleanupStack::PushL( store ); // *** + iMmsHeaders->RestoreL( *store ); + iMmsHeaders->SetRelatedEntry( KMsvNullIndexEntryId ); + iMmsHeaders->StoreL( *store ); + store->CommitL(); + CleanupStack::PopAndDestroy( store ); + } + iMmsHeaders->Reset(); // headers not needed any more +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Related-entry and the link cleared") ); +#endif + } + + // Clean up. + // iServerEntry will be needed and set next time + // at the start of this loop. + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + // Leave entry suspended (applies to both entry types) + // i.e. take entryId out from to-be-scheduled list +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- not scheduling entry") ); +#endif + iForwardOperation->Failed().Delete( count ); + } + break; + default: +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- Forward entry's sendState == %d, not scheduling"), tEntry.SendingState() ); +#endif + iForwardOperation->Failed().Delete( count ); + break; + } + } // while loop + + CleanupStack::PopAndDestroy( selection ); + iCommand = EMmsForward; + // whatever was left gets rescheduled + MakeDatesIdenticalL( iForwardOperation->Failed(), KMmsGarbageCollectionDelay ); + TRAP( err, UpdateEntriesL() ); +#ifndef _NO_MMSS_LOGGING_ + if( err != KErrNone ) + { + TMmsLogger::Log( _L("- UpdateEntriesL failed with code %d"), err ); + } +#endif + } + +// --------------------------------------------------------- +// CMmsServerMtm::GcMmboxFolderNotifications +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GcMmboxFolderNotifications() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::GcMmboxFolderNotifications") ); +#endif + + TMsvId mmboxFolder = iMmsSettings->MMBoxFolder(); + // if mmbox folder is not found, no need to check notifications from there. + if ( mmboxFolder == KMsvNullIndexEntryId ) + { + return; + } + + TInt error = iServerEntry->SetEntry( mmboxFolder ); + iMsvSelection->Reset(); + if ( error == KErrNone ) + { + error = iServerEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *iMsvSelection ); + } + TInt count = iMsvSelection->Count(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L(" - mmbox folder contains %d notifications"), count ); +#endif + if ( error == KErrNone && count > 0 ) + { + + for ( TInt i=0; i < count; i++ ) + { + if ( iServerEntry->SetEntry( iMsvSelection->At( i ) ) == KErrNone ) + { + TMsvEntry entry = iServerEntry->Entry(); + + // If forbidden flag is on, mark as failed. + if( entry.iMtmData2 & KMmsNewOperationForbidden ) + { + entry.iError = KMmsGeneralError; + entry.SetSendingState( KMsvSendStateSuspended ); + CMmsBaseOperation::MarkNotificationOperationFailed( entry ); + entry.SetReadOnly( ETrue ); + entry.iMtmData2 &= ~KMmsOperationIdentifier; + iServerEntry->ChangeEntry( entry ); + } + } + } + } + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + } + +// --------------------------------------------------------- +// CMmsServerMtm::FindDuplicateNotificationL +// +// --------------------------------------------------------- +// +TInt CMmsServerMtm::FindDuplicateNotificationL( + TMsvId aParent, CMmsHeaders& aHeaders, TMsvId& aDuplicate ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsServerMtm::FindDuplicateNotificationL") ); +#endif + + aDuplicate = KMsvNullIndexEntryId; + + if ( aParent == KMsvNullIndexEntryId ) + { + return KErrNotSupported; + } + + TInt error = iServerEntry->SetEntry( aParent ); + if ( error != KErrNone ) + { + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + return error; + } + + CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + + error = iServerEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *selection ); + + TInt count = selection->Count(); + if ( count == 0 ) + { + error = KErrNotSupported; + } + + if ( error != KErrNone ) + { + CleanupStack::PopAndDestroy( selection ); + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + return error; + } + + CMmsHeaders* mmsHeaders = CMmsHeaders::NewL( iMmsSettings->MmsVersion() ); + CleanupStack::PushL( mmsHeaders ); + + for ( TInt i = count; i > 0 && ( aDuplicate == KMsvNullIndexEntryId ); i-- ) + { + error = iServerEntry->SetEntry( selection->At( i - 1 ) ); + if ( error == KErrNone ) + { + CMsvStore* store = iServerEntry->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 + TMsvEntry entry = iServerEntry->Entry(); + aDuplicate = entry.Id(); + } + + } + + } + + CleanupStack::PopAndDestroy( mmsHeaders ); + CleanupStack::PopAndDestroy( selection ); + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + return error; + } + +// --------------------------------------------------------- +// CMmsServerMtm::SendReadReportL +// +// --------------------------------------------------------- +// +void CMmsServerMtm::SendReadReportL() + { + if ( iMsvSelection->Count() > 0 ) + { + delete iReadReport; + iReadReport = NULL; + iReadReport = CMmsReadReport::NewL( iFs, iMmsSettings ); + + iReadReport->StartL( *iMsvSelection, *iServerEntry, + iServiceEntryId, iStatus ); + *iRequestStatus = KRequestPending; + SetActive(); + } + else + { + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, KErrNotFound ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::LogCommandCode( TInt aCommand ) + { +#ifndef _NO_MMSS_LOGGING_ + switch ( aCommand ) + { + case EMmsSend: + TMmsLogger::Log( _L(" - Send") ); + break; + case EMmsReceive: + TMmsLogger::Log( _L(" - Receive") ); + break; + case EMmsScheduledSend: + TMmsLogger::Log( _L(" - Scheduled send") ); + break; + case EMmsScheduledReceive: + TMmsLogger::Log( _L(" - Scheduled fetch") ); + break; + case EMmsDeleteSchedule: + TMmsLogger::Log( _L(" - Delete schedule") ); + break; + case EMmsDecodePushedMessage: + TMmsLogger::Log( _L(" - Decode pushed message") ); + break; + case EMmsLogDeliveryReport: + TMmsLogger::Log( _L(" - Log delivery report") ); + break; + case EMmsDeleteEntries: + TMmsLogger::Log( _L(" - Delete entries") ); + break; + case EMmsReceiveForced: + TMmsLogger::Log( _L(" - Receive forced") ); + break; + case EMmsScheduledReceiveForced: + TMmsLogger::Log( _L(" - Scheduled receive forced") ); + break; + case EMmsGarbageCollection: + TMmsLogger::Log( _L(" - Garbage collection") ); + break; + case EMmsDeleteExpiredNotifications: + TMmsLogger::Log( _L(" - Delete expired notifications") ); + break; + case EMmsRetryServiceLoading: + TMmsLogger::Log( _L(" - Retry because service loading failed!") ); + break; + case EMmsMessageGeneration: + TMmsLogger::Log( _L(" - Message generation") ); + break; + case EMmsForward: + TMmsLogger::Log( _L(" - EMmsForward") ); + break; + case EMmsScheduledForward: + TMmsLogger::Log( _L(" - EMmsScheduledForward") ); + break; + case EMmsNotificationDelete: + TMmsLogger::Log( _L(" - EMmsNotificationDelete") ); + break; + case EMmsScheduledNotificationDelete: + TMmsLogger::Log( _L(" - EMmsScheduledNotificationDelete") ); + break; + case EMmsUpdateMmboxList: + TMmsLogger::Log( _L(" - EMmsUpdateMmboxList") ); + break; + case EMmsSendReadReport: + TMmsLogger::Log( _L(" - EMmsSendReadReport") ); + break; + case EMmsScheduledReadReport: + TMmsLogger::Log( _L(" - EMmsScheduledReadReport") ); + break; + default: + TMmsLogger::Log( _L(" - Unknown command") ); + break; + } + TMemoryInfoV1Buf memory; + UserHal::MemoryInfo( memory ); + TInt available = memory().iFreeRamInBytes; + TMmsLogger::Log(_L("Free memory %d"), available ); +#endif // _NO_MMSS_LOGGING_ + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::GetRealServiceId( CMsvEntrySelection& aSelection ) + { + TInt error = KErrNone; + TMsvId messageEntryId = KMsvNullIndexEntryId; + // the function cannot be called without a selection so the selection + // array always has at least one item. + if ( aSelection.Count() > 0 && aSelection.At( 0 ) != KMsvLocalServiceIndexEntryId ) + { + messageEntryId = aSelection.At( 0 ); + } + else if ( aSelection.Count() > 1 ) + { + messageEntryId = aSelection.At( 1 ); + } + else + { + // We never get here. This is just a safety valve. + error = KErrNotFound; + } + + // if only a service entry in selection list, + // nothing can be done. If we have only service entry, + // and that is not ours, we won't get here anyway. + + if ( messageEntryId != KMsvNullIndexEntryId ) + { + error = iServerEntry->SetEntry( messageEntryId ); + } + if ( error == KErrNone ) + { + if ( iServerEntry->Entry().iServiceId == KMsvLocalServiceIndexEntryId && + iServerEntry->Entry().iRelatedId != KMsvNullIndexEntryId ) + // This is our actual service. + { + iServiceEntryId = iServerEntry->Entry().iRelatedId; + } + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::HandleLoadServiceError( TInt aError ) + { + if ( iCommand == EMmsDecodePushedMessage ) + { + // We must delete the dummy entry given to us, + // otherwise nobody will delete it. + + if ( iMsvSelection->Count() > 0 && iMsvSelection->At( 0 ) == iServiceEntryId ) + { + iMsvSelection->Delete( 0 ); + } + if ( iMsvSelection->Count() > 0 ) + { + if ( iServerEntry->SetEntry( iMsvSelection->At( 0 ) ) == KErrNone ) + { + if ( iServerEntry->SetEntry( iServerEntry->Entry().Parent() ) == KErrNone ) + { + iServerEntry->DeleteEntries( *iMsvSelection ); + } + } + } + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + + // If we are trying to decode a notification from WAP stack + // return error to caller, not to ourselves + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, aError ); + return; + } + + if ( aError == KErrNotFound || aError == KErrNoMemory ) + { + // This is a hopeless case. + // It could lead to endless loop. + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, aError ); + return; + } + + // We only have to help client mtm when it is trying to start automatic + // fetch after changing fetch mode from "deferred" to "on" + if ( iCurrentCommand == EMmsScheduledReceive || + iCurrentCommand == EMmsScheduledReceiveForced ) + { + iCurrentCommand = EMmsRetryServiceLoading; + TRequestStatus* status = &iStatus; + // caller should be in pending state, too. + *iRequestStatus = KRequestPending; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, aError ); + return; + } + else + { + *iRequestStatus = KRequestPending; + User::RequestComplete( iRequestStatus, aError ); + return; + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::LogEntryParent() + { +#ifndef _NO_MMSS_LOGGING_ + TMsvId msgEntryId = KMsvNullIndexEntryId; + TInt error = KErrNone; + if ( iMsvSelection->Count() > 0 ) + { + msgEntryId = iMsvSelection->At( 0 ); + error = iServerEntry->SetEntry( msgEntryId ); + if ( error == KErrNone ) + { + if ( iServerEntry->Entry().Parent() == KMsvGlobalOutBoxIndexEntryId ) + { + TMmsLogger::Log( _L("- EntryParent: Outbox") ); + } + else + { + TMmsLogger::Log( _L("- EntryParent: 0x%08X"), iServerEntry->Entry().Parent() ); + } + } + } + // free whatever entry we are holding + iServerEntry->SetEntry( KMsvNullIndexEntryId ); +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsServerMtm::RestoreVisibilityAndService() + { + TInt error = KErrNone; + TInt i = 0; + TMsvEntry entry; + + // Make sure that the entries are visible. + // If they are "in preparation" that's the caller's problem + + for ( i = 0; i < iMsvSelection->Count(); i++ ) + { + error = iServerEntry->SetEntry( iMsvSelection->At( 0 ) ); + if ( error == KErrNone ) + { + entry = iServerEntry->Entry(); + if ( entry.Visible() == EFalse && iCurrentCommand != EMmsDeleteSchedule ) + { + entry.SetVisible( ETrue ); + // if this fails we cannot help... + iServerEntry->ChangeEntry( entry ); + } + if ( iCurrentCommand == EMmsDeleteSchedule && + entry.iServiceId == KMsvLocalServiceIndexEntryId && + entry.iRelatedId != KMsvNullIndexEntryId) + { + // restore the correct service id + entry.iServiceId = entry.iRelatedId; + entry.iRelatedId = KMsvNullIndexEntryId; + // if this fails we cannot help... + iServerEntry->ChangeEntry( entry ); + } + } + } + + // free the entry we are holding + iServerEntry->SetEntry( KMsvNullIndexEntryId ); + } + +// ================= OTHER EXPORTED FUNCTIONS ============== + +#ifndef _NO_MMSS_LOGGING_ +const TInt KLogBufferLength = 256; +_LIT(KLogDir, "mmss"); +_LIT(KLogFile, "mmss.txt"); + +void TMmsLogger::Log(TRefByValue aFmt,...) + { + VA_LIST list; + VA_START(list, aFmt); + + // Print to log file + TBuf buf; + buf.FormatList(aFmt, list); + + // Write to log file + RFileLogger::Write(KLogDir, KLogFile, EFileLoggingModeAppend, buf); + } +#endif + +// +// --------------------------------------------------------- +// gPanic implements +// panic function, should be used by debug version only +// +GLDEF_C void gPanic( + TMmsPanic aPanic ) // error number enumerations + { + _LIT( KMmsPanic,"MMS" ); + User::Panic( KMmsPanic, aPanic ); + } + +// End of File