/*
* Copyright (c) 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: This file implements class CIpsPlgMsgMapper.
*
*/



#include "emailtrace.h"
#include "ipsplgheaders.h"

_LIT( KMimeTypeTextPlain, "text/plain" );
_LIT( KMimeTypeTextHtml, "text/html" );
_LIT( KMimeTypeTextRtf, "text/rtf" );
_LIT( KSlash, "/" );

_LIT( KCharSemicolon, ";" );
_LIT( KCharEquals, "=" );
_LIT( KCharsetTag, "CHARSET" );
_LIT( KMessageExtension, ".eml" );

// Supported multipart content types
_LIT( KMimeTypeMultipartRelated, "multipart/related" );
_LIT( KMimeTypeMultipartMixed, "multipart/mixed" );
_LIT( KMimeTypeMultipartAlternative, "multipart/alternative" );
_LIT( KMimeTypeMultipartDigest, "multipart/digest" );
_LIT( KMimeTypeMultipartRfc822, "message/rfc822" );
_LIT( KMimeTypeMultipartPartial, "message/partial" );
_LIT( KMimeTypeMultipartDirectory, "text/directory" );
_LIT( KMimeTypeMultipartExternalBody, "message/external-body" );
_LIT( KMimeTypeMultipartParallel, "multipart/parallel" );

const TInt KCharEscape       = 0x5C;
const TInt KCharNonPrintable = 0x1F;
const TInt KCharAt           = 0x40;
const TInt KMaxContentTypeLength = 200;
const TInt KEmbeddedMsgExtensionLength  = 4;

// ======== CONSTRUCTORS & DESTRUCTOR ========

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::NewL()
// ---------------------------------------------------------------------------
//
CIpsPlgMsgMapper* CIpsPlgMsgMapper::NewL(
    CMsvSession& aSession,
    CIpsPlgSosBasePlugin& aPlugin )
    {
    FUNC_LOG;
    CIpsPlgMsgMapper* self = CIpsPlgMsgMapper::NewLC( aSession, aPlugin );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::NewLC()
// ---------------------------------------------------------------------------
//
CIpsPlgMsgMapper* CIpsPlgMsgMapper::NewLC(
    CMsvSession& aSession,
    CIpsPlgSosBasePlugin& aPlugin )
    {
    FUNC_LOG;
    CIpsPlgMsgMapper* self =
        new( ELeave ) CIpsPlgMsgMapper( aSession, aPlugin );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::~CIpsPlgMsgMapper()
// ---------------------------------------------------------------------------
//
CIpsPlgMsgMapper::~CIpsPlgMsgMapper()
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::CIpsPlgMsgMapper()
// ---------------------------------------------------------------------------
//
CIpsPlgMsgMapper::CIpsPlgMsgMapper(
    CMsvSession& aSession,
    CIpsPlgSosBasePlugin& aPlugin )
    : iSession( aSession ), iPlugin( aPlugin )
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::ConstructL()
// ---------------------------------------------------------------------------
//
void CIpsPlgMsgMapper::ConstructL()
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::GetMailMessageL()
// ---------------------------------------------------------------------------
//
CFSMailMessage* CIpsPlgMsgMapper::GetMailMessageL(
    CMsvEntry& aEntry )
    {
    FUNC_LOG;
    TFSMailMsgId msgId( iPlugin.PluginId(), aEntry.Entry().Id() );
    CFSMailMessage* fsMsg = CFSMailMessage::NewLC( msgId );

    fsMsg->SetFolderId(
        TFSMailMsgId( iPlugin.PluginId(), aEntry.Entry().Parent() ) );

    CMsvStore* store( NULL );

    if ( aEntry.HasStoreL() )
        {
        store = aEntry.ReadStoreL();
        CleanupStack::PushL( store );
        }

    SetEnvelopeL( &aEntry, store, *fsMsg );

    // Apparently, this should be done only with
    // EFSMsgDataStructure, but EFSMsgDataEnvelope is currently
    // used by assuming that it reads also the content-type of
    // the message
    SetStructureL( &aEntry, *fsMsg );

    TMsvEmailEntry tEntry( aEntry.Entry() );

    SetFlags( tEntry, *fsMsg );

    if ( store )
        {
        CleanupStack::PopAndDestroy( store );
        }

    CleanupStack::Pop( fsMsg );
    return fsMsg;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::GetMailMessageL()
// ---------------------------------------------------------------------------
//
CFSMailMessage* CIpsPlgMsgMapper::GetMailMessageL(
    const TFSMailMsgId& aMailboxId,
    const TMsvEmailEntry& aEntry,
    const TFSMailDetails& aDetails )
    {
    FUNC_LOG;
    TFSMailMsgId msgId( iPlugin.PluginId(), aEntry.Id() );
    CFSMailMessage* result = CFSMailMessage::NewLC( msgId );

    result->SetMailBoxId( aMailboxId );
    result->SetFolderId(
        TFSMailMsgId( iPlugin.PluginId(), aEntry.Parent() ) );

    SetFlags( aEntry, *result );
    SetFetchStateL( aEntry, aEntry.Id(), EFalse, *result );

    switch( aDetails )
        {
        case EFSMsgDataDate:
            SetDateL( aEntry, *result );
            break;
        case EFSMsgDataSubject:
            SetSubjectL( aEntry, *result );
            break;
        case EFSMsgDataSender:
            SetSenderL( aEntry.iDetails, *result );
            break;
        case EFSMsgDataEnvelope:
        case EFSMsgDataStructure:
            {
            CMsvStore* store( NULL );
            CMsvEntry* cEntry = iSession.GetEntryL( aEntry.Id() );
            CleanupStack::PushL( cEntry );

            if ( cEntry )
            	{
            	if ( cEntry->HasStoreL() )
					{
					store = cEntry->ReadStoreL();
					CleanupStack::PushL( store );
					}

				SetEnvelopeL( cEntry, store, *result );
	
				// Apparently, this should be done only with
				// EFSMsgDataStructure, but EFSMsgDataEnvelope is currently
				// used by assuming that it reads also the content-type of
				// the message
				SetStructureL( cEntry, *result );
	
				if ( store )
					{
					CleanupStack::PopAndDestroy( store );
					}
				CleanupStack::PopAndDestroy( cEntry );
            	}
            break;
            }
        case EFSMsgDataIdOnly:
        default:
            break;
        }

    CleanupStack::Pop( result );
    return result;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::GetChildPartsL(
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId,
    const TFSMailMsgId& aParentId,
    RPointerArray<CFSMailMessagePart>& aParts )
    {
    FUNC_LOG;

    if ( aParentId.IsNullId() )
        // the caller wants the children of the message
        {
        GetChildPartsOfMessageEntryL( aMailBoxId, aMessageId, aParts );
        }
    else
        // the caller wants the parts of the 'real' subpart
        {
        GetChildPartsOfFolderEntryL( aMailBoxId, aMessageId, aParentId.Id(),
            aParts );
        }
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
CFSMailMessagePart* CIpsPlgMsgMapper::GetMessagePartL(
    TMsvId aEntryId,
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId )
    {
    FUNC_LOG;
    CFSMailMessagePart* result( NULL );
    TInt status;
    TMsvId serviceId;
    TMsvEmailEntry tEntry;

    status = iSession.GetEntry( aEntryId, serviceId, tEntry );

    if ( status == KErrNone )
        {
        switch ( tEntry.iType.iUid )
            {
            case KUidMsvFolderEntryValue:
                {
                result = ConvertFolderEntry2MessagePartL( tEntry,
                    aMailBoxId, aMessageId );
                break;
                }
            case KUidMsvAttachmentEntryValue:
            case KUidMsvMessageEntryValue:
                {
                result = ConvertAttachmentEntry2MessagePartL( tEntry,
                    aMailBoxId, aMessageId );
                break;
                }
            case KUidMsvEmailTextEntryValue:
            case KUidMsvEmailHtmlEntryValue:
            case KUidMsvEmailExternalBodyEntryValue:
            case KUidMsvEmailRtfEntryValue:
                {
                result = ConvertBodyEntry2MessagePartL( tEntry, aMailBoxId,
                    aMessageId );
                break;
                }
            }
        }

    return result;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
TBool CIpsPlgMsgMapper::ChangeTEntryFlagsL(
    TMsvEmailEntry& aEmlEntry,
    const CFSMailMessage& aMessage )
    {
    FUNC_LOG;
    TInt msgFlags = aMessage.GetFlags();
    TBool modified ( EFalse );
    TBool unread( aEmlEntry.Unread() );

    if ( !LogicalXor( unread, msgFlags & EFSMsgFlag_Read ) )
        {
        aEmlEntry.SetUnread( !unread );
        modified = ETrue;
        }

    // EFSMsgFlag_Low
    // EFSMsgFlag_Important
    TMsvPriority msgPriority;

    if ( msgFlags & EFSMsgFlag_Important )
        {
        msgPriority = EMsvHighPriority;
        }
    else if ( msgFlags & EFSMsgFlag_Low )
        {
        msgPriority = EMsvLowPriority;
        }
    else
        {
        msgPriority = EMsvMediumPriority;
        }

    if ( aEmlEntry.Priority() != msgPriority )
        {
        aEmlEntry.SetPriority( msgPriority );
        modified = ETrue;
        }

    // EFSMsgFlag_FollowUpComplete: no counterpart in Symbian message

    // EFSMsgFlag_FollowUp: supported only with IMAP4 (see below)

    // EFSMsgFlag_Attachments: It does not seem reasonable to update
    // this flag here (but when attachments are added)

    // EFSMsgFlag_Multiple: no counterpart in Symbian message

    // EFSMsgFlag_CalendarMsg
    if( ( aEmlEntry.iMtm == KSenduiMtmSmtpUid ) && ( msgFlags & EFSMsgFlag_CalendarMsg ) )
        {
        if( !aEmlEntry.ICalendar() )
            {
            aEmlEntry.SetICalendar( ETrue );
            modified = ETrue;
            }
        }


    // EFSMsgFlag_Answered: supported only with IMAP4 (see below)

    // EFSMsgFlag_Forwarded: no counterpart in Symbian message in S60 3.1

    // EFSMsgFlag_OnlyToMe: no counterpart in Symbian message

    // EFSMsgFlag_RemoteDeleted: no counterpart in Symbian message

    // EFSMsgFlag_HasMsgSender: no counterpart in Symbian message


    //this applies to POP too. Just ignore the misleading naming
    // moved to fix EANA-7HUD2G, pop doesn't support permanent flags
    // EFSMsgFlag_Answered

    // IMAP flags
    if ( aEmlEntry.iMtm == KSenduiMtmImap4Uid )
        {
            // EFSMsgFlag_FollowUp
        if ( LogicalXor( aEmlEntry.FlaggedIMAP4Flag(),
                         ( msgFlags & EFSMsgFlag_FollowUp ) ) )
            {
            aEmlEntry.SetFlaggedIMAP4Flag( !aEmlEntry.FlaggedIMAP4Flag() );
            modified = ETrue;
            }
        // EFSMsgFlag_Answered
        if ( LogicalXor( aEmlEntry.AnsweredIMAP4Flag(),
                         ( msgFlags & EFSMsgFlag_Answered ) ) )
            {
            aEmlEntry.SetAnsweredIMAP4Flag( !aEmlEntry.AnsweredIMAP4Flag() );
            modified = ETrue;
            }
        }
    return modified;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::UpdateMessageFlagsL(
    TMsvId aEntryId,
    const CFSMailMessage& aMessage )
    {
    FUNC_LOG;
    // This function updates tEntry from aMessage
    CMsvEntry* cEntry = iSession.GetEntryL( aEntryId );
    CleanupStack::PushL( cEntry );

    TMsvEmailEntry tEntry( cEntry->Entry() );
    TBool isModified = ChangeTEntryFlagsL( tEntry, aMessage );

    if ( isModified )
        {
        CIpsPlgOperationWait* waiter = CIpsPlgOperationWait::NewLC();
        CMsvOperation* ops = cEntry->ChangeL( tEntry, waiter->iStatus );
        CleanupStack::PushL( ops );
        waiter->Start();
        CleanupStack::PopAndDestroy( 2, waiter );
        }

    CleanupStack::PopAndDestroy( cEntry );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
CMsvOperation* CIpsPlgMsgMapper::UpdateMessageFlagsAsyncL(
    TMsvId aEntryId,
    const CFSMailMessage& aMessage,
    TRequestStatus& aStatus )
    {
    FUNC_LOG;
    // This function updates tEntry from aMessage
    CMsvEntry* cEntry = iSession.GetEntryL( aEntryId );
    CleanupStack::PushL( cEntry );
    TMsvEmailEntry tEntry( cEntry->Entry() );

    TBool isModified = ChangeTEntryFlagsL( tEntry, aMessage );
    CMsvOperation* ops = NULL;
    if ( isModified )
        {
        ops = cEntry->ChangeL( tEntry, aStatus );
        }

    CleanupStack::PopAndDestroy( cEntry );
    return ops;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::SetFSMessageFlagsL(
    const TMsvEmailEntry& aEntry, CFSMailMessage& aMsg )
    {
    FUNC_LOG;
    // this is just stupid through call, but now not need to
    // move SetFlags to public
    SetFlags( aEntry, aMsg );
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::NewChildPartL
// The method supports currently only the multipart/alternative structure
// construction (which is needed in the meeting request messages).
// The creation of the multipart structure happens by changing the folder
// type of the (SMTP) message entry itself. The folder entry is created
// by Symbian MTM later when new parts are added below the multipart
// structure. This causes some problems because there is no new entry
// in this phase and so the new CFSMailMessagePart instance do not
// ---------------------------------------------------------------------------
CFSMailMessagePart* CIpsPlgMsgMapper::NewChildPartL(
    const TFSMailMsgId& /* aMailBoxId */,
    const TFSMailMsgId& aMessageId,
    const TFSMailMsgId& /* aParentPartId */,
    const TDesC& /*aContentType*/ )
    {
    FUNC_LOG;
    CFSMailMessagePart* result( NULL );
    result = CFSMailMessagePart::NewLC( aMessageId,
                TFSMailMsgId( aMessageId.PluginId().iUid,
                    KMsvNullIndexEntryIdValue ) );
    CleanupStack::Pop( result );
    return result;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::SetDateL
// ---------------------------------------------------------------------------
//
void CIpsPlgMsgMapper::SetDateL(
    const TMsvEntry& aEntry,
    CFSMailMessage& aMsg )
    {
    FUNC_LOG;
    const TTime date =  aEntry.iDate;
    aMsg.SetDate( date );
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::SetSubjectL
// ---------------------------------------------------------------------------
//
void CIpsPlgMsgMapper::SetSubjectL(
    const TMsvEntry& aEntry,
    CFSMailMessage& aMsg )
    {
    FUNC_LOG;
    aMsg.SetSubject( aEntry.iDescription );
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::SetSenderL
// ---------------------------------------------------------------------------
//
void CIpsPlgMsgMapper::SetSenderL(
    TPtrC aSender,
    CFSMailMessage& aMsg )
    {
    FUNC_LOG;
    CFSMailAddress* addr = CFSMailAddress::NewLC();
    ConvertAddressL( aSender, *addr );
    aMsg.SetSender( addr );
    CleanupStack::Pop( addr );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::SetEnvelopeL(
    const CMsvEntry* aEntry,
    CMsvStore* aStore,
    CFSMailMessage& aMsg )
    {
    FUNC_LOG;
    CFSMailAddress* addr = NULL;

    TMsvEmailEntry tEntry( aEntry->Entry() );

    SetDateL( tEntry, aMsg );
    SetSubjectL( tEntry, aMsg );

    if ( aStore && aStore->IsPresentL( KUidMsgFileIMailHeader ) )
        {
        CImHeader* header = CImHeader::NewLC();
        header->RestoreL( *aStore );

        SetSenderL( header->From(), aMsg );

        const CDesCArray& toRecs = header->ToRecipients();

        for( TInt i=0; i<toRecs.Count(); i++ )
            {
            addr = CFSMailAddress::NewLC();
            ConvertAddressL( toRecs[i], *addr );
            aMsg.AppendToRecipient( addr );
            CleanupStack::Pop( addr );
            }

        const CDesCArray& ccRecs = header->CcRecipients();

        for( TInt i=0; i<ccRecs.Count(); i++ )
            {
            addr = CFSMailAddress::NewLC();
            ConvertAddressL( ccRecs[i], *addr );
            aMsg.AppendCCRecipient( addr );
            CleanupStack::Pop( addr );
            }

        const CDesCArray& bccRecs = header->BccRecipients();

        for( TInt i=0; i< bccRecs.Count(); i++ )
            {
            addr = CFSMailAddress::NewLC();
            ConvertAddressL( bccRecs[i], *addr );
            aMsg.AppendBCCRecipient( addr );
            CleanupStack::Pop( addr );
            }

        CleanupStack::PopAndDestroy( header );
        }
    else
        {
        // if the email header stream is not available, the sender
        // information is taken from the index entry
        SetSenderL( aEntry->Entry().iDetails, aMsg );
        }
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::SetStructureL(
    const CMsvEntry* aEntry,
    CFSMailMessage& aMessage )
    {
    FUNC_LOG;
    // Find out the content type by studying the child entry:
    // If there are no children, the message contents are not fetched and
    // thus the content type cannot be inferred. On the other hand,
    // the message entry should never have more than one child entry
    // (otherwise, it should be a multipart message)
    if ( aEntry->Count() == 1 )
        {
        TMsvEmailEntry tEntry = (*aEntry)[0];
        if ( tEntry.iType == KUidMsvFolderEntry )
            {
            aMessage.SetContentType(
                ConvertMultipartMimeType( tEntry.MessageFolderType() ) );
            }
        else
            // The case in which the content entry is directly below
            // the message entry..
            {
            aMessage.SetContentType( KMimeTypeMultipartMixed );
            }
        }
    else
        {
        // mark message structure state unknown if we don't have any
        // child parts. This needed to ui start fetch
        aMessage.SetMessagePartsStatus( EFSEmailStructureUnknown );
        }

    aMessage.SetContentSize( aEntry->Entry().iSize );
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::SetFlags
// FS framework's flag definitions and methods are somewhat clumsy and thus
// there is quite much code for a pretty simple function..
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::SetFlags(
    const TMsvEmailEntry& aEntry,
    CFSMailMessage& aMsg )
    {
    FUNC_LOG;

    TBool forwardedMeetingRequest = EFalse;
    
    // EFSMsgFlag_Read
    // EFSMsgFlag_Read_Locally
    if ( aEntry.Unread() )
        {
            aMsg.ResetFlag( EFSMsgFlag_Read );
            aMsg.ResetFlag( EFSMsgFlag_Read_Locally );
            }
    else
        {
        aMsg.SetFlag( EFSMsgFlag_Read );
        aMsg.SetFlag( EFSMsgFlag_Read_Locally );
        }

    // EFSMsgFlag_Low
    // EFSMsgFlag_Important
    switch ( aEntry.Priority() )
        {
        case EMsvHighPriority:
            {
            aMsg.SetFlag( EFSMsgFlag_Important );
            aMsg.ResetFlag( EFSMsgFlag_Low );
            break;
            }
        case EMsvMediumPriority:
            {
            aMsg.ResetFlag( EFSMsgFlag_Important );
            aMsg.ResetFlag( EFSMsgFlag_Low );
            break;
            }
        case EMsvLowPriority:
            {
            aMsg.ResetFlag( EFSMsgFlag_Important );
            aMsg.SetFlag( EFSMsgFlag_Low );
            break;
            }
        }

    // EFSMsgFlag_FollowUpComplete: cannot be supported with Symbian messages?

    // EFSMsgFlag_FollowUp: supported only with IMAP4 messages (see below)
    aMsg.ResetFlag( EFSMsgFlag_FollowUp );

// <cmail>
    //only for incomplete POP3 messages
    if ( aEntry.iMtm.iUid == KSenduiMtmPop3UidValue &&
            ( !aEntry.Complete() || aEntry.PartialDownloaded () ) )
        {
        TRAP_IGNORE( AttaCheckForIncompleteMsgL( aEntry, aMsg ) );
        }
// </cmail>
    else
        {
        // EFSMsgFlag_Attachments
        if ( aEntry.Attachment() )
            {
            aMsg.SetFlag( EFSMsgFlag_Attachments );

            if ( aEntry.ICalendar() )
                {
                // <cmail> implementation changed due to cs warning
                TInt attCount = 0;
                TRAPD ( err, attCount = GetAttachmentCountL( aEntry ) );
                if ( err == KErrNone && attCount == 1 )
                	{
                	CMsvEntry* cEntry = NULL;
                	TRAPD ( err2, cEntry = iSession.GetEntryL( aEntry.Id() ) );
                	if ( err2 == KErrNone && cEntry->Count() == 1 )
                		{
                		TMsvEmailEntry tEntry = (*cEntry)[0];
                		TImEmailFolderType ft = tEntry.MessageFolderType();
                		if ( tEntry.iType == KUidMsvFolderEntry && ft == EFolderTypeMixed  )
                			{
                           	// Message with calendar object. But based on content type
                			// (multipart/mixed) we know that this is meeting request
                			// forwarded as email, so it must be seen as normal email.
                   			forwardedMeetingRequest = ETrue;
                   			aMsg.ResetFlag( EFSMsgFlag_CalendarMsg );
                   			aMsg.SetFlag( EFSMsgFlag_Attachments );
                        	}
						else
							{
							// Only text/calendar part included as attachment
							aMsg.ResetFlag( EFSMsgFlag_Attachments );
							//Set Attachment flag for CMsvEntry (needed for sorting)
							TRAP_IGNORE( SetAttachmentFlagL( aEntry, EFalse ) );
							}
                		}
                	delete cEntry;
                	cEntry = NULL;
                	}
                // </cmail>
                }
            }
        else
            {
            aMsg.ResetFlag( EFSMsgFlag_Attachments );
            }
        }

    // EFSMsgFlag_Multiple: currently this is not used anywhere and
    // to evaluate the value, the CMsvStore of the message should be
    // accessed which is relatively slow (compared to the access of
    // the index entry)

    // EFSMsgFlag_CalendarMsg
    if ( aEntry.ICalendar() && !forwardedMeetingRequest ) // <cmail>
        {
        aMsg.SetFlag( EFSMsgFlag_CalendarMsg );
        }
    else if( !( aMsg.GetFlags() & EFSMsgFlag_CalendarMsg ) )
        {
        // Flag not set by mrui, we can reset it
        aMsg.ResetFlag( EFSMsgFlag_CalendarMsg );
        }

    // EFSMsgFlag_Answered: supported only with IMAP4 messages (see below)
    aMsg.ResetFlag( EFSMsgFlag_Answered );

    // EFSMsgFlag_Forwarded: not supported in S60 3.1
    aMsg.ResetFlag( EFSMsgFlag_Forwarded );

    // EFSMsgFlag_OnlyToMe: like EFSMsgFlag_Multiple

    // EFSMsgFlag_RemoteDeleted: no reasonable use in IPS

    // EFSMsgFlag_HasMsgSender: currently not used anywhere. Could be
    // probably be supported by checking the contents of TmsvEntry::iDetails
    // but this should be tested

    // Additional logic for IMAP4 messages
    if ( aEntry.iMtm == KSenduiMtmImap4Uid )
        {
        if ( aEntry.FlaggedIMAP4Flag() )
            {
            aMsg.SetFlag( EFSMsgFlag_FollowUp );
            }
        // moved to fix EANA-7HUD2G, pop doesn't support permanent flags
        if ( aEntry.AnsweredIMAP4Flag() )
            {
            aMsg.SetFlag( EFSMsgFlag_Answered );
            }
        }
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::SetFetchStateL(
    const TMsvEmailEntry& aEntry,
    TMsvId aMsgMainId,
    TBool aIsAtta,
    CFSMailMessagePart& aMessage )
    {
    FUNC_LOG;
    if ( aEntry.iMtm.iUid == KSenduiMtmPop3UidValue && 
            aEntry.Id() != aMsgMainId &&
            !aIsAtta )
        {
        TInt error = KErrNone;
        TMsvEmailEntry emlEntry;
        TMsvId dummy;

        error = iSession.GetEntry( aMsgMainId , dummy, emlEntry );

        if ( error == KErrNone )
            {
            DoSetFetchStateL( emlEntry, aMsgMainId, aIsAtta, aMessage );
            }
        else
            {
            aMessage.SetMessagePartsStatus( EFSEmailStructureUnknown );
            }
        }
    else
        {
        DoSetFetchStateL( aEntry, aMsgMainId, aIsAtta, aMessage );
        }
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::DoSetFetchStateL(
    const TMsvEmailEntry& aEntry,
    TMsvId aMsgMainId,
    TBool aIsAtta,
    CFSMailMessagePart& aMessage )
    {
    FUNC_LOG;
    if ( aEntry.PartialDownloaded() )
        {
        aMessage.SetMessagePartsStatus( EFSPartial );
        }
    else if ( aEntry.BodyTextComplete() && !aIsAtta )
        {
        aMessage.SetMessagePartsStatus( EFSFull );
        }
    else if ( aEntry.Complete() && !aIsAtta )
        {
        aMessage.SetMessagePartsStatus( EFSFull );
        }
    else if ( aEntry.Complete() && aIsAtta )
        {
        CMsvEntry* cEntry = iSession.GetEntryL( aEntry.Id() );

        CleanupStack::PushL( cEntry );

        TBool hasStore = cEntry->HasStoreL();

        if ( hasStore )
            {
            CMsvStore* store = cEntry->EditStoreL();
    
            CleanupStack::PushL( store );

            MMsvAttachmentManager& attMgr = store->AttachmentManagerL();
        
            if ( attMgr.AttachmentCount() )
                {
                aMessage.SetMessagePartsStatus( EFSFull );
                }
            else
                {
                aMessage.SetMessagePartsStatus( EFSNone );
                }
            
            CleanupStack::PopAndDestroy( store );
            }

        CleanupStack::PopAndDestroy( cEntry );
        }
    else if ( aEntry.Id() != aMsgMainId )
        {
        // fetch state of some message part, then we sure know
        // structure, but its not complete nor partial so state must be
        // none
        aMessage.SetMessagePartsStatus( EFSNone );
        }
    else
        {
        aMessage.SetMessagePartsStatus( EFSEmailStructureUnknown );
        }
    }


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
TPtrC CIpsPlgMsgMapper::ConvertMultipartMimeType(
    TImEmailFolderType aFolderType ) const
    {
    FUNC_LOG;
    TPtrC result;

    switch ( aFolderType )
    {
    case EFolderTypeRelated:
        {
        result.Set( KMimeTypeMultipartRelated );
        break;
        }
    case EFolderTypeMixed:
        {
        result.Set( KMimeTypeMultipartMixed );
        break;
        }
    case EFolderTypeParallel:
        {
        result.Set( KMimeTypeMultipartParallel );
        break;
        }
    case EFolderTypeAlternative:
        {
        result.Set( KMimeTypeMultipartAlternative );
        break;
        }
    case EFolderTypeDigest:
        {
        result.Set( KMimeTypeMultipartDigest );
        break;
        }
    case EFolderTypeRFC822:
        {
        result.Set( KMimeTypeMultipartRfc822 );
        break;
        }
    case EFolderTypePartial:
        {
        result.Set( KMimeTypeMultipartPartial );
        break;
        }
    case EFolderTypeDirectory:
        {
        result.Set( KMimeTypeMultipartDirectory );
        break;
        }
    case EFolderTypeExternal:
        {
        result.Set( KMimeTypeMultipartExternalBody );
        break;
        }
    case EFolderTypeUnknown:
    default:
        {
        result.Set( KNullDesC16 );
        break;
        }
    }

    return result;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// <cmail>
TInt CIpsPlgMsgMapper::ConvertBodyPartMimeType(
    const TUid& aEntryType,
    TDes& aMimeType )
    {
    FUNC_LOG;
    TInt result( KErrNone );

    switch ( aEntryType.iUid )
    {
    case KUidMsvEmailTextEntryValue:
        {
            aMimeType.Append( KMimeTypeTextPlain );
        break;
        }
    case KUidMsvEmailHtmlEntryValue:
        {
            aMimeType.Append( KMimeTypeTextHtml );
        break;
        }
    case KUidMsvEmailExternalBodyEntryValue:
        {
        // This seems a bit suspicious, but it is improbable that this case
        // is ever used..
            aMimeType.Append( KMimeTypeMultipartExternalBody );
        break;
        }
    case KUidMsvEmailRtfEntryValue:
        {
            aMimeType.Append( KMimeTypeTextRtf );
        break;
        }
    default:
        {
        result = KErrNotFound;
        }
    }
    return result;
    }
// </cmail>

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::ConvertAddressL(
    TPtrC aSourceAddress,
    CFSMailAddress& aTargetAddress )
    {
    FUNC_LOG;
    TInt status( KErrNone );
    TImMessageField mailField;

    aTargetAddress.SetEmailAddress(
        mailField.GetValidInternetEmailAddressFromString( aSourceAddress ) );

// <cmail>
    // If previous validation fails because of invalid email address.
    // e.g '@' is missing. Then address between '<' and '>' marks is used.
    TInt start( aSourceAddress.LocateReverse('<') );
    if ( start != KErrNotFound && start > 0)
    	{
    	TInt end( aSourceAddress.LocateReverse('>') );
    	start += 1;
    	aTargetAddress.SetEmailAddress( aSourceAddress.Mid( start, ( end - start ) ) );
    	}
// </cmail>

    TPtrC alias = mailField.GetValidAlias( aSourceAddress, status );
    if ( status == KErrNone )
        {
        TPtr alias2 = alias.AllocLC()->Des(); // have to create copy because alias might change
        for ( TInt ii = 0; ii < alias2.Length(); ii++ )
            {
            if ( alias2[ii] <= KCharNonPrintable )
                {
                alias2.Delete( ii, 1 );
                ii--;
                }
            else if ( ii < ( alias2.Length() - 1 ) &&
                      alias2[ii] == KCharEscape )
                {
                if( alias2[ii+1] == KCharAt )
                    {
                    alias2.Delete( ii, 1 );
                    ii--;
                    }
                }
            }
        aTargetAddress.SetDisplayName( alias2 ); // creates its own copy
        CleanupStack::PopAndDestroy();
        }
    else
        {
// <cmail>
       	aTargetAddress.SetDisplayName( aTargetAddress.GetEmailAddress() );
// </cmail>
        }
    }


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::SetContentTypeL(
    CImMimeHeader& aMimeHeader,
    CFSMailMessagePart& aMessagePart )
    {
    FUNC_LOG;
    HBufC8* buffer8 = HBufC8::NewLC(
        aMimeHeader.ContentType().Length() +
        aMimeHeader.ContentSubType().Length() + 1 );
    buffer8->Des().Append( aMimeHeader.ContentType() );
    buffer8->Des().Append( KSlash );
    buffer8->Des().Append( aMimeHeader.ContentSubType() );
    HBufC* buffer = HBufC::NewLC( buffer8->Length() );
    buffer->Des().Copy( buffer8->Des() );
    aMessagePart.SetContentType( *buffer );
    CleanupStack::PopAndDestroy( 2, buffer8 ); // buffer
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::GetChildPartsOfMessageEntryL(
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId,
    RPointerArray<CFSMailMessagePart>& aParts )
    {
    FUNC_LOG;
    CFSMailMessagePart* childPart( NULL );
    CMsvEntry* cEntry = iSession.GetEntryL( aMessageId.Id() );
    CleanupStack::PushL( cEntry );

    if ( cEntry->Count() > 0 )
        {
        // Note: it is assumed that there is only one child which is
        // either the body part (simple message) or the folder entry
        // representing the main level multipart structure
        if ( (*cEntry)[0].iType == KUidMsvFolderEntry )
            {
            GetChildPartsOfFolderEntryL( aMailBoxId, aMessageId,
                (*cEntry)[0].Id(), aParts);
            }
        else
            {
            childPart = GetMessagePartL( (*cEntry)[0].Id(), aMailBoxId, aMessageId );
            if( childPart )
                {
                aParts.Append( childPart );
                }
            }
        }
    CleanupStack::PopAndDestroy( cEntry );
    }

// ---------------------------------------------------------------------------
// CIpsPlgMsgMapper::GetChildPartsOfFolderEntryL
// Note: if the aParentId does not refer to a folder entry, childCount
// will be zero and the method does not do anything.
// It assumed that the method is called mainly for the folder entries
// and thus CMsvEntry is constructed without checking the entry type
// beforehand. If the assumption is not correct, the performance can be
// optimized by checking the entry type first.
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::GetChildPartsOfFolderEntryL(
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId,
    TMsvId aParentId,
    RPointerArray<CFSMailMessagePart>& aParts )
    {
    FUNC_LOG;
    CMsvEntry* cEntry = iSession.GetEntryL( aParentId );
    CleanupStack::PushL( cEntry );
    CFSMailMessagePart* childPart( NULL );
    TInt childCount( cEntry->Count() );
    TInt i;
    TInt position;
    TBool textBodyPartFound( EFalse );

    for (i = 0; i < childCount; i++ )
        {
        childPart = GetMessagePartL( (*cEntry)[i].Id(), aMailBoxId, aMessageId );

        // Child parts have to be ordered so that the plain text body
        // part is first (if such exists) and the HTML body is next.
        // The rest of the children can be in any order.
        if ( (*cEntry)[i].iType == KUidMsvEmailTextEntry )
            {
            position = 0;
            textBodyPartFound = ETrue;
            }
        else if ( (*cEntry)[i].iType == KUidMsvEmailHtmlEntry )
            {
            if ( textBodyPartFound )
                {
                position = 1;
                }
            else
                {
                position = 0;
                }
            }
        else
            {
            position = i;
            }

        // Insert the new child part to the result array
        if ( childPart )
            {
            aParts.Insert( childPart, position );
            childPart = NULL;
            }
        }
    CleanupStack::PopAndDestroy( cEntry );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
CFSMailMessagePart* CIpsPlgMsgMapper::ConvertBodyEntry2MessagePartL(
    const TMsvEmailEntry& aEntry,
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId )
    {
    FUNC_LOG;
    CFSMailMessagePart* result( NULL );
    TInt status;
// <cmail>
    HBufC* buf = HBufC::NewLC( KMaxContentTypeLength );
    TPtr contentType = buf->Des();

    status = ConvertBodyPartMimeType( aEntry.iType, contentType );
    __ASSERT_DEBUG( ( status == KErrNone ),
        User::Panic( KIpsPlgPanicCategory, EIpsPlgInvalidEntry ) );
    if ( status == KErrNone )
        {
        result = CFSMailMessagePart::NewLC(
            aMessageId, TFSMailMsgId( iPlugin.PluginId(), aEntry.Id() ) );

        // If mimetype is 'text/html' add charset parameter
        if ( aEntry.iType.iUid == KUidMsvEmailHtmlEntryValue )
            {
            GetCharsetParameterL( aEntry, contentType );
            }
// </cmail>
        result->SetContentType( contentType );
        result->SetMailBoxId( aMailBoxId );

        // Size
        result->SetContentSize( aEntry.iSize );

        if ( aEntry.Complete() )
            {
            TMsvEmailEntry parent;
            TMsvId dummy;
            if ( aEntry.PartialDownloaded() )
                {
                CMsvEntry* cEntry = iSession.GetEntryL( aEntry.Id() );
                CleanupStack::PushL( cEntry );
                if ( cEntry->HasStoreL() )
                    {
                    CMsvStore* store = cEntry->ReadStoreL();
                    CleanupStack::PushL( store );
                    result->SetFetchedContentSize( store->SizeL() );
                    CleanupStack::PopAndDestroy( store );
                    }

                // sometimes store size is bigger than msg size
                // even if msg is partially downloaded, then set
                // fetched content size smaller than msg size
                TUint fetchedSize = result->FetchedContentSize();
                TUint contentSize = result->ContentSize();
                if ( (contentSize > 0) && (fetchedSize > contentSize) )
                    {
                    result->SetFetchedContentSize( contentSize - 1 );
                    }

                CleanupStack::PopAndDestroy( cEntry );
                }
            else if ( aEntry.iMtm == KSenduiMtmPop3Uid &&
                    iSession.GetEntry( aEntry.Parent(), dummy, parent )
                    == KErrNone && parent.PartialDownloaded() )
                {
                // we cant know remote size of pop body part, but
                // we know it is not completely fetched at this point
                // set content size fetchedSize+1 to make framework know that
                // this message is not completely fetched

                result->SetContentSize( aEntry.iSize+1 );
                result->SetFetchedContentSize( aEntry.iSize );

                }
            else
                {
                result->SetFetchedContentSize( aEntry.iSize );
                }
            // This is "gludge-fix" for situation when
            // content size is zero and message is complete
            // mark sizes to 1 then ui not assume that something
            // missing and not try to fetch it
            if ( aEntry.iSize == 0 )
                {
                result->SetContentSize(1);
                result->SetFetchedContentSize(1);
                }
            }
        else
            {
            result->SetFetchedContentSize( 0 );
            }

        CleanupStack::Pop( result );
        }
    SetFetchStateL( aEntry, aMessageId.Id(), EFalse, *result );
    CleanupStack::PopAndDestroy( buf );
    return result;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
CFSMailMessagePart* CIpsPlgMsgMapper::ConvertAttachmentEntry2MessagePartL(
    const TMsvEmailEntry& aEntry,
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId )
    {
    FUNC_LOG;
    CFSMailMessagePart* result( NULL );
    HBufC* buffer;
    CMsvEntry* cEntry;

    __ASSERT_DEBUG( ( aEntry.iType == KUidMsvAttachmentEntry || 
    	aEntry.iType == KUidMsvMessageEntry ),
        User::Panic( KIpsPlgPanicCategory, EIpsPlgInvalidEntry ) );

    cEntry = iSession.GetEntryL( aEntry.Id() );
    CleanupStack::PushL( cEntry );

    if ( cEntry->HasStoreL() )
        {
        result = CFSMailMessagePart::NewLC( aMessageId,
            TFSMailMsgId( iPlugin.PluginId(), aEntry.Id() ) );

        CMsvStore* store = cEntry->ReadStoreL();
        CleanupStack::PushL( store );

        if ( store->IsPresentL( KUidMsgFileMimeHeader ) )
            {
            CImMimeHeader* mimeHeader = CImMimeHeader::NewLC();
            mimeHeader->RestoreL( *store );

            // Content-type
            if ( aEntry.iType.iUid == KUidMsvMessageEntryValue )
                {
                result->SetContentType( KMimeTypeMultipartRfc822 );
                }
            else
                {
                SetContentTypeL( *mimeHeader, *result );
                }

            // Content-description
            buffer = HBufC::NewLC(
                mimeHeader->ContentDescription().Length ( ) );
            buffer->Des().Copy ( mimeHeader->ContentDescription ( ) );
            result->SetContentDescription ( *buffer );
            CleanupStack::PopAndDestroy ( buffer );
            buffer = NULL;

            // Content-disposition
            buffer = HBufC::NewLC(
                mimeHeader->ContentDisposition().Length ( ) );
            buffer->Des().Copy( mimeHeader->ContentDisposition() );
            result->SetContentDisposition( *buffer );
            CleanupStack::PopAndDestroy( buffer );
            buffer = NULL;

            // Content-type parameters

            CDesC8Array& sourceArray( mimeHeader->ContentTypeParams() );
            CDesCArray& targetArray( result->ContentTypeParameters() );
            for ( TInt i=0; i<sourceArray.Count(); i++ )
                {
                buffer = HBufC::NewLC( sourceArray[i].Length() );
                buffer->Des().Copy( sourceArray[i] );
                targetArray.AppendL( buffer->Des() );
                CleanupStack::PopAndDestroy( buffer );
                buffer = NULL;
                i++;
                }

            // Content ID
            buffer = HBufC::NewLC(
                mimeHeader->ContentID().Length ( ) );
            buffer->Des().Copy( mimeHeader->ContentID() );
            result->SetContentIDL( *buffer );
            CleanupStack::PopAndDestroy( buffer );
            buffer = NULL;

            // Content-class: not supported by Symbian (non-standard field)

            CleanupStack::PopAndDestroy( mimeHeader );
            }
        // Name
        if ( aEntry.iType.iUid == KUidMsvMessageEntryValue )
            {
            HBufC* att = HBufC::NewLC( aEntry.iDescription.Length() + KEmbeddedMsgExtensionLength );
            att->Des().Copy( aEntry.iDescription );
            att->Des().Append( KMessageExtension );
            result->SetAttachmentNameL( att->Des() );
            CleanupStack::PopAndDestroy( att );
            }
        else
            {
            result->SetAttachmentNameL( aEntry.iDetails );
            }

        // Size
        result->SetContentSize( aEntry.iSize );

        if ( aEntry.Complete() )
            {
            result->SetFetchedContentSize( aEntry.iSize );
            }
        else
            {
            result->SetFetchedContentSize( 0 );
            }
        result->SetMailBoxId( aMailBoxId );
        CleanupStack::PopAndDestroy(store);
        
        SetFetchStateL( aEntry, aMessageId.Id(), ETrue, *result );
        CleanupStack::Pop( result );
        }
    CleanupStack::PopAndDestroy( cEntry );

    return result;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
CFSMailMessagePart* CIpsPlgMsgMapper::ConvertFolderEntry2MessagePartL(
    const TMsvEmailEntry& aEntry,
    const TFSMailMsgId& aMailBoxId,
    const TFSMailMsgId& aMessageId )
    {
    FUNC_LOG;
    CFSMailMessagePart* result( NULL );

    __ASSERT_DEBUG( ( aEntry.iType == KUidMsvFolderEntry ),
        User::Panic( KIpsPlgPanicCategory, EIpsPlgInvalidEntry ) );

    TPtrC mimeType = ConvertMultipartMimeType( aEntry.MessageFolderType() );

    if ( mimeType.Length() > 0 )
        {
        result = CFSMailMessagePart::NewLC(
            aMessageId, TFSMailMsgId( iPlugin.PluginId(), aEntry.Id() ) );

        result->SetContentType( mimeType );
        result->SetMailBoxId( aMailBoxId );

        CleanupStack::Pop( result );
        }

    return result;
    }


// ---------------------------------------------------------------------------
//
//
// ---------------------------------------------------------------------------
// <cmail> implemented to get rid of cs warning
TInt CIpsPlgMsgMapper::GetAttachmentCountL(
    const TMsvEmailEntry& aEntry )
    {
    FUNC_LOG;
    TInt result( KErrNone );

    CMsvEntry *cEntry = iSession.GetEntryL( aEntry.Id() );
    CleanupStack::PushL( cEntry );
    CImEmailMessage* message = CImEmailMessage::NewLC( *cEntry );

    message->GetAttachmentsListL( cEntry->Entry().Id( ),
        CImEmailMessage::EAllAttachments, CImEmailMessage::EThisMessageOnly );

    result = message->Selection().Count();

    CleanupStack::PopAndDestroy( 2, cEntry ); // message, cEntry

    return result;
    }
// </cmail>

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::AttaCheckForIncompleteMsgL(
        const TMsvEmailEntry& aEntry,
        CFSMailMessage& aMsg )
    {
    aMsg.ResetFlag( EFSMsgFlag_Attachments );

    TImEmailFolderType ft = aEntry.MessageFolderType();

    if ( ft == EFolderTypeUnknown )
        {
        CMsvEntry* cEntry = iSession.GetEntryL( aEntry.Id() );
        CleanupStack::PushL( cEntry );

        if ( cEntry->HasStoreL() )
            {
            CMsvStore* store = cEntry->ReadStoreL();
            CleanupStack::PushL( store );

            if ( store->IsPresentL( KUidMsgFileMimeHeader ) )
                {
                CImMimeHeader* mimeHeader = CImMimeHeader::NewLC();
                mimeHeader->RestoreL( *store );

                HBufC8* buffer8 = HBufC8::NewLC(
                mimeHeader->ContentType().Length() +
                mimeHeader->ContentSubType().Length() + 1 );

                buffer8->Des().Append( mimeHeader->ContentType() );
                buffer8->Des().Append( KSlash );
                buffer8->Des().Append( mimeHeader->ContentSubType() );

                HBufC* buffer = HBufC::NewLC( buffer8->Length() );
                buffer->Des().Copy( buffer8->Des() );

                if ( buffer->CompareF( KMimeTypeMultipartMixed ) == 0 ||
                     buffer->CompareF( KMimeTypeMultipartRelated ) == 0 )
                    {
                    aMsg.SetFlag( EFSMsgFlag_Attachments );
                    if ( !aEntry.Attachment() )
                    	{
                    	SetAttachmentFlagL( aEntry, ETrue );
                    	}
                    }

                CleanupStack::PopAndDestroy( 3, mimeHeader );
                }

            CleanupStack::PopAndDestroy( store );
            }
        CleanupStack::PopAndDestroy( cEntry );
        }
    else if ( ft == EFolderTypeMixed || ft == EFolderTypeRelated )
        {
        aMsg.SetFlag( EFSMsgFlag_Attachments );
        if ( !aEntry.Attachment() )
        	{
        	SetAttachmentFlagL( aEntry, ETrue );
        	}
        }

    if ( !aMsg.IsFlagSet( EFSMsgFlag_Attachments ) && aEntry.Attachment() )
    	{
    	SetAttachmentFlagL( aEntry, EFalse );
    	}
    }
// </cmail>


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// <cmail>
void CIpsPlgMsgMapper::SetAttachmentFlagL( const TMsvEmailEntry& aEntry,
										   TBool aHasAttachment )
	{
	FUNC_LOG;
	CMsvEntry* cEntry = iSession.GetEntryL( aEntry.Id() );
	CleanupStack::PushL( cEntry );
	// Only text/calendar part included as attachment
	TMsvEmailEntry entryToBeChanged( aEntry );
	entryToBeChanged.SetAttachment( aHasAttachment );
	CIpsPlgOperationWait* waiter = CIpsPlgOperationWait::NewLC();
	CMsvOperation* ops = cEntry->ChangeL( entryToBeChanged, waiter->iStatus );
	CleanupStack::PushL( ops );
	waiter->Start();
	CleanupStack::PopAndDestroy( 3, cEntry );
	}
// </cmail>

// <cmail>
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void CIpsPlgMsgMapper::GetCharsetParameterL(
    const TMsvEmailEntry& aEntry, TDes& aContentType )
    {
    FUNC_LOG;

    CMsvEntry* cEntry = iSession.GetEntryL( aEntry.Id() );
    CleanupStack::PushL( cEntry );
    if ( cEntry->HasStoreL() )
        {
        CMsvStore* store = cEntry->ReadStoreL();
        CleanupStack::PushL( store );
        if ( store->IsPresentL( KUidMsgFileMimeHeader ) )
            {
            CImMimeHeader* mimeHeader = CImMimeHeader::NewLC();
            mimeHeader->RestoreL( *store );

            TInt count = mimeHeader->ContentTypeParams().MdcaCount();
            INFO_1("# of CT params: %d", count);
            for ( TInt i = 0; i < count; i++ )
                {
                TPtrC8 key8 = mimeHeader->ContentTypeParams().MdcaPoint( i );
                INFO_1("%S", &key8);
                TPtr16 keyUppercase16 = HBufC::NewLC( key8.Length() )->Des();
                keyUppercase16.Copy( key8 );
                keyUppercase16.UpperCase();
                if ( keyUppercase16.Compare( KCharsetTag ) == 0 &&
                     count >= i+1 ) // prevent possible indexing over array limits
                    {
                    // Starting to append text to the current content-type header..
                    // First, add ';'
                    aContentType.Append( KCharSemicolon );

                    // Then, 'CHARSET'
                    aContentType.Append( keyUppercase16 );

                    // '='
                    aContentType.Append( KCharEquals );

                    // Finally, the actual charset value (e.g. 'utf-8' or similar)
                    TPtrC8 value8 = mimeHeader->ContentTypeParams().MdcaPoint( i+1 );
                    TPtr16 value16 = HBufC::NewLC( value8.Length() )->Des();
                    value16.Copy( value8 );
                    aContentType.Append( value16 );
                    CleanupStack::PopAndDestroy(); // value16
                    }
                CleanupStack::PopAndDestroy(); // keyUppercase16
                } // for loop
            CleanupStack::PopAndDestroy( mimeHeader );
            }
        CleanupStack::PopAndDestroy( store );
        }
    CleanupStack::PopAndDestroy( cEntry );
    }
// </cmail>
