mmsengine/mmsserver/src/mmsserver.cpp
author William Roberts <williamr@symbian.org>
Thu, 22 Jul 2010 16:32:06 +0100
branchGCC_SURGE
changeset 47 5b14749788d7
parent 23 238255e8b033
parent 31 ebfee66fde93
permissions -rw-r--r--
Catchup to latest Symbian^4

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