mmsengine/mmscodecclient/src/mmscodecclient.cpp
changeset 31 ebfee66fde93
child 47 5b14749788d7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmscodecclient/src/mmscodecclient.cpp	Fri Jun 04 10:25:39 2010 +0100
@@ -0,0 +1,1458 @@
+/*
+* Copyright (c) 2003 - 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:   Provides access to message store.
+*
+*/
+
+ 
+
+
+// INCLUDE FILES
+#include "mmsheaders.h"
+#include "mmscodecclient.h"
+#include "mmsversion.h"
+#include "mmsmsventry.h" // for the TMmsMsvEntry
+#include "mmsgenutils.h"
+#include "mmssettings.h"
+#include "mmsdecode.h"
+#include "mmsencode.h"
+#include "mmscliententry.h"
+#include "mmsclient.h"
+#include "mmscodecclientlogger.h"
+
+#include    <mmsvattachmentmanager.h>
+#include    <mmsvattachmentmanagersync.h>
+#include    <mtclreg.h>     //CClientMtmRegistry
+#include    <msvapi.h>         // CMsvOperation 
+
+ 
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES  
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+const TInt  KMaxDetailsLength = 64;   // Copy max this many chars to TMsvEntry
+                                      //::iDetails
+_LIT( KAddressSeparator, ";" );       //Separator used between addresses in 
+                                      //TMsvEntry::iDetails
+
+const TInt KMmsCodecClientChunkSize = 10 * 1024; // 10k buffer - can be adjusted if needed
+                                      
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::CMmsCodecClient
+// -----------------------------------------------------------------------------
+//
+CMmsCodecClient::CMmsCodecClient():CActive( EPriorityStandard ),
+    iEntryBeingHandled ( KMsvNullIndexEntryId ),
+    iFolder ( KMsvNullIndexEntryId ),
+    iMmsVersion ( KMmsDefaultVersion ),
+    iState ( EIdle )
+    {     
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::ConstructL
+// -----------------------------------------------------------------------------
+//
+void CMmsCodecClient::ConstructL( CMsvSession& aMsvSession )
+    {
+    iMsvSession = &aMsvSession; 
+    
+    iFs = iMsvSession->FileSession();
+    
+    iClientEntry = iMsvSession->GetEntryL( KMsvRootIndexEntryId );   
+        
+    iMmsHeaders = CMmsHeaders::NewL( KMmsDefaultVersion );
+
+    iDecoder = CMmsDecode::NewL( iFs );
+    
+    iEncoder = CMmsEncode::NewL( iFs );
+
+    iClientMtmRegistry = CClientMtmRegistry::NewL( *iMsvSession );
+    
+    iMmsClient = (CMmsClientMtm *) iClientMtmRegistry->NewMtmL( 
+        KUidMsgTypeMultimedia );
+     
+    iClientEntryWrapper = CMmsClientEntry::NewL( iFs, *iClientEntry, EFalse );
+    
+    CMmsSettings* settings = CMmsSettings::NewL();
+    CleanupStack::PushL( settings );
+    settings->LoadSettingsL();
+    iMmsVersion = settings->MmsVersion();
+    CleanupStack::PopAndDestroy( settings );
+    
+    CActiveScheduler::Add( this );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::NewL
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CMmsCodecClient* CMmsCodecClient::NewL( CMsvSession& aMsvSession )
+    {
+    CMmsCodecClient* self = new( ELeave ) CMmsCodecClient;
+    
+    CleanupStack::PushL( self );
+    self->ConstructL( aMsvSession );
+    CleanupStack::Pop( self );
+
+    return self;
+    }
+
+// -----------------------------------------------------------------------------    
+// Destructor
+// -----------------------------------------------------------------------------
+//
+CMmsCodecClient::~CMmsCodecClient()
+    { 
+    // from CActive
+    Cancel();
+    
+    // If we come to destructor before all the member variables have been created,
+    // iState == EIdle, and none of the cleanup operations are attempted (pointers
+    // not used before they are created).
+    // By the time iState is something else besides idle, all member pointers exist
+    // already and can be used.
+    
+    // Cleanup after incomplete operation
+    ResetChunkedMode();
+    //Let's make sure that if entry has just been created
+    //it is be deleted.
+    // we also check if iClientEntryWrapper is not NULL,
+    // but actually, if is null, iState is idle.
+    if ( iClientEntryWrapper &&
+        ( iState == EEntryCreated || iState == EChunkedAdd || iState == EChunkedReplace ) &&
+        iEntryBeingHandled != KMsvNullIndexEntryId ) 
+        {
+        iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+        }
+    
+    delete iMmsHeaders;
+    delete iDecoder;
+    delete iEncoder;
+    delete iClientEntryWrapper;
+    delete iClientEntry; 
+    delete iMmsClient;
+    delete iClientMtmRegistry;
+    delete iEncodeBuffer;
+
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::AddMML
+// This function is called after CreateNewMessageEntryL so the new created id
+// aMmId already exist.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CMmsCodecClient::AddMML(
+    CBufFlat& aMm,
+    TMsvId aFolder,
+    TUint32 aFlags,
+    TBool aUnread,
+    TMsvId& aMmId,
+    TRequestStatus& aStatus )
+    {
+    iClientStatus = &aStatus;   
+    iEntryBeingHandled = aMmId;
+    iFolder = aFolder;
+    iFlags = aFlags;
+    iUnread = aUnread;
+
+    //The entry must have just been created
+    if ( iState != EEntryCreated )
+        {
+        ResetChunkedMode();
+        iState = EIdle;
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );
+        return;
+        }
+    
+    // Check if the buffer is empty
+    if ( aMm.Size() == 0 )
+        {
+        // delete entry - if we go back to idle state, we can create new entry
+        DeleteCurrentEntryL();
+        iState = EIdle;
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );
+        return;
+        }
+         
+    iClientEntryWrapper->SetCurrentEntry( iEntryBeingHandled );
+    iDecoder->StartL( *iClientEntryWrapper, *iMmsHeaders, aMm, iStatus);
+    *iClientStatus = KRequestPending;
+    iState = EFinalizeDecodedMM;
+    SetActive();   
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::DeleteMM
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TInt CMmsCodecClient::DeleteMM(
+    TMsvId aMmId )
+    {
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMML -function is allowed to 
+    //be called after the new entry has been created.
+    ResetChunkedMode();
+    
+    if ( iState == EEntryCreated ) 
+        {
+        iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+        iState = EIdle;  //No more new entry
+        return KErrArgument;                      
+        }   
+    
+    //The wrapper sets the entry to parent for us    
+    return iClientEntryWrapper->DeleteEntry( aMmId ); 
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::RetrieveMML
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CMmsCodecClient::RetrieveMML(
+    TMsvId aMmId,
+    CBufFlat& aMM,
+    TMsvId& aFolder,
+    TUint32& aFlags,                   
+    TBool& aUnread,
+    TRequestStatus& aStatus )
+    
+    {
+    iClientStatus = &aStatus;
+    
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to 
+    //be called after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry
+        DeleteCurrentEntryL();
+        iState = EIdle;  //No more new entry
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );                      
+        return;
+        }
+        
+    iEntryBeingHandled = aMmId;
+    CBufFlat*   encodeBuffer = &aMM;
+
+    iClientEntry->SetEntryL( aMmId ); 
+    
+    TMsvEntry tEntry = iClientEntry->Entry();
+
+    // Get the flags of the entry.
+    RetrieveFlags( tEntry, aFlags, aUnread );
+    // Get the folder where the message is stored.
+    aFolder = tEntry.Parent();
+
+    tEntry.SetReadOnly( EFalse );
+    iClientEntry->ChangeL( tEntry );
+
+    // Prepare MMS headers for encoding.
+    // Gets the message store for the current context with read access.
+    CMsvStore* store = iClientEntry->ReadStoreL();
+    CleanupStack::PushL( store );
+    iMmsHeaders->RestoreL( *store );
+
+    iMmsHeaders->SetMessageType( KMmsMessageTypeMSendReq );
+
+    // Set MMS version if it is undefined
+    if ( iMmsHeaders->MmsVersion() == 0 )
+        {
+        // Version not set
+        iMmsHeaders->SetMmsVersion( iMmsVersion );
+        }
+
+    // Don't change the original message.
+    CleanupStack::PopAndDestroy( store ); 
+    
+    // Encode the MMS.
+    iClientEntryWrapper->SetCurrentEntry( aMmId );
+    iEncoder->StartL( *iClientEntryWrapper, *iMmsHeaders, *encodeBuffer, iStatus );
+    *iClientStatus = KRequestPending;
+    iState = EFinalizeEncodedMM;
+      
+    SetActive();
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::SendMML
+// -----------------------------------------------------------------------------
+//
+
+EXPORT_C CMsvOperation* CMmsCodecClient::SendMML(
+    TMsvId aMmId ,
+    TRequestStatus& aStatus )
+    {
+    iClientStatus = &aStatus;
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to 
+    //be called after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry
+        DeleteCurrentEntryL();   
+        iState = EIdle;  //No more new entry
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );                      
+        return NULL;
+        }
+    
+    iMmsClient->SwitchCurrentEntryL(aMmId);
+    iMmsClient->LoadMessageL();
+    CMsvOperation* op = NULL;   
+    op = iMmsClient->SendL(iStatus);
+    *iClientStatus = KRequestPending;
+    iState = ESendMM;
+    SetActive();   
+    return op;
+    }
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::ReplaceMML
+// -----------------------------------------------------------------------------
+//
+
+EXPORT_C void CMmsCodecClient::ReplaceMML(
+    TMsvId& aMmId, 
+    CBufFlat& aMm,
+    TUint32 aFlags,
+    TBool aUnread,
+    TRequestStatus& aStatus )
+    {
+    iClientStatus = &aStatus;
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to 
+    //be called after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry
+        DeleteCurrentEntryL();   
+        iState = EIdle;  //No more new entry
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );                      
+        return;
+        }
+    
+    // Check if the aMm is empty. 
+    if ( aMm.Size() == 0 )
+        {
+        iState = EIdle;
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );
+        return;
+        }
+
+    // Messages in outbox must not be replaced.
+    if ( ParentOutbox( aMmId ) )
+        {
+        iState = EIdle;
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );
+        return;        
+        }
+
+    iFlags = aFlags;
+    iUnread = aUnread;
+     
+    // Set the Entry as being handled
+    iEntryBeingHandled = aMmId;
+
+    iClientEntry->SetEntryL( iEntryBeingHandled );
+    TMsvEntry tEntry = iClientEntry->Entry();
+     
+    tEntry.SetVisible( EFalse );
+    tEntry.SetComplete( EFalse );
+    tEntry.SetInPreparation( ETrue );
+    tEntry.SetReadOnly( EFalse );
+ 
+    iClientEntry->ChangeL( tEntry );
+
+    // Remove the attachments of the Entry   
+    CMsvStore* store = iClientEntry->EditStoreL();
+    CleanupStack::PushL( store );
+
+    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+    MMsvAttachmentManagerSync& attachManSynch = store->AttachmentManagerExtensionsL(); 
+    
+    TInt numOfAttach( attachMan.AttachmentCount() );
+    TInt i(0);
+    while ( i < numOfAttach )
+        {
+    	attachManSynch.RemoveAttachmentL( 0 );  //This is correct
+        i++;
+        }
+    
+
+    store->CommitL();
+    CleanupStack::PopAndDestroy( store );
+    
+    iClientEntryWrapper->SetCurrentEntry( iEntryBeingHandled );
+    iDecoder->StartL( *iClientEntryWrapper, *iMmsHeaders, aMm, iStatus);
+    *iClientStatus = KRequestPending;
+    iState = EFinalizeDecodedMM;
+    SetActive(); 
+    }
+    
+    
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::MoveMML
+// This function is implemeted synchronously altought the interface looks like
+// asynchronous
+// -----------------------------------------------------------------------------
+//
+
+EXPORT_C void CMmsCodecClient::MoveMML( TMsvId aMmId, 
+                               TMsvId aParentId, 
+                               TRequestStatus& aStatus )
+    {
+    iClientStatus = &aStatus;
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to 
+    //be called after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry
+        DeleteCurrentEntryL();
+        iState = EIdle;  //No more new entry
+         *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );                    
+        return;
+        }
+    
+    iState = EIdle;  //Move is stateless operation
+    if ( aParentId == KMsvGlobalOutBoxIndexEntryId )
+        {
+    	 *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );                    
+        return;
+        }
+    
+    iClientEntry->SetEntryL( aMmId );
+    
+    TMsvEntry tEntry = iClientEntry->Entry();
+    TMsvId parent = tEntry.Parent();
+    
+    
+    // Can't move within same folder
+    if ( parent == aParentId )
+        {
+        *iClientStatus = KRequestPending;
+        User::RequestComplete( iClientStatus, KErrArgument );                    
+        return;	
+        }
+    
+    iClientEntry->SetEntryL( parent );
+    
+    iClientEntry -> MoveL( aMmId, aParentId );
+    
+    //Let's accept all kind of moves between standard folders except to outbox 
+    //altought all of them are not accepted from the mms ui of the phone
+    //Some flags in TMsvEntry must be set according to target folder.
+    TMmsMsvEntry* mmsEntry = STATIC_CAST( TMmsMsvEntry*, &tEntry );
+    
+    if ( aParentId == KMsvGlobalInBoxIndexEntryId )
+        {
+        mmsEntry->SetMobileTerminated( ETrue );
+        tEntry.SetReadOnly( ETrue );
+        } 
+    
+    else if ( aParentId == KMsvSentEntryId )
+        {
+        mmsEntry->SetMobileTerminated( EFalse );  
+        tEntry.SetReadOnly( ETrue );
+        }
+        
+    else // ( aParentId == KMsvDraftEntryId )
+        {
+        mmsEntry->SetMobileTerminated( EFalse );   
+        tEntry.SetReadOnly( EFalse );
+        // Messages in drafts folders must be always editor oriented.
+        tEntry.iMtmData1 &= (~KMmsMessageMobileTerminated);
+        tEntry.iMtmData1 |= KMmsMessageEditorOriented; // editor oriented    
+        }
+
+    
+    *iClientStatus = KRequestPending;
+    User::RequestComplete( iClientStatus, KErrNone );              
+    }    
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::SetFlags 
+// -----------------------------------------------------------------------------
+//
+
+EXPORT_C TInt CMmsCodecClient::SetFlags(
+    TMsvId aMmId,
+    TUint32 aFlags,
+    TBool aUnread )
+    {
+    TInt error( KErrNone );
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to 
+    //be called after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry, not allowed to leave
+        if ( iEntryBeingHandled != KMsvNullIndexEntryId )
+            {
+            iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+            }
+        iState = EIdle;  //No more new entry
+        return KErrArgument;                      
+        }
+    // The flags of the message that are in Outbox must not be changed.
+    if ( ParentOutbox( aMmId ) )
+        {
+        return KErrNotSupported;
+        }
+    iFlags = aFlags;
+    iUnread = aUnread;
+
+    TRAP ( error, 
+        {
+        iClientEntry->SetEntryL( aMmId );
+        TMsvEntry tEntry = iClientEntry->Entry();
+        SetFlagsToTMsvEntry( tEntry );
+        iClientEntry->ChangeL( tEntry );
+        } );
+    
+    return error;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::SetFlagsToTMsvEntry 
+// -----------------------------------------------------------------------------
+//
+
+void CMmsCodecClient::SetFlagsToTMsvEntry(
+    TMsvEntry& aEntry)
+    {
+    aEntry.SetUnread( iUnread );
+    
+    //We do not want the client to set all flags in TMsvEntry::iMtmData1
+    //Only EMmsDrmCorruptedAttachment is allowed to be set.
+    //Not even the KMmsMessageMobileTerminated / KMmsMessageEditorOriented
+    //flags because those are always set in FinalizeDecodecL when the meassege
+    //is created or replaced. Later there can not be changes.
+    if ( iFlags & EMmsDrmCorruptedAttachment )
+        {
+    	aEntry.iMtmData1 |= EMmsDrmCorruptedAttachment;
+        }
+    else
+        {
+    	aEntry.iMtmData1 |= ~EMmsDrmCorruptedAttachment;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::DoCancel
+// -----------------------------------------------------------------------------
+//
+
+void CMmsCodecClient::DoCancel()
+    {
+    // Cancel all class members that are active objects
+    iDecoder->Cancel();
+    iEncoder->Cancel();
+    User::RequestComplete( iClientStatus, KErrCancel );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::RunL 
+// -----------------------------------------------------------------------------
+//
+
+void CMmsCodecClient::RunL( )
+    {
+    TInt error = KErrNone;
+
+    if ( iState == EFinalizeDecodedMM )
+        {
+        if ( iStatus != KErrNone ) // Something failed when the message was decoded.
+            {   
+            // delete entry
+            DeleteCurrentEntryL();
+            iState = EIdle;
+            User::RequestComplete( iClientStatus, KErrNotSupported );
+            return;
+            }
+        else // Decode succeeded. Finalize the entry.
+            {           
+            TRAP ( error, FinalizeDecodedMML() );
+            if ( error != KErrNone ) // Finalizing leaves
+                {
+                // delete entry
+                DeleteCurrentEntryL();
+                iState = EIdle;
+                User::RequestComplete( iClientStatus, KErrNotSupported );
+                return;               
+                }
+            else  // Message is decoded and finalized correctly.
+                {        
+                iEntryBeingHandled = KMsvNullIndexEntryId;
+                iState = EIdle;                
+                User::RequestComplete( iClientStatus, KErrNone );
+                return;
+                }
+            }
+        }
+    else if ( iState == EFinalizeEncodedMM )
+        {
+        if ( iStatus != KErrNone ) // Encoding failed.
+            {
+            iState = EIdle;
+            User::RequestComplete( iClientStatus, KErrNotSupported );
+            return;            
+            }
+        else // Encoding succeeded.
+            {
+            // The readonly flag has been set off in order to retrieve the message.
+            // The read only flag has to be set on if the folder is not draft or outbox folder.
+            iClientEntry->SetEntryL( iEntryBeingHandled );
+            TMsvEntry tEntry = iClientEntry->Entry();
+            TMsvId parent = tEntry.Parent();
+
+            if ( parent != KMsvDraftEntryId &&
+                 parent != KMsvGlobalOutBoxIndexEntryId )
+                {
+                tEntry.SetReadOnly( ETrue );           
+                iClientEntry->ChangeL( tEntry );
+                }
+            else
+                {
+                tEntry.SetReadOnly( EFalse );           
+                iClientEntry->ChangeL( tEntry );
+                }
+                
+            iEntryBeingHandled = KMsvNullIndexEntryId;
+            iState = EIdle;
+            
+            User::RequestComplete( iClientStatus, KErrNone );
+            return;
+            }
+        }
+    
+    else // ( iState == ESendMM )
+        {
+        iState = EIdle;
+        User::RequestComplete( iClientStatus, iStatus.Int() );
+        return;
+        }   
+    }
+        
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::RunError
+// -----------------------------------------------------------------------------
+//	
+TInt CMmsCodecClient::RunError( TInt aError )
+	{
+	iState = EIdle;
+    User::RequestComplete( iClientStatus, aError );
+	return KErrNone;	
+	}
+    
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::CreateNewMessageEntry
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TInt CMmsCodecClient::CreateNewMessageEntryL( TMsvId aFolder, TMsvId& aCreatedId )
+    {
+     
+    // Create a message entry into the target folder.
+    // The entry is invisible and in preparation until
+    // the message is decoded and finalised successfully.
+
+    // 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.
+
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to be called
+    //after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry
+        DeleteCurrentEntryL();
+        iState = EIdle;  //No more new entry
+        return KErrArgument;                      
+        }
+    
+    // set first default flags 
+    TMsvEntry tEntry;
+
+    // set all relevant flags in tMsvEntry
+    tEntry.iType = KUidMsvMessageEntry;
+    tEntry.iMtm = KUidMsgTypeMultimedia;
+    tEntry.iServiceId = iMmsClient->DefaultServiceL();
+    tEntry.SetNew( EFalse );
+
+    tEntry.SetVisible( EFalse );
+    tEntry.SetComplete( EFalse );
+    tEntry.SetInPreparation( ETrue );
+
+    // Query disk space:
+    TInt error = iClientEntryWrapper->DiskSpaceBelowCriticalLevelL( KMmsIndexEntryExtra );
+    if ( error != KErrNone )
+        {
+        return error; // KErrDiskFull
+        }
+
+    iClientEntry->SetEntryL( aFolder );
+    iClientEntry->CreateL( tEntry );
+    iEntryBeingHandled = tEntry.Id();
+    aCreatedId = iEntryBeingHandled;
+    iState = EEntryCreated;
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::FinalizeDecodedMML 
+// -----------------------------------------------------------------------------
+//
+void CMmsCodecClient::FinalizeDecodedMML()
+    { 
+    iClientEntry->SetEntryL( iEntryBeingHandled );
+    TMsvEntry tEntry = iClientEntry->Entry();
+       
+    //Resest the message class bits. There may be something in case of replace
+    tEntry.iMtmData1 &= ~KMmsMessageClassMask;
+    if ( iMmsHeaders->MessageClass() == EMmsClassAdvertisement )
+        {
+        tEntry.iMtmData1 |= KMmsMessageAdvertisement;
+        }
+    else if (iMmsHeaders->MessageClass() == EMmsClassInformational )
+        {
+        tEntry.iMtmData1 |= KMmsMessageInformational;
+        }
+    else
+        {
+        }
+
+    tEntry.iDate.UniversalTime();
+
+    if ( iMmsHeaders->ToRecipients().MdcaCount() +
+        iMmsHeaders->CcRecipients().MdcaCount() +
+        iMmsHeaders->BccRecipients().MdcaCount() > 1 )
+        {
+        tEntry.SetMultipleRecipients( ETrue );
+        }
+
+    SetFlagsToTMsvEntry( tEntry );
+
+    // Certain flags has to be in a particular way
+    TMsvId parent = tEntry.Parent();
+    TMmsMsvEntry* mmsEntry = STATIC_CAST( TMmsMsvEntry*, &tEntry );
+    TBuf<KMaxDetailsLength> detailsBuf; 
+    if ( parent == KMsvGlobalInBoxIndexEntryId )
+        {
+        mmsEntry->SetMobileTerminated( ETrue );
+        tEntry.SetReadOnly( ETrue );
+        GenerateSenderL( detailsBuf );
+        } 
+    
+    else if ( parent == KMsvSentEntryId )
+        {
+        mmsEntry->SetMobileTerminated( EFalse ); // not mobile terminated
+        tEntry.SetReadOnly( ETrue );
+        GenerateRecipientsL( detailsBuf );
+        }
+
+    else if ( parent == KMsvGlobalOutBoxIndexEntryId )
+        {
+        mmsEntry->SetMobileTerminated( EFalse ); // not mobile terminated
+        mmsEntry->SetEditorOriented ( ETrue );
+        tEntry.SetReadOnly( EFalse );
+        tEntry.SetSendingState( KMsvSendStateUponRequest );
+        GenerateRecipientsL( detailsBuf );       
+        }
+    else if ( parent == KMsvDraftEntryId )
+        {
+        mmsEntry->SetMobileTerminated( EFalse );  // not mobile terminated 
+        tEntry.SetReadOnly( EFalse );
+        // Messages in drafts folders must be always editor oriented.
+        tEntry.iMtmData1 &= (~KMmsMessageMobileTerminated);
+        tEntry.iMtmData1 |= KMmsMessageEditorOriented; // editor oriented
+        GenerateRecipientsL( detailsBuf );    
+        }
+    else
+        {
+    	mmsEntry->SetMobileTerminated( ETrue );
+        tEntry.SetReadOnly( ETrue );
+        GenerateSenderL( detailsBuf );
+ 
+        }
+    tEntry.iDetails.Set( detailsBuf );
+    tEntry.SetVisible( ETrue );
+    tEntry.SetComplete( ETrue );
+    tEntry.SetInPreparation( EFalse );
+    iClientEntry->ChangeL( tEntry );        
+    }
+
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::GenerateRecipientsL
+// -----------------------------------------------------------------------------
+//
+void CMmsCodecClient::GenerateRecipientsL( TDes& aDetails )
+    { 
+    HBufC* alias = HBufC::NewL( KMaxDetailsLength );
+    CleanupStack::PushL( alias );
+    TPtr aliasPtr = alias->Des();
+    const CDesCArray& addresses = iMmsHeaders->ToRecipients();
+    TInt addrCnt = addresses.Count();
+
+    TPtrC stringToAdd;
+    for ( TInt i = 0; i < addrCnt; ++i)
+        {      
+        TPtrC address = TMmsGenUtils::PureAddress( addresses[i] );
+
+        //GetAlias guarantees that the alias length is KMaxDetailsLength at max
+        TMmsGenUtils::GetAlias(
+            address,
+            aliasPtr,
+            KMaxDetailsLength,
+            iFs );
+        
+        if ( aliasPtr.Length() > 0 )
+        	{
+    		//Alias found
+            stringToAdd.Set( aliasPtr );
+        	}
+    	else
+            {
+        	//Fatal error or no alias found
+            stringToAdd.Set( address );
+            }
+
+        
+        if ( ( aDetails.Length() != 0 ) &&   // Not a first address
+             ( aDetails.Length() 
+                 + KAddressSeparator().Length() < KMaxDetailsLength ) )
+            {
+            // Add separator
+            aDetails.Append( KAddressSeparator() );
+            }
+
+        if ( aDetails.Length() + stringToAdd.Length() < KMaxDetailsLength ) 
+            {
+            // whole string fits. Add it.
+            aDetails.Append( stringToAdd );
+            }
+        else
+            {
+            // Only part of the string fits
+            TInt charsToAdd = KMaxDetailsLength - aDetails.Length();
+
+            if ( charsToAdd <= 0 )
+                {
+                // Cannot add any more chars 
+                break;
+                }
+
+            if ( charsToAdd >= stringToAdd.Length() )
+                {
+                //Guarantee that charsToAdd is not larger that stringToAdd 
+                //length
+                charsToAdd = stringToAdd.Length();
+                }
+
+            aDetails.Append( stringToAdd.Left( charsToAdd ) );
+            break;
+            }
+        }
+    CleanupStack::PopAndDestroy( alias ); 
+    }
+    
+    
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::GenerateSenderL
+// -----------------------------------------------------------------------------
+//
+void CMmsCodecClient::GenerateSenderL ( TDes& aDetails )
+{
+    HBufC* alias = HBufC::NewL( KMaxDetailsLength );
+    CleanupStack::PushL( alias );
+    TPtr aliasPtr = alias->Des();   
+            
+    iMmsClient->SwitchCurrentEntryL(iEntryBeingHandled);
+    iMmsClient->LoadMessageL();    
+    const TPtrC senderAddress = iMmsClient->Sender();
+  
+    //GetAlias guarantees that the alias length is KMaxDetailsLength at max
+    TMmsGenUtils::GetAlias(
+        senderAddress,
+        aliasPtr,
+        KMaxDetailsLength,
+        iFs );
+        
+    if ( aliasPtr.Length() == 0 )
+        {
+        //In theory the senderAddress may exceed the KMaxDetailsLength
+        //but the following line will not crash.
+        aDetails.Append( senderAddress.Left( KMaxDetailsLength ) );
+        }
+
+    else
+    	{
+        //Alias found
+        aDetails.Append( aliasPtr.Left( KMaxDetailsLength ) );	
+    	}
+   	
+    CleanupStack::PopAndDestroy( alias );  
+}
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::RetrieveFlags
+// This function is used to construt flags for the user of the codec client.
+// The unread flag is a boolean variable and the other flags uses the structure
+// of the TMsvEntry::iMtmData1 but only few bit are provided.
+// -----------------------------------------------------------------------------
+//
+void CMmsCodecClient::RetrieveFlags(                                
+    TMsvEntry aEntry,
+    TUint32 &aFlags,
+    TBool &aUnread )
+    {       
+    aUnread = aEntry.Unread();
+    aFlags = 0; //Reset the bitfields
+    
+    if ( aEntry.iMtmData1 & EMmsDrmCorruptedAttachment )
+        {    	
+    	aFlags |= EMmsDrmCorruptedAttachment;
+        }
+
+    if ( aEntry.iMtmData1 & KMmsMessageMobileTerminated )
+        {
+        aFlags |= KMmsMessageMobileTerminated;
+        }
+    else //KMmsMessageEditorOriented 
+        {
+    	aFlags |= KMmsMessageEditorOriented;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient:: ParentOutbox
+// -----------------------------------------------------------------------------
+//
+
+TBool CMmsCodecClient::ParentOutbox( TMsvId aMmId )
+    {
+
+    TInt error = KErrNone;
+    error = iClientEntryWrapper->SetCurrentEntry( aMmId );
+    if ( error == KErrNotFound ) // The entry does not exist.
+        {
+        return EFalse;
+        }
+    
+    TMsvEntry tEntry = iClientEntry->Entry();
+    TMsvId parent = tEntry.Parent();
+    if ( parent == KMsvGlobalOutBoxIndexEntryId )
+        {
+        return ETrue;
+        }
+    return EFalse;
+    }
+
+
+EXPORT_C TInt CMmsCodecClient::DiskSpaceBelowCriticalLevelL(TInt aSize)
+	{
+	return( iClientEntryWrapper->DiskSpaceBelowCriticalLevelL( aSize ) );
+	}
+	
+
+// -----------------------------------------------------------------------------
+// CMmsCodecClient::DeleteCurrentEntryL
+// Reason for this function is that iClientEntryWrapper::DeleteEntry uses traps
+// to prevent leave. This DeleteCurrentEntryL is used in leaving functions.
+// -----------------------------------------------------------------------------
+//
+void CMmsCodecClient::DeleteCurrentEntryL()
+    {
+    if ( iEntryBeingHandled != KMsvNullIndexEntryId )
+        {
+        iClientEntry->SetEntryL( iEntryBeingHandled );
+        TMsvEntry tEntry = iClientEntry->Entry();
+        iClientEntry->SetEntryL( tEntry.Parent() );
+        iClientEntry->DeleteL( iEntryBeingHandled );
+        iEntryBeingHandled = KMsvNullIndexEntryId;	
+        }
+    }
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CMmsCodecClient::InitializeChunkedRetrievingL(
+    TMsvId aMessageId,
+    TMsvId& aFolder,
+    TUint32& aFlags,
+    TBool& aUnread,
+    TInt& aOverallDataSize,
+    TRequestStatus& aStatus )
+    {
+    iClientStatus = &aStatus;
+    
+    ResetChunkedMode();
+    if ( iState == EEntryCreated )
+        {
+        // delete entry
+        DeleteCurrentEntryL();
+        iState = EIdle;  //No more new entry
+        User::Leave( KErrArgument );
+        }
+        
+    iEntryBeingHandled = aMessageId;
+    
+    iLastChunk = EFalse;
+    if ( iEncodeBuffer )
+        {
+        // delete old data from buffer
+        iEncodeBuffer->ResizeL( 0 );    
+        }
+    else
+        {
+        // create new buffer
+        iEncodeBuffer = CBufFlat::NewL( 0x400 ); // expand in multiple kilos
+        }
+
+    iClientEntry->SetEntryL( aMessageId ); 
+    
+    TMsvEntry tEntry = iClientEntry->Entry();
+
+    // Get the flags of the entry.
+    RetrieveFlags( tEntry, aFlags, aUnread );
+    // Get the folder where the message is stored.
+    aFolder = tEntry.Parent();
+    tEntry.SetReadOnly( EFalse );
+    iClientEntry->ChangeL( tEntry );
+
+    // Prepare MMS headers for encoding.
+    // Gets the message store for the current context with read access.
+    CMsvStore* store = iClientEntry->ReadStoreL();
+    CleanupStack::PushL( store );
+    iMmsHeaders->RestoreL( *store );
+
+    iMmsHeaders->SetMessageType( KMmsMessageTypeMSendReq );
+
+    // Set MMS version if it is undefined
+    if ( iMmsHeaders->MmsVersion() == 0 )
+        {
+        // Version not set
+        iMmsHeaders->SetMmsVersion( iMmsVersion );
+        }
+
+    // we do not change anything on the disk.
+    CleanupStack::PopAndDestroy( store ); 
+    
+    // Encode the MMS.
+    iEncodeBuffer->ResizeL( KMmsCodecClientChunkSize );
+    
+    iClientEntryWrapper->SetCurrentEntry( aMessageId );
+
+    // iEncoder sets client status to pending if the function does not leave
+    iEncoder->StartChunkedL( *iClientEntryWrapper, *iMmsHeaders, *iEncodeBuffer, *iClientStatus );
+    
+    iState = EChunkedRetrieve;
+    // iEncoder calculates the overall data size when encoding headers in StartChunkedL
+    aOverallDataSize = iEncoder->OverallDataSize();
+    
+    // CodecClient does not become active here.
+    // iEncoder completes client
+    
+    }
+	
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TInt CMmsCodecClient::GetNextDataPart( TPtrC8& aDataPart, TBool& aLastDataChunk )
+    {
+    if ( iState != EChunkedRetrieve )
+        {
+        // called out of context
+        return KErrArgument;
+        }
+    
+    TInt error = KErrNone;
+    error = iEncoder->GetNextDataPart( aDataPart, iLastChunk );
+    aLastDataChunk = iLastChunk;
+    return error;
+    }
+
+	
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TInt CMmsCodecClient::ReleaseData()
+    {
+    if ( iState != EChunkedRetrieve )
+        {
+        // called out of context
+        return KErrArgument;
+        }
+    
+    TInt error = KErrNone;
+    error = iEncoder->ReleaseData();
+    if ( iLastChunk || error != KErrNone )
+        {
+        // Read only flag is restored and iState goes back to idle
+        ReleaseRetrievedEntry();
+        }
+    return error;    
+    }
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CMmsCodecClient::InitializeChunkedAddingL(
+    TMsvId aFolder,
+    TMsvId& aMessageId,
+    TUint32 aFlags,
+    TBool aUnread )
+    {
+    
+    ResetChunkedMode();
+    if ( iState == EEntryCreated )
+        {
+        // delete entry
+        DeleteCurrentEntryL();
+        iState = EIdle;  //No more new entry
+        User::Leave( KErrArgument );
+        }
+        
+    // all folders are valid    
+       
+    iEntryBeingHandled = aMessageId;
+    iFolder = aFolder;
+    iFlags = aFlags;
+    iUnread = aUnread;
+    
+    // create the entry that is going to receive data
+    
+    // set first default flags 
+    TMsvEntry tEntry;
+
+    // set all relevant flags in tMsvEntry
+    tEntry.iType = KUidMsvMessageEntry;
+    tEntry.iMtm = KUidMsgTypeMultimedia;
+    tEntry.iServiceId = iMmsClient->DefaultServiceL();
+    tEntry.SetNew( EFalse );
+
+    tEntry.SetVisible( EFalse );
+    tEntry.SetComplete( EFalse );
+    tEntry.SetInPreparation( ETrue );
+
+    // Query disk space:
+    TInt error = iClientEntryWrapper->DiskSpaceBelowCriticalLevelL( KMmsIndexEntryExtra );
+    User::LeaveIfError( error );
+
+    iClientEntry->SetEntryL( aFolder );
+    iClientEntry->CreateL( tEntry );
+    iEntryBeingHandled = tEntry.Id();
+    aMessageId = iEntryBeingHandled;
+    iState = EChunkedAdd;
+    
+    iPosition = 0;
+    if ( iEncodeBuffer )
+        {
+        // delete old data from buffer
+        iEncodeBuffer->ResizeL( 0 );    
+        }
+    else
+        {
+        // create new buffer
+        iEncodeBuffer = CBufFlat::NewL( 0x400 ); // expand in multiple kilos
+        }
+    
+    iClientEntryWrapper->SetCurrentEntry( iEntryBeingHandled );
+    iMmsHeaders->Reset();
+    
+    iDecoder->InitializeChunkedMode(
+        *iClientEntryWrapper,
+        *iMmsHeaders,
+        *iEncodeBuffer
+        );
+    }
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CMmsCodecClient::InitializeChunkedReplacingL(
+    TMsvId aMessageId,
+    TUint32 aFlags,
+    TBool aUnread )
+    {
+    
+    ResetChunkedMode();
+    //Let's first make sure that the client is not illegally trashing the 
+    //message store with empty entries. Only the AddMM -function is allowed to 
+    //be called after the new entry has been created.
+    if ( iState == EEntryCreated ) 
+        {
+        // delete entry
+        DeleteCurrentEntryL();   
+        iState = EIdle;  //No more new entry
+        User::Leave( KErrArgument );
+        }
+    
+    // Messages in outbox must not be replaced.
+    if ( ParentOutbox( aMessageId ) )
+        {
+        iState = EIdle;
+        User::Leave( KErrArgument );
+        }
+
+    iFlags = aFlags;
+    iUnread = aUnread;
+     
+    // Set the Entry as being handled
+    iEntryBeingHandled = aMessageId;
+
+    iClientEntry->SetEntryL( iEntryBeingHandled );
+    TMsvEntry tEntry = iClientEntry->Entry();
+     
+    tEntry.SetVisible( EFalse );
+    tEntry.SetComplete( EFalse );
+    tEntry.SetInPreparation( ETrue );
+    tEntry.SetReadOnly( EFalse );
+ 
+    iClientEntry->ChangeL( tEntry );
+
+    // Remove the attachments of the Entry   
+    CMsvStore* store = iClientEntry->EditStoreL();
+    CleanupStack::PushL( store );
+
+    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+    MMsvAttachmentManagerSync& attachManSynch = store->AttachmentManagerExtensionsL(); 
+    
+    TInt numOfAttach( attachMan.AttachmentCount() );
+    TInt i( 0 );
+    while ( i < numOfAttach )
+        {
+        // we keep deleting the first one 
+    	attachManSynch.RemoveAttachmentL( 0 );
+        i++;
+        }
+    
+    store->CommitL();
+    CleanupStack::PopAndDestroy( store );
+    
+    iState = EChunkedReplace;
+    
+    iPosition = 0;
+    if ( iEncodeBuffer )
+        {
+        // delete old data from buffer
+        iEncodeBuffer->ResizeL( 0 );    
+        }
+    else
+        {
+        // create new buffer
+        iEncodeBuffer = CBufFlat::NewL( 0x400 ); // expand in multiple kilos
+        }
+        
+    iClientEntryWrapper->SetCurrentEntry( iEntryBeingHandled );
+    iMmsHeaders->Reset();
+    
+    iDecoder->InitializeChunkedMode(
+        *iClientEntryWrapper,
+        *iMmsHeaders,
+        *iEncodeBuffer
+        );
+    }
+
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+EXPORT_C TInt CMmsCodecClient::NextDataPart(
+    TPtrC8& aDataPart,
+    TBool aLastDataChunk )
+    {
+    if ( iState != EChunkedAdd && iState != EChunkedReplace )
+        {
+        // called out of context
+        return KErrArgument;
+        }
+        
+    if ( iEntryBeingHandled == KMsvNullIndexEntryId )
+        {
+        // something has gone wrong earlier and entry has been deleted
+        // but the caller has ignored the error and tries to continue
+        return KErrNotFound;
+        }
+    
+    // This must work the same way as if the data were coming over
+    // HTTP transport...
+    TInt error = KErrNone;
+    TInt currentData = aDataPart.Length();
+    TRAP( error,
+        {
+        iEncodeBuffer->ResizeL( aDataPart.Length() + iPosition );
+        iEncodeBuffer->Write( iPosition, aDataPart, currentData );
+        }
+        );
+    // The data is now in our own buffer if memeory did not run out
+    
+    iPosition = 0;
+    
+    if ( error == KErrNone )
+        {
+        error = iDecoder->NextDataPart( *iEncodeBuffer, iPosition, aLastDataChunk );
+        }
+    
+    TInt amount = iEncodeBuffer->Size() - iPosition;
+    if ( iPosition != 0 )
+        {
+        // some data handled
+        iEncodeBuffer->Delete( 0, iPosition );
+        }
+    // This does not leave - we are reducing the size    
+    TRAP_IGNORE( iEncodeBuffer->ResizeL( amount ) );
+    iPosition = amount; // continue writing from here
+
+    if ( error != KErrNone )
+        {
+        iDecoder->RelaseDataSink();
+        // delete entry, not allowed to leave
+        iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+        iEntryBeingHandled = KMsvNullIndexEntryId;
+        iState = EIdle;
+        }
+        
+    if ( aLastDataChunk && error == KErrNone )
+        {
+        // The message is complete - finalize the entry
+        iDecoder->RelaseDataSink();
+        TRAP ( error, FinalizeDecodedMML() );
+        if ( error != KErrNone ) // Finalizing leaves
+            {
+            // delete entry, not allowed to leave
+            iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+            }
+        iEntryBeingHandled = KMsvNullIndexEntryId;
+        iState = EIdle;
+        }
+    
+    return error;
+    }
+
+void CMmsCodecClient::ResetChunkedMode()
+    {
+    // If the previous operation has not completed correctly,
+    // do what cleanup we can and then allow operation to continue
+    switch ( iState )
+        {
+        case EChunkedRetrieve:
+            {
+            if ( iEntryBeingHandled != KMsvNullIndexEntryId )
+                {
+                // This also sets the iState back to idle
+                ReleaseRetrievedEntry();
+                }
+            break;
+            }
+        case EChunkedAdd:
+            {
+            iDecoder->RelaseDataSink();
+            if ( iEntryBeingHandled != KMsvNullIndexEntryId )
+                {
+                iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+                }
+            iState = EIdle;
+            break;
+            }
+        case EChunkedReplace:
+            {
+            iDecoder->RelaseDataSink();
+            // Delete entry. It could still be incomplete
+            if ( iEntryBeingHandled != KMsvNullIndexEntryId )
+                {
+                iClientEntryWrapper->DeleteEntry( iEntryBeingHandled ); 
+                }
+            iState = EIdle;
+            break;
+            }
+        default:
+            {
+            break;
+            }
+        }
+    }
+
+void CMmsCodecClient::ReleaseRetrievedEntry()
+    {
+    // Restore read only flag if we turned it off
+    // If not able to access the entry, we cannot help it
+    if ( iEntryBeingHandled != KMsvNullIndexEntryId )
+        {
+        TRAP_IGNORE( 
+            {
+            iClientEntry->SetEntryL( iEntryBeingHandled );
+            TMsvEntry tEntry = iClientEntry->Entry();
+            TMsvId parent = tEntry.Parent();
+
+            if ( parent != KMsvDraftEntryId &&
+                parent != KMsvGlobalOutBoxIndexEntryId )
+                {
+                tEntry.SetReadOnly( ETrue );           
+                // We do our best
+                iClientEntry->ChangeL( tEntry );
+                }
+            }
+            );
+        }
+    // Last chunk released or terminated by error - done with this entry
+    iEntryBeingHandled = KMsvNullIndexEntryId;
+    iState = EIdle;
+    }
+
+
+// ========================== OTHER EXPORTED FUNCTIONS =========================
+
+//  End of File