ipsservices/ipssosplugin/src/ipsplgmsgmapper.cpp
changeset 0 8466d47a6819
child 1 12c456ceeff2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipsservices/ipssosplugin/src/ipsplgmsgmapper.cpp	Thu Dec 17 08:39:21 2009 +0200
@@ -0,0 +1,1574 @@
+/*
+* 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 && 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);
+        CleanupStack::Pop( result );
+        }
+    SetFetchStateL( aEntry, aMessageId.Id(), ETrue, *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>