--- /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 <apparc.h>
+#include <msventry.h>
+#include <msvschedulepackage.h>
+#include <msvschedulesettings.h>
+#include <msvsenderroraction.h>
+#include <bautils.h>
+#include <e32math.h> // for notification generation
+#include <logcli.h>
+#include <logview.h>
+#include <flogger.h>
+#include <e32svr.h>
+#include <centralrepository.h>
+#include <utf.h>
+#include <cmsvmimeheaders.h>
+#include "LogsApiConsts.h"
+#include <logengdurations.h>
+
+// 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<TCommandParameters> 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<KMMSMAXTIDLENGTH> 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<TMMSGarbageCollectionParameters> 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<TCommandParameters> 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<TMMSGarbageCollectionParameters> 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<TMMSGarbageCollectionParameters> 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<TMMSGarbageCollectionParameters> 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<TMMSGarbageCollectionParameters> 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<const TDesC> aFmt,...)
+ {
+ VA_LIST list;
+ VA_START(list, aFmt);
+
+ // Print to log file
+ TBuf<KLogBufferLength> 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