--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmsserver/src/mmsreceivemessage.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,3028 @@
+/*
+* 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
+