mobilemessaging/unieditor/mtm/src/UniClientMtm.cpp
changeset 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mobilemessaging/unieditor/mtm/src/UniClientMtm.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1427 @@
+/*
+* Copyright (c) 2005-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: UniClientMtm implementation
+*
+*/
+
+
+
+// INCLUDE FILES 
+#include <txtrich.h>
+#include <msvids.h>
+#include <badesca.h>
+#include <msvstore.h>
+#include <mtmuids.h> 
+#include <mtclbase.h>
+#include <mtmdef.h>
+#include <msvftext.h>       // CMsvFindText 
+#include <cmsvmimeheaders.h>
+#include <mmsvattachmentmanager.h>
+#include <mmsvattachmentmanagersync.h>
+
+#include <msgtextutils.h>
+#include <mmsgenutils.h>
+#include <mmsattachmentwaiter.h>
+#include <mmsattachmenthandler.h>
+
+#include "UniMsvEntry.h"
+#include "UniMtmPanic.h"
+#include "UniHeaders.h"
+#include "UniClientMtm.h"
+#include "UniEditorUids.hrh"
+#include "UniEditorLogging.h"
+
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------
+// Factory function
+// ---------------------------------------------------------
+// 
+EXPORT_C CUniClientMtm* CUniClientMtm::NewL(
+    CRegisteredMtmDll& aRegisteredMtmDll,
+    CMsvSession& aSession )
+    {
+    CUniClientMtm* self=new ( ELeave ) CUniClientMtm(
+        aRegisteredMtmDll, aSession );
+    
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------
+// Constructor
+// ---------------------------------------------------------
+// 
+CUniClientMtm::CUniClientMtm(
+    CRegisteredMtmDll& aRegisteredMtmDll,
+    CMsvSession& aSession )
+    : CBaseMtm( aRegisteredMtmDll, aSession ),
+    iUniHeaders ( NULL )
+    {
+    }
+
+// ---------------------------------------------------------
+// Destructor
+// ---------------------------------------------------------
+//     
+CUniClientMtm::~CUniClientMtm()
+    {
+    delete iAttaWaiter;
+    delete iUniHeaders;
+    delete iTextUtils;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::CreateNewEntryL
+// ---------------------------------------------------------
+//
+TMsvId CUniClientMtm::CreateNewEntryL( TMsvId aDestination )
+    {
+    UNILOGGER_WRITE_TIMESTAMP( "CUniClientMtm::CreateNewEntryL start" );
+    // Create new message entry
+    TMsvEntry entry;
+    entry.iType = KUidMsvMessageEntry;
+    entry.iServiceId = KMsvLocalServiceIndexEntryId;
+    entry.iMtm = TUid::Uid( KUidUniMtm );
+    entry.iDate.UniversalTime();
+
+    // The following ones will be set in completion.
+    entry.SetInPreparation( ETrue );    
+    entry.SetVisible( EFalse );         
+    entry.iSize = 0;
+            
+    // Check that sufficient disk space available
+    // for index entry
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
+            &Session().FileSession(), 
+            sizeof( TMsvEntry ),
+            Session().CurrentDriveL() ) )
+        {
+        // we use standard error code here
+        User::Leave( KErrDiskFull );
+        }
+
+    SwitchCurrentEntryL( aDestination );
+    iMsvEntry->CreateL( entry );
+    SwitchCurrentEntryL( entry.Id() );
+    
+    UNILOGGER_WRITE_TIMESTAMP( "CUniClientMtm::CreateNewEntryL end" );
+    
+    return entry.Id();
+    }
+
+//----------------------------------------------------------
+// METHODS FROM BASE CLASS
+//----------------------------------------------------------
+
+// ---------------------------------------------------------
+// CUniClientMtm::SaveMessageL
+// Stores the multimedia message
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SaveMessageL()
+    {
+    // First we should assert that iMsvEntry is not NULL, and panic, if it is
+    __ASSERT_DEBUG( iMsvEntry!=NULL, Panic( EUniNotAMessage ));
+    // SaveMessageL should only be supported for message entries.
+    __ASSERT_DEBUG( iMsvEntry->Entry().iType.iUid == KUidMsvMessageEntryValue,
+        Panic( EUniNotAMessage ) );
+
+    CMsvStore* store = iMsvEntry->EditStoreL();
+    CleanupStack::PushL( store );
+
+    TMsvEntry indexEntry = iMsvEntry->Entry();
+
+    SaveMessageL( *store, indexEntry ); 
+    
+    // Commit the stream store
+    store->CommitL();
+    CleanupStack::PopAndDestroy( store );
+
+    // commit the index changes.
+    iMsvEntry->ChangeL( indexEntry );
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::SaveMessageL
+// Stores the multimedia message
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SaveMessageL( CMsvStore& aEditStore, TMsvEntry& aEntry ) 
+    {
+    // Store headers of a message
+
+    // Because of attachments are handled using the new
+    // attacment manager, the caller must store and commit the attachments
+    // either one by one or after all have been added.
+    // After saving all attachments the edit store used for that purpose
+    // must be freed.
+    // The store must be freed because all attachment info and uni headers
+    // are saved in the actual message entry, there are no separate 
+    // attachment entries anymore.
+    
+    // Check that sufficient disk space available
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
+        &Session().FileSession(), 
+        iUniHeaders->Size(),
+        iMessageDrive ) )
+        {
+        User::Leave( KErrDiskFull );
+        }
+       
+    // Note: Body text not supported.
+    iUniHeaders->StoreL( aEditStore );
+    
+    // needed to convert unimessages to sms messages
+    StoreBodyL( aEditStore ); 
+    
+    // attachment size
+    TInt32 totalSizeOfAllAttachments = AttachmentsSizeL( aEditStore );
+    aEntry.iSize = iUniHeaders->Size() + totalSizeOfAllAttachments;
+
+    // If there are multiple recipients then set the flag
+    if (( iUniHeaders->ToRecipients().Count() + //lint !e115 !e1013 !e48 !e10 !e1055 !e746 !e628
+        iUniHeaders->CcRecipients().Count() +  //lint !e115 !e1013 !e48 !e10 !e1055 !e746 !e628
+        iUniHeaders->BccRecipients().Count() ) >= 2 ) //lint !e115 !e1013 !e48 !e10 !e1055 !e746 !e628
+        {
+        aEntry.SetMultipleRecipients( ETrue );
+        }
+    else
+        {
+        // clear multiple recipients in case recipients have
+        // been deleted after the message was saved the last time
+        aEntry.SetMultipleRecipients( EFalse );
+        }
+
+    // Set iDetails (recipient)
+    TPtrC to;
+    if ( iUniHeaders->ToRecipients().Count() ) //lint !e115 !e1013 !e48 !e10
+        {
+        to.Set( TMmsGenUtils::Alias( iUniHeaders->ToRecipients()[0] ) ); //lint !e115 !e1013 !e48 !e10 !e64
+        if ( to.Length() <= 0 )
+            {
+            // If no alias part then set the real address in details
+            to.Set( iUniHeaders->ToRecipients()[0] ); //lint !e115 !e1013 !e48 !e10 !e1025 !e1703 !e64 !e118
+            }
+        }
+    aEntry.iDetails.Set( to );
+    
+    if ( totalSizeOfAllAttachments > 0 )
+        {
+        aEntry.SetAttachment( ETrue );
+        }
+        
+    switch ( iUniHeaders->MessageTypeLocking() )
+        {
+        case EUniMessageTypeLocked:
+            TUniMsvEntry::SetMessageTypeLocked( aEntry, ETrue );
+            break;
+        case EUniMessageTypeNotLocked:
+            TUniMsvEntry::SetMessageTypeLocked( aEntry, EFalse );
+            break;
+        case EUniMessageTypeLockingNotSet:
+        default:
+            // Don't touch TMsvEntry.
+            break;
+        }
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::LoadMessageL
+// Loads the unified message
+// ---------------------------------------------------------
+//
+void CUniClientMtm::LoadMessageL()
+    {
+    // First we should assert that iMsvEntry is not NULL, and panic, if it is
+   	__ASSERT_DEBUG( iMsvEntry, Panic( EUniNotAMessage ) );
+    // LoadMessageL should only be supported for message entries.
+    __ASSERT_DEBUG( iMsvEntry->Entry().iType.iUid == KUidMsvMessageEntryValue,
+        Panic( EUniNotAMessage ) );
+
+    // Old data must be reset first....
+    // CMmsSettings resets itself before doing restore, so that's ok.
+
+    TMsvEntry indexEntry = iMsvEntry->Entry(); //lint !e1924
+    
+
+    // load the correct data
+    // get read-only message store
+    CMsvStore* store = iMsvEntry->ReadStoreL();
+    CleanupStack::PushL( store );
+
+    // Restore headers of unified message
+    // if headers do not exist does nothing. Default headers will be used.
+    iUniHeaders->RestoreL( *store );
+
+    if ( iUniHeaders->MessageTypeLocking() == EUniMessageTypeLockingNotSet &&
+        TUniMsvEntry::IsMessageTypeLocked( indexEntry ) )
+        {
+        switch ( TUniMsvEntry::CurrentMessageType( indexEntry ) )
+            {
+            case EUniMessageCurrentTypeSms:
+                {
+                iUniHeaders->SetMessageTypeSetting( EUniMessageTypeSettingSms );
+                iUniHeaders->SetMessageTypeLocking( EUniMessageTypeLocked );
+                break;
+                }
+            case EUniMessageCurrentTypeMms:
+                {
+                iUniHeaders->SetMessageTypeSetting( EUniMessageTypeSettingMms );
+                iUniHeaders->SetMessageTypeLocking( EUniMessageTypeLocked );
+                break;
+                }
+            default:
+                break;
+            }
+        }
+
+    // Attachment info is not restored.
+    // It makes no sense to cache the attachment info as new attachments
+    // can be added with the help of the attachment magager without 
+    // informing uni Client MTM of the additions.
+    // Caller must use attachment manager to get attachment info.
+    
+    CleanupStack::PopAndDestroy( store );  
+    store = NULL;
+
+    // Build the iAddresseeList up
+    BuildAddresseeListL();
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::ReplyL
+// Send a reply to current message
+// ---------------------------------------------------------
+//
+CMsvOperation* CUniClientMtm::ReplyL(
+    TMsvId /*aDestination*/,
+    TMsvPartList /*aPartList*/,
+    TRequestStatus& /*aCompletionStatus*/ )
+    {
+    User::Leave( KErrNotSupported );
+    return NULL;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::ForwardL
+// Forward current message
+// ---------------------------------------------------------
+//
+CMsvOperation* CUniClientMtm::ForwardL(
+    TMsvId /*aDestination*/,
+    TMsvPartList /*aPartList*/,
+    TRequestStatus& /*aCompletionStatus*/ )
+    {
+    User::Leave( KErrNotSupported );
+    return NULL;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::ValidateMessage
+// Validate selected parts of current message
+// ---------------------------------------------------------
+//
+TMsvPartList CUniClientMtm::ValidateMessage(
+    TMsvPartList aPartList )
+    {
+    __ASSERT_DEBUG( iMsvEntry, Panic( EUniNotAMessage ));
+    
+    TMsvPartList retVal = 0;
+    if ( iMsvEntry->Entry().iType.iUid != KUidMsvMessageEntryValue )
+        {
+        // not a message, no part is valid
+        retVal = aPartList;
+        }
+
+    if ( aPartList & KMsvMessagePartRecipient )
+        {
+        if ( iAddresseeList->Count() == 0)
+            {
+            retVal |= KMsvMessagePartRecipient;
+            }
+        else
+            {
+            // check the recipient list for valid 'addresses'
+            for (TInt ii=0; ii < iAddresseeList->Count(); ++ii)
+                {
+                TPtrC oneAddress = (*iAddresseeList)[ii];
+                TPtrC pureAddress = TMmsGenUtils::PureAddress( oneAddress );
+                if ( ( pureAddress.Length() == 0 ) || 
+                    !TMmsGenUtils::IsValidAddress( pureAddress, ETrue ) )
+                    {
+                    retVal |= KMsvMessagePartRecipient;
+                    break;
+                    }
+                }
+            }
+        }
+
+    // all attachments are considered valid - even no attachments
+
+    return retVal;
+
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::Find
+// Find text in selected message parts
+// ---------------------------------------------------------
+//
+TMsvPartList CUniClientMtm::Find(
+    const TDesC& aTextToFind,
+    TMsvPartList aPartList )
+	{
+	TMsvPartList foundList( KMsvMessagePartNone );
+	TRAP_IGNORE( foundList = DoFindL( aTextToFind, aPartList) );
+	return foundList;
+	}
+
+// ---------------------------------------------------------
+// CUniClientMtm::DoFindL
+// Find text in selected message parts
+// ---------------------------------------------------------
+//
+TMsvPartList CUniClientMtm::DoFindL(
+    const TDesC& aTextToFind,
+    TMsvPartList aPartList )
+    {
+    // The final version will not have a rich text body, but we could
+    // search for example the originator and description.
+
+    __ASSERT_DEBUG( iMsvEntry, Panic( EUniNotAMessage ) );
+    TMsvPartList foundList = KMsvMessagePartNone;
+    TMsvEntry entry = iMsvEntry->Entry();
+
+    CMsvFindText* findText = CMsvFindText::NewL();
+    CleanupStack::PushL( findText );
+
+    if ( aPartList & KMsvMessagePartRecipient )
+        {
+        // Find from To, Cc and Bcc fields
+        if ( FindInRecipientL( aTextToFind, 
+            aPartList, iUniHeaders->ToRecipients(), *findText))
+            {
+            foundList |= KMsvMessagePartRecipient;
+            }
+        else if ( FindInRecipientL( aTextToFind, 
+            aPartList, iUniHeaders->CcRecipients(), *findText ))
+            {
+            foundList |= KMsvMessagePartRecipient;
+            }
+        else if ( FindInRecipientL( aTextToFind, 
+            aPartList, iUniHeaders->BccRecipients(), *findText ))
+            {
+            foundList |= KMsvMessagePartRecipient;
+            }
+        }
+    
+    if ( aPartList & KMsvMessagePartDescription )
+        {
+        if ( findText->FindTextL( aTextToFind, entry.iDescription, 
+            aPartList ) )
+            {
+            foundList |= KMsvMessagePartDescription;
+            }
+        }
+
+    CleanupStack::PopAndDestroy( findText );
+    return foundList;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AddAddresseeL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddAddresseeL( const TDesC& aRealAddress )
+    {
+    // Add to general list
+    // When no type is specified, the address will have type "to"
+    iAddresseeList->AppendL( EMsvRecipientTo, aRealAddress );
+    
+    // Add to "To" recipient list
+    iUniHeaders->AddTypedAddresseeL( aRealAddress, EMsvRecipientTo ); //lint !e115 !e1013 !e48 !e10 !e1514 !e64
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AddAddresseeL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddAddresseeL(
+    const TDesC& aRealAddress,
+    const TDesC& aAlias )
+    {
+
+    if ( aAlias.Length() > 0 )
+        {
+        HBufC* buf = TMmsGenUtils::GenerateAddressL( aRealAddress, aAlias );
+        CleanupStack::PushL( buf );
+        AddAddresseeL( buf->Des() );
+        CleanupStack::PopAndDestroy( buf );  
+        }
+    else
+        {        
+        AddAddresseeL( aRealAddress );
+        }
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::AddAddresseeL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddAddresseeL( TMsvRecipientType aType,
+    const TDesC& aRealAddress )
+    {
+    // Add to general list
+    // When no type is specified, the address will have type "to",
+    // "cc" or "bcc"
+    iAddresseeList->AppendL( aType, aRealAddress );
+    
+    // Add to recipient list
+    iUniHeaders->AddTypedAddresseeL( aRealAddress, aType );
+
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AddAddresseeL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddAddresseeL(
+    TMsvRecipientType aType,
+    const TDesC& aRealAddress,
+    const TDesC& aAlias )
+    {
+
+    if ( aAlias.Length() > 0 )
+        {
+        HBufC* buf = TMmsGenUtils::GenerateAddressL( aRealAddress, aAlias );
+        CleanupStack::PushL( buf );
+        AddAddresseeL(aType, buf->Des());
+        CleanupStack::PopAndDestroy( buf ); 
+        }
+    else
+        {        
+        AddAddresseeL( aType, aRealAddress );
+        }
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::RemoveAddressee
+// ---------------------------------------------------------
+//
+void CUniClientMtm::RemoveAddressee( TInt aIndex )
+    {
+
+	if ( aIndex < 0 )
+		{
+		return;	
+		}
+    if ( iAddresseeList->Count() > aIndex )
+        {
+        // Delete from typed list
+        TPtrC address = (*iAddresseeList)[ aIndex ];
+        iUniHeaders->RemoveAddressee( address );
+        // delete from untyped list
+        iAddresseeList->Delete( aIndex );
+        }
+        
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::SetSubjectL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SetSubjectL( const TDesC& aSubject )
+    {
+    iUniHeaders->SetSubjectL( aSubject ); //lint !e115 !e1013 !e48 !e10 !e1514 !e64
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::SubjectL
+// ---------------------------------------------------------
+//
+const TPtrC CUniClientMtm::SubjectL() const
+    {
+    return iUniHeaders->Subject(); //lint !e115 !e1013 !e48 !e10 !e746 !e64 !e1055 !e628
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AddAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddAttachmentL( const TDesC& aFilePath,
+    const TDesC8& aMimeType, TUint aCharset, TRequestStatus& aStatus )
+    {
+    
+    TInt error = KErrNone;
+    TUint charset = aCharset;
+    RFile file;
+    if ( aMimeType.CompareF( KMmsTextPlain ) == 0 && charset == 0 )
+        {
+        // try to recognize character set
+        // We trap the recognization process
+        // If cannot recognize, the result will be 0 and default to us-ascii
+        TRAP ( error,
+            {
+            error = file.Open( Session().FileSession(), aFilePath,
+                EFileRead|EFileShareReadersOnly );
+            if ( error == KErrNone )
+                {
+                CleanupClosePushL( file );
+                charset = RecognizeCharSetL( file );
+                CleanupStack::PopAndDestroy( &file ); 
+                }
+            }
+            );
+        }
+        
+    // if the attachment character set is unicode, it should be converted to utf-8
+    // (see MMS conformance document)
+    
+    if ( aMimeType.CompareF( KMmsTextPlain ) == 0 &&
+        ( charset == KCharacterSetMIBEnumIso10646Ucs2  ||
+        charset == KCharacterSetMIBEnumUTF16 ||
+        charset == KCharacterSetMIBEnumUTF16BE ||
+        charset == KCharacterSetMIBEnumUTF16LE ) )
+        {
+        // If we have unicode character set, we must convert the file to utf8
+        error = file.Open( Session().FileSession(), aFilePath,
+            EFileRead|EFileShareReadersOnly );
+        if ( error == KErrNone )
+            {
+            CleanupClosePushL( file );
+              CMsvStore* store = iMsvEntry->EditStoreL();
+            CleanupStack::PushL( store );
+            TMsvAttachmentId attaId = 0;
+            CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL(
+                *store, attaId, file, Session().FileSession(),
+                Session().CurrentDriveL() );
+                
+            SetContentLocationForConvertedAttL( *store, attaId, aFilePath ); 
+                
+            store->CommitL();
+            CleanupStack::PopAndDestroy( store );
+            CleanupStack::PopAndDestroy( &file );  
+            }
+        TRequestStatus* status = &aStatus;
+        aStatus = KRequestPending;
+        User::RequestComplete( status, error );
+        return;    
+        }
+    else
+        {
+        // Disk space is checked in AddFilePathAttachmentL after everything has been initialized
+        AddFilePathAttachmentL( aFilePath, aMimeType, CMsvAttachment::EMsvFile, aStatus, charset );
+        }
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::AddAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddAttachmentL( RFile& aFile, const TDesC8& aMimeType,
+    TUint aCharset, TRequestStatus& aStatus )
+    {
+    TInt size = 0;
+    User::LeaveIfError( aFile.Size( size ) );
+
+    TFileName fileName;
+    User::LeaveIfError( aFile.Name( fileName ) );
+
+    TInt charset = aCharset;
+    if ( aMimeType.CompareF( KMmsTextPlain ) == 0  && charset == 0 )
+        {
+        // If no character set defined for a plain text attachment
+        // we try to recognize the character set.
+        // But if recogization fails, we say 0 (us-ascii)
+        TRAP_IGNORE ( charset = RecognizeCharSetL( aFile ) );
+        }
+
+    if ( aMimeType.CompareF( KMmsTextPlain ) == 0 )
+        {
+        // If we have unicode character set, we must convert the file to utf8
+        if ( charset == KCharacterSetMIBEnumIso10646Ucs2  ||
+            charset == KCharacterSetMIBEnumUTF16 ||
+            charset == KCharacterSetMIBEnumUTF16BE ||
+            charset == KCharacterSetMIBEnumUTF16LE ) 
+            {
+            CMsvStore* store = iMsvEntry->EditStoreL();
+            CleanupStack::PushL( store );
+            TMsvAttachmentId attaId = 0;
+            CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL( *store,
+                attaId, aFile, Session().FileSession(),
+                Session().CurrentDriveL() );
+              
+            SetContentLocationForConvertedAttL( *store, attaId, fileName );    
+
+            store->CommitL();
+            CleanupStack::PopAndDestroy( store ); // store
+            // We must close the file handle because the attachment manager will also
+            // close the handle.
+            // The open file handle is always closed unless the funtion leaves
+            aFile.Close();
+            TRequestStatus* status = &aStatus;
+            aStatus = KRequestPending;
+            User::RequestComplete( status, KErrNone );
+            return;
+            }
+        }
+    
+    if( !iAttaWaiter )
+        {
+        iAttaWaiter = CMmsAttachmentWaiter::NewL();
+        }
+
+    // store must be the first item allocated because it is the last one to be popped
+    CMsvStore* store = iMsvEntry->EditStoreL();
+    CleanupStack::PushL( store );
+    MMsvAttachmentManager& manager = store->AttachmentManagerL();
+    
+    CMsvAttachment* attachment = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
+    CleanupStack::PushL( attachment );
+
+    size += InitializeAttachmentL(
+        manager,
+        *attachment,
+        fileName,
+        aMimeType,
+        size,
+        charset );
+    
+    // Check that sufficient disk space available
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
+        &Session().FileSession(), 
+        size,
+        iMessageDrive ) )
+            {
+            User::Leave( KErrDiskFull );
+            }
+
+    // attachment is initialised, pass to the attachment manager
+    if ( iAttaWaiter->IsActive() )
+        {
+        // can't start an active operation because already active
+        User::Leave( KErrInUse );
+        }
+    manager.AddAttachmentL( aFile, attachment, iAttaWaiter->iStatus ); 
+    CleanupStack::Pop( attachment ); //ownership passed to manager
+    // We cannot start waiting before we know that the function we are waiting for
+    // did not leave. If we become active, and the function leaves, we are in trouble    
+    iAttaWaiter->StartWaitingL( aStatus, store, &manager );
+    CleanupStack::Pop( store ); //ownership passed
+    }
+// ---------------------------------------------------------
+// CUniClientMtm::AddLinkedAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddLinkedAttachmentL( const TDesC& aFilePath,
+    const TDesC8& aMimeType, TUint aCharset, TRequestStatus& aStatus )
+    {
+    TInt error = KErrNone;
+    TUint charset = aCharset;
+    if ( aMimeType.CompareF( KMmsTextPlain ) == 0 && aCharset == 0)
+        {
+        // try to recognize character set
+        // We trap the recognization process
+        // If cannot recognize, the result will be 0 and default to us-ascii
+        TRAP ( error,
+            {
+            RFile file;
+            error = file.Open( Session().FileSession(), aFilePath,
+                EFileRead|EFileShareReadersOnly);
+            if ( error == KErrNone )
+                {
+                CleanupClosePushL( file );
+                charset = RecognizeCharSetL( file );
+                CleanupStack::PopAndDestroy( &file );  
+                }
+            }
+            );
+        }
+    // Linked files cannot be converted to utf8. They must be sent as is no
+    // matter what the character set is.
+    
+    // Disk space is checked in AddFilePathAttachmentL after everything has
+    // been initialized.
+    if ( aMimeType.CompareF( KMmsTextPlain ) == 0 &&
+        ( charset == KCharacterSetMIBEnumIso10646Ucs2  ||
+        charset == KCharacterSetMIBEnumUTF16 ||
+        charset == KCharacterSetMIBEnumUTF16BE ||
+        charset == KCharacterSetMIBEnumUTF16LE ) )
+        {
+        AddAttachmentL( aFilePath, aMimeType, charset, aStatus );
+        }
+    else
+        {
+        AddFilePathAttachmentL( aFilePath, aMimeType,
+            CMsvAttachment::EMsvLinkedFile, aStatus, charset );
+        }
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AddEntryAsAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddEntryAsAttachmentL( TMsvId /*aAttachmentId*/,
+    TRequestStatus& aStatus )
+    {
+    TRequestStatus* status = &aStatus;
+    User::RequestComplete( status, KErrNotSupported );
+    } 
+
+// ---------------------------------------------------------
+// CUniClientMtm::CreateAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::CreateAttachmentL( const TDesC& aFileName,
+    RFile& aAttachmentFile, const TDesC8& aMimeType,
+    TUint aCharset, TRequestStatus& aStatus )
+    {
+    if( !iAttaWaiter )
+        {
+        iAttaWaiter = CMmsAttachmentWaiter::NewL();
+        }
+
+    // store must be the first item allocated because it is the last one to be popped
+    CMsvStore* store = iMsvEntry->EditStoreL();
+    CleanupStack::PushL( store );
+    MMsvAttachmentManager& manager = store->AttachmentManagerL();
+    
+    CMsvAttachment* attachment = 
+        CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
+    CleanupStack::PushL( attachment );
+    
+    TInt size = InitializeAttachmentL(
+        manager,
+        *attachment,
+        aFileName,
+        aMimeType,
+        0, // Creating empty attachment!
+        aCharset );
+
+    // Check that sufficient disk space available
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
+        &Session().FileSession(), 
+        size,
+        iMessageDrive ) )
+            {
+            User::Leave( KErrDiskFull );
+            }
+
+    if ( iAttaWaiter->IsActive() )
+        {
+        // can't start an active operation because already active
+        User::Leave( KErrInUse );
+        }
+    manager.CreateAttachmentL( aFileName, aAttachmentFile, attachment,
+        iAttaWaiter->iStatus );
+    CleanupStack::Pop( attachment ); //ownership passed to manager
+    iAttaWaiter->StartWaitingL( aStatus, store, &manager );
+    CleanupStack::Pop( store ); //ownership passed
+    }
+// ---------------------------------------------------------
+// CUniClientMtm::CancelAttachmentOperation
+// ---------------------------------------------------------
+//
+void CUniClientMtm::CancelAttachmentOperation()
+    {
+    
+    if ( iAttaWaiter )
+        {
+        iAttaWaiter->Cancel();
+        delete iAttaWaiter;
+        iAttaWaiter = NULL;
+        }
+    }
+
+    
+// ---------------------------------------------------------
+// CUniClientMtm::CreateTextAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::CreateTextAttachmentL(
+            CMsvStore& aStore,
+            TMsvAttachmentId& aAttachmentId,
+            const TDesC& aText,
+            const TDesC& aFile,
+            TBool aConvertParagraphSeparator )
+    {
+
+    CMmsAttachmentHandler::CreateTextAttachmentL( aStore,
+        aAttachmentId,
+        aText,
+        aFile,
+        Session().FileSession(),
+        Session().CurrentDriveL(),
+        aConvertParagraphSeparator );
+ 
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::MessageTypeSetting
+// ---------------------------------------------------------
+//
+TUniMessageTypeSetting CUniClientMtm::MessageTypeSetting() const
+    {
+    return TUniMessageTypeSetting( iUniHeaders->MessageTypeSetting() );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::SetMessageTypeSetting
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SetMessageTypeSetting( TUniMessageTypeSetting aSetting )
+    {
+    iUniHeaders->SetMessageTypeSetting( aSetting );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::MessageTypeLocking
+// ---------------------------------------------------------
+//
+TUniMessageTypeLocking CUniClientMtm::MessageTypeLocking() const
+    {
+    return TUniMessageTypeLocking( iUniHeaders->MessageTypeLocking() );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::SetMessageTypeLocking
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SetMessageTypeLocking( TUniMessageTypeLocking aLocking )
+    {
+    iUniHeaders->SetMessageTypeLocking( aLocking );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::MessageRoot
+// ---------------------------------------------------------
+//
+TMsvAttachmentId CUniClientMtm::MessageRoot() const
+    {
+    return iUniHeaders->MessageRoot();
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::SetMessageRoot
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SetMessageRoot( TMsvAttachmentId aRoot )
+    {
+    iUniHeaders->SetMessageRoot( aRoot );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::CreateMessageL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::CreateMessageL(
+    TMsvId aServiceId )
+    {
+    // Check that sufficient disk space available
+    // for index entry
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
+        &Session().FileSession(), 
+        KMmsIndexEntryExtra,
+        iMessageDrive ) )
+            {
+            // we use standard error code here
+            User::Leave( KErrDiskFull );
+            }
+
+    // just call the base class function
+    CBaseMtm::CreateMessageL( aServiceId );
+    
+    iUniHeaders->Reset();
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::BioTypeChangedL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::BioTypeChangedL( TUid /*aBioTypeUid*/ )
+    {
+    // Do nothing.
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::DefaultServiceL
+// ---------------------------------------------------------
+//
+TMsvId CUniClientMtm::DefaultServiceL() const
+    {
+    return KMsvLocalServiceIndexEntryId;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::RemoveDefaultServiceL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::RemoveDefaultServiceL()
+    {
+    // not supported
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::ChangeDefaultServiceL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::ChangeDefaultServiceL( const TMsvId& /*aService*/ )
+    {
+    // not supported
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::QueryCapability
+// ---------------------------------------------------------
+//
+TInt CUniClientMtm::QueryCapability(
+    TUid aCapability,
+    TInt& aResponse )
+    {
+    TInt error = KErrNone;
+    switch ( aCapability.iUid )
+        {
+        // Supported:
+        case KUidMtmQueryMaxTotalMsgSizeValue:
+            aResponse = KMaxTInt;
+            break;
+        case KUidMsvMtmQueryEditorUidValue:
+            aResponse = KUidMsgMmsEditor;
+            break;
+        case KUidMtmQuerySupportSubjectValue:
+        case KUidMtmQuerySupportAttachmentsValue:
+        case KUidMtmQueryCanSendMsgValue:
+        case KUidMtmQuerySendAsMessageSendSupportValue:
+        case KUidMtmQuerySupportsRecipientTypeValue:
+            // returns KErrNone
+            break;
+        // All others - Not Supported:
+        default:
+            error = KErrNotSupported;
+        }
+    return error;
+
+    } //lint !e1746
+
+// ---------------------------------------------------------
+// CUniClientMtm::InvokeSyncFunctionL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::InvokeSyncFunctionL(
+    TInt /*aFunctionId*/,
+    const CMsvEntrySelection& /*aSelection*/,
+    TDes8& /*aParameter*/ )
+    {
+    User::Leave( KErrNotSupported );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::InvokeAsyncFunctionL
+// ---------------------------------------------------------
+//
+CMsvOperation* CUniClientMtm::InvokeAsyncFunctionL(
+    TInt /* aFunctionId */,
+    const CMsvEntrySelection& /* aSelection*/,
+    TDes8& /* aParameter*/,
+    TRequestStatus& aCompletionStatus )
+    {
+    TPckgC < TMsvId > progress = 0;
+    return  CMsvCompletedOperation::NewL( Session(), Type(), progress, 
+            KMsvLocalServiceIndexEntryId, aCompletionStatus, KErrNotSupported );
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::ContextEntrySwitched
+// ---------------------------------------------------------
+//
+void CUniClientMtm::ContextEntrySwitched()
+    {
+    // Context change notification.
+    // Reset data.
+
+    // Note: Body text reset would be here if supported.
+    iAddresseeList->Reset();
+    iUniHeaders->Reset();
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::HandleEntryEventL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::HandleEntryEventL(
+    TMsvEntryEvent /*aEvent*/,
+    TAny* /*arg1*/,
+    TAny* /*arg2*/,
+    TAny* /*arg3*/ )
+    {
+    // No operation
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::ConstructL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::ConstructL()
+    {
+    iMessageDrive = EDriveC;
+    TInt error = KErrNone;
+
+    TRAP ( error, { iMessageDrive = Session().CurrentDriveL(); } );
+    
+    if ( error != KErrNone )
+        {
+        // if cannot ask, use default
+        iMessageDrive = EDriveC;
+        }
+
+    iUniHeaders = CUniHeaders::NewL();
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::MessageSize
+// ---------------------------------------------------------
+//
+TInt32 CUniClientMtm::MessageSize()
+    {
+    // First we should assert that iMsvEntry is not NULL, and panic, if it is
+    __ASSERT_DEBUG( iMsvEntry, Panic( EUniNotAMessage ));
+    TUint size = 0;
+    TRAPD( error, {size = AttachmentsSizeL() + iUniHeaders->Size();} );
+    if ( error != KErrNone ) // this is needed to get rid of compiler warning
+        {
+        size = 0;
+        }
+    return size;  //lint !e713 nothing lost here
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::SetMessageDescriptionL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SetMessageDescriptionL( const TDesC& aText )
+    {
+    // First we should assert that iMsvEntry is not NULL, and panic, if it is
+    __ASSERT_DEBUG( iMsvEntry, Panic( EUniNotAMessage ) );
+    TMsvEntry entry = iMsvEntry->Entry();
+    entry.iDescription.Set( aText );
+    iMsvEntry->ChangeL( entry );
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::BuildAddresseeListL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::BuildAddresseeListL() 
+    {
+
+    iAddresseeList->Reset();
+
+    const CDesCArray& array1 = iUniHeaders->ToRecipients(); //lint !e115 !e1013 !e48 !e10 !e64 
+    const CDesCArray& array2 = iUniHeaders->CcRecipients(); //lint !e115 !e1013 !e48 !e10 !e64
+    const CDesCArray& array3 = iUniHeaders->BccRecipients(); //lint !e115 !e1013 !e48 !e10 !e64
+
+    BuildAddresseeListL( array1, EMsvRecipientTo );
+    BuildAddresseeListL( array2, EMsvRecipientCc );
+    BuildAddresseeListL( array3, EMsvRecipientBcc );
+
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::BuildAddresseeListL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::BuildAddresseeListL(
+    const CDesCArray& aArray, TMsvRecipientType aValue ) 
+    {
+
+    TInt size;
+    size = aArray.Count();
+    for ( TInt i=0; i < size; i++ )
+        {
+        iAddresseeList->AppendL( aValue, aArray[i] );
+        }
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AttachmentsSizeL
+// ---------------------------------------------------------
+//
+TInt32 CUniClientMtm::AttachmentsSizeL()
+    {
+    CMsvStore* store = iMsvEntry->ReadStoreL();
+    CleanupStack::PushL( store );
+    TInt32 attaSize = AttachmentsSizeL( *store );
+    CleanupStack::PopAndDestroy( store );
+    return attaSize;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AttachmentsSizeL
+// ---------------------------------------------------------
+//
+TInt32 CUniClientMtm::AttachmentsSizeL( CMsvStore& aStore )
+    {
+    TInt32 size = 0;
+
+    MMsvAttachmentManager& attachMan = aStore.AttachmentManagerL();
+    TInt numAttachments = attachMan.AttachmentCount();
+
+    for ( TInt i = 0; i < numAttachments; i++ )
+        {
+        CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL( i );
+        CleanupStack::PushL( attachmentInfo );
+        
+        CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
+        CleanupStack::PushL( mimeHeaders );
+        
+        mimeHeaders->RestoreL( *attachmentInfo );
+        
+        RFile attaFile = attachMan.GetAttachmentFileL( i );
+        CleanupClosePushL( attaFile );
+        TInt fileSize = 0;
+        
+        // If we cannot access the file, we are in trouble
+        User::LeaveIfError( attaFile.Size( fileSize ) ); 
+        
+        // This adds up mime header size + actual attachment binary data
+        size += mimeHeaders->Size() + fileSize;
+
+        CleanupStack::PopAndDestroy( &attaFile );
+        CleanupStack::PopAndDestroy( mimeHeaders );
+        CleanupStack::PopAndDestroy( attachmentInfo );
+        }
+
+    return size;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::FindInRecipientL
+// ---------------------------------------------------------
+//
+TBool CUniClientMtm::FindInRecipientL( 
+    const TDesC& aTextToFind,
+    TMsvPartList aPartlist,
+    const CDesCArray& aRecipients,
+    CMsvFindText& aFindText )
+    {
+    TInt count = aRecipients.Count();
+    TBool found = EFalse;
+    for  (TInt i=0; i < count; i++ )
+        {
+        // Check alias and real address parts
+        // separately. Otherwise separator character could 
+        // spoil the check.
+        if ( aFindText.FindTextL( aTextToFind, 
+            TMmsGenUtils::Alias( aRecipients[i] ), aPartlist ) )
+            {
+            found = ETrue;
+            break;
+            }
+        else if ( aFindText.FindTextL( aTextToFind, 
+            TMmsGenUtils::PureAddress( aRecipients[i] ), aPartlist ) )
+            {
+            found = ETrue;
+            break;
+            }
+        }
+    return found;
+    }
+
+// ---------------------------------------------------------
+// CUniClientMtm::AddFilePathAttachmentL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::AddFilePathAttachmentL(
+    const TDesC& aFilePath,
+    const TDesC8& aMimeType,
+    CMsvAttachment::TMsvAttachmentType aType,
+    TRequestStatus& aStatus,
+    const TUint aCharacterSet /* = 0 */ )
+	{
+    __ASSERT_DEBUG( aType != CMsvAttachment::EMsvMessageEntry,
+        User::Invariant() );
+
+    TEntry fileEntry;
+    User::LeaveIfError( Session().FileSession().Entry( aFilePath, fileEntry ) );
+
+    if( !iAttaWaiter )
+        {
+        iAttaWaiter = CMmsAttachmentWaiter::NewL();
+        }
+    
+    // store must be the first item allocated because it is the last one to be popped
+    CMsvStore* store = iMsvEntry->EditStoreL();
+    CleanupStack::PushL( store );
+    MMsvAttachmentManager& manager = store->AttachmentManagerL();
+    
+    CMsvAttachment* attachment = CMsvAttachment::NewL( aType );
+    CleanupStack::PushL( attachment );
+
+    TInt size = InitializeAttachmentL(
+        manager,
+        *attachment,
+        aFilePath,
+        aMimeType,
+        fileEntry.iSize,
+        aCharacterSet );
+        
+    // now we know how much disk space we need
+    if ( aType == CMsvAttachment::EMsvFile )
+        {
+        size += attachment->Size();
+        }
+        
+    // Check that sufficient disk space available
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
+        &Session().FileSession(), 
+        size,
+        iMessageDrive ) )
+            {
+            // we use standard error code here
+            User::Leave( KErrDiskFull );
+            }
+    
+    // attachment is initialised, pass to the attachment manager
+    if ( iAttaWaiter->IsActive() )
+        {
+        // can't start an active operation because already active
+        User::Leave( KErrInUse );
+        }
+    switch( aType )
+        {
+        case CMsvAttachment::EMsvLinkedFile:
+            manager.AddLinkedAttachmentL( aFilePath, attachment,
+                iAttaWaiter->iStatus );
+            break;
+        default: // CMsvAttachment::EMsvFile
+            manager.AddAttachmentL( aFilePath, attachment,
+                iAttaWaiter->iStatus );
+            break;
+        }
+
+    CleanupStack::Pop( attachment); //ownership passed to manager
+    // We cannot start waiting before we know that the function we are waiting for
+    // did not leave. If we become active, and the function leaves, we are in trouble    
+    iAttaWaiter->StartWaitingL( aStatus, store, &manager );
+    CleanupStack::Pop( store ); // ownership passed to iAttaWaiter
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::RecognizeCharSetL
+// ---------------------------------------------------------
+//
+TUint CUniClientMtm::RecognizeCharSetL( RFile& aFile )
+    {
+    TUint charSet = CMsgTextUtils::RecognizeCharSetL( Session().FileSession(), aFile );
+    if ( !iTextUtils )
+        {
+        iTextUtils = CMsgTextUtils::NewL( Session().FileSession() );
+        }
+    return iTextUtils->CharconvIdToMibIdL( charSet );
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::InitializeAttachmentL
+// ---------------------------------------------------------
+//
+TInt CUniClientMtm::InitializeAttachmentL(
+    MMsvAttachmentManager& aManager,
+    CMsvAttachment& aAttachment,
+    const TDesC& aFileName,
+    const TDesC8& aMimeType,
+    TInt aFileSize,
+    TUint aCharset )
+    {
+    CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
+    CleanupStack::PushL( mimeHeaders );
+    
+    aAttachment.SetSize( aFileSize );
+    
+    // set the mime-type if provided
+    if ( aMimeType.Length() > 0 )
+        {
+        aAttachment.SetMimeTypeL( aMimeType );
+        TInt position = aMimeType.Find( KMmsSlash8 );
+        if ( position > 0 )
+            {
+            mimeHeaders->SetContentTypeL( aMimeType.Left( position ) );
+            }
+        if ( position < aMimeType.Length() - 1 )
+            {
+            mimeHeaders->SetContentSubTypeL( aMimeType.Mid( position + 1 ) );
+            }
+        }
+
+    TParsePtrC parse( aFileName );
+    mimeHeaders->SetSuggestedFilenameL( parse.NameAndExt() );
+    HBufC* contentLocation = CMsgTextUtils::GetSafeAttachmentNameLC(
+        aManager,
+        parse.NameAndExt(),
+        aAttachment.Id(),
+        ETrue );
+	mimeHeaders->SetContentLocationL( *contentLocation );
+    CleanupStack::PopAndDestroy( contentLocation );
+
+    mimeHeaders->SetMimeCharset( aCharset );
+
+    TInt size = KMmsIndexEntryExtra + mimeHeaders->Size();    
+    mimeHeaders->StoreL( aAttachment );
+    
+    // mime headers have been streamed to CMsvAttachment, they can go now
+    CleanupStack::PopAndDestroy( mimeHeaders );  
+    return size;
+    }
+    
+// ---------------------------------------------------------
+// CUniClientMtm::SetContentLocationForConvertedAttL
+// ---------------------------------------------------------
+//
+void CUniClientMtm::SetContentLocationForConvertedAttL(
+    CMsvStore& aStore,
+    TMsvAttachmentId aAttaId,
+    const TDesC& aFileName )
+    {
+    MMsvAttachmentManager& manager = aStore.AttachmentManagerL();    
+    CMsvAttachment* attachment = manager.GetAttachmentInfoL( aAttaId );    
+    CleanupStack::PushL( attachment );
+    
+    CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
+    CleanupStack::PushL( mimeHeaders );
+    mimeHeaders->RestoreL( *attachment );
+    
+    TParsePtrC parse( aFileName );
+    HBufC* contentLocation = CMsgTextUtils::GetSafeAttachmentNameLC(
+        manager,
+        parse.NameAndExt(),
+        aAttaId,
+        ETrue );
+    mimeHeaders->SetContentLocationL( *contentLocation );
+    CleanupStack::PopAndDestroy( contentLocation );
+    
+    mimeHeaders->StoreL( *attachment );
+    // Mime headers have been streamed into the attachment info
+    CleanupStack::PopAndDestroy( mimeHeaders );
+
+    MMsvAttachmentManagerSync& syncManager = aStore.AttachmentManagerExtensionsL();
+        
+    syncManager.ModifyAttachmentInfoL( attachment );
+    // Ownership transferred
+    CleanupStack::Pop( attachment );
+    }
+    
+
+//  End of File