mmsengine/mmsmessage/src/mmsmessageoperation.cpp
changeset 0 72b543305e3a
child 23 238255e8b033
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmsmessage/src/mmsmessageoperation.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1025 @@
+/*
+* 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:  
+*      CMsvOperation class for MMS message creation.
+*
+*/
+
+
+
+// INCLUDE FILES
+#include    <msvids.h>
+#include    <msvstore.h>
+#include    <mtmdef.h>
+#include    <mtmuids.h> 
+#include    <mmsvattachmentmanager.h>
+#include    <cmsvattachment.h>
+#include    <cmsvmimeheaders.h>
+
+#include    "mmsmessageoperation.h"
+#include    "mmsconst.h"
+#include    "mmssettings.h"
+#include    "mmsheaders.h"
+#include    "mmsgenutils.h"
+#include    "mmsownnumber.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES  
+extern void gPanic( TMmsPanic aPanic );
+
+// CONSTANTS
+const TInt KMmsRecipientGranularity = 6;
+
+// 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.
+//
+
+CMmsMessageOperation::~CMmsMessageOperation()
+    {
+    Cancel();
+    delete iSourceStore;
+    delete iTargetStore;
+    delete iSourceEntry;
+    delete iTargetEntry;
+    delete iMsvOperation;
+    delete iSettings;
+    delete iNewMmsHeaders;
+    delete iDescription;
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CreateNewL
+//
+// ---------------------------------------------------------
+EXPORT_C CMmsMessageOperation* CMmsMessageOperation::CreateNewL(
+    TRequestStatus& aObserverRequestStatus,
+    CMsvSession& aMsvSession,
+    TMsvId aDestination,
+    TMsvId aServiceId,
+    TBool  aVisible,
+    TBool  aInPreparation )
+    {
+
+    TMsvPartList parts = 0;
+
+    CMmsMessageOperation* self = new(ELeave) CMmsMessageOperation(
+        aObserverRequestStatus,
+        aMsvSession,
+        aDestination,
+        parts,
+        aServiceId,
+        aVisible,
+        aInPreparation );
+    CleanupStack::PushL( self );
+    self->ConstructL( KMsvNullIndexEntryId, ENew );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CreateReplyL
+//
+// ---------------------------------------------------------
+EXPORT_C CMmsMessageOperation* CMmsMessageOperation::CreateReplyL(
+    TRequestStatus& aObserverRequestStatus,
+    CMsvSession& aMsvSession,
+    TMsvId aMessageId,
+    TMsvId aDestination,
+    TMsvPartList aPartList,
+    TMsvId aServiceId,
+    TBool  aVisible,
+    TBool  aInPreparation )
+    {
+    if ( aMessageId == KMsvNullIndexEntryId )
+        {
+        // if no source cannot reply
+        User::Leave( KErrArgument );
+        }
+
+    // Set the attachment part flag off
+    TMsvPartList parts = aPartList & ~KMsvMessagePartAttachments;
+
+    CMmsMessageOperation* self = new(ELeave) CMmsMessageOperation(
+        aObserverRequestStatus,
+        aMsvSession,
+        aDestination,
+        parts,
+        aServiceId,
+        aVisible,
+        aInPreparation );
+    CleanupStack::PushL( self );
+    self->ConstructL( aMessageId, EReply );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CreateForwardL
+//
+// ---------------------------------------------------------
+EXPORT_C CMmsMessageOperation* CMmsMessageOperation::CreateForwardL(
+    TRequestStatus& aObserverRequestStatus,
+    CMsvSession& aMsvSession,
+    TMsvId aMessageId,
+    TMsvId aDestination,
+    TMsvPartList aPartList,
+    TMsvId aServiceId,
+    TBool  aVisible,
+    TBool  aInPreparation )
+    {
+
+    if ( aMessageId == KMsvNullIndexEntryId )
+        {
+        // if no source cannot forward
+        User::Leave( KErrArgument );
+        }
+
+    // Set the attachment part flag on
+    TMsvPartList parts = aPartList | KMsvMessagePartAttachments;
+
+    CMmsMessageOperation* self = new(ELeave) CMmsMessageOperation(
+        aObserverRequestStatus,
+        aMsvSession,
+        aDestination,
+        parts,
+        aServiceId,
+        aVisible,
+        aInPreparation );
+    CleanupStack::PushL( self );
+    self->ConstructL( aMessageId, EForward );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CreateForwardWithoutRetrievalL
+//
+// ---------------------------------------------------------
+//
+EXPORT_C TMsvId CMmsMessageOperation::CreateForwardWithoutRetrievalL( 
+    CMsvSession& aMsvSession,
+    const CMmsHeaders& aNotificationHeaders,
+    const TMsvId aRelatedEntry,
+    const TMsvId aDestination,
+    TMsvId aServiceId,
+    TBool  aVisible,
+    TBool  aInPreparation )
+    {
+    //
+    // Create entry
+    //
+    TMsvEntry entry;
+    CMsvEntry* cMsvEntry = CMsvEntry::NewL( 
+        aMsvSession, aDestination, TMsvSelectionOrdering() );
+    CleanupStack::PushL( cMsvEntry );
+    entry.iType = KUidMsvMessageEntry;
+    entry.iServiceId = aServiceId;
+    entry.iMtm = KUidMsgMMSNotification;
+    entry.iMtmData1 |= KMmsMessageForwardReq;
+    entry.iDate.UniversalTime();
+    entry.SetInPreparation( aInPreparation );    
+    entry.SetVisible( aVisible );
+    cMsvEntry->CreateL( entry );
+
+    //
+    // Get MMS settings
+    //
+    CMmsSettings* settings = CMmsSettings::NewL();
+    CleanupStack::PushL( settings );
+    settings->LoadSettingsL();
+    
+    //
+    // Create headers object
+    //
+    CMmsHeaders* headers = CMmsHeaders::NewL( settings->MmsVersion() );
+    CleanupStack::PushL( headers );
+    headers->SetSettings( settings );
+    
+    //
+    // Set ContentLocation to headers object
+    //
+    headers->SetContentLocationL( aNotificationHeaders.ContentLocation() );
+    headers->SetRelatedEntry( aRelatedEntry );
+    // Following entries are copied to the forward request on purpose
+    // even though they do not belong to the PDU sent to the network
+    headers->SetSubjectL( aNotificationHeaders.Subject() );
+    headers->SetMessageSize( aNotificationHeaders.MessageSize() );   
+    headers->SetMessageClass( aNotificationHeaders.MessageClass() );
+    headers->SetMessagePriority( aNotificationHeaders.MessagePriority() );
+    // Set Report Allowed according to settings
+    headers->SetReportAllowed( settings->DeliveryReportSendingAllowed() );
+
+    //
+    // Check diskspace before storing headers
+    //
+    RFs fs = aMsvSession.FileSession();
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
+        &fs, 
+        headers->Size(),
+        aMsvSession.CurrentDriveL() ) )
+        {
+        User::Leave( KErrDiskFull );
+        }
+
+    //
+    // Store headers to MessageStore
+    //
+    cMsvEntry->SetEntryL( entry.Id() );
+    CMsvStore* store = cMsvEntry->EditStoreL();
+    CleanupStack::PushL( store );   // ***
+    headers->StoreL( *store );
+    store->CommitL();
+
+    //
+    // Update and save index entry
+    //
+    entry.iSize = headers->Size();
+    cMsvEntry->ChangeL( entry );
+
+    CleanupStack::PopAndDestroy( store );
+    CleanupStack::PopAndDestroy( headers );
+    CleanupStack::PopAndDestroy( settings );
+    CleanupStack::PopAndDestroy( cMsvEntry );
+    return entry.Id();
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::ConstructL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::ConstructL( 
+            TMsvId aMessageId, 
+            TMmsOperation aOperation )
+    {
+
+    iOrigMessageId = aMessageId;
+    iOperation = aOperation;
+    iDataMember = KMsvNullIndexEntryId;
+    iState = ECreateMmsHeaders;
+    iAttachmentsSize = 0;
+    iDescription = NULL;
+    iOriginalRoot = 0;
+    iAttachmentCount = 0;
+    iNewRoot = -1; // not found yet
+
+    // Create new message entry object to work with
+    // iDestinationId is the target folder
+    iTargetEntry = CMsvEntry::NewL( 
+        iMsvSession, iDestinationId, TMsvSelectionOrdering() );
+    // Show invisible entries too.
+    iTargetEntry->SetSortTypeL( TMsvSelectionOrdering( 
+        KMsvNoGrouping, EMsvSortByNone, ETrue ));
+
+    if ( iOrigMessageId != KMsvNullIndexEntryId )
+        {
+        // Create entry for the original entry
+        iSourceEntry = CMsvEntry::NewL( 
+            iMsvSession, iOrigMessageId, TMsvSelectionOrdering() );
+        // Show invisible entries too.
+        iSourceEntry->SetSortTypeL( TMsvSelectionOrdering( 
+            KMsvNoGrouping, EMsvSortByNone, ETrue ));
+        // if we have an original entry, it will be read    
+        iSourceStore = iSourceEntry->ReadStoreL();    
+        }
+
+    iSettings = CMmsSettings::NewL();
+    
+    // Add this objet to the scheduler
+    CActiveScheduler::Add( this );
+
+    ChangeStateL();
+
+    iObserverRequestStatus = KRequestPending;
+
+    SetActive();
+
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::FinalProgress
+//
+// ---------------------------------------------------------
+//
+EXPORT_C const TDesC8& CMmsMessageOperation::FinalProgress()
+   {
+    __ASSERT_ALWAYS( !IsActive(), gPanic( EMmsActiveInFinalProgress ));
+    const TDesC8* progress = &KNullDesC8();
+    TRAPD( leave, {progress = &ProgressL();} );
+    __ASSERT_ALWAYS( leave == KErrNone, gPanic( EMmsFinalProgressFailed ));
+    return *progress;
+   }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::ProgressL
+//
+// ---------------------------------------------------------
+//
+const TDesC8& CMmsMessageOperation::ProgressL()
+   {
+   if (iState == EFinished )
+       {
+       iDataMember() = iNewMessageId;
+       }
+   return iDataMember;
+   }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::RunL
+//
+// ---------------------------------------------------------
+//
+void CMmsMessageOperation::RunL()
+    {
+
+    if (iStatus.Int() != KErrNone )
+        {
+        ErrorRecovery( iStatus.Int() );
+        return;
+        }
+
+    // Check the progress status in case that real progress is available.
+    // only entry creation returns correct progress info
+    if ( iState == ECreateMessageEntry ) 
+        {
+        TMsvLocalOperationProgress  progress = 
+            McliUtils::GetLocalProgressL( *iMsvOperation );
+        if ( progress.iError != KErrNone )
+            {
+            ErrorRecovery( progress.iError );
+            return;
+            }
+        }
+    if ( iState != EFinished )
+        {
+        TRAPD( error, SelectAndChangeToNextStateL() );
+        if ( error )
+            {
+            ErrorRecovery( error );
+            return;
+            }
+        else if ( iState != EFinished )
+            {
+            // Here we go again.
+            SetActive();
+            }
+        else
+            {
+            // keep LINT happy
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CMmsMessageOperation
+//
+// ---------------------------------------------------------
+CMmsMessageOperation::CMmsMessageOperation(
+    TRequestStatus& aObserverRequestStatus, 
+    CMsvSession& aMsvSession, 
+    TMsvId aDestination, 
+    TMsvPartList aPartList,
+    TMsvId aServiceId,
+    TBool aVisible,
+    TBool  aInPreparation )
+    : CMsvOperation( aMsvSession, EPriorityStandard, aObserverRequestStatus ),
+      iDestinationId( aDestination ),
+      iPartList( aPartList ),
+      iServiceId( aServiceId ),
+      iSettings( NULL ),
+      iNewMmsHeaders( NULL ),
+      iVisible( aVisible ),
+      iInPreparation ( aInPreparation )
+    {
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::ErrorRecovery
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::ErrorRecovery( TInt aError )
+    {
+    // Delete entry and store objects associated with the new entry.
+    // If an error is encountered, store is not committed, and the
+    // entry under construction is deleted.
+    // Deleting the message entry deletes all attachments that are
+    // stored in the message store. Linked attachments are not deleted.
+    delete iTargetStore;
+    iTargetStore = NULL;
+    delete iTargetEntry;
+    iTargetEntry = NULL;
+    // Remove the new message entry
+    if ( iNewMessageId != KMsvNullIndexEntryId )
+        {
+        iMsvSession.RemoveEntry( iNewMessageId );
+        }
+    delete iSourceStore;
+    iSourceStore = NULL;
+    delete iSourceEntry;
+    iSourceEntry = NULL;
+        
+    // complete the observer with error
+    TRequestStatus* status = &iObserverRequestStatus;
+    User::RequestComplete( status, aError );
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::ChangeStateL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::ChangeStateL()
+    {
+    switch (iState)
+        {
+        case ECreateMmsHeaders:
+            CreateNewMmsHeaderL();
+            break;
+        case ECreateMessageEntry:
+            CreateMessageEntryL();
+            break;
+        case ECopyAttachments:
+            CopyAttachmentsL();
+            // Attachments are copied one by one.
+            // We stay in ECopyAttachment state until all attachments have
+            // been handled.
+            iCurrentAttachment++;
+            break;
+        case EStoreMmsHeaders:
+            StoreMmsHeadersL();
+            break;
+        case ECompleteMessage:
+            CompleteMessageL();
+            break;
+        case EFinished:
+            {
+            TRequestStatus* status = &iObserverRequestStatus;
+            User::RequestComplete( status, KErrNone );
+            }
+            break;
+        default:
+            // We are never supposed to be here.
+            User::LeaveIfError( KErrNotSupported );
+            break;
+        }
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::RequestComplete
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::RequestComplete( TInt aError )
+    {
+    iStatus = KRequestPending;
+    TRequestStatus* status = &iStatus;
+    User::RequestComplete( status, aError );
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::SelectNextStateL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::SelectNextStateL()
+    {
+
+    switch (iState)
+        {
+        case ECreateMmsHeaders:
+            iState = ECreateMessageEntry;
+            break;
+        case ECreateMessageEntry:
+            {
+            // Get the id of new entry
+            TMsvLocalOperationProgress progress = 
+                McliUtils::GetLocalProgressL( *iMsvOperation );
+            iNewMessageId = progress.iId;
+            
+            iTargetEntry->SetEntryL( iNewMessageId );
+            iTargetStore = iTargetEntry->EditStoreL();
+            }
+            // Fall through on purpose.
+            // Attachment are copied before MMS headers are saved
+            // so that we can adjust the root attachment id.
+            // If the message has no attachments, we proceed directly
+            // to saving MMS headers.
+        case ECopyAttachments:
+            if ( iPartList & KMsvMessagePartAttachments &&
+                iCurrentAttachment < iAttachmentCount )
+                {
+                iState = ECopyAttachments;
+                }
+            else
+                {
+                iState = EStoreMmsHeaders;
+                }
+            break;
+        case EStoreMmsHeaders:
+            iState = ECompleteMessage;
+            break;
+        case ECompleteMessage:
+            iState = EFinished;
+            break;
+        default:
+            User::LeaveIfError( KErrNotSupported );
+        }
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::SelectAndChangeToNextStateL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::SelectAndChangeToNextStateL()
+    {
+    SelectNextStateL();
+    ChangeStateL();
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CreateNewMmsHeaderL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::CreateNewMmsHeaderL()
+    {
+    // Load settings
+    iSettings->LoadSettingsL();
+
+    // Create MMS headers object
+    CMmsHeaders* originalMmsHeaders = NULL;
+    delete iNewMmsHeaders;
+    iNewMmsHeaders = NULL;
+
+    if ( ( iOperation == EReply ) ||
+         ( iOperation == EForward ))
+        {
+        // Copy appropiate parts from original MMS headers
+        // We checked in the beginning that we have a source entry
+        originalMmsHeaders = CMmsHeaders::NewL( iSettings->MmsVersion() );
+        CleanupStack::PushL( originalMmsHeaders );     // ***
+        // Get the original message's MMS headers
+        originalMmsHeaders->RestoreL( *iSourceStore );
+        // Copy data
+        TBool isReply = iOperation == EReply ? ETrue : EFalse;
+        
+        iNewMmsHeaders = originalMmsHeaders->CopyL( iPartList, isReply, iMsvSession.FileSession() );
+        
+        // Remove own number from list if creating Reply
+        if ( iOperation == EReply )
+            {
+            CDesCArray* recipientList = new ( ELeave )CDesCArrayFlat( KMmsRecipientGranularity );
+            CleanupStack::PushL( recipientList );
+            TInt i;
+            for ( i = 0; i < iNewMmsHeaders->ToRecipients().MdcaCount(); i++ )
+                {
+                recipientList->AppendL(
+                    TMmsGenUtils::PureAddress( iNewMmsHeaders->ToRecipients().MdcaPoint( i ) ) );
+                }
+            for ( i = 0; i < iNewMmsHeaders->CcRecipients().MdcaCount(); i++ )
+                {
+                recipientList->AppendL(
+                    TMmsGenUtils::PureAddress( iNewMmsHeaders->CcRecipients().MdcaPoint( i ) ) );
+                }
+            for ( i = 0; i < iNewMmsHeaders->BccRecipients().MdcaCount(); i++ )
+                {
+                recipientList->AppendL(
+                    TMmsGenUtils::PureAddress( iNewMmsHeaders->BccRecipients().MdcaPoint( i ) ) );
+                }
+                
+            // Find own number
+            // don't remove if only one recipient in the list
+            if ( recipientList->MdcaCount() > 1 )
+                {
+                CMmsOwnNumber* ownNumberSearcher = 
+                    CMmsOwnNumber::NewL( &iMsvSession.FileSession() );
+                CleanupStack::PushL( ownNumberSearcher );
+                
+                TInt index = -1;
+                index = ownNumberSearcher->StartL( *recipientList );
+                
+                if ( index >= 0 )
+                    {
+                    // found something
+                    while ( iNewMmsHeaders->RemoveAddressee( recipientList->MdcaPoint( index ) ) )
+                        {
+                        }
+                    }
+                    
+                CleanupStack::PopAndDestroy( ownNumberSearcher );
+                }
+            CleanupStack::PopAndDestroy( recipientList );
+            }
+
+        // the root must always be retained if it is specified
+        if ( iPartList & KMsvMessagePartAttachments )
+            {
+            iOriginalRoot = originalMmsHeaders->MessageRoot();
+            iNewMmsHeaders->SetRootContentIdL( originalMmsHeaders->RootContentId() );
+            }
+        CleanupStack::PopAndDestroy( originalMmsHeaders );
+        }
+    else
+        {
+        // No original message available
+        iNewMmsHeaders = CMmsHeaders::NewL( iSettings->MmsVersion() );
+        }
+    // Reset MMS settings
+    iNewMmsHeaders->SetSettings( iSettings );
+
+    RequestComplete( KErrNone );
+
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CreateMessageEntryL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::CreateMessageEntryL()
+    {
+
+    // Create new message entry
+
+    // CREATE NEW INDEX ENTRY
+    TMsvEntry entry;
+    entry.iType = KUidMsvMessageEntry;
+    entry.iServiceId = iServiceId;
+    entry.iMtm = KUidMsgTypeMultimedia;
+    entry.iDate.UniversalTime();
+
+    // Set iMtmData extension flags
+    if ( iOperation == EForward )
+        {
+        entry.iMtmData1 |= KMmsMessageForwarded;
+        }
+
+    // Set iDetails
+    TPtrC to;
+    if ( iNewMmsHeaders->ToRecipients().Count() > 0 )
+        {
+        to.Set( iNewMmsHeaders->ToRecipients()[0] );
+        }
+    HBufC* buffer = HBufC::NewL( KMmsMaxDescription );
+    CleanupStack::PushL( buffer );
+    TPtr pBuffer = buffer->Des();
+
+    if ( TMmsGenUtils::IsValidMMSPhoneAddress( to, EFalse ) )
+        {
+        if ( TMmsGenUtils::GenerateDetails( to,
+            pBuffer, KMmsMaxDescription, iMsvSession.FileSession() ) == KErrNone )
+            {
+            entry.iDetails.Set( pBuffer );
+            }
+        else
+            {
+            entry.iDetails.Set( to );
+            }
+        }
+    else
+        {
+        entry.iDetails.Set( to );
+        }
+
+    // Set iDescription
+    entry.iDescription.Set( iNewMmsHeaders->Subject());
+    // if no subject, copy old description
+    if ( ( iPartList & KMsvMessagePartAttachments ) &&
+         ( iOrigMessageId != KMsvNullIndexEntryId ) &&
+         ( entry.iDescription.Length() == 0 ) )
+        {
+        // Copy the old description, no need to scan message files again ...
+        TPtrC pp;
+        pp.Set( iSourceEntry->Entry().iDescription );
+        delete iDescription;
+        iDescription = NULL;
+        iDescription = pp.AllocL();
+        entry.iDescription.Set( *iDescription );
+        }
+
+    // The following ones will be set in completion.
+    entry.SetInPreparation( ETrue );    
+    entry.SetVisible( EFalse );         
+    entry.iSize = 0;
+    
+    // Create the new message entry asynchronically.
+    delete iMsvOperation;
+    iMsvOperation = NULL;
+    iCurrentAttachment = 0;
+    
+    if ( iPartList & KMsvMessagePartAttachments )
+        {
+        // get number of attachments
+        MMsvAttachmentManager& sourceAttaMan = iSourceStore->AttachmentManagerL();
+        iAttachmentCount = sourceAttaMan.AttachmentCount();
+        }
+
+    // Check that sufficient disk space available
+    // for index entry
+    iTargetEntry->SetEntryL( iDestinationId );
+    RFs fs = iMsvSession.FileSession();
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( &fs, 
+        sizeof( TMsvEntry ) 
+        + entry.iDescription.Length() 
+        + entry.iDetails.Length(),
+        iMsvSession.CurrentDriveL() ) )
+            {
+            // we use standard error code here
+            User::Leave( KErrDiskFull );
+            }
+    // Target entry still points to destination folder
+    iMsvOperation = iTargetEntry->CreateL( entry, iStatus );
+
+    CleanupStack::PopAndDestroy( buffer );
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::StoreMmsHeadersL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::StoreMmsHeadersL()
+    {
+
+    // save the correct id for new root
+    if ( iPartList & KMsvMessagePartAttachments && 
+        iNewRoot != -1 )
+        {
+        MMsvAttachmentManager& targetAttaMan = iTargetStore->AttachmentManagerL();
+        CMsvAttachment* targetAtta = targetAttaMan.GetAttachmentInfoL( iNewRoot );
+        CleanupStack::PushL( targetAtta );
+        iNewMmsHeaders->SetMessageRoot( targetAtta->Id() );
+        CleanupStack::PopAndDestroy( targetAtta );
+        }
+    else
+        {
+        iNewMmsHeaders->SetRootContentIdL( TPtrC8() );
+        }
+        
+
+    // Check that sufficient disk space available
+    // for MMS headers stream
+    RFs fs = iMsvSession.FileSession();
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( &fs, 
+        iNewMmsHeaders->Size(),
+        iMsvSession.CurrentDriveL() ) )
+        {
+        // we use standard error code here
+        User::Leave( KErrDiskFull );
+        }
+
+    // Externalize MMS headers
+    iNewMmsHeaders->StoreL( *iTargetStore );
+    // This is the last stage in creating the new entry.
+    // Now the store is committed and deleted.
+    iTargetStore->CommitL();
+    delete iTargetStore;
+    iTargetStore = NULL;
+
+    // Free the memory
+    iMmsHeaderSize = iNewMmsHeaders->Size();
+    delete iNewMmsHeaders;
+    iNewMmsHeaders = NULL;
+    delete iDescription;
+    iDescription = NULL;
+
+    RequestComplete( KErrNone );
+
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CopyAttachmentsL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::CopyAttachmentsL()
+    {
+    
+    // Attachments must be copied one by one because of the asynchronous
+    // nature of the attachment manager functions.
+    
+    // We get here only if the message has attachments and the part list
+    // specifies that the attachments must be copied.
+
+    // Get next attachment
+    
+    MMsvAttachmentManager& sourceAttaMan = iSourceStore->AttachmentManagerL();
+    CMsvAttachment* sourceAtta = sourceAttaMan.GetAttachmentInfoL( iCurrentAttachment );
+    CleanupStack::PushL( sourceAtta );
+    CMsvMimeHeaders* mime = CMsvMimeHeaders::NewL();
+    CleanupStack::PushL( mime );
+    mime->RestoreL( *sourceAtta );
+    
+    delete iMsvOperation;
+    iMsvOperation = NULL;
+    
+    TInt size = 0;
+    // We need to check disk space for mime headers and file attachments
+    // Linked attachments are not copied, and do not take up space
+    
+    size += mime->Size(); // size of mime headers
+    if ( sourceAtta->Type() == CMsvAttachment::EMsvFile )
+        {
+        // Add file size only if the attachment file must be copied.
+        // Linked attachment files are not copied, just the link is added
+        size += sourceAtta->Size();
+        }
+    
+    RFs fs = iMsvSession.FileSession();
+    
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( &fs, 
+        size,
+        iMsvSession.CurrentDriveL() ) )
+        {
+        delete iTargetStore;
+        iTargetStore = NULL;
+        delete iTargetEntry;
+        iTargetEntry = NULL;
+        CleanupStack::PopAndDestroy( mime );
+        CleanupStack::PopAndDestroy( sourceAtta );
+        // we use standard error code here
+        User::Leave( KErrDiskFull );
+        }
+    
+    // Copy the attachment
+    
+    MMsvAttachmentManager& targetAttaMan = iTargetStore->AttachmentManagerL();
+    CMsvAttachment* targetAtta = NULL;
+    
+    HBufC8* mimeType = sourceAtta->MimeType().AllocL();
+    CleanupStack::PushL( mimeType );
+    TParse fParse;
+    User::LeaveIfError( fParse.Set( sourceAtta->FilePath(), NULL, NULL ) );
+    HBufC* attaName = fParse.NameAndExt().AllocL();
+    CleanupStack::PushL( attaName );
+    
+    targetAtta = CMsvAttachment::NewL(
+         sourceAtta->Type(),
+         sourceAtta->Size(),
+         mimeType,
+         attaName
+         );
+    CleanupStack::Pop( attaName ); // ownership of attaName transferred to targetAtta
+    CleanupStack::Pop( mimeType ); // ownership of mimeType transferred to targetAtta
+         
+    CleanupStack::PushL( targetAtta );
+    
+    mime->StoreL( *targetAtta );
+        
+    if ( sourceAtta->Type() == CMsvAttachment::EMsvFile )
+        {
+        RFile sourceFile = sourceAttaMan.GetAttachmentFileL( iCurrentAttachment );
+        CleanupClosePushL( sourceFile );
+        targetAttaMan.AddAttachmentL( sourceFile, 
+                                      targetAtta, 
+                                      iStatus );
+        CleanupStack::Pop( &sourceFile ); // close file
+        }
+    else // linked file
+        {
+        targetAttaMan.AddLinkedAttachmentL( sourceAtta->FilePath(), targetAtta, iStatus );
+        }
+        
+    if ( sourceAtta->Id() == iOriginalRoot )
+        {
+        iNewRoot = iCurrentAttachment; // save the index
+        }
+    
+    // Ownership of targetAtta was transferred to attachment manager
+    CleanupStack::Pop( targetAtta ); // targetAtta
+    CleanupStack::PopAndDestroy( mime );
+    CleanupStack::PopAndDestroy( sourceAtta );
+
+// RunL sets us active
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::CompleteMessageL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::CompleteMessageL()
+    {
+// As the attachments are not separate entries, iTargetEntry always points to
+// the new message entry. No need to set iTargetEntry to the new entry anymore.
+    TMsvEntry entry = iTargetEntry->Entry();
+    AttachmentsSizeL();
+    // iTargetStore is recreated in AttachmentSizeL(), we must kill it
+    // to prevent a crash if the user cancels the operation at the last minute.
+    // The actual problem is in Symbian attachment manager code, DoCancel() 
+    // should not do anything if the active object is not active (Symbian code does
+    // not check the state).
+    delete iTargetStore;
+    iTargetStore = NULL;
+    entry.iSize = ( iAttachmentsSize + iMmsHeaderSize );
+    entry.SetVisible( iVisible );
+    entry.SetInPreparation( iInPreparation );
+    entry.SetAttachment( iAttachmentsSize ? ETrue : EFalse );
+    
+    iTargetEntry->ChangeL( entry );
+
+    RequestComplete( KErrNone );
+
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::DoCancel
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::DoCancel()
+    {
+
+    // Cancel all the pending requests
+    if ( iMsvOperation )
+        {
+        iMsvOperation->Cancel();
+        }
+        
+    if ( iTargetStore )
+        {
+        // cancel attachment manager operations if any are active
+        TRAP_IGNORE( 
+            {
+            // This will leave only if there is no attachment manager
+            // Otherwise it will just return reference to the existing manager
+            MMsvAttachmentManager& attaMan = iTargetStore->AttachmentManagerL();
+            // If there is no attachment manager, there cannot be any ongoing request
+            // So the request needs to be cancelled only if the previous function
+            // did not leave
+            attaMan.CancelRequest();
+            });
+        delete iTargetStore;
+        iTargetStore = NULL;
+        }
+        
+    delete iTargetEntry;
+    iTargetEntry = NULL;
+    delete iSourceStore;
+    iSourceStore = NULL;
+    delete iSourceEntry;
+    iSourceEntry = NULL;
+                
+    // Remove the new message entry
+    if ( iNewMessageId != KMsvNullIndexEntryId )
+        {
+        iMsvSession.RemoveEntry( iNewMessageId );
+        }
+    iNewMessageId = KMsvNullIndexEntryId;
+
+    TRequestStatus* st = &iObserverRequestStatus;
+    User::RequestComplete( st, KErrCancel );
+    }
+
+// ---------------------------------------------------------
+// CMmsMessageOperation::AttachmentsSizeL
+//
+// ---------------------------------------------------------
+void CMmsMessageOperation::AttachmentsSizeL()
+    {
+    // We just scan the attachment entries and
+    // and calculate the total amount of size.
+
+    iAttachmentsSize = 0;
+    
+    if ( !iTargetStore )
+        {
+        iTargetStore = iTargetEntry->ReadStoreL();
+        }
+    MMsvAttachmentManager& targetAttaMan = iTargetStore->AttachmentManagerL();
+    
+    TInt counter = targetAttaMan.AttachmentCount();
+    
+    for ( TInt i = 0; i < counter; i++ )
+        {
+        CMsvAttachment* attaInfo = targetAttaMan.GetAttachmentInfoL( i );
+        CleanupStack::PushL( attaInfo );
+        iAttachmentsSize += attaInfo->Size();
+        CMsvMimeHeaders* mime = CMsvMimeHeaders::NewL();
+        CleanupStack::PushL( mime );
+        mime->RestoreL( *attaInfo );
+        iAttachmentsSize += mime->Size();
+        CleanupStack::PopAndDestroy( mime );
+        CleanupStack::PopAndDestroy( attaInfo );
+        }
+    }
+
+// ================= OTHER EXPORTED FUNCTIONS ==============
+
+//  End of File  
+