mmsengine/mmsserver/src/mmsreceivemessage.cpp
author Simon Howkins <simonh@symbian.org>
Mon, 22 Nov 2010 17:05:03 +0000
branchRCL_3
changeset 83 26c290f28dd1
parent 60 7fdbb852d323
permissions -rw-r--r--
Removed duplicate instructions for creating some messaging MIFs

/*
* 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:  
*     State machine for message receiving
*
*/



// INCLUDE FILES
#include    <apparc.h>
#include    <msventry.h>
#include    <msvids.h>
#include    <charconv.h>
#include    <badesca.h>
#include    <cmsvmimeheaders.h>
#include    <mmsvattachmentmanager.h>
#include    <mmsvattachmentmanagersync.h>

#include    <centralrepository.h>

// mms private headers
#include    "mmsconst.h"
#include    "mmsreceivemessage.h"
#include    "mmssession.h"
#include    "mmssettings.h"
#include    "mmsservercommon.h"
#include    "mmsheaders.h"
#include    "mmsdecode.h"
#include    "mmsencode.h"
#include    "mmsscheduledentry.h"
#include    "mmserrors.h"
#include    "mmsserverentry.h"
#include    "mmsconninit.h"
#include    "MmsEnginePrivateCRKeys.h"
#include    "MmsPhoneClient.H"

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES  
extern void gPanic(TMmsPanic aPanic);

// CONSTANTS
// return codes for handling of messages addressed to an application
const TInt KMmsMessageForUnregisteredApplication = 1;
const TInt KMmsMessageMovedToApplicationFolder = 2;

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// ==================== LOCAL FUNCTIONS ====================


// ================= MEMBER FUNCTIONS =======================

// C++ default constructor can NOT contain any code, that
// might leave.
//
CMmsReceiveMessage::CMmsReceiveMessage( RFs& aFs )
    :CMmsBaseOperation( aFs )
    {
    }

// Symbian OS default constructor can leave.
void CMmsReceiveMessage::ConstructL( CMmsSettings* aMmsSettings )
    {
    iFileOpen = EFalse;
    CMmsBaseOperation::ConstructL( aMmsSettings );
    iMmsHeaders = CMmsHeaders::NewL( iMmsSettings->MmsVersion() );
    iNotification = CMmsHeaders::NewL( iMmsSettings->MmsVersion() );
    iBad = new( ELeave ) CMsvEntrySelection;
    TRAPD ( error, {iMessageDrive = MessageServer::CurrentDriveL( iFs );});
    if ( error != KErrNone )
        {
        // if cannot ask, use default
        iMessageDrive = EDriveC;
        }

    CActiveScheduler::Add( this );
    }

// Two-phased constructor.
CMmsReceiveMessage* CMmsReceiveMessage::NewL( RFs& aFs, CMmsSettings* aMmsSettings )
    {
    CMmsReceiveMessage* self = new ( ELeave ) CMmsReceiveMessage( aFs );
    
    CleanupStack::PushL( self );
    self->ConstructL( aMmsSettings );
    CleanupStack::Pop( self );

    return self;
    }

    
// Destructor
CMmsReceiveMessage::~CMmsReceiveMessage()
    {
    
    Cancel();

    delete iMmsHeaders;
    delete iNotification;
    delete iBad;
    // This is the message for local mode testing
    // It is closed here only to make sure that it is
    // always closed even if we encounter a serious error.
    // otherwise it should already be closed by now.
    if ( iFileOpen )
        {
        iFile.Close();
        }
    }


// ---------------------------------------------------------
// CMmsReceiveMessage::StartL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::StartL(
    CMsvEntrySelection& aSelection,
    CMsvServerEntry& aServerEntry,
    TMsvId aService,
    TRequestStatus& aStatus )
    {
    __ASSERT_DEBUG( iState == EMmsOperationIdle, gPanic( EMmsAlreadyBusy ) );

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("ReceiveMsg StartL") );
#endif
    // just initialize common member variables
    CMmsBaseOperation::InitializeL( aSelection, aServerEntry, aService );
    
    iReceivingMode = iMmsSettings->ReceivingModeHome();

    iNotificationParent = KMsvNullIndexEntryId;
    iBad->Reset();
    iNotification->Reset();
    iResponse->Reset(); // This sets the correct version
    iMmsHeaders->Reset();
    iEntryUnderConstruction = KMsvNullIndexEntryId;
    iAlreadyDeferred = EFalse;
    iFileOpen = EFalse;
    
    // selection is supposed to point to a bunch of notifications
    // containing the URIs the messages are to be fetched from
    TInt count = iCurrentMessageNo;
    iError = KErrNone;

    while ( count > 0 && ( iNotificationParent == KMsvNullIndexEntryId ) )
        {
        // Load the notification
        // iCurrentMessageNo (and therefore also count) has originally been
        // set to iSelection->Count() so the index is always valid.
        count--; // indexes are zero based
        if ( count < iSelection->Count() )
            {
            iError = iServerEntry->SetEntry( iSelection->At( count ) );
            }
        else
            {
            iError = KErrNotFound;
            }
        if ( iError == KErrNone )
            {
            TMsvEntry entry = iServerEntry->Entry();
            iNotificationParent = entry.Parent();
            }
        }
    
    // release the last notification
    iServerEntry->SetEntry( KMsvNullIndexEntryId );

    if (  iNotificationParent == KMsvNullIndexEntryId )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- could not access any entry to get parent") );
#endif
        // nothing to receive. Give up immediately
        aStatus = KRequestPending;
        TRequestStatus* status = &aStatus;
        User::RequestComplete( status, iError );
        return;
        }

    // Initialize everything into the failed list.
    // Very pessimistic indeed.
    
    iBad->SetReserveL( iCurrentMessageNo );

    Queue( aStatus );
    
    // We complete ourselves to get into the state machine loop.
    // The first thing to do is to check the roaming state.
    FallThrough();

    }

// ---------------------------------------------------------
// CMmsReceiveMessage::DoComplete
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::DoComplete(TInt& aError )
    {

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg DoComplete"));
#endif
    // We should get here if we are cancelled, or if
    // the cycle has completed (with or without error)
    // Make sure the store is released
    iDecoder->RelaseDataSink();

    // If we have an error, we must delete the entry
    // under construction.
    // If we have successfully created the entry, we have set
    // iEntryUnderConstruction to KMsvNullIndexEntryId.
    // If iEntryUnderConstruction still points to an entry, it means
    // that the cycle has been aborted, and the incomplete entry
    // must be deleted.
    if ( iEntryUnderConstruction != KMsvNullIndexEntryId )
        {
        if ( iServerEntry->SetEntry( KMsvGlobalInBoxIndexEntryId ) == KErrNone )
            {
            iServerEntry->DeleteEntry ( iEntryUnderConstruction );
            }
        iEntryUnderConstruction = KMsvNullIndexEntryId;
        }

    // Set error to entries that are still left
    // If some of the entries already have an error set,
    // it is not overridden.
    UnSetSendMask( *iFailed, aError );
    
    // If we are supporting MMBox, we must also clear the duplicates
    // if we have notifications in bad list
    
    if ( iMmsSettings->MMBoxFolder() != KMsvNullIndexEntryId )
        {
        TInt i = 0;
#ifndef _NO_MMSS_LOGGING_
        if ( iBad->Count() > 0 )
            {
            TMmsLogger::Log( _L("- %d notifications on bad list "), iBad->Count() );
            }
#endif

        for ( i = 0; i < iBad->Count(); i++ )
            {
            if ( iServerEntry->SetEntry( iBad->At( i )) == KErrNone )
                {
                // we don't want to leave here, we do our best
                TRAP_IGNORE( ClearDuplicateEntryOperationL() ); 
                }
            }
        }
    }

// start of ROAMING CHECK handling
// ---------------------------------------------------------
// CMmsReceiveMessage::RoamingCheck
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::RoamingCheck()
    {
    
    TInt error = KErrNone;
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg roaming check start"));
#endif

    if ( !iMmsSettings->LocalMode() && !iMmsSettings->FetchOverride() &&
         ( ( iMmsSettings->ReceivingModeHome() != iMmsSettings->ReceivingModeForeign() ) ||
         ( iMmsSettings->ReceivingModeForeign() == EMmsReceivingManual ) ||
         ( iMmsSettings->ReceivingModeForeign() == EMmsReceivingPostpone ) ) )
        {
        // In offline mode we cannot ask if we are roaming - just give up
        if ( !NetworkOperationsAllowed() )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- Offline mode - cannot check network status"));
#endif
            error = KMmsErrorOfflineMode;
            iError = error;
            }
        else
            {
            // if we get an error we have not been able to connect to phone module
            // and we cannot ask if we are roaming
            TRAP( error, iPhone = CMmsPhoneClient::NewL() )
            }

        if ( error == KErrNone )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- Receivemsg getting network status"));
#endif
            iPhone->Roaming( iStatus );
            }
        }
    else
        {
        // no roaming check needed
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Roaming check not needed"));
#endif
        TRequestStatus* status = &iStatus;
        iStatus = KRequestPending;
        // 0 = home network. If we don't care, we behave as at home
        User::RequestComplete( status, 0 );
        }
        
    if ( error != KErrNone )
        {
        // we got an error - iPhone is not going to complete us
        TRequestStatus* status = &iStatus;
        iStatus = KRequestPending;
        User::RequestComplete( status, error );
        }
    SetActive();
        
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::GetRoamingState
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::GetRoamingState()
    {
    
    // if current receiving modes are valid, we have to set the message fetch state
    // according to the receiving mode of the current (home or foreign ) network 
    
    // Set the message fetch state according to current network value
    if( iRegistrationStatus != 0 )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("roaming") );
#endif
        iReceivingMode = iMmsSettings->ReceivingModeForeign();
        }
    else
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("in home network") );
#endif
        iReceivingMode = iMmsSettings->ReceivingModeHome();        
        }
        
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- message receiving mode is %d"), iReceivingMode );
#endif
    
    // if something is wrong, entries are marked failed.
    if ( iError != KErrNone )
        {
        // Set error to all entries
        // If some of the entries already have an error set,
        // it is not overridden.
        UnSetSendMask( *iFailed, iError );

        }
    // if we have something in iError, we shall not continue
    // to open IAP, and RunL will complete. 
    // All entries will be rescheduled

    }
// end of ROAMING CHECK handling

// ---------------------------------------------------------
// CMmsReceiveMessage::DecodeResponseL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::DecodeResponseL()
    {
    FallThrough();
    return;
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::ConnectToIapL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::ConnectToIAPL()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg open IAP") );
#endif
    if ( !iMmsSettings->LocalMode() )
        {
        iError = iMmsSettings->ValidateSettings();
        }
    CMmsBaseOperation::ConnectToIAPL();
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::InitializeSessionL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::InitializeSessionL()
    {

    // Here we should connect to MMSC

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg Connecting") );
#endif

    // for testing purposes our messages are in files in the folder
    // defined in iMmsSettings->iRootFolder.
    if ( iMmsSettings->LocalMode() )
        {
        HBufC* filename = HBufC::NewL( iMmsSettings->LocalModeIn().Length() );
        CleanupStack::PushL( filename );
        TPtr fileNamePtr = filename->Des();
        fileNamePtr.Copy( iMmsSettings->LocalModeIn() );
        iFs.SetSessionPath( fileNamePtr );
        CleanupStack::PopAndDestroy( filename );
        }

    CMmsBaseOperation::InitializeSessionL();

    }

// ---------------------------------------------------------
// CMmsReceiveMessage::SubmitTransactionL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::SubmitTransactionL()
    {

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg Receiving") );
#endif
    iEntryUnderConstruction = KMsvNullIndexEntryId;
    iResponse->Reset(); // Reset sets the correct version
    iAlreadyDeferred = EFalse;
    iMmsHeaders->Reset();
    iEncodeBuffer->Reset();

    // We don't totally give up in case of an error.
    // We may have a long list of entries to handle.
    // One entry may be garbage while others will be ok.
    
    // Call to IsConnected() clears error for a new entry or
    // sets the error if we are not connected. If we are not
    // connected, we must terminate the state machine loop
    if ( !IsConnected() )
        {
        return;
        }

    TMsvEntry entry;

    // Load the notification. If an error occurs, we cannot continue
    // with this notification.
    if ( !LoadNotificationL( entry ) )
        {
        // LoadNotificationL sets the state machine active if we can continue
        // to next entry.
        // If the situation is hopeless, the state machine is not active, and
        // the loop terminates
        return;
        }


    TInt size = iNotification->MessageSize();
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- message size %d"), size);
#endif
    // If the size in notification is bigger than the maximum size we can receive
    // use the maximum size instead for the memory checks (we are not receiving
    // more than that anyway)
    if ( size > iMmsSettings->MaximumReceiveSize() )
        {
        // reduce the size to maximum allowed
        size = iMmsSettings->MaximumReceiveSize();
        }

    // Now we must check if the message should be rejected or deferred
    // The function IsNotificationInsaneL() checks if the notification
    // has incorrect message type or incorrect major version number.
    // If the message is unrecognized, the response will be "unrecognized"
    // The funtion sets the status, and later the response will be sent
    // but there is no need to check for expiration or disk space in
    // case the message will be rejected anyway.
    
    if ( !IsNotificationInsaneL() )
        {
        // Check if message has expired.
        // If it has, and we are in automatic mode, there is no need
        // to send a response.
        if ( !CheckExpirationL() )
            {
            return;
            }

        // Check disk space.    
        // we must add the safety margin here...
        size += KMmsDiskSafetyMargin;

        // Query about disk space.
        if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
            &iFs, size, iMessageDrive ) )
            {
            iNotification->SetStatus( KMmsMessageStatusDeferred );
            iError = KErrDiskFull;
            }
        }

    // if we have got an error for this message (KErrNoMemory or
    // KErrDiskFull) we must store the information in the notification
    // entry to discern these situations from cases where there
    // is something wrong with the actual transaction.
    if ( iError != KErrNone )
        {
        TInt error = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
        // If we cannot update our entry - that's too bad
        if ( error == KErrNone )
            {
            TMsvEntry tEntry;
            tEntry = iServerEntry->Entry();
            tEntry.iError = iError;
            iServerEntry->ChangeEntry( tEntry );
            }
        // we clear the error now. The rest of the branching
        // is not based on iError but on the notification status
        iError = KErrNone;
        // Release the entry
        iServerEntry->SetEntry( KMsvNullIndexEntryId );
        }

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- notification status %d"), iNotification->Status() );
    switch ( iNotification->Status() )
        {
        case KMmsMessageStatusRejected:
            TMmsLogger::Log( _L("- rejected") );
            break;
        case KMmsMessageStatusDeferred:
            TMmsLogger::Log( _L("- deferred") );
            break;
        case KMmsMessageStatusRetrieved:
            TMmsLogger::Log( _L("- retrieved") );
            break;
        case KMmsMessageStatusUnrecognized:
            TMmsLogger::Log( _L("- unrecognized") );
            break;
        default :
            TMmsLogger::Log( _L("- whatever") );
            break;
        }
#endif

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- receiving mode: %d"), iReceivingMode );
#endif

    // Sending the response tells the MMSC that the 
    // notification has been received and understood.
    // If we do not want to fetch the message the
    // WantToFetchThisL() subroutine will change the state
    // so that after return the response will be sent to MMSC
    
    if ( !WantToFetchThisL( entry ) )
        {
        return;
        }

    // Now it seems that we are supposed to fetch something
    // The notification has cleared all our tests and fetching is on

    // Now we should start a GET transaction with the
    // URI we obtain from the headers
           
    // If we fail to get anything because the message has expired,
    // we set iEntryUnderConstruction to KMsvNullIndexEntryId
    // Otherwise we must reschedule the receive

    // At this point we will invoke the transaction that sends
    // our get request to MMSC and when the transaction completes,
    // the data has been stored in our encode buffer, if the status
    // is KErrNone. Otherwise the completion status will contain the
    // error code.

    if ( iMmsSettings->LocalMode() )
        {
        LocalModeFetchL();
        // if only local mode, we complete ourselves
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- local mode, completing fetch with status %d"), iError );
#endif
        if ( !IsActive() )
            {
            // If Local mode fetch fails it already calls ChangeStateL() and becomes active
            // we cannot set us active a second time
            FallThrough();
            }
        }
    else // global mode
        {
        // Chunked mode in global mode, too
        // chunked mode is turned on here - other changes are generic
        DoCreateEntryL();
        iDecoder->InitializeChunkedMode( *iEntryWrapper, *iMmsHeaders, *iEncodeBuffer );
        // end of chunked preparation
        
        // we get this far only if we are connected
        HBufC* uri = HBufC::NewL( iNotification->ContentLocation().Length() + KMMSBufferExtra );
        CleanupStack::PushL( uri );
        uri->Des().Copy( iNotification->ContentLocation() );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("...Fetching from URI: %S"), uri );
#endif
        iMmsSession->FetchMessageL(
            *uri, 
            *iEncodeBuffer,
// Use this buffer size when in chunked mode             
            KMmsChunkedBufferSize, // tell that only this buffer size is needed
            *iEncoder,
            *iDecoder,
            iStatus );
        CleanupStack::PopAndDestroy( uri );
        SetActive();
        }

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
TBool CMmsReceiveMessage::IsConnected()
    {
    if ( iConnected )
        {
        // We clear the error at this point to tell that this message
        // is all right so far. Notifications for failed messages
        // remain in the selection, and they are rescheduled later
        // The error is saved in the schedule data for the notification.
        iError = KErrNone;
        }
    else
        {
        // We are in a totally hopeless mess.
        // We don't have a connection. iError should contain the reason
        // all we can do is give up
        // If we return from RunL without becoming active again
        // RunL completes.
        // That's all we can do here...
        // The selection arrays have information about
        // which messages have succeeded and which not.

        // DoComplete sets an error code to those entries in the
        // failed list that do not already have their error set.
        // If we don't know what went wrong, we just say "disconnected"
        if ( iError == KErrNone )
            {
            iError = KErrDisconnected;
            }
        }
    return iConnected;    
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsReceiveMessage::LoadNotificationL( TMsvEntry& aEntry )
    {
    // set entry to next notification in list
    iError = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
    if ( iError != KErrNone )
        {
        // The code called by SkipEntryL will make us active again
        // We may now try the next entry.
        SkipEntryL();
        return EFalse;
        }

    aEntry = iServerEntry->Entry();
    if ( aEntry.iMtmData2 & KMmsNotifyResponseSent )
        {
        iAlreadyDeferred = ETrue;
        }

    CMsvStore* store = NULL;

    TRAP (iError, {store = iServerEntry->ReadStoreL();});
    // We have managed to generate some mysterious notifications
    // where the ReadStoreL fails.
    // These must be deleted, as they are garbage.
    // I assume they are used by someone else.
    // This means we just delete them from our list and forget them
    // If we cannot get a read store, we cannot do anything with the
    // schedule either.
    if ( iError == KErrNoMemory )
        {
        // Out of memory - hopeless.
        // We return without becoming active again and the 
        // whole loop completes.
        // We should try to disconnect, but if there is a serious
        // memory problem I doubt that anything will work.

        // Release the entry
        iServerEntry->SetEntry( KMsvNullIndexEntryId );
        return EFalse;
        }
        
    if ( iError != KErrNone )
        {
        // don't discard the entry completely now
        delete store;
        store = NULL;
        // The code called by SkipEntryL will make us active again
        SkipEntryL();
        return EFalse;
        }
        
    // We have access to the notification
    CleanupStack::PushL( store );
    iNotification->RestoreL( *store );
    CleanupStack::PopAndDestroy( store );
    
    // We must clear deferred status always:
    // If we did not have enough memory the first time,
    // the message was deferred. If we now have enough 
    // memory the old status must be cleared

    iNotification->SetResponseStatus( 0 ); // clear possible old status
    iNotification->SetResponseTextL( TPtrC() ); // clear possible old text

    if ( iNotification->Status() == KMmsMessageStatusDeferred )
        {
        iNotification->SetStatus( 0 );
        }

    // We must check if message fetching is deferred or totally off.
    // if iReceivingMode == EMmsReceivingReject, notification has been deleted already.
    if ( !iMmsSettings->FetchOverride() &&
        iNotification->Status() == 0 &&
        ( iReceivingMode == EMmsReceivingManual || iReceivingMode == EMmsReceivingPostpone ) )
        {
        iNotification->SetStatus( KMmsMessageStatusDeferred );
        }
    
    // We get this far only if we are connected
    aEntry.SetConnected( ETrue );
    aEntry.SetSendingState( KMsvSendStateSending );
    aEntry.iError = KErrNone; // optimistic...
    iServerEntry->ChangeEntry( aEntry );

    // Release the entry
    iServerEntry->SetEntry( KMsvNullIndexEntryId );

    // Continue with this notification
    return ETrue;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsReceiveMessage::CheckExpirationL()
    {
    
    // If the message has expired, it can be rejected without further ado
    // We allow a little slack here, if it expired just a few seconds ago,
    // we still have hope...
    // use universal time in case user changes location
    TTime now;
    TMsvEntry entry;
    TBool canContinue = ETrue; // optimistic

    now.UniversalTime();
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("MMS terminal universal datetime: ") );
    CMmsBaseOperation::LogDateL( now );
    TMmsLogger::Log( _L("MMS message expiry datetime:") );
    CMmsBaseOperation::LogNetworkFormatDateL( iNotification->ExpiryDate() );
#endif
    if ( ( ( iNotification->ExpiryDate() + iMmsSettings->ExpiryOvershoot() ) * KMmsMillion ) <
        now.MicroSecondsFrom( TTime( KMmsYear1970String ) ).Int64() )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("message has expired by %d seconds"),
            ( now.MicroSecondsFrom( TTime( KMmsYear1970String ) ).Int64())/KMmsMillion -
            iNotification->ExpiryDate() - iMmsSettings->ExpiryOvershoot() );
#endif
        // If the notification is in inbox or in MMBox folder, fetch will continue.
        if( entry.Parent() == KMsvGlobalInBoxIndexEntryId  ||
            entry.Parent() == iMmsSettings->MMBoxFolder() )
            {
            // Mark the notification expired
            iError = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
            if ( iError == KErrNone )
                {
                entry = iServerEntry->Entry();
                entry.iMtmData2 |= KMmsMessageExpired;
                iServerEntry->ChangeEntry( entry );
                }
            // Release the entry
            iServerEntry->SetEntry( KMsvNullIndexEntryId );

#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("Expired notification. Fetch continues") ); 
#endif
            }
        else
            {
            // iError == KErrNone, the last stage will just remove our entry from the list
            // There is no need to send any reply to an expired message.
            // The MMSC will not be interested in getting a "reject"
            // response to an expired message.
            iResponse->SetStatus( KMmsMessageStatusRejected );
            iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
            // return to the beginning of the state machine loop
            iState = EMmsOperationUpdatingEntryStatus;
            // The code called by ChangeStateL will make us active again
            ChangeStateL();
            canContinue = EFalse; // loop to next state
            }
        }
    return canContinue; // can continue
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsReceiveMessage::IsNotificationInsaneL()
    {
    TBool isInsane = EFalse; // optimistic
    // If the message is unrecognized, the response will be "unrecognized"
    // Either the message type is unknown, or the major version number differs
    if  ( ( iNotification->MmsVersion() & KMmsMajorVersionMask ) !=
        ( iMmsSettings->MmsVersion() & KMmsMajorVersionMask ) )
        {
        // major versions differ, send back 1.0 version response
        iResponse->SetMmsVersion( KMmsVersion1 );
        iNotification->SetStatus( KMmsMessageStatusUnrecognized );
        iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
        isInsane = ETrue;
        }
    else if ( iNotification->MessageType() != KMmsMessageTypeMNotificationInd && 
        iNotification->MessageType() != KMmsMessageTypeMBoxDescr )
        { 
        // We have been pushed something we cannot handle.
        // delivery reports have been picked out earlier, so they cause
        // no problems here.
        iNotification->SetStatus( KMmsMessageStatusUnrecognized );
        iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
        isInsane = ETrue;
        }
    return isInsane;
    }
       
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsReceiveMessage::WantToFetchThisL( TMsvEntry& aEntry )
    {
    if ( ( iReceivingMode != EMmsReceivingAutomatic && !iMmsSettings->FetchOverride() ) ||
        iNotification->Status() == KMmsMessageStatusRejected ||
        iNotification->Status() == KMmsMessageStatusDeferred ||
        iNotification->Status() == KMmsMessageStatusRetrieved ||
        iNotification->Status() == KMmsMessageStatusUnrecognized )
        {
        // We don't want this message for one reason or another
        // We shall send reject message or deferred message and
        // remove this notification from the scheduling.
        // If the fetching is deferred, the notification should remain
        // in the machine waiting for better times.
        // If the message is rejected, the notification is deleted.
        // In both cases the schedule must be removed. A deferred
        // notification is not rescheduled before the message fetching
        // is turned on again.

        // If the whole message is in the extended notification,
        // the status is "retrieved"

        // However, if the "deferred" response has already been sent,
        // we should not send it again

        iResponse->SetStatus( iNotification->Status() );

        if ( iResponse->Status() != KMmsMessageStatusRejected &&
            iResponse->Status() != KMmsMessageStatusDeferred &&
            iResponse->Status() != KMmsMessageStatusRetrieved &&
            iResponse->Status() != KMmsMessageStatusUnrecognized )
            {
            // We have an unlucky message. The notification arrived
            // while we were supposed to fetch it, but before it was
            // actually fetched, receiving was turned off.
            // We try to defer it hoping it can be fetched before it
            // expires.
            // A message should be scheduled for fetching only a few
            // seconds after the notification arrives, so that if the 
            // user manages to turn the fetching off before we fetch 
            // the message we can assume that he does not want this one either
            // (at least not now). We are a bit conservative, though,
            // we don't reject this one as it was supposed to be fetched
            // before it fell into the time trap.
            iResponse->SetStatus( KMmsMessageStatusDeferred );
            }

        if ( aEntry.iMtmData2 & KMmsNotifyResponseSent )
            {
            // if the notify response has already been sent for this message,
            // we don't send it again.
            // When the message fetch state is switched back on,
            // server Mtm must check the pending notifications and
            // reschedule them for fetching.
            // An already deferred notification should not be in our
            // list, but I'm paranoid and check in case some notification
            // has fallen into a time trap.
            iAlreadyDeferred = ETrue;
            iState = EMmsOperationStoringResponseData;
            }
        else
            {
            // The function called by ChangeStateL will make us active again
            iState = EMmsOperationStoringResponseData;
            }
        // We are not fetching this message.
        // Either we send a response to MMSC or we just move to next notification.
        ChangeStateL();
        return EFalse;
        }
    return ETrue;
    }
        
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::DoCreateEntryL()
    {
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg DoCreateEntryL") );
#endif

    // We create a message entry into the inbox
    // it is invisible and in preparation until
    // we decide that we have successfully fetched the message
    // The entry must exist before the message is fetched, because
    // decode puts data into an existing entry, and
    // creates children to the entry for attachments.

    // If we have failed in storing data to the entry, it may be non-zero
    // We try to delete it before creating a new one

    if ( iEntryUnderConstruction != KMsvNullIndexEntryId )
        {
        if ( iServerEntry->SetEntry( KMsvGlobalInBoxIndexEntryId ) == KErrNone )
            {
            iServerEntry->DeleteEntry ( iEntryUnderConstruction );
            }
        iEntryUnderConstruction = KMsvNullIndexEntryId;
        }

    // If the creation of the entry is successful, we
    // set our entry to point to the newly created entry
    // to get data content to it.
    if ( iError == KErrNone )
        {
        TMsvId targetId = KMsvGlobalInBoxIndexEntryId;

        TMsvEntry tEntry;

        // set all relevant flags in tMsvEntry
        tEntry.iType = KUidMsvMessageEntry;
        tEntry.iMtm = KUidMsgTypeMultimedia;
        tEntry.iServiceId = iService;
        tEntry.SetUnread( ETrue );
        tEntry.SetNew( ETrue );
        tEntry.SetVisible( EFalse );
        tEntry.SetComplete( EFalse );
        tEntry.SetInPreparation( ETrue );
        tEntry.SetReadOnly( EFalse );

        // Query about disk space.
        if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
            &iFs, KMmsIndexEntryExtra, iMessageDrive ) )
            {
            // we use standard error code here
            iError = KErrDiskFull;
            }
        if ( iError == KErrNone )
            {
            iError = iServerEntry->SetEntry( targetId );
            }

        if ( iError == KErrNone )
            {
            iError = iServerEntry->CreateEntry( tEntry );
            }
        iEntryUnderConstruction = tEntry.Id();
        if ( iError == KErrNone )
            {
            iError = iEntryWrapper->SetCurrentEntry( iEntryUnderConstruction );
            }
        }
    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::StoreResponseL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::StoreResponseL()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg Store Response") );
#endif
    TInt error;
    TInt fatal = EFalse; // fatal error, no use to retry

    // If we have got this far, we don't return to CreateEntry anymore
    // even if we encounter backup/restore problems later.

    if ( iError == KErrNone )
        {
        // if we have no error, me must retry to make the entry visible
        iCriticalState = EMmsOperationStoringResponseData;
        }
    else
        {
        iCriticalState = EMmsOperationSendingAck;
        }

    // At the moment we should not receive any extra message types,
    // so that the only error of this type would
    // be wrong type of message either from WAP stack or from MMSC.
    // Send request is all right in local mode, as we get back
    // the messages we send ourselves.
    // We accept the send requests as retrieve confirmations,
    // as they are importand during testing phase:
    // We can put any message someone has sent into the folder
    // representing the simulated MMSC and try to fetch it as
    // if it were a real message.
    // We can't check the message type if we have encountered an error.
    // In that case the headers may not have been correctly decoded
    if ( iError == KErrNone &&
        iResponse->Status() != KMmsMessageStatusRejected &&
        iResponse->Status() != KMmsMessageStatusDeferred &&
        iResponse->Status() != KMmsMessageStatusRetrieved &&
        iMmsHeaders->MessageType() != KMmsMessageTypeMRetrieveConf &&
        iMmsHeaders->MessageType() != KMmsMessageTypeMSendReq )
        {
        iResponse->SetStatus( KMmsMessageStatusUnrecognized );
        iError = KMmsErrorStatusUnsupportedMessage;
        }

    // If we have managed to decode the response, it may contain a status
    // that must be mapped to an error. The error may be permanent or temporary

    if ( iError == KErrNone )
        {
        // we have got a response from MMSC, and decoded it, but in version 1.1
        // or later the response may still contain an error code.
        // if response text length was 0, it was deleted

        // - if error is permanent, notification will be deleted.
        // - if error is transient, response text is copied to notification
        // and UI components may show it to the user if needed.
        
        // iError is set to the correct value depending on response status
        // "fatal" indicates if the error is permanent or temporary
        fatal = MapErrorStatus( iMmsHeaders->ResponseStatus() );
        }

    // If we get a transient failure from MMSC, we store the possible text and status code
    // into our notification. UI may show the text to the user in case there is something
    // the user might do about the failure. Permament failures are stored in the message
    // entry.
    // The application id is also copied to the notification in case application id is present
    // in the message but not in the notification.
    // If the application has not been registered or memory runs out, the user may be
    // informed that the notification refers to a message that belongs to an application.
        
    CopyDataFromMessageToNotificationL();

    // If there has been an error and logging is allowed, dump the buffer into a file
    // to support error analysis later
    DumpIfNeededL();
    
    // We must try to finalize even if we have encountered an error.
    // If the decode has failed, iError contains something like "KErrCorrupt".
    // If the operation has been successful, we make the entry visible
    // and remove the in preparation flag. The user should then be able
    // to see the new message entry.

    // free the memory we don't need anymore
    if ( iEncodeBuffer )
        {
        iEncodeBuffer->Reset();
        }
    TMsvEntry tEntry;
    
    // Check if the message is going to an unregistered application.
    // If it is, it's going to be rejected
    iRegistered = EFalse;
    
    if ( iMmsHeaders->ApplicId().Length() > 0 )
        {
        iRegistered = RegisteredL( iMmsHeaders->ApplicId() );
        if ( !iRegistered )
            {
            // not registered - reject it
            iResponse->SetStatus( KMmsMessageStatusRejected );
            // we clear whatever error we may have because we are
            // going to reject the message anyway.
            iError = KErrNone;
            }
        }
        
    // If the message is going to an application we must check if we can free disk
    // space by deleting older messages addressed to the same application.
    
    if ( iError == KErrDiskFull && iRegistered )
        {
        // Check if we can free disk space and reschedule the fetch.
        // In manual mode the fetch will not be rescheduled.
        // If we can free memory we will change the error code to KMmsErrorApplicationDiskFull
        // If we cannot free memory, the error code is left as is and the user must
        // delete something else in order to be able to receive the message.
        DeleteApplicationMessagesL();
        }

    // If we have tried to fetch a message, and got 
    // EMRUExceeded, we'll reject the message. If it
    // was already deferred, no new response is sent.
    // The notification will be deleted, it is hopeless.

    if ( iError == KMmsErrorEMRUExceeded )
        {
        iResponse->SetStatus( KMmsMessageStatusRejected );
        iFailed->Delete( iCurrentMessageNo - 1 );
        iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
        }

    if ( iError != KErrNone ||
        iResponse->Status() == KMmsMessageStatusRejected ||
        iResponse->Status() == KMmsMessageStatusDeferred ||
        iResponse->Status() == KMmsMessageStatusRetrieved ||
        iResponse->Status() == KMmsMessageStatusUnrecognized )
        {
        // If the failure was fatal (permanent error from MMSC)
        // we don't send any response back. If the failure was
        // not fatal, we send "deferred" hoping we can retry later
        if ( fatal )
            {
            // The notification is deleted, as we have got an MMS message.
            iFailed->Delete( iCurrentMessageNo - 1 );
            iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
            // we don't send back a response if failing was fatal.
            iResponse->Reset();
            }
        else if ( iError == KMmsErrorHTTPConfiguration ||
            iError == KMmsErrorHTTPNotFound ||
            iError == KMmsErrorHTTPServerDown ||
            iError == KMmsErrorHTTPClientError )
            {
            // if we have got a HTTP error, there is no sense to send ack back
            iResponse->Reset();
            }
        else if ( !iAlreadyDeferred &&
            iResponse->Status() != KMmsMessageStatusRejected &&
            iResponse->Status() != KMmsMessageStatusRetrieved &&
            iResponse->Status() != KMmsMessageStatusUnrecognized)
            {
            iResponse->SetStatus( KMmsMessageStatusDeferred );
            EncodeNotifyResponseL();
            }
        else if ( iResponse->Status() == KMmsMessageStatusUnrecognized || 
            iResponse->Status() == KMmsMessageStatusRejected)
            {
            EncodeNotifyResponseL();
            }
        else if ( iResponse->Status() == KMmsMessageStatusRetrieved )
            {
            EncodeNotifyResponseL();
            iFailed->Delete( iCurrentMessageNo - 1 );
            iSuccessful->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
            }
        else
            {
            // We have nothing to send, we hope we can try
            // again later.
            iResponse->Reset();
            }
        }

#ifndef _NO_MMSS_LOGGING_
    // log the response status we are about to send
    TMmsLogger::Log( _L("- Response status to be: %d"), iResponse->Status() );
#endif

    // If we have no entry, the fetching has failed. If the failing was fatal,
    // the notification has been appended to the iBad list.
    // It will be deleted from the iFailed list later.
    // Those entries that end in the iBad list are just
    // deleted, not rescheduled.
    
    TInt applicationMessageStatus = 0;
    
    // If we got an error, we delete the entry under construction.
    // But if the error is "fatal" it means MMSC returned some error message,
    // and the error message entry must be kept and shown to the user
    // The corresponding notification remains in the failed list
    // in order to be rescheduled.
    // If the notification was bad, the corresponding notification
    // has already been appended to bad list to be removed from 
    // task scheduler.
    if ( iEntryUnderConstruction != KMsvNullIndexEntryId && iError != KErrNone && !fatal )
        {
        if ( iMmsSettings->LocalMode() )
            {
            CloseLocalFileL( EFalse );
            }
        // if setting server entry to inbox fails, deletion of entry will fail,
        // but there is nothing we can do about it.    
        iServerEntry->SetEntry( KMsvGlobalInBoxIndexEntryId );
        iServerEntry->DeleteEntry ( iEntryUnderConstruction );
        iEntryUnderConstruction = KMsvNullIndexEntryId;
        }
        
    if ( iEntryUnderConstruction != KMsvNullIndexEntryId )
        {
        // We don't care if this leaves. We just do our best.
        // If this function fails, our message will have some extra parts
        TRAP( error, HandleMultipartAlternativeL() );
        }
    
    // If we have an entry we have either successfully fetched a message
    // or received an error message about a fatal (permanent) error from MMSC
    // The entry must now be made visible to the user.    
    if ( iEntryUnderConstruction != KMsvNullIndexEntryId )
        {
        error = iServerEntry->SetEntry( iEntryUnderConstruction );

        if ( error == KErrNone )
            {
            // we don't check disk space here, as this
            // is local mode only: We just save the message id
            // for testing purposes. The message id is about 5 bytes
            // And we clear possible TID, that makes message shorter than before.
            CMsvStore* store = NULL;
            TRAP ( error, store = iServerEntry->EditStoreL() )
            if ( store )
                {
                CleanupStack::PushL( store );
                }
            if ( iMmsSettings->LocalMode() &&
                iMmsHeaders->MessageId().Length() == 0 )
                {
                iMmsHeaders->SetMessageIdL(
                    iNotification->ContentLocation().Mid(
                    iMmsSettings->LocalModeIn().Length() ) );
                }
            if ( iAlreadyDeferred )
                {
                // we need to save the TID from the message to the response
                iResponse->SetTidL( iMmsHeaders->Tid() );
                }
            // clear TID from the message to prevent the same TID from being used later
            // in case the message is copied around.
            iMmsHeaders->SetTidL( TPtrC8() );
            // Data is copied in case the message does not contain sender or subject
            // Data is copied only if disk space is not below critical level
            CopyDataFromNotificationToMessageL();
            if ( store )
                {
                TRAP ( error, 
                    {
                    iMmsHeaders->StoreL(*store);
                    store->CommitL();
                    });
                // If we managed to create the store without leaving, it is on cleanup stack
                CleanupStack::PopAndDestroy( store );
                store = NULL;
                }
            if ( iAlreadyDeferred )
                {
                // restore the TID
                iMmsHeaders->SetTidL( iResponse->Tid() );
                }

            tEntry = iServerEntry->Entry();
            
            // If message contains application id, check, 
            // if a folder, the name of which is application id is found
            // and move the message to this folder.
            // If the application is not registered, the message is deleted
            applicationMessageStatus = MoveToApplicationFolderIfNeededL( tEntry );
            
            if ( applicationMessageStatus != KMmsMessageForUnregisteredApplication )
                {
                // reload the entry in case the message was moved
                tEntry = iServerEntry->Entry();
                            
                SetIndexEntryBitsForReceivedMessage( tEntry );

                // if we cannot make our entry visible because of backup/restore, we must retry.
                error = iServerEntry->ChangeEntry( tEntry );

                // This is the critical point.
                // We must return before iEntryUnderConstruction is set to KMsvNullIndexEntryId
                // and the notification list is edited

                if ( error <= (TInt) KMsvMediaUnavailable &&
                    error >= (TInt) KMsvIndexRestore &&
                    !iDoNotWaitForBackupEnd )
                    {
#ifndef _NO_MMSS_LOGGING_
                    TMmsLogger::Log( _L("- must wait for backup end ") );
#endif
                    iState = EMmsOperationWaitingForBackupEnd;
                    // This will take us back to RunL
                    WaitForBackupEnd();
                    return;
                    }
                }

            if ( iMmsSettings->LocalMode() )
                {
                CloseLocalFileL( ETrue );
                }

            // done with this entry
            iEntryUnderConstruction = KMsvNullIndexEntryId;
            }

        // The task manager seems to handle deletion of entries
        // gracefully nowadays. We can just delete handled entries
        // We delete the notification as soon as possible
        // as we don't want it hanging around after the message
        // has been successfully fetched

        if ( !fatal )
            {
            // Check duplicate before deleting notification entry
            if ( iNotificationParent == KMsvGlobalInBoxIndexEntryIdValue ||
                ( iNotificationParent == iMmsSettings->MMBoxFolder() ))
                {
                // For the time being Java wants these notifications to be deleted
                // and applicationMessageStatus is always 0.
                // This may change for other type of applications.
                ClearOperationL( applicationMessageStatus );
                }
            else
                {
                // If the notification is not visible to the user,
                // we don't need to save the application routing status.
                // The rest of the handling can proceed normally - 
                // the notification will be deleted
                applicationMessageStatus = 0;
                }

            // If the notification does not need to be left around to inform user,
            // it can be deleted now                    
            if ( applicationMessageStatus == 0 )
                {
                error = iServerEntry->SetEntry( iNotificationParent );
                if ( error == KErrNone )
                    {
                    iServerEntry->DeleteEntry( iSelection->At( iCurrentMessageNo - 1 ) );
                    }
                else
                    {
                    // we use this list as backup:
                    // if we cannot delete now, we try again later.
                    iSuccessful->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
                    }
                }
            // Even if the notification was left in the inbox,
            // it is deleted from the failed list because it needs no more handling    
            iFailed->Delete( iCurrentMessageNo - 1 );
            
            // All went well. We can acknowledge the delivery.
            // If the MMSC is not interested in the acknowledgement,
            // it has sent us no TID.
            // If there is no TID, the response won't be sent.
            // If Retrieve confirmation has not been sent, it must be
            // sent instead of acknowledge indication
            
            // We don't send an ack back if we have received a response
            // from MMSC that indicates a fatal failure.
            // Most likely the message was expired or has disappeared
            // for some other reason (or did not exist in the first place)
            
            iResponse->SetStatus( KMmsMessageStatusRetrieved );
            if ( !iAlreadyDeferred )
                {
                EncodeNotifyResponseL();
                }
            else
                {
                EncodeAcknowledgeIndicationL();
                }
            }
        }

    if ( iFileOpen )
        {
        iFile.Close(); // just in case...
        iFileOpen = EFalse;
        }
        
    // release entry in case we were holding the service entry
    // We should not leave our entry pointing to the serive entry 
    // when we become active, others may want to use it.
    iServerEntry->SetEntry( KMsvNullIndexEntryId );

    iStatus = KRequestPending;
    SetActive();
    TRequestStatus* status = &iStatus;
    // We propagate the original error no matter what has happened here
    User::RequestComplete( status, iError );

    }

// ---------------------------------------------------------
// CMmsReceiveMessage::SendAckL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::SendAckL()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg Sending response to receive request") );
#endif

    // Send response to MMSC.
    // response is in iResponse.
    // If no reply should actually be sent, length of TID is 0.

    // We may try to send response even if we have encountered an error.
    // We try to send "deferred" if we couldn't fetch the message.
    // We hope the MMSC will try to keep the message in that case.

    // iResponse has Tid, if it contains something reasonable.

    if ( iResponse->Tid().Length() > 0 )
        {
        if ( !iMmsSettings->LocalMode() )
            {
            // send the response
            iMmsSession->SendMessageL(
                iUri->Des(),
                *iEncodeBuffer,
                *iEncoder,
                *iDecoder,
                iStatus );
            SetActive();
            }
        else
            {
            iStatus = KRequestPending;
            SetActive();
            TRequestStatus* status = &iStatus;
            // We propagate the original error no matter what has happened here
            User::RequestComplete( status, iError );
            }
        }
    else
        {
        // No response to send.
        // Just continue to next state.
        iStatus = KRequestPending;
        SetActive();
        TRequestStatus* status = &iStatus;
        // We propagate the original error no matter what has happened here
        User::RequestComplete( status, iError );
        }
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::UpdateEntryStatusL()
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::UpdateEntryStatusL()
    {

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Receivemsg Checking response success") );
#endif
    
    TInt error;
    TMsvEntry tEntry;
    TInt count = iBad->Count() - 1;

    // update scheduling info for this notification
    error = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
    if ( error == KErrNone )
        {
        tEntry = iServerEntry->Entry();
        error = tEntry.iError;
        if ( error == KErrNone )
            {
            error = iError;
            }
        // In some rare cases accessing the store for the notification may fail
        // in that case the schedule is not updated.
        TRAP( error,
            {
            CMsvStore* store = iServerEntry->EditStoreL();
            CleanupStack::PushL( store );
            CMmsScheduledEntry* mmsScheduledEntry =
                CMmsScheduledEntry::NewL( iServerEntry->Entry() );
            CleanupStack::PushL( mmsScheduledEntry );
            mmsScheduledEntry->RestoreL( *store );
            UpdateRecipient( iError, *mmsScheduledEntry );
            mmsScheduledEntry->StoreL( *store );
            store->CommitL();
            CleanupStack::PopAndDestroy( mmsScheduledEntry );
            CleanupStack::PopAndDestroy( store );
            });
        }

    // If the current notification has entered the "bad" list,
    // it should not be rescheduled.
    // For example, the message may be "deferred" because it is too
    // big to ever fit in our phone. It must be deleted from the "failed"
    // list, because it should not be rescheduled.

    TInt count2 = iFailed->Count() - 1;
    if ( count >= 0 &&
        count2 >= ( iCurrentMessageNo - 1 ) &&
        iBad->At( count ) == iFailed->At( iCurrentMessageNo - 1 ) )
        {
        // should not be rescheduled.
        iFailed->Delete( iCurrentMessageNo - 1 );
        }
    else if ( iError == KErrNone )
        {

#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("Receivemsg all is well") );
#endif
        // All is well.
        // If message was rejected the notification can be thrown away
        
        if ( iResponse->Status() == KMmsMessageStatusRejected ||
             iResponse->Status() == KMmsMessageStatusUnrecognized )
            {
            // The notifications in the received list will be deleted.
            // If the message was rejected, the notification will be
            // deleted.
            error = iServerEntry->SetEntry( iNotificationParent );
            if ( error == KErrNone )
                {
                iServerEntry->DeleteEntry( iSelection->At( iCurrentMessageNo - 1 ) );
                }
            else
                {
                // we use this list as backup:
                // if we cannot delete now, we try again later.
                iSuccessful->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
                }
            iFailed->Delete( iCurrentMessageNo - 1 );
            }

        if ( iResponse->Status() == KMmsMessageStatusDeferred )
            {

#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("Receivemsg responsestatus is deferred.") );
#endif
            // we have sent a "deferred" response.
            // We should keep the notification, but not reschedule it.
            // If we delete it from failed list, it wont get rescheduled.
            // If we don't add it into received list, it will not be deleted.
            // We must mark, that the "deferred" response was successfully sent
            // so that we will not send it again later.

            error = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
            // If we cannot update our entry - that's too bad
            // it just means that the response may be sent a second time...
            // The system must not get mad about that.
            // The system may be unreliable anyway, and some response may get lost.
            // We just do our best.
            if ( error == KErrNone )
                {
                iNotification->SetStatus( KMmsMessageStatusDeferred );
                CMsvStore * store = iServerEntry->EditStoreL();
                CleanupStack::PushL( store );
                iNotification->StoreL( *store );
                store->CommitL();
                CleanupStack::PopAndDestroy( store );
                tEntry = iServerEntry->Entry();
                tEntry.iMtmData2 |= KMmsNotifyResponseSent;
                tEntry.iMtmData2 &= ~KMmsDeferredButResponseNotSent;
                tEntry.SetConnected( EFalse );
                tEntry.SetFailed( EFalse );
                tEntry.SetSendingState( KMsvSendStateUnknown );

#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("Receivemsg notify response sent") );
#endif

                // change the iMtm type to notification 
                // and move the notification to the inbox
                TBool moveToInbox = EFalse;
                
#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("- message receiving mode %d"), iReceivingMode );
#endif
                if( iReceivingMode == EMmsReceivingManual && !iMmsSettings->FetchOverride() )
                    {          
                    // Change the iMtm from KUidMsgTypeMultimedia to 
                    // KUidMsgMMSNotification   
                    tEntry.iMtm = KUidMsgMMSNotification;
#ifndef _NO_MMSS_LOGGING_
                    TMmsLogger::Log( _L("marking as a notification") );
#endif

#ifndef _NO_MMSS_LOGGING_
TMmsLogger::Log( _L("tentry.iMtm is %d"), tEntry.iMtm.iUid );
#endif
                    tEntry.SetVisible( ETrue );
                    tEntry.SetComplete( ETrue );
                    tEntry.SetInPreparation( EFalse );
                    tEntry.SetReadOnly( ETrue );
                    // This is a notification, therefore there is no sending time available.
                    // We can only display the arrival time
                    tEntry.iDate.UniversalTime(); // this is arrival time
                    
                    tEntry.iMtmData1 |= KMmsMessageMobileTerminated;
            
                    // Subject, sender and size are set in CMmsDecode.
                    // Notification cannot contain multiple recipients
                    // no need to check or set the multiple recipient bit
                    
                    moveToInbox = ETrue;
                    }
                iServerEntry->ChangeEntry( tEntry );

                           
                // if manual mode is on and the fetchoverride is not on.
                // we have received a notification and sent a notifyresponse to the MMSC
                // the notification should be moved to the inbox
                if ( moveToInbox )
                    {

#ifndef _NO_MMSS_LOGGING_
                    TMmsLogger::Log( _L("Receivemsg notification entry to Inbox") );
#endif
                   
                    if ( iServerEntry->SetEntry( tEntry.Parent() ) == KErrNone )
                        {
                        iServerEntry->MoveEntryWithinService( tEntry.Id(),
                            KMsvGlobalInBoxIndexEntryIdValue );
                        }
                    }
                }
            
            if ( iReceivingMode != EMmsReceivingAutomatic && !iMmsSettings->FetchOverride() )
                {
                // If fetching is on, the message was deferred because of an error.
                // In that case we let it remain in the failed list for retries.
                // If fetching is deferred, manual or off, we don't keep the failed list,
                // The receiving mode must be changed before we retry the operation.

                // Should we remove the schedule...
                iFailed->Delete( iCurrentMessageNo - 1 );
#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("Receivemsg deleting from the iFailedList") );
#endif
                }
            }
        }
    else
        {
        // keep LINT happy
        }

    // release entry in case we were holding the service entry
    // We should not leave our entry pointing to the serive entry 
    // when we become active, others may want to use it.
    iServerEntry->SetEntry( KMsvNullIndexEntryId );
    // just return back to the start of the loop
    // to handle next message
    iStatus = KRequestPending;
    SetActive();
    TRequestStatus* status = &iStatus;
    // We propagate the original error no matter what has happened here
    User::RequestComplete( status, iError );

    }

// ---------------------------------------------------------
// CMmsReceiveMessage::EncodeNotifyResponseL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::EncodeNotifyResponseL()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- encoding m-notifyresp-ind") );
#endif
    iResponse->SetMessageType( KMmsMessageTypeMNotifyRespInd );
    iResponse->SetTidL( iNotification->Tid() );
    // message status has been set before we are called.
    iResponse->SetReportAllowed( iMmsSettings->DeliveryReportSendingAllowed() );
    iEncoder->EncodeHeadersL( *iResponse, *iEncodeBuffer );
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::EncodeAcknowledgeIndicationL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::EncodeAcknowledgeIndicationL()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- encoding m-acknowledge-ind") );
#endif
    iResponse->SetMessageType( KMmsMessageTypeAcknowledgeInd );
    
    // The TID has already been set to iResponse earlier, do not override it here
    
    // version value is set by the encoder.
    // This response has no status. It is only sent when the status is "Retrieved"
    iResponse->SetReportAllowed( iMmsSettings->DeliveryReportSendingAllowed() );
    iEncoder->EncodeHeadersL( *iResponse, *iEncodeBuffer );
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::HandleMultipartAlternativeL()
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::HandleMultipartAlternativeL()
    {
    // This is just "best effort"
    // If we cannot properly clear the extra attachments, they are just left "as is"
    
    if ( iMmsHeaders->MultipartType() != KMmsAssignedApplicationVndWapMultipartAlternative )
        {
        return; // nothing to do
        }

    User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iEntryUnderConstruction ) );
    CMsvStore* store = iEntryWrapper->EditStoreL();
    CleanupStack::PushL( store );
    
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    MMsvAttachmentManagerSync& attachManSync = store->AttachmentManagerExtensionsL();
    
    TInt count = attachMan.AttachmentCount();

    if ( count <= 1 )
        {
        CleanupStack::PopAndDestroy( store );
        return; // only one part, keep it
        }

    TInt i;
    CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
    CleanupStack::PushL( mimeHeaders );

    for ( i = count; i > 0; i-- )
        {
        CMsvAttachment* attachInfo = attachMan.GetAttachmentInfoL( i - 1 );
        CleanupStack::PushL( attachInfo );
        TPtrC8 pointer;
        pointer.Set( attachInfo->MimeType() );
        HBufC8* temp = NULL;
        if ( pointer.Length() == 0 )
            {
            // mime type not set. Try mime headers
            mimeHeaders->RestoreL( *attachInfo );
            temp = HBufC8::NewL( mimeHeaders->ContentType().Length() +
                mimeHeaders->ContentSubType().Length() + 1 );
            temp->Des().Copy( mimeHeaders->ContentType() );
            temp->Des().Append( KMmsSlash8 );
            temp->Des().Append( mimeHeaders->ContentSubType() );
            pointer.Set( temp->Des() );
            }
        CleanupStack::PushL( temp );                
        if ( pointer.CompareF( KMmsTextPlain ) != 0 )
            {
            attachManSync.RemoveAttachmentL( i - 1 );
            count--;
            }
        CleanupStack::PopAndDestroy( temp );
        CleanupStack::PopAndDestroy( attachInfo );
        temp = NULL; 
        attachInfo = NULL;
        }

    if ( count > 0 )
        {
        // we have something left in the selection
        // When we commit the store, deleted attachments should disappear
        store->CommitL();
        }

    CleanupStack::PopAndDestroy( mimeHeaders );
    CleanupStack::PopAndDestroy( store );
    iEntryWrapper->SetCurrentEntry( KMsvNullIndexEntryId );
    }

// ---------------------------------------------------------
// CMmsReceiveMessage::::EncodePDUL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::EncodePDUL()
    {
    // This state is used to check what must be done to the notification
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsReceiveMessage EncodePDUL - Finalize of continue"));
#endif

    iError = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
    
    if ( iError != KErrNone )
        {
        // The code called by SkipEntryL will make us active again
        SkipEntryL();
        return;
        }
        
    TMsvEntry tEntry;
    tEntry = iServerEntry->Entry();
        
    if ( iReceivingMode == EMmsReceivingAutomatic || iMmsSettings->FetchOverride() )
        {
        // if notification is in Inbox, set the fetch flags on
        if ( tEntry.Parent() == KMsvGlobalInBoxIndexEntryIdValue )
            {
            // mark the flags to reserve the notification for fetch
            MarkNotificationOperationReserved( tEntry, KMmsOperationFetch );
            
            iServerEntry->ChangeEntry( tEntry );
            #ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("mark notification that is being fetched") );
            #endif
            }
        // continue fetching
        FallThrough();
        return;  
        }

    // mode is deferred of reject
    
    // entry is in inbox - clear flags and leave it
    if ( tEntry.Parent() == KMsvGlobalInBoxIndexEntryIdValue )
        {
        tEntry.iMtmData2 &= ~KMmsNewOperationForbidden;
        tEntry.SetReadOnly( ETrue );
        iServerEntry->ChangeEntry( tEntry );
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("notification in inbox. Let it be") );
        #endif
        LoopToNextEntryL();
        return;
        }
    
    // entry is in mms folder or mmbox folder

    // already deferred - leave it and loop to next entry
    if ( tEntry.iMtmData2 & KMmsNotifyResponseSent )
        {
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("notification already deferred. Let it be") );
        #endif
        tEntry.iMtmData2 &= ~KMmsNewOperationForbidden;
        iServerEntry->ChangeEntry( tEntry );
        LoopToNextEntryL();
        return;
        }  
        
    // reject mode is on 
    if ( iReceivingMode == EMmsReceivingReject )
        {
        // Let the notification be, if we have received this notification 
        // earlier in the manual or postpone mode when we have roamed.
        // In that case KMmsDeferredButResponseNotSent flag is set.
        // Delete it, if it is a new one that has arrives while we are in reject mode 
        tEntry.iMtmData2 &= ~KMmsNewOperationForbidden;
        iServerEntry->ChangeEntry( tEntry );
        if ( !( tEntry.iMtmData2 & KMmsDeferredButResponseNotSent ) )
            {
            #ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("deleting the notification") );
            #endif
            // we ignore the error. If setting entry to parent fails, deletion will fail
            iServerEntry->SetEntry( tEntry.Parent() );
            iServerEntry->DeleteEntry( iSelection->At( iCurrentMessageNo - 1 ) );
            }               
        LoopToNextEntryL();
        return;
        }
 
    // deferred and response not yet sent
    
    if ( iRegistrationStatus == 0 ) // home network - continue
        {
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("in homenw and deferred mode") );
        #endif
        // continue fetching - will send "deferred" response
        FallThrough();
        return;  
        }
        
    // state is deferred, and we are roaming    
    
    // if the status of the notification is retrieved, this
    // notification will be deleted.
    // This happens if the notification was extended notification 
    // and the whole message arrived in the notification.
    // When roaming, no response is sent. The extended notification
    // is already in the inbox. This notification is deleted.

    CMsvStore* store = iServerEntry->ReadStoreL();
    CleanupStack::PushL( store );
    iNotification->RestoreL( *store );
    CleanupStack::PopAndDestroy( store );
    store = NULL;
    
    if ( iNotification->Status() == KMmsMessageStatusRetrieved )
        {
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L(" the whole extended notification received") );
        TMmsLogger::Log( _L("deleting the notification") );
        #endif
        // ignore error, if setting parent fails, deletion will fail
        iServerEntry->SetEntry( tEntry.Parent() );
        iServerEntry->DeleteEntry( iSelection->At( iCurrentMessageNo - 1 ) );
        LoopToNextEntryL();
        return;
        }
        
    // The notification is not completely fetched, finalize flags    
    
    #ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Finalizing the notification") );
    #endif
    iNotification->SetStatus( KMmsMessageStatusDeferred );
    store = iServerEntry->EditStoreL();
    CleanupStack::PushL( store );
    iNotification->StoreL( *store );
    store->CommitL();
    CleanupStack::PopAndDestroy( store );
    store = NULL;

    tEntry.SetConnected( EFalse );
    tEntry.SetFailed( EFalse );
    tEntry.SetSendingState( KMsvSendStateUnknown );

    tEntry.iMtmData1 |= KMmsMessageMobileTerminated;

    // When roaming it is not allowed to send a notify response
    // to MMSC.
    tEntry.iMtmData2 |= KMmsDeferredButResponseNotSent;

    // The receiving mode is either manual or postpone, when roaming. 
    // If the manual mode is on, the mtm type is changed to notification
    // and the notification is moved to Inbox.
    // 
    if ( iMmsSettings->ReceivingModeForeign() != EMmsReceivingManual )
        {
        // this is all we need
        iServerEntry->ChangeEntry( tEntry );
        }
    else
        {
        // Change the iMtm from KUidMsgTypeMultimedia to 
        // KUidMsgMMSNotification   
        tEntry.iMtm = KUidMsgMMSNotification; 
        tEntry.iMtmData2 &= ~KMmsNewOperationForbidden;
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("marking as a notification") );
        #endif                  
        // Readonly flag has to be set on in inbox.
        // Not in mms folder
        tEntry.SetReadOnly( ETrue );
        iServerEntry->ChangeEntry( tEntry );
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("moving to inbox") );
        #endif
        // Ignore error from set entry. If it fails, move will fail (not panic)
        // Setting service entry to a folder should not fail.
        iServerEntry->SetEntry( tEntry.Parent() );
        iServerEntry->MoveEntryWithinService( tEntry.Id(), KMsvGlobalInBoxIndexEntryIdValue );
        }
        
    LoopToNextEntryL();

    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::::SkipEntryL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::SkipEntryL()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("receivemsg - can't access notification") );
#endif
    // If the notification cannot be accessed, we must try the next one
    // If the notification was not found, or was used by someone else,
    // then nothing can be done. Nothing can be done to the scheduling data.

    // We completely discard only entries that cannot be found
    if ( iError == KErrNotFound )
        {
        iFailed->Delete( iCurrentMessageNo - 1 );
        }
    // we must return to the beginning of the loop without doing anything
    iState = EMmsOperationUpdatingEntryStatus;
    iServerEntry->SetEntry( KMsvNullIndexEntryId );
    // The code called by ChangeStateL will make us active again
    ChangeStateL();
    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::::LoopToNextEntryL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::LoopToNextEntryL()
    {
    // release entry
    iServerEntry->SetEntry( KMsvNullIndexEntryId );
    // loop to next entry
    iFailed->Delete( iCurrentMessageNo - 1 );
    iState = EMmsOperationUpdatingEntryStatus;
    SelectNextState();
    // The code called by ChangeStateL will make us active again
    ChangeStateL();
    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::FolderEntryL
//
// ---------------------------------------------------------
//
TInt CMmsReceiveMessage::FolderEntryL(
    TMsvId aParent, const TDesC& aFolderName, TMsvId& aFolderId )
    {
    aFolderId = KMsvNullIndexEntryId;

    TInt error = KErrNone;
    
    // get a new entry, don't mess up with the original entry.
    CMsvServerEntry* workingEntry = NULL;
    TRAP( error, workingEntry = iServerEntry->NewEntryL( aParent ) );
    CleanupStack::PushL( workingEntry );
    
    if ( error != KErrNone )
        {
        CleanupStack::PopAndDestroy( workingEntry ); // workingEntry
        return error;
        }
   
    // 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 )
        {           
        // If selection contains folders, check if the folder is found.
        TInt count = selection->Count();
        for ( TInt i = 0; i < count && aFolderId == KMsvNullIndexEntryId; i++ )
            {
            error = workingEntry->SetEntry( selection->At( i ) );
            if ( error == KErrNone )
                {
                // must be exact match
                if ( workingEntry->Entry().iDetails.Compare( aFolderName ) == 0 )
                    {
                    aFolderId = selection->At( i );
                    }                    
                }                           
            }
        }
    delete selection; 
    selection = NULL;
    CleanupStack::PopAndDestroy( workingEntry );
    workingEntry = NULL;
    
    return error; 
        
    }
 
 // -----------------------------------------------------------------------------
// CMmsReceiveMessage::CreateFolderEntryL
//
// -----------------------------------------------------------------------------
//
TInt CMmsReceiveMessage::CreateFolderEntryL(
    TMsvId aParentFolder, const TDesC& aFolderName, TMsvId& aFolderId )
    {
    
    // Check if the folder already exists under parent folder.
    TInt error = FolderEntryL( aParentFolder, aFolderName, aFolderId );    
    if ( aFolderId != KMsvNullIndexEntryId || error != KErrNone )
        {
        // if error == KErrNone and aFolderId not equal to KMsvNullIndexEntryId
        // the folder already exists, and we don't need to create anything
        return error;   
        }
     
    CMsvServerEntry* serverEntry = NULL;
    TRAP( error, serverEntry = iServerEntry->NewEntryL( aParentFolder ) );
    CleanupStack::PushL( serverEntry );
    
    if ( error != KErrNone )
        {
        CleanupStack::PopAndDestroy( serverEntry );
        return error;
        }
          
    // Create a new folder.
    error = serverEntry->SetEntry( aParentFolder );
    
    if ( error == KErrNone )
	    {
        TMsvEntry entry;
	    entry.iType = KUidMsvFolderEntry;
	    entry.iMtm = KUidMsvLocalServiceMtm;
	    entry.iDetails.Set( aFolderName );
	    entry.SetVisible( EFalse );
	    entry.SetInPreparation( EFalse );
	    entry.iServiceId = KMsvLocalServiceIndexEntryId;
	    error = serverEntry->CreateEntry( entry );    
	    aFolderId = entry.Id();    
	    }
	CleanupStack::PopAndDestroy( serverEntry );
    serverEntry = NULL;    
	return error;   
       
    }

    
// ---------------------------------------------------------
// CMmsReceiveMessage::FindDuplicateNotificationL
// 
// ---------------------------------------------------------
//
TInt CMmsReceiveMessage::FindDuplicateNotificationL(
    TMsvId aParent, CMmsHeaders& aHeaders, TMsvId& aDuplicate )
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsReceiveMessage::FindDuplicateNotificationL") );
#endif
    
    aDuplicate = KMsvNullIndexEntryId;
 
    if ( aParent == KMsvNullIndexEntryId )
        {
        return KErrNotSupported;
        }

    // Get a new entry, dont mess up with our original entry
    TInt error = KErrNone;

    CMsvServerEntry* workingEntry = NULL;
    TRAP( error, workingEntry = iServerEntry->NewEntryL( aParent ));
    CleanupStack::PushL( workingEntry );
    
    if ( error != KErrNone )
        {
        CleanupStack::PopAndDestroy( workingEntry );
        return error;
        }

    CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection; 
    CleanupStack::PushL( selection );

    error = workingEntry->GetChildrenWithMtm( KUidMsgMMSNotification, *selection );

    TInt count = selection->Count();
    if ( count == 0 )
        {
        error = KErrNotSupported;
        }

    if ( error != KErrNone  )
        {
        CleanupStack::PopAndDestroy( selection );
        CleanupStack::PopAndDestroy( workingEntry );
        return error;
        }

    CMmsHeaders* mmsHeaders = CMmsHeaders::NewL( iMmsSettings->MmsVersion() );
    CleanupStack::PushL( mmsHeaders );
     
    for ( TInt i = count; i > 0 && ( aDuplicate == KMsvNullIndexEntryId ); i-- )
        {
        error = workingEntry->SetEntry( selection->At( i - 1 ) );
        if ( error == KErrNone )
            {            
            CMsvStore* store = workingEntry->ReadStoreL();
            CleanupStack::PushL( store );
            mmsHeaders->RestoreL( *store );
            CleanupStack::PopAndDestroy( store );

            // content location must match 
            if ( mmsHeaders->ContentLocation().Compare( aHeaders.ContentLocation() ) == 0 )
                {
                // Identical. This probably means that we have not sent a response yet,
                // and MMSC has sent us a new notification.

#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("- content locations match") );
#endif
                TMsvEntry entry = workingEntry->Entry();
                aDuplicate = entry.Id();
                }
            }
        }

    CleanupStack::PopAndDestroy( mmsHeaders );
    CleanupStack::PopAndDestroy( selection );
    CleanupStack::PopAndDestroy( workingEntry );

    return error;
    }    
    
// ---------------------------------------------------------
// CMmsReceiveMessage::LocalModeFetchL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::LocalModeFetchL()
    {
    
    HBufC* buffer = HBufC::NewL( KMaxFileName );
    CleanupStack::PushL( buffer );
    if ( iNotification->ContentLocation().Length() <= KMaxPath && 
        iMmsSettings->LocalMode() )
        {
        buffer->Des().Copy( iNotification->ContentLocation() );
        }
    TPtr temp = buffer->Des();
    #ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("...Fetching from : %S"), &temp );
    #endif

    HBufC* filename = HBufC::NewL( iMmsSettings->LocalModeIn().Length() );
    CleanupStack::PushL( filename );
    TPtr fileNamePtr = filename->Des();
    fileNamePtr.Copy( iMmsSettings->LocalModeIn() );
            
    if ( iNotification->ContentLocation().Length() <= KMaxPath &&
        buffer->Des().FindF( fileNamePtr ) == 0 )
        {
        // The file reading code is for local mode (simulated MMSC)
        // Only one can try to get the file at a time
        #ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("opening file") );
        #endif
        iError = iFile.Open( iFs, temp, EFileShareExclusive );

        if ( iError == KErrNone )
            {
            #ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("file opened") );
            #endif
            iFileOpen = ETrue;
            TInt size = 0;
            iError = iFile.Size( size );
            
            // Here we try to use chunked buffer.
            // Read the message in chunks and keep calling decode
            // Before we can do that, we must create the message entry
            // Because it must be ready to receive data
            // If receiving fails, the entry must be removed
            
            DoCreateEntryL();
            iDecoder->InitializeChunkedMode( *iEntryWrapper, *iMmsHeaders, *iEncodeBuffer );
            TInt chunkSize = KMmsChunkedBufferSize;
            if ( iError == KErrNone )
                {
                TRAP( iError, iEncodeBuffer->ResizeL( chunkSize ) );
                }
            if ( iError != KErrNone )
                {
#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("Receivemsg can't resize buffer, error %d"), iError );
                TMmsLogger::Log( _L("- message size %d"), chunkSize );
#endif
                iFile.Close();
                iFileOpen = EFalse;
                iDecoder->RelaseDataSink();
                CleanupStack::PopAndDestroy( filename );
                CleanupStack::PopAndDestroy( buffer );
                return;
                }
                
            // We now have an open file.
            // We read it in chunks and feed it to decoder piecewise
            TPtr8 pos = iEncodeBuffer->Ptr( 0 );
            
            TInt position = 0;
            TInt readData = 0;
            TInt amount = 0;
            TBool lastChunk = EFalse;
            TInt error = KErrNone;
            
            while ( readData < size && error == KErrNone )
                {
                chunkSize = KMmsChunkedBufferSize;
                const TInt KMmsTwo = 2;
                if ( chunkSize - amount < KMmsChunkedBufferSize / KMmsTwo )
                    {
                    // if almost all data has been left to buffer, increase buffer size
                    chunkSize = amount + KMmsChunkedBufferSize / KMmsTwo;
                    }
                iEncodeBuffer->ResizeL( chunkSize );
                pos.Set( iEncodeBuffer->Ptr( amount ) );
                pos.SetLength( chunkSize - amount );
                error = iFile.Read( pos, chunkSize - amount );
                amount += pos.Length();
                readData += pos.Length();
                if ( readData >= size )
                    {
                    lastChunk = ETrue;
                    }
                if ( error == KErrNone )
                    {
                    // this is how much data we have
                    iEncodeBuffer->ResizeL( amount ); 
                    error = iDecoder->NextDataPart( *iEncodeBuffer, position, lastChunk );
                    }
                if ( position > iEncodeBuffer->Size() )
                    {
                    position = iEncodeBuffer->Size();
                    }
                // Check if we can continue and shift the buffer if needed
                if ( error == KErrNone )
                    {
                    // now position points to the beginning of unhandled data
                    // it must be shifted to the beginning
                    amount -= position; // this much left unhandled
                    if ( amount < 0 )
                        {
                        amount = 0;
                        }
                    iEncodeBuffer->Delete( 0, position );
                    // start from the beginning the next time
                    position = 0;
                    }
                }
            if ( error != KErrNone )
                {
                iError = error;
                }
            // when the loop ends, all data should be handled and all attachments should be created
            // Decode has also saved message headers.
            // The changed state indicates what is complete
            iDecoder->RelaseDataSink();
            iState = EMmsOperationDecodingResponse; 
            }
        else
            {
            // This corresponds to the situation, where the message
            // has expired or for some other reason is no longer available.
            // We lie to RunL that we have already decoded the message
            iFile.Close();
            iFileOpen = EFalse;
            iState = EMmsOperationDecodingResponse;
            if ( iError != KErrInUse )
                {
                iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
                }
            }
        // We don't close the file yet.
        // We want to keep it exclusively to ourselves until we are done with it
        }
    else
        {
        // No directory found in notification
        // this notification cannot be handled.
        // It will be marked bad and deleted.
        iError = KErrNotFound;
        iState = EMmsOperationDecodingResponse;
        iBad->AppendL( iSelection->At( iCurrentMessageNo - 1 ) );
        }
    CleanupStack::PopAndDestroy( filename );
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::DumpIfNeededL
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::DumpIfNeededL()
    {
#ifndef _NO_MMSS_LOGGING_
    TInt error = KErrNone;
    TBool dumpIncoming = EFalse;
    //
    // Reading binary dumping from CenRep
    //
    CRepository* repository = NULL;
    TInt retval = KErrNone;
    TRAP( retval, repository = CRepository::NewL( KUidMmsServerMtm ) ); // ***
    if( retval == KErrNone )
        {
        CleanupStack::PushL( repository );
        TInt temp = 0;
        retval = repository->Get( KMmsEngineBinaryDump, temp );
        if( retval == KErrNone )
            {
            dumpIncoming = (TBool)temp;
            }
        CleanupStack::PopAndDestroy( repository );
        }    

    TMmsLogger::Log( _L("Receivemsg Dumping Entry") );
    // If the result was bad, we dump the binary data into file
    // - unless decode already did it.
    if ( ( ( !dumpIncoming ) && iError != KErrNone ) &&
        iEncodeBuffer &&
        iEncodeBuffer->Size() > 0 )
        {
        TParse parse;
        TFileName fileName;
        fileName.Copy( KMmsDefaultLogDirectory );
        TUint att;
        if ( iFs.Att( fileName, att ) == KErrNone )
            {
            _LIT( KRelated, "Rec.mms");
            parse.Set( fileName, &KRelated, NULL );
            fileName = parse.FullName();
            error = CApaApplication::GenerateFileName( iFs, fileName );
            if ( error == KErrNone )
                {
                RFile file;
                error = file.Create( iFs, fileName, EFileWrite | EFileShareExclusive );
                // for message id generation
                parse.Set( fileName, NULL, NULL );

                if ( error == KErrNone )
                    {
                    // the data is supposed to be in the encode buffer
                    TPtr8 ptr = iEncodeBuffer->Ptr( 0 );
                    file.Write( ptr );
                    file.Flush();
                    }

                // done - close files
                file.Close();
                }
            }
        }
#endif
    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::CloseLocalFile
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::CloseLocalFileL( TBool aDeleteFile )
    {
    HBufC* buffer = HBufC::NewL( KMaxFileName );
    CleanupStack::PushL( buffer );
    buffer->Des().Copy( iNotification->ContentLocation() );
    HBufC* filename = HBufC::NewL( iMmsSettings->LocalModeIn().Length() );
    CleanupStack::PushL( filename );
    TPtr fileNamePtr = filename->Des();
    fileNamePtr.Copy( iMmsSettings->LocalModeIn() );
    if ( buffer->Des().FindF( fileNamePtr ) == 0 )
        {
        // this was our local message file
        iFile.Close();
        iFileOpen = EFalse;
        if ( aDeleteFile )
            {
            iFs.Delete( *buffer );
            }
        }
    CleanupStack::PopAndDestroy( filename );
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
// CMmsReceiveMessage::MoveToApplicationFolderIfNeededL
//
// ---------------------------------------------------------
//
TInt CMmsReceiveMessage::MoveToApplicationFolderIfNeededL( TMsvEntry& aEntry )
    {
    TInt error = KErrNone;
    TInt returnCode = 0;
    
    if ( iMmsHeaders->ApplicId().Length() > 0 )
        {
        // check if application id is registered
        if ( iRegistered )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- message to registered application") );
#endif
            TMsvId targetFolder = KMsvNullIndexEntryId;
            // target folder's parent is application folder
            TMsvId applicationFolder = iMmsSettings->ApplicationFolder();
            // code scanner gives false positive from the next line
            error = CreateFolderEntryL( applicationFolder, iMmsHeaders->ApplicId(), targetFolder );
            if ( targetFolder != KMsvNullIndexEntryId && error == KErrNone )
                {
                error = iServerEntry->SetEntry( aEntry.Parent() );
                if ( error == KErrNone )
	                {
	                error = iServerEntry->MoveEntryWithinService( aEntry.Id(), targetFolder );     	
	                }
	            if ( error == KErrNone )
	                {
	                // Message was successfully moved to application folder
	                // If we want to keep the notification in inbox we return 
                    // KMmsMessageMovedToApplicationFolder.
                    // As long as we serve only Java applications, the notifications
                    // will disappear from inbox if the message was moved to application folder.
                    // Java will start the applet if it is not running, and that should
                    // be sufficient information for the user.
#ifndef _NO_MMSS_LOGGING_
                    TMmsLogger::Log( _L("- moved to application folder") );
#endif
	                returnCode = 0;
	                }
	            // If we cannot set our entry back to the one we are handling,
	            // we are in deep trouble    
	            User::LeaveIfError( iServerEntry->SetEntry( iEntryUnderConstruction ));
                }                      
            }
        else
            {
            // There was an application id, but the application was not registered
            // Message will be deleted
            // If deletion fails, the message remains invisible and under construction
            // and will be deleted by message server at next boot.
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- message to unregistered application - deleted") );
#endif
            error = iServerEntry->SetEntry( aEntry.Parent() );
            if ( error == KErrNone )
                {
                iServerEntry->DeleteEntry( aEntry.Id() );
                }
            returnCode = KMmsMessageForUnregisteredApplication;
            }
        }
    // If no application id, the message is normal    
    return returnCode;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::ClearDuplicateEntryOperationL()
    {
    CMsvStore* store = iServerEntry->ReadStoreL();
    CleanupStack::PushL( store );
    iMmsHeaders->RestoreL( *store );
    CleanupStack::PopAndDestroy( store );
    TMsvId duplicate = KMsvNullIndexEntryId;
    TMsvId mmboxFolder = iMmsSettings->MMBoxFolder();
    TMsvEntry dupEntry;

    if ( iNotificationParent == KMsvGlobalInBoxIndexEntryIdValue )
        {
        // if duplicate is found from mmbox folder, 
        // mark it as "deleted from mmbox server"
        if ( mmboxFolder != KMsvNullIndexEntryId )
            {
            FindDuplicateNotificationL( mmboxFolder, *iMmsHeaders, duplicate );
            }
        if ( duplicate != KMsvNullIndexEntryId )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- duplicate found ") );
#endif
            if ( iServerEntry->SetEntry( duplicate ) == KErrNone )
                {
                // Mark duplicate as deleted from mmbox server.
                // If it was successfully fetched it is no longer in MMBox.
                // This is not exactly correct for full MMBox support but as long as
                // we don't have full MMBox support this works correctly
                dupEntry = iServerEntry->Entry();
                MarkNotificationDeletedFromMmbox( dupEntry );
                iServerEntry->ChangeEntry( dupEntry );
#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("- duplicate marked as deleted from mmbox ") );
#endif
                }                                         
            }    
        }
    else if ( iNotificationParent == mmboxFolder )
        {
        duplicate = iMmsHeaders->RelatedEntry();                                
        if ( duplicate != KMsvNullIndexEntryId )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- duplicate found ") );
#endif
            if ( iServerEntry->SetEntry( duplicate ) == KErrNone )
                {
                // Mark it deleted just in case the actual deletion fails.
                // This is not exactly correct for full MMBox support but as long as
                // we don't have full MMBox support this works correctly
                dupEntry = iServerEntry->Entry();
                MarkNotificationDeletedFromMmbox( dupEntry );
                iServerEntry->ChangeEntry( dupEntry );
                // delete the duplicate notification from inbox 
                if ( iServerEntry->SetEntry( KMsvGlobalInBoxIndexEntryIdValue )
                    == KErrNone )
                    {
                    TInt err = iServerEntry->DeleteEntry( duplicate );
#ifndef _NO_MMSS_LOGGING_
                    if ( err == KErrNone )
                        {
                        TMmsLogger::Log( _L("- duplicate deleted from inbox ") );
                        }
#endif
                    }
                }
            }
        }
    else
        {
        // nothing to do - just keep compiler and code analyzer tools happy
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::ClearOperationL( TInt aApplicationMessageStatus )
    {
    if ( iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) ) == KErrNone )
        {
        if ( aApplicationMessageStatus != 0 )
            {
            // The entry is not deleted if it is in inbox or MMBox
            // and has been routed to application.
            // We save the error code or routing status to the entry.
            TMsvEntry tEntry = iServerEntry->Entry();
            MarkNotificationDeletedFromMmbox( tEntry );
            tEntry.SetConnected( EFalse );
            tEntry.SetReadOnly( ETrue );

            if ( aApplicationMessageStatus == KMmsMessageMovedToApplicationFolder )
                {
                // This is a successful case
                tEntry.iMtmData2 |= KMmsMessageRoutedToApplication;
                }
            if ( aApplicationMessageStatus ==
                KMmsMessageForUnregisteredApplication )
                {
                // This will be marked as failed operation
                // even if the message was actually successfully fetched
                // but it was discarded because it does not belong to anybody
                tEntry.iError = KMmsErrorUnregisteredApplication;
                // The earlier operation cleared the possible MMBox bit
                // This operation marks the result as failed.
                MarkNotificationOperationFailed( tEntry );
                }
                
            // error status not checked here - we just do our best    
            iServerEntry->ChangeEntry( tEntry );    
            }
        ClearDuplicateEntryOperationL();
        }
    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::CopyDataFromNotificationToMessageL()
    {
    // Only subject and sender take up space when headers are added
    TInt size = iNotification->Sender().Length() +
                iNotification->Subject().Length();
    if ( size > 0 &&
        TMmsGenUtils::DiskSpaceBelowCriticalLevelL( &iFs, size, iMessageDrive ) )
        {
        size = 0;
        }
     
    // We copy data from notification to message if the values are missing
    // But if the message is already complete (status == OK)
    // we do not copy anything.
    if ( iMmsHeaders->ResponseStatus() > KMmsResponseStatusOK )
        {
        // The critical disk space is checked only when adding the subject.
        // The sender number is important and only takes a small amount of space
        // The other fields take the same amount of space regardless of the value.
        // As the headers have been saved already, only subject may consume
        // significantly more disk space.
        if ( size > 0 )
            {
            if ( iMmsHeaders->Subject().Length() == 0 )
                {
                iMmsHeaders->SetSubjectL( iNotification->Subject() );
                }
            }
        if ( iMmsHeaders->Sender().Length() == 0 )
            {
            iMmsHeaders->SetSenderL( iNotification->Sender() );
            }
        if ( iMmsHeaders->MessageClass() == 0 )
            {
            iMmsHeaders->SetMessageClass( iNotification->MessageClass() );
            }
        if ( iMmsHeaders->MessagePriority() == 0 )
            {
            iMmsHeaders->SetMessagePriority( iNotification->MessagePriority() );
            }
        // Copy the expiry, too
        if ( iMmsHeaders->ExpiryDate() == 0 && iMmsHeaders->ExpiryInterval() == 0 )
            {
            // No expiration data in the message - use what is in the notification
            iMmsHeaders->SetExpiryDate( iNotification->ExpiryDate() );
            }
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::CopyDataFromMessageToNotificationL()
    {
    TBool saveNotification = EFalse;
    TInt error = KErrNone;
    
    iNotification->SetResponseTextL( iMmsHeaders->ResponseText() );
    iNotification->SetResponseStatus( iMmsHeaders->ResponseStatus() );

    // Notifications do not originally contain any response texts.
    // If the length is not 0, we have added something
    if ( iNotification->ResponseText().Length() > 0 || iNotification->ResponseStatus() != 0 )
        {
        saveNotification = ETrue;
        }
        
    if ( iNotification->ApplicId().Length() == 0 )
        {
        iNotification->SetApplicIdL( iMmsHeaders->ApplicId() );
        if ( iNotification->ApplicId().Length() > 0 )
            {
            // If we copied the application id, we must save it.
            saveNotification = ETrue;
            }
        }
       
    // update the notification
    if ( saveNotification )
        {
        //  We update the notification if we added something
        error = iServerEntry->SetEntry( iSelection->At( iCurrentMessageNo - 1 ) );
        if ( error == KErrNone )
            {
            TRAP( error,
                {
                CMsvStore* store = iServerEntry->EditStoreL();
                CleanupStack::PushL( store );
                iNotification->StoreL( *store );
                store->CommitL();
                CleanupStack::PopAndDestroy( store );
                })
            }
        // If we cannot update the entry, the user does not see the error text, but that's not fatal
        }
    // Release the entry
    iServerEntry->SetEntry( KMsvNullIndexEntryId );
    
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsReceiveMessage::MapErrorStatus( const TInt32 aErrorStatus )
    {
    TBool fatal = EFalse;
    
    switch ( aErrorStatus )
        {
        case 0: // if the header is not present, it is 0
        case KMmsStatusOk:
            // No error, don't change iError
            break;
        case KMmsErrorPermanentFailure:
            fatal = ETrue;
            iError = KMmsErrorStatusPermanentFailure;
            break;
        case KMmsErrorTransientFailure:
            iError = KMmsErrorStatusTransientFailure;
            break;
        case KMmsErrorReceivePermanentMessageNotFound:
            fatal = ETrue;
            iError = KMmsErrorStatusMessageNotFound;
            break;
        case KMmsErrorReceiveTransientMessageNotFound:
            iError = KMmsErrorStatusTransientMessageNotFound;
            break;
        case KMmsErrorReceiveTransientNetworkProblem:
            iError = KMmsErrorStatusNetworkProblem;
            break;
        case KMmsErrorPermanentServiceDenied:
            fatal = ETrue;
            iError = KMmsErrorStatusServiceDenied;
            break;
        case KMmsErrorReceivePermanentContentUnsupported:
            fatal = ETrue;
            iError = KMmsErrorStatusContentUnsupported;
            break;
        default:
            // Unknown error, if not transient, it is fatal
            if ( ( aErrorStatus & KMmsErrorRangeMask ) == KMmsErrorTransient )
                {
                iError = KMmsErrorStatusTransientFailure;
                }
            else
                {
                iError = KMmsErrorPermanentFailure;
                fatal = ETrue;
                }
            break;
        }
            
    return fatal;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::SetIndexEntryBitsForReceivedMessage( TMsvEntry& aEntry )
    {
    aEntry.iMtmData1 &= ~KMmsMessageTypeMask;
    // We override message type for now.
    // We get send requests instead of retrieve confirmations if in local mode
    aEntry.iMtmData1 |= KMmsMessageMRetrieveConf | KMmsMessageMobileTerminated;

    aEntry.SetVisible( ETrue );
    aEntry.SetComplete( ETrue );
    aEntry.SetInPreparation( EFalse );
    aEntry.SetReadOnly( ETrue );
    aEntry.iDate.UniversalTime(); // this is arrival time
    
    if ( iMmsSettings->ShowSentTime() )
        {
        // show the time the message was received by MMSC instead of 
        // the time the message was received by the terminal
        if ( iMmsHeaders->Date() > 0 )
            {
            // date is given as seconds from 1.1.1970, UTC time
            TTime time = TTime( KMmsYear1970String ) +
                TTimeIntervalMicroSeconds( iMmsHeaders->Date() * KMmsMillion );
            aEntry.iDate = time.Int64();
            }
        }
    // subject, sender and size are set in CMmsDecode
    // - except if we get an empty error message.
    // In that case the fields were copied from the notification,
    // and we set sender and subject here.
    if ( aEntry.iDetails.Length() == 0 )
        {
        aEntry.iDetails.Set( iMmsHeaders->Sender() );
        }
    if ( aEntry.iDescription.Length() == 0 )  
        {
        aEntry.iDescription.Set( iMmsHeaders->Subject() );
        }

    // Set multiple recipients
    if ( iMmsHeaders->ToRecipients().MdcaCount() +
        iMmsHeaders->CcRecipients().MdcaCount() +
        iMmsHeaders->BccRecipients().MdcaCount() > 1 )
        {
        aEntry.SetMultipleRecipients( ETrue );
        }
    }
    
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsReceiveMessage::DeleteApplicationMessagesL()
    {
    // target folder's parent is application folder
    TMsvId messageFolder = KMsvNullIndexEntryId;
    TMsvId applicationFolder = iMmsSettings->ApplicationFolder();
    // code scanner gives false positive from the next line
    TInt error = KErrNone;
    error = FolderEntryL( applicationFolder, iMmsHeaders->ApplicId(), messageFolder );
   
    if ( messageFolder == KMsvNullIndexEntryId || error != KErrNone )
        {
        // We could not find the folder - we cannot free space
        return;
        }
        
    // List all messages in the folder and try to see if we can free enough room.
    TInt size = iNotification->MessageSize();
    if ( size > iMmsSettings->MaximumReceiveSize() )
        {
        // We cannot fetch more than this anyway
        size = iMmsSettings->MaximumReceiveSize();
        }
    // The safety margin must be added to the indicated size as the size in the
    // notification only specifies the payload, not all headers and space taken
    // by message entry and folder entry needed.
    size += KMmsDiskSafetyMargin;
    
    error = iServerEntry->SetEntry( messageFolder );
    if ( error != KErrNone )
        {
        // no can do
        return;
        }
    
    // sort entries by date
    TMsvSelectionOrdering ordering =
        TMsvSelectionOrdering( KMsvNoGrouping, EMsvSortByDate, EFalse );
    iServerEntry->SetSort( ordering );
    
    CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection;
    CleanupStack::PushL( selection );
    error = iServerEntry->GetChildrenWithType( KUidMsvMessageEntry, *selection );
    if ( error != KErrNone )
        {
        // no can do
        iServerEntry->SetEntry( KMsvNullIndexEntryId );
        CleanupStack::PopAndDestroy( selection );
        }
   
    TInt i;
    TInt deleteCount = 0; // entries to be deleted to free space
    TInt oldSize = 0;
    for ( i = 0; i < selection->Count() && deleteCount == 0; i++ )
        {
        error = iServerEntry->SetEntry( selection->At( i ) );
        if ( error == KErrNone )
            {
            // If we could not access some entry, it will end up in the 
            // delete list anyway. It may or may not be deleted.
            oldSize += iServerEntry->Entry().iSize;
            }
        if ( oldSize >= size )
            {
            deleteCount = i + 1;
            }
        }
        
    // Check if we fond enough old messages to free space.
    if ( deleteCount > 0 )
        {
        for ( i = selection->Count(); i > deleteCount; i-- )
            {
            // We may be able to leave some entries
            selection->Delete( i - 1 );
            }
        
        error = iServerEntry->SetEntry( messageFolder );
        if ( error == KErrNone )
            {
            error = iServerEntry->DeleteEntries( *selection );
            }
        // If we get an error because some entry is locked or open
        // the original KErrNoDisk remains and no entries are deleted
        // The situation will follow normal low disk space handling
        // If the entries are being accessed by the application, the
        // space may be freed soon by the application.
        if ( error == KErrNone )
            {
            // We have successfully deleted enough entries
            // The new message should now fit.
            // It can be rescheduled or fetch can be manually restarted
            iError = KMmsErrorApplicationDiskFull;
            }
        }
    iServerEntry->SetEntry( KMsvNullIndexEntryId );
    CleanupStack::PopAndDestroy( selection );    
    }
    

// ================= OTHER EXPORTED FUNCTIONS ==============

//  End of File