/*
* 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 entrys. 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 entrys. 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 entrys. 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 entrys. 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 entrys. 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 entrys. 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 entrys. 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 
                //lenght 
                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 entrys. 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  
