mobilemessaging/postcard/postcardsrc/PostcardOperationOpen.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 11:55:08 +0200
changeset 4 e9eae96aa117
parent 0 72b543305e3a
permissions -rw-r--r--
Revision: 201001 Kit: 201004

/*
* Copyright (c) 2005 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:  
*       CPostcardOperationOpen
*
*/



// ========== INCLUDE FILES ================================

#include <AknsConstants.h>
#include <data_caging_path_literals.hrh> 
#include <apmstd.h>  // TDataType
#include <gulicon.h> // CGulIcon
#include <txtetext.h> // For TDocumentStorage

#include <MsgMediaInfo.h>
#include <MsgMedia.hrh>
#include <MsgImageInfo.h>
#include <MsgMediaResolver.h>		// for CMmsMediaResolver
#include <MsgTextInfo.h>
#include <msgtextutils.h>
#include <MsgMimeTypes.h>

#include <IHLImageFactory.h>
#include <IHLViewerFactory.h>
#include <MIHLFileImage.h>
#include <MIHLBitmap.h>
#include <MIHLImageViewer.h>

#include <mmsvattachmentmanager.h>		// AttachmentManager
#include <mmsvattachmentmanagersync.h>	// AttachmentManagerSync
#include <cmsvmimeheaders.h>		// MimeHeaders
#include <mmsclient.h>

#include <vprop.h>	// VCardPropertyValue
#include <cntitem.h>
#include <vcard.h>
#include <cntfldst.h> // CContactTextField

#include <StringLoader.h>			// for StringLoader

#include <Postcard.rsg>       	// for Postcard resources
#include "PostcardLaf.h"
#include "PostcardUtils.h"
#include "Postcard.hrh"
#include "PostcardOperationOpen.h"
#include "PostcardRecipientWrapper.h"
#include "PostcardContact.h"
#include "PostcardPrivateCRKeys.h"  // cenrep keys
#include "PostcardCenRep.h"

// ========== EXTERNAL DATA STRUCTURES =====================

// ========== EXTERNAL FUNCTION PROTOTYPES =================

// ========== CONSTANTS ====================================

// ========== MACROS =======================================

// ========== LOCAL CONSTANTS AND MACROS ===================

// This was "FN" in 3.0-3.1 but OMA says it should be "N". Accept
// both when opening and be compatible with both.
_LIT8(KPostcardVCardName, "N");
_LIT8(KPostcardVCardNameFN, "FN");
_LIT8(KPostcardVCardAddr, "ADR");

// ========== MODULE DATA STRUCTURES =======================

// ========== LOCAL FUNCTION PROTOTYPES ====================

// ========== LOCAL FUNCTIONS ==============================

// ========== MEMBER FUNCTIONS =============================

// ---------------------------------------------------------
// CPostcardOperationOpen::NewL
// ---------------------------------------------------------
CPostcardOperationOpen* CPostcardOperationOpen::NewL(
            MPostcardOperationObserver& aObserver,
            CPostcardDocument& aDocument,
            CPostcardAppUi& aAppUi,
            RFs& aFs )
    {
    CPostcardOperationOpen* self = 
        new ( ELeave ) CPostcardOperationOpen( aObserver, aDocument, aAppUi, aFs );
    CleanupStack::PushL( self );
    self->ConstructL( );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::CPostcardOperationOpen
// ---------------------------------------------------------
CPostcardOperationOpen::CPostcardOperationOpen(
            MPostcardOperationObserver& aObserver,
            CPostcardDocument& aDocument,
            CPostcardAppUi& aAppUi,
            RFs& aFs ) :
    CPostcardOperation( aObserver, aDocument, aAppUi, aFs ),
    iSpecialFormat( aAppUi.CenRep().GetString( KPocaKeyServiceSpecialFormat ) ),
    iSeparatorChar( aAppUi.CenRep().GetString( KPocaKeyServiceSeparator ) )
    {
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::CPostcardOperationOpen
// ---------------------------------------------------------
CPostcardOperationOpen::~CPostcardOperationOpen( )
    {
    if( iEditFile )
    	{
    	iEditFile->Close();
    	delete iEditFile;
    	}
    delete iImageInfo;
    delete iImageProcessor;
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::Start
// ---------------------------------------------------------
void CPostcardOperationOpen::Start( TInt /*aArgument*/ )
    {
    ToState( EPostcardOpenInitializing );
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::Launch
// ---------------------------------------------------------
void CPostcardOperationOpen::DoLaunchStepL()
    {
    iFlags &= ~EPostcardOpenRequestActive;
    switch ( iLaunchState )
        {
        case EPostcardOpenInitializing:
            {
            InitL( );
            iDocument.InitializeL( iFs );
            ToState( EPostcardOpenCheckAttas );
            break;
            }
        case EPostcardOpenCheckAttas:
            {
            if( !CheckAttasL( ) )
            	{ // If there was error, this has been completed already
	            ToState( EPostcardOpenHandleImage );            		
            	}
            else
                {
                iLaunchState = EPostcardOpenError;
                }
            break;
            }
        case EPostcardOpenHandleImage:
            {
            DoHandleImageL( );
            break;
            }
        case EPostcardOpenProcessImage:
            {
            DoStartProcessImageL( );
            break;
            }
        case EPostcardOpenHandleText:
            {
            DoHandleTextL( );
            break;
            }
        case EPostcardOpenHandleRecipient:
            {
            DoHandleRecipientL( );
            ToState( EPostcardOpenFinished );
            break;
            }
        case EPostcardOpenFinished:
            {
            UnInitL( );
            iObserver.PostcardOperationEvent(
                EPostcardOperationOpen,
                EPostcardOperationComplete ); 
            break;
            }
        default:
            UnInitL( );
            iObserver.PostcardOperationEvent(
                EPostcardOperationOpen,
                EPostcardOperationError ); 
            break;
        }
    }

// ---------------------------------------------------------
// DoHandleL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoHandleImageL( )
    {
    if ( iImage != 0 )
        {
        if( iDocument.MessageType( ) == EPostcardSendAs )
        	{ // SendAs -> Lets see what's inside
			RFile imageFile = iManager->GetAttachmentFileL( iImage );
            CleanupClosePushL( imageFile );
			iImageInfo = iDocument.MediaResolver()->CreateMediaInfoL( imageFile );
			iDocument.MediaResolver()->ParseInfoDetailsL( iImageInfo, imageFile );

            // Lets first check if the file seems to be ok for inserting
            if( iImageInfo->Protection( ) )
                {
                CleanupStack::PopAndDestroy( &imageFile ); // imageFile
				ToErrorState( R_POSTCARD_CANNOT_SEND_PROTECTED );
				return;
                }
            if( iImageInfo->Corrupt( ) )
		        {
		        // Corrupt is set to only supported media types
                CleanupStack::PopAndDestroy( &imageFile ); // imageFile
				ToErrorState( R_POSTCARD_OBJECT_CORRUPTED );
				return;
		        }
		    else if ( !iDocument.IsImage( iImageInfo->MimeType( ) ) )
		        {
                CleanupStack::PopAndDestroy( &imageFile ); // imageFile
				ToErrorState( R_POSTCARD_FORMAT_NOT_SUPPORTED );
				return;
		        }
		    else if ( iImageInfo->MediaType() != EMsgMediaImage )
		        {
                CleanupStack::PopAndDestroy( &imageFile ); // imageFile
				ToErrorState( R_POSTCARD_FORMAT_NOT_SUPPORTED );
				return;
		        }
		    else if ( static_cast<CMsgImageInfo*>(iImageInfo)->IsAnimation( ) )
		        {
                CleanupStack::PopAndDestroy( &imageFile ); // imageFile
				ToErrorState( R_POSTCARD_FORMAT_NOT_SUPPORTED );
				return;
		        }

			TSize dimensions = static_cast<CMsgImageInfo*>(iImageInfo)->Dimensions( );
			TInt imageWidth = dimensions.iWidth>dimensions.iHeight?dimensions.iWidth:dimensions.iHeight;
			TInt imageHeight = dimensions.iWidth>dimensions.iHeight?dimensions.iHeight:dimensions.iWidth;
            
            TSize maxSize = iDocument.MaxImageDimensions( );
            
            // OK, it seems to be pretty ok, now lets see what we can do
            if( ( 	iImageInfo->MimeType( ).CompareF( KMsgMimeImagePng ) == 0 ) 
            	||  ( 		( imageWidth > maxSize.iWidth || imageHeight > maxSize.iHeight ) 
            			&&	iImageInfo->FileSize( ) > iDocument.MaxImageSize( ) ) )
                { // This is PNG or it's a too wide JPEG file
			    iEditFile = new ( ELeave ) RFile;
			    CMsvAttachment* attachment = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
    			CleanupStack::PushL( attachment );
			    attachment->SetAttachmentNameL( KPostcardFilenameScaled );
		        iManager->CreateAttachmentL( KPostcardFilenameScaled, *iEditFile, attachment, iStatus );
                iFlags |= EPostcardOpenRequestActive;                
		        CleanupStack::Pop( attachment );    // attachment
			    iEditAtta = attachment;
				iLaunchState = EPostcardOpenProcessImage;	
				SetActive();                
                CleanupStack::PopAndDestroy( &imageFile ); // imagefile
                return;
                }
			else if( iImageInfo->FileSize( ) > iDocument.MaxImageSize( ) )
		    	{ // Image is too big, but not too wide -> create empty attachment for now
			    iEditFile = new ( ELeave ) RFile;
			    CMsvAttachment* attachment = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
    			CleanupStack::PushL( attachment );
			    attachment->SetAttachmentNameL( KPostcardFilenameCompressed );
		        iManager->CreateAttachmentL( KPostcardFilenameCompressed, *iEditFile, attachment, iStatus );
                iFlags |= EPostcardOpenRequestActive;
		        CleanupStack::Pop( attachment );    // attachment
			    iEditAtta = attachment;
				iLaunchState = EPostcardOpenProcessImage;	
				SetActive();	        
                CleanupStack::PopAndDestroy( &imageFile ); // imagefile
                return;
				}
			else
			    { // OK, the file is ok, we just need to update the MsvHeaders
			    DoUpdateSendasAttachmentL( iImageInfo->MimeType( ) );
			    }
            CleanupStack::PopAndDestroy( ); // imagefile
        	}
        // Either this is not sendas or the image did not need conversion or compression
		RFile imageFile = iManager->GetAttachmentFileL( iImage );
        CleanupClosePushL( imageFile );
        
        // Scale image to bitmap to be drawn on the screen
        iSourceImage = IHLImageFactory::OpenFileImageL( imageFile );
        iDestinationBitmap = IHLBitmap::CreateL();        
        iImageHandler = IHLViewerFactory::CreateImageViewerL( PostcardLaf::Image( ).Size( ), 
                                                        *iSourceImage, 
                                                        *iDestinationBitmap, 
                                                        *this, 
                                                        TUint32( 0 ) );
        User::LeaveIfError( iImageHandler->SetSourceRectPosition( TPoint( 0, 0 ) ) );
        
	    CleanupStack::PopAndDestroy( &imageFile );   // imageFile
        }
    else
        {
        ToState( EPostcardOpenHandleText );
        }
    }

// ---------------------------------------------------------
// DoStartProcessImageL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoStartProcessImageL( )
	{
	RFile sourceFile = iManager->GetAttachmentFileL( iImage );
	CleanupClosePushL( sourceFile );	
	if( !iImageProcessor )
	    {
        iImageProcessor = new (ELeave) CUniImageProcessor( this );	    
	    }
	TSize maxDimensions = iDocument.MaxImageDimensions( );

	TSize origDimensions = static_cast<CMsgImageInfo*>(iImageInfo)->Dimensions( );

	if( origDimensions.iWidth < origDimensions.iHeight )
		{ // It's portrait so swap max dimensions
		origDimensions = maxDimensions;
		maxDimensions.iWidth = origDimensions.iHeight;
		maxDimensions.iHeight = origDimensions.iWidth;
		}
	
    iImageProcessor->ProcessImageL( 	sourceFile, 
    									*iEditFile, 
    									maxDimensions,
    									KMsgMimeImageJpeg, 
    									ETrue,
    									iDocument.MaxImageSize( ) );
    CleanupStack::PopAndDestroy(); // sourceFile
	}

// ---------------------------------------------------------
// DoUpdateSendasAttachmentL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoUpdateSendasAttachmentL( const TDataType& aMimeType )
    {
	CMsvMimeHeaders* headers = CMsvMimeHeaders::NewLC( );
	if( aMimeType.Des8( ).CompareF( KMsgMimeImageGif ) == 0  )
	    { // Image is gif
    	headers->SetContentLocationL( KPostcardFilenameNoOpGif);
	    }
	else
	    {
    	headers->SetContentLocationL( KPostcardFilenameNoOp );		    	    
	    }
	headers->SetContentTypeL( aMimeType.Des8( ) );

    // As this function is called only when in SendAs
    // there's certainly just one attachment so we index it directly
    CMsvAttachment* attachment = iManager->GetAttachmentInfoL( 0 );
    CleanupStack::PushL( attachment );
	headers->StoreL( *attachment );
    attachment->SetMimeTypeL( aMimeType.Des8( ) );
    iStore->AttachmentManagerExtensionsL( ).ModifyAttachmentInfoL( attachment );
    CleanupStack::Pop( attachment );

	CleanupStack::PopAndDestroy( headers ); 
	            	
	iStore->Commit( );
    // Lets check again (to get right AttachmentIds)
	CheckAttasL( );        
    }

// ---------------------------------------------------------
// ImageProcessingReady
// ---------------------------------------------------------
void CPostcardOperationOpen::ImageProcessingReady( TSize aBitmapSize, TInt aFileSize, TBool /*aCompressed*/ )
	{
    if( iEditFile )
        {
        iEditFile->Close();
        delete iEditFile;
        iEditFile = NULL;            
        }

	TBool imageFits = EFalse;
	TSize maxDimensions = iDocument.MaxImageDimensions( );
	if( aBitmapSize.iWidth <= maxDimensions.iWidth && 
		aBitmapSize.iHeight <= maxDimensions.iHeight )
		{
		imageFits = ETrue;
		}
	else if( aBitmapSize.iWidth <= maxDimensions.iHeight && 
		aBitmapSize.iHeight <= maxDimensions.iWidth )
		{
		imageFits = ETrue;
		}

	if( !imageFits || aFileSize > iDocument.MaxImageSize( ) )
		{ // The process was not successful
		ToErrorState( R_POSTCARD_PROCESS_NOT_SUCCESSFUL );
		return;
		}

    // Call this to start scaling to screen (deletes the previous image etc..)
    TRAPD( err, StartScalingToScreenL( ) );
    if( err )
        {
        ToErrorState( R_POSTCARD_FORMAT_NOT_SUPPORTED );
        }
	}

// ---------------------------------------------------------
// StartScalingToScreenL
// ---------------------------------------------------------
void CPostcardOperationOpen::StartScalingToScreenL( )
    {
    TInt oldImage =  TMsvIdToIndexL( iImage );
    iStore->AttachmentManagerExtensionsL( ).RemoveAttachmentL( oldImage );
	iStore->Commit( );

    DoUpdateSendasAttachmentL( iImageInfo->MimeType( ) );
    
    // Lets add headers
	RFile imageFile = iManager->GetAttachmentFileL( iImage );
	CleanupClosePushL( imageFile );
	
    iSourceImage = IHLImageFactory::OpenFileImageL( imageFile );
    iDestinationBitmap = IHLBitmap::CreateL();
    
    iImageHandler = IHLViewerFactory::CreateImageViewerL( PostcardLaf::Image( ).Size( ), 
                                                    *iSourceImage, 
                                                    *iDestinationBitmap, 
                                                    *this, 
                                                    TUint32( 0 ) );
    User::LeaveIfError( iImageHandler->SetSourceRectPosition( TPoint( 0, 0 ) ) );
	        	
    CleanupStack::PopAndDestroy( &imageFile ); // imageFile
    }

// ---------------------------------------------------------
// ViewerBitmapChangedL
// ---------------------------------------------------------
void CPostcardOperationOpen::ViewerBitmapChangedL()
    {
    CFbsBitmap* bitmap = new (ELeave) CFbsBitmap;
    CFbsBitmap* mask = NULL;
    bitmap->Duplicate( iDestinationBitmap->Bitmap().Handle( ) );
    
    if ( iDestinationBitmap->HasMask() )
        {
        mask = new (ELeave) CFbsBitmap;
        mask->Duplicate( iDestinationBitmap->Mask().Handle( ) );
        }
    
    delete iDestinationBitmap;
    iDestinationBitmap = NULL;
    
    delete iSourceImage;
    iSourceImage = NULL;
    
    delete iImageHandler;
    iImageHandler = NULL;
    
    iLaunchState = EPostcardOpenHandleText;
        
    CGulIcon* icon = NULL;
    TRAPD( err, icon = CGulIcon::NewL( bitmap, mask ) );

    if( err )
        { // Lets nullify it if there was an error
        icon = NULL;
        }
        
	iAppUi.SetImage( icon );

	CompleteSelf( KErrNone );        
        
    }

// ---------------------------------------------------------
// ViewerError
// ---------------------------------------------------------
void CPostcardOperationOpen::ViewerError( TInt /*aError*/ )
    {
	ToErrorState( R_POSTCARD_FORMAT_NOT_SUPPORTED );
    }

// ---------------------------------------------------------
// DoHandleTextL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoHandleTextL( )
    {
/*    if ( iText != 0 )
        { // Lets externalize the text itself
        RFile file = iManager->GetAttachmentFileL( iText );
        
		CMmsTextInfo* info = static_cast<CMmsTextInfo*>(iDocument.MediaResolver()->ResolveFileTransferInfoL( file ));
		CleanupStack::PushL( info );
        TUint attaCharset = info->CharacterSet();
        CleanupStack::PopAndDestroy( info );
        RFileReadStream input_stream( file );

        CPlainText::TImportExportParam param;
        param.iForeignEncoding = attaCharset; //charconvCharsetID;
        param.iOrganisation = CPlainText::EOrganiseByParagraph; 
        CPlainText::TImportExportResult result;
        TRAPD( error, text->ImportTextL( 0, input_stream, param, result) );
        input_stream.Close( );
        file.Close( );
        }
*/

    TPostcardMsgType type = iDocument.MessageType( );
    if( iText > 0 && type == EPostcardSendAs )
        { // If type is SendAs and file is from Phonebook -> Handle this case first
        CMsvAttachment* atta = iManager->GetAttachmentInfoL( iText );
        CleanupStack::PushL( atta );
        if( atta->AttachmentName( ).Find( _L("X-Nokia-PhonebookId_") ) != -1  )
            {
            // All right, it was from Phonebook
            RFile file = iManager->GetAttachmentFileL( iText );
            CleanupClosePushL( file );

            CPostcardContact* contact = CPostcardContact::NewL( iFs );
            CleanupStack::PushL( contact );
            contact->FetchContactL( file );
            DoHandlePhonebookSendAsL( *contact );
            CleanupStack::PopAndDestroy( 3, atta ); // contact, file, atta

			ToState( EPostcardOpenFinished );
			return;                     
            }
        // Ok, it was not from Phonebook, so lets continue normally..
        CleanupStack::PopAndDestroy( atta );
        }

    CPlainText* text = CPlainText::NewL( CEditableText::ESegmentedStorage, KPostcardDefaultGreetingSegment );
    CleanupStack::PushL( text );

    if ( iText > 0 )
        { // Lets externalize the text itself
        RFile file = iManager->GetAttachmentFileL( iText );
        CleanupClosePushL( file );

        CMsvAttachment* atta = iManager->GetAttachmentInfoL( iText );
        CleanupStack::PushL( atta );
        
        CMsvMimeHeaders* msvMime = CMsvMimeHeaders::NewLC();
        msvMime->RestoreL( *atta );
        
        TUint attaCharset = msvMime->MimeCharset( ); // only charset needed
        
        CleanupStack::PopAndDestroy( 2, atta ); // mime, atta

        if( attaCharset == 0 )
            {
            //assume US-ASCII - mandated by RFC 2046
            attaCharset = KMmsUsAscii;
            }
  
        TUint finalCharset = iDocument.TextUtils()->MibIdToCharconvIdL( attaCharset );
          
        RFileReadStream input_stream( file );

        CPlainText::TImportExportParam param;
        param.iForeignEncoding = finalCharset;
        param.iOrganisation = CPlainText::EOrganiseByParagraph; 
        CPlainText::TImportExportResult result;
        TRAP_IGNORE( text->ImportTextL( 0, input_stream, param, result) );
        input_stream.Close( );
        CleanupStack::PopAndDestroy( &file ); // file
        }

    if( type == EPostcardSendAs )
        {
        // Otherwise there's no text or text is just text
	    CleanupStack::Pop( text ); 
	    iAppUi.SetTextL( *text ); // AppUI takes the ownership        		
		ToState( EPostcardOpenHandleRecipient );
	    return;
        }
	else if( 	type == EPostcardForward || 
        		type == EPostcardEdit || 
        		type == EPostcardSent ||
        		type == EPostcardDraft )
        {
        // The recipient might be added into the greeting text only when the entry
        // is opened from Sent folder or is forwarded or edited
        if( iSpecialFormat.Length( ) > 0 && text->DocumentLength( ) > 0 && iRecipient == 0 )
			{ // if special send format is defined and there's no separate recipient attachment
		    TPtrC16 textBuf ( text->Read( 0 ) );
            if( textBuf.Find( iSeparatorChar ) != KErrNotFound )
                { 	// There are separators -> this msg has been sent
					// lets dig the recipient info from the text
			    DoHandleSpecialFormatOpenL( *text );
				CleanupStack::PopAndDestroy( text );
    			ToState( EPostcardOpenFinished );
				return;         
                }
            else
                {
                // There are no separators -> we can normally continue
                // and use "text" as the real greeting text
                }
			}
        }
    CleanupStack::Pop( text ); 
    iAppUi.SetTextL( *text ); // AppUI takes the ownership
    ToState( EPostcardOpenHandleRecipient );
    }

// ---------------------------------------------------------
// DoHandleRecipientL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoHandleRecipientL( )
    {
    CContactCard* card = CContactCard::NewLC();

    if( iRecipient != 0 )
        {
        TPostcardMsgType type = iDocument.MessageType( );

        if( type == EPostcardForward )
            { // We are opening a forward entry -> delete VCard
            TInt oldImage = TMsvIdToIndexL( iRecipient );
            iStore->AttachmentManagerExtensionsL( ).RemoveAttachmentL( oldImage );
            iCommit = ETrue;    		
            }
        else
            {
            ParseRecipientL( *card );
            }
        }
    CleanupStack::Pop( card );
	iAppUi.SetRecipientL( *card ); //ownership moves..    the function does not leave before card is set as a member
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::ParseRecipientL
// ---------------------------------------------------------
void CPostcardOperationOpen::ParseRecipientL( CContactCard& aContactCard )
    {
    RFile file = iManager->GetAttachmentFileL( iRecipient );
    CleanupClosePushL( file );

    CParserVCard* parser = CParserVCard::NewL();
    CleanupStack::PushL(parser);

    RFileReadStream vCardStream( file );
    vCardStream.PushL();
    MStreamBuf* buf = vCardStream.Source();
    if( !buf )
        {
        User::Leave( KErrGeneral );
        }

    parser->InternalizeL(vCardStream);

    vCardStream.Close();
    vCardStream.Pop();

    // Does not take ownership
    CArrayPtr<CParserProperty>* arr = parser->ArrayOfProperties(EFalse);
    for( TInt a = 0; a < arr->Count( ); a++ )
        {
        CParserProperty* prop = arr->At( a );
        TPtrC8 propName = prop->Name();
        CParserPropertyValue *propValue = prop->Value();
        if( propValue )
            {
            if( propValue->Uid().iUid == KVersitPropertyCDesCArrayUid )
                {
                CParserPropertyValueCDesCArray* arrayProperty = 
			        reinterpret_cast<CParserPropertyValueCDesCArray*>( propValue );
                CDesCArray* values = arrayProperty->Value();
                if( propName.Compare( KPostcardVCardName ) == 0 )
                    {
                    TPostcardUtils::AddContactFieldL( aContactCard,
                        TPostcardUtils::ContactItemNameFromId( EPostcardAddressName ),
                        values->MdcaPoint( 0 ) );
                    }
                else if( propName.Compare( KPostcardVCardAddr ) == 0 )
                    {
                    // This is the order items are read from the VCard store
                    static const TInt indexToId[] =
                        {
                        0, // PO Box in the array will be ignored
                        EPostcardAddressInfo, EPostcardAddressStreet,
                        EPostcardAddressCity, EPostcardAddressState,
                        EPostcardAddressZip, EPostcardAddressCountry
                        };
                    const TInt KNumIds =
                        sizeof( indexToId ) / sizeof( indexToId[0] );
                    TPostcardMsgType type = iDocument.MessageType();
                    // Skip PO Box at beginning of the array
                    for (TInt i = 1; i < values->Count() && i < KNumIds; i++)
                        {
                        TPtrC value = values->MdcaPoint(i);
                        // If viewing a sent postcard, all available fields are
                        // initialized. Otherwise the field is initialized if
                        // it's visible in address dialog (max. length is not zero).
                        if( value.Length() &&
                          ( type == EPostcardSent || iAppUi.MaxTextLength( indexToId[i] ) ) )
                            {
                            TPostcardUtils::AddContactFieldL( aContactCard,
                                TPostcardUtils::ContactItemNameFromId( indexToId[i] ),
                                value );
                            }
                        }
                    }
                }
            else if( propValue->Uid().iUid == KVersitPropertyHBufCUid )
                {
                // Postcard before S60 3.2 release used FN (full name) property.
                // Since 3.2, N property is used
                if( propName.Compare( KPostcardVCardNameFN ) == 0 )
                    {
                    TPostcardUtils::AddContactFieldL( aContactCard,
                        TPostcardUtils::ContactItemNameFromId( EPostcardAddressName ),
                        static_cast<CParserPropertyValueHBufC*>( propValue )->Value() );
                    }
                }
            }
        }
    CleanupStack::PopAndDestroy( 2, &file ); // file, parser
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::ConstructL
// ---------------------------------------------------------
void CPostcardOperationOpen::ConstructL( )
    {
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::DoCancel
// ---------------------------------------------------------
void CPostcardOperationOpen::DoCancel( )
    {
    if( iManager )
        {
        if( iFlags & EPostcardOpenRequestActive )
            {
            iManager->CancelRequest();        
            }
        }
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::RunL
// ---------------------------------------------------------
void CPostcardOperationOpen::RunL( )
    {
    DoLaunchStepL( );
    }

// ---------------------------------------------------------
// RunError
// ---------------------------------------------------------
TInt CPostcardOperationOpen::RunError( TInt aError )
    {
    iLaunchState = -1;
    SetError( aError );
    iObserver.PostcardOperationEvent(
        EPostcardOperationOpen,
        EPostcardOperationError );     
    return KErrNone;
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::DoHandlePhonebookSendAsL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoHandlePhonebookSendAsL( CPostcardContact& aContact )
    {
    CPostcardRecipientWrapper* recWrapper = 
        CPostcardRecipientWrapper::NewL( iDocument, iAppUi, aContact );
    CleanupStack::PushL( recWrapper );
    
    // First remove wait note so that the dialog can be shown
    iAppUi.RemoveWaitNote( );

    // Location asking is done in recipientWrapper
    CPostcardContact::TLocation location;
    TBool hasLocation = recWrapper->AskLocationL( location );
    CleanupStack::PopAndDestroy( recWrapper );

    iAppUi.ShowWaitNoteL( R_POSTCARD_WAIT_OPENING, EFalse );
        
    if ( !hasLocation )
        {
        // Contact not found or cancel selected..
        CContactCard* card = CContactCard::NewL();
        // Ownership moves..
        // The function does not leave before card is set as a member
	    iAppUi.SetRecipientL( *card );
        CPlainText* emptyText = CPlainText::NewL(
            CEditableText::ESegmentedStorage, KPostcardDefaultGreetingSegment );
        // Ownership moves..
        // The function does not leave before card is set as a member
    	iAppUi.SetTextL( *emptyText );
        }
    else
        {
        // Otherwise location now tells the chosen location

        CContactCard* card = CContactCard::NewLC();
        UpdateContactCardL( *card, aContact, location );

        CleanupStack::Pop( card ); // card
        // Ownership moves..
        // The function does not leave before card is set as a member
	    iAppUi.SetRecipientL( *card );
	    iDocument.SetChanged( EPostcardRecipient );
	    iCommit = ETrue; // Remove the original atta way as it's just a phonebook atta
        MMsvAttachmentManagerSync& managerSync = iStore->AttachmentManagerExtensionsL();
	    TInt textIndex = TMsvIdToIndexL( iText );
        managerSync.RemoveAttachmentL( textIndex );
        iText = 0;
        CPlainText* emptyText = CPlainText::NewL(
            CEditableText::ESegmentedStorage, KPostcardDefaultGreetingSegment );
        // Ownership moves..
        // The function does not leave before card is set as a member
	    iAppUi.SetTextL( *emptyText );
        }
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::UpdateContactCardL
// ---------------------------------------------------------
void CPostcardOperationOpen::UpdateContactCardL( CContactCard& aCard,
    CPostcardContact& aContact, CPostcardContact::TLocation aLocation )
    {

    // Get contact name, combined last and first name
    HBufC* contactName = aContact.GetNameLC();
    if ( contactName->Length() )
        {
        // Create a new CContactCard field
        AddContactCardFieldL( aCard, *contactName, EPostcardAddressName );
        }
   CleanupStack::PopAndDestroy( contactName ); // contactName

    // Handle rest of the address fields
    for( TInt i = EPostcardAddressInfo; i <= EPostcardAddressCountry; i++)
        {
        // Initialize address field only if it's visible to user
        // (max. length is not zero)
        if ( iAppUi.MaxTextLength( i ) )
            {
            HBufC* fieldText = aContact.GetAddressFieldLC( aLocation,
                CPostcardContact::ControlIdToAddrField( i ) );
            if ( fieldText->Length() )
                {
                // Create a new CContactCard field
                AddContactCardFieldL( aCard, *fieldText, i );
                }
            CleanupStack::PopAndDestroy( fieldText ); // fieldText
            }
        }
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::AddContactCardFieldL
// ---------------------------------------------------------
void CPostcardOperationOpen::AddContactCardFieldL( CContactCard& aCard,
    const TPtrC& aText, TInt aFieldSelect )
    {
    TPostcardUtils::AddContactFieldL(aCard,
        TPostcardUtils::ContactItemNameFromId( aFieldSelect ),
        aText);
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::ParseSpecialFormatTextL
// ---------------------------------------------------------
void CPostcardOperationOpen::ParseSpecialFormatTextL( CPlainText& aText,
    CContactCard& aCard, CPlainText& aGreeting )
    {
    TPtrC format( iSpecialFormat );
    TPtrC srcText( aText.Read( 0 , aText.DocumentLength() ) );

    const TInt KTagLen = 3;

    // Find tags from format and parse source text accordingly
    TInt tag;
    TInt position = TPostcardUtils::NextTag( format, tag );
    // Eat prefix and tag from format. Assume the format has at least one tag.
    format.Set( format.Right( format.Length() - position - KTagLen ) );
    // Eat prefix from source text
    srcText.Set( srcText.Right( srcText.Length() - position ) );
    while( position != KErrNotFound )
        {
        TInt newTag;
        position = TPostcardUtils::NextTag( format, newTag );
        TPtrC text;
        if ( position != KErrNotFound )
            {
            // Separator string from previous tag to this one. This cannot
            // be empty, otherwise text fields cannot be separated from each other.
            TPtrC separator( format.Left( position ) );
            TInt separatorPos = srcText.Find( separator );
            TInt srcLen = srcText.Length() - position - separatorPos;
            if ( separatorPos == KErrNotFound || srcLen < 0 )
                {
                // Unexpected. Cannot parse.
                return;
                }
            text.Set( srcText.Left( separatorPos ) );
            // Eat separator and tag from format
            format.Set( format.Right( format.Length() - position - KTagLen ) );
            // Eat text and separator from source
            srcText.Set( srcText.Right( srcLen ) );
            }
        else
            {
            // No new tags. Text is source string minus postfix
            text.Set( srcText.Left( srcText.Length() - format.Length() ) );
            }
        if ( text.Length() )
            {
            if ( tag == 0 )
                {
                aGreeting.InsertL( 0, text );
                }
            else
                {
                if ( iDocument.MessageType() != EPostcardForward )
                    { // Add the fields only when not in Forward mode
                    TPostcardUtils::AddContactFieldL(aCard,
                        TPostcardUtils::ContactItemNameFromTag( tag ),
                        text);
                    }
                }
            }
        tag = newTag;
        }
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::DoHandleSpecialFormatOpenL
// ---------------------------------------------------------
void CPostcardOperationOpen::DoHandleSpecialFormatOpenL( CPlainText& aText )
    {
    CContactCard* newCard = CContactCard::NewLC(); // New ContactCard
    CPlainText* newText = CPlainText::NewL( CEditableText::ESegmentedStorage, KPostcardDefaultGreetingSegment ); // New PlainText for greeting text
    CleanupStack::PushL( newText );

    ParseSpecialFormatTextL( aText, *newCard, *newText );

    CleanupStack::Pop(); // newText
    iAppUi.SetTextL( *newText ); // AppUI takes the ownership
    CleanupStack::Pop(); // newCard
    iAppUi.SetRecipientL( *newCard );
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::ToState
// ---------------------------------------------------------
void CPostcardOperationOpen::ToState( TPostcardOpenState aState )
    {
    iLaunchState = aState;
    CompleteSelf( KErrNone );
    }

// ---------------------------------------------------------
// CPostcardOperationOpen::ToErrorState
// ---------------------------------------------------------
void CPostcardOperationOpen::ToErrorState( TInt aResourceId )
    {
    SetError( aResourceId );
    iLaunchState = EPostcardOpenError;
    CompleteSelf( KErrCancel );
    }

// EOF