mobilemessaging/postcard/postcardsrc/PostcardOperationSave.cpp
changeset 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mobilemessaging/postcard/postcardsrc/PostcardOperationSave.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1003 @@
+/*
+* 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:  
+*       CPostcardOperationSave
+*
+*/
+
+
+
+// ========== INCLUDE FILES ================================
+
+#include <badesca.h>
+#include <AknsConstants.h>
+#include <data_caging_path_literals.hrh> 
+#include <apmstd.h>  // TDataType
+#include <mmsvattachmentmanager.h>	// AttachmentManager
+#include <mmsvattachmentmanagersync.h>	// AttachmentManager
+#include <cmsvmimeheaders.h>		// MimeHeaders
+
+#include <StringLoader.h>			// For StringLoader
+
+#include <mmsclient.h>
+#include <mmsconst.h>
+#include <mmsmsventry.h>
+
+#include <MsgMimeTypes.h>
+
+#include <vprop.h>	// VCardPropertyValue
+#include <cntitem.h>
+#include <vcard.h>
+#include <cntfldst.h> // CContactTextField
+
+#include <Postcard.rsg>
+#include "PostcardOperationSave.h"
+#include "PostcardUtils.h"
+#include "Postcard.hrh"
+#include "PostcardAddressForm.h"
+#include "PostcardPrivateCRKeys.h"  // cenrep keys
+#include "PostcardCenRep.h"
+
+// ========== EXTERNAL DATA STRUCTURES =====================
+
+// ========== EXTERNAL FUNCTION PROTOTYPES =================
+
+// ========== CONSTANTS ====================================
+
+// ========== MACROS =======================================
+
+// ========== LOCAL CONSTANTS AND MACROS ===================
+
+_LIT8( KReplaceChar8, "_" );
+_LIT8( KPostcardVCardName, "N"); // This was "FN" in 3.0-3.1 but OMA says it should be "N"
+_LIT8( KPostcardVCardAddr, "ADR");
+_LIT8( KPostcardGreetingXParameter, "X-MMS-GREETING-TEXT" );
+
+// ========== MODULE DATA STRUCTURES =======================
+
+// ========== LOCAL FUNCTION PROTOTYPES ====================
+
+// ========== LOCAL FUNCTIONS ==============================
+
+// ========== MEMBER FUNCTIONS =============================
+
+// ---------------------------------------------------------
+// NewL
+// ---------------------------------------------------------
+CPostcardOperationSave* CPostcardOperationSave::NewL(
+            MPostcardOperationObserver& aObserver,
+            CPostcardDocument& aDocument,
+            CPostcardAppUi& aAppUi,
+            RFs& aFs,
+            CContactCard& aContact,
+            CPlainText& aPlainText )
+    {
+    CPostcardOperationSave* self = 
+        new ( ELeave ) CPostcardOperationSave( aObserver, aDocument, aAppUi, aFs, aContact, aPlainText );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------
+// CPostcardOperationSave
+// ---------------------------------------------------------
+CPostcardOperationSave::CPostcardOperationSave(
+            MPostcardOperationObserver& aObserver,
+            CPostcardDocument& aDocument,
+            CPostcardAppUi& aAppUi,
+            RFs& aFs,
+			CContactCard& aContact,
+			CPlainText& aPlainText ) :
+    CPostcardOperation( aObserver, aDocument, aAppUi, aFs ),
+    iContact( aContact ),
+    iPlainText( aPlainText )
+    {
+    }
+
+// ---------------------------------------------------------
+// CPostcardOperationSave
+// ---------------------------------------------------------
+CPostcardOperationSave::~CPostcardOperationSave( )
+    {
+    Cancel( );
+    if( iTempFile )
+        {
+        iTempFile->Close( );
+        delete iTempFile;
+        }
+    delete iSendOperation;
+    delete iCharConv;
+    delete iRecipientName;
+    }
+
+// ---------------------------------------------------------
+// Start
+// ---------------------------------------------------------
+void CPostcardOperationSave::Start( TInt aArgument )
+    {
+    if( aArgument )
+    	{
+    	iFlags |= EPostcardSaveSend;
+    	}
+    if( iAppUi.CenRep().GetString( KPocaKeyServiceSpecialFormat ).Length() > 0 &&
+        ( iFlags & EPostcardSaveSend ) )
+        { // if special send format is defined and we are sending -> take it into use
+        iFlags |= EPostcardSaveSpecialFormat;
+        }
+    iLaunchState = EPostcardSaveInitializing;
+    CompleteSelf( KErrNone );
+    }
+
+// ---------------------------------------------------------
+// DoLaunchStepL
+// ---------------------------------------------------------
+void CPostcardOperationSave::DoLaunchStepL( )
+    {
+    iFlags &= ~EPostcardSaveRequestActive;
+    switch ( iLaunchState )
+        {        
+    	case EPostcardSaveInitializing:
+    		{
+    		InitL( );			
+            iLaunchState = EPostcardSaveCheckAttas;
+            CompleteSelf( KErrNone );
+            break;    			
+    		}
+        case EPostcardSaveCheckAttas:
+            {
+            if( !CheckAttasL( ) )
+            	{ // If there was an error, this has been completed already
+	            iLaunchState = EPostcardSaveRemoveOldAttas; 
+	            CompleteSelf( KErrNone );            		
+            	}
+            break;
+            }
+        case EPostcardSaveRemoveOldAttas:
+            {
+            RemoveOldAttasL( );
+            // Let's see the next phase..
+            if( iPlainText.DocumentLength( ) > 0 && iDocument.Changed( EPostcardText ) )
+                { // We have text and it has changed -> so save it..
+                iLaunchState = EPostcardSaveCreateTextAtta;
+                }
+            else if ( ( iFlags & EPostcardSaveSend ) &&
+                      ( iFlags & EPostcardSaveSpecialFormat ) )
+                { // Text has not changed, but we have special format on -> so create text atta anyway
+                iLaunchState = EPostcardSaveCreateTextAtta;                
+                }
+            else if ( iDocument.Changed( EPostcardRecipient ) )
+                { // We are not sending special format, but the recipient has changed
+                iLaunchState = EPostcardSaveCreateRecipientAtta;
+                }
+            else if(   !iSmil && 
+                    (  iFlags & EPostcardSaveSend ) &&
+                    !( iFlags & EPostcardSaveSpecialFormat ) )
+                { // Text and recipient have not changed, but we are sending OMA Postcard and there's no SMIL yet
+                iLaunchState = EPostcardSaveCreateSmilAtta;
+                }
+            else
+                {
+                iLaunchState = EPostcardSaveUninit;
+                }
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveCreateTextAtta:
+            {
+            CreateNewTextAttaL( );
+            iLaunchState = EPostcardSaveSaveTextAtta;
+		    SetActive();    
+            break;
+            }
+        case EPostcardSaveSaveTextAtta:
+            {
+            HandleTextL( );
+            iLaunchState = EPostcardSaveFinalizeTextAtta;
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveFinalizeTextAtta:
+            {
+            FinalizeTextL( );
+            if ( iDocument.Changed( EPostcardRecipient ) &&
+                ( iFlags & EPostcardSaveHasContact ) )
+                { // We are not sending special format, but the recipient has changed
+                iLaunchState = EPostcardSaveCreateRecipientAtta;
+                }
+            else if(   !iSmil && 
+                    (  iFlags & EPostcardSaveSend ) &&
+                    !( iFlags & EPostcardSaveSpecialFormat ) )
+                { // Recipient has not changed, but we are sending OMA Postcard and there's no SMIL yet
+                iLaunchState = EPostcardSaveCreateSmilAtta;
+                }
+            else
+                {
+                iLaunchState = EPostcardSaveUninit;
+                }
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveCreateRecipientAtta:
+            {
+            CreateNewRecipientAttaL( );
+            iLaunchState = EPostcardSaveSaveRecipientAtta;
+		    SetActive( );        	
+            break;
+            }
+        case EPostcardSaveSaveRecipientAtta:
+            {
+            HandleRecipientL( );
+            iLaunchState = EPostcardSaveFinalizeRecipientAtta;
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveFinalizeRecipientAtta:
+            {
+            FinalizeRecipientL( );
+            if( !iSmil && 
+                 ( iFlags & EPostcardSaveSend ) &&
+                !( iFlags & EPostcardSaveSpecialFormat ) )
+                { // Smil is created only to confront OMA Postcard spec
+                iLaunchState = EPostcardSaveCreateSmilAtta;
+                }
+            else
+                {
+                // Smil seems to exists already or we are not sending
+                iLaunchState = EPostcardSaveUninit;                
+                }
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveCreateSmilAtta:
+            {
+            CreateNewSmilAttaL( );
+            iLaunchState = EPostcardSaveSaveSmilAtta;
+		    SetActive( );        	
+            break;
+            }
+        case EPostcardSaveSaveSmilAtta:
+            {
+            HandleSmilL( );
+            iLaunchState = EPostcardFinalizeSmilAtta;
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardFinalizeSmilAtta:
+            {
+            FinalizeSmilL( );
+            iLaunchState = EPostcardSaveUninit;            
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveUninit:
+            {
+            UnInitL( );
+            iLaunchState = EPostcardSaveFinalizeMessage;
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveFinalizeMessage:
+            {
+            FinalizeMessageL( );
+            if( iFlags & EPostcardSaveSend )
+            	{
+	            iLaunchState = EPostcardSaveStartSend;        	            		
+            	}
+            else
+            	{
+	            iLaunchState = EPostcardSaveFinished;        	
+            	}
+            CompleteSelf( KErrNone );
+            break;
+            }
+        case EPostcardSaveStartSend:
+            {
+            StartSendingL( );
+            iLaunchState = EPostcardSaveFinished;        	
+		    SetActive();    
+            break;
+            }
+        case EPostcardSaveFinished:
+            {
+            iObserver.PostcardOperationEvent(
+                EPostcardOperationSave,
+                EPostcardOperationComplete ); 
+            break;
+            }
+        default:
+            iObserver.PostcardOperationEvent(
+                EPostcardOperationSave,
+                EPostcardOperationError ); 
+            break;
+        }
+    }
+
+// ---------------------------------------------------------
+// RemoveOldAttasL
+// ---------------------------------------------------------
+void CPostcardOperationSave::RemoveOldAttasL( )
+	{
+	if( 	iDocument.Changed( EPostcardText ) || 
+		( ( iFlags & EPostcardSaveSend ) && ( iFlags & EPostcardSaveSpecialFormat ) ) )
+		{
+		if( iText )
+			{ // Remove old
+			TInt old = TMsvIdToIndexL( iText );
+			if( old != KErrNotFound )
+			    {
+			    // If there is an old text atta and a no new text atta, we need to
+			    // ask commit here, otherwise old atta would be left with the message.
+			    iCommit = ETrue;
+    			iStore->AttachmentManagerExtensionsL( ).RemoveAttachmentL( old );			    
+			    }
+			}		
+		}
+	if( 	iDocument.Changed( EPostcardRecipient ) || 
+		( ( iFlags & EPostcardSaveSend ) && ( iFlags & EPostcardSaveSpecialFormat ) ) )
+		{
+		if( iRecipient )
+			{ // Remove old
+			TInt old = TMsvIdToIndexL( iRecipient );
+			if( old != KErrNotFound )
+			    {
+    			iStore->AttachmentManagerExtensionsL( ).RemoveAttachmentL( old );
+			    }
+			}		
+		}	
+	}
+	
+// ---------------------------------------------------------
+// CreateNewTextAttaL
+// ---------------------------------------------------------
+void CPostcardOperationSave::CreateNewTextAttaL( )
+	{
+    // Now lets create a new attachment
+    TFileName file( _L("greeting.txt") );
+    StartCreatingEmptyAttachmentL( file );
+	}
+	
+// ---------------------------------------------------------
+// HandleTextL
+// ---------------------------------------------------------
+void CPostcardOperationSave::HandleTextL( )
+	{
+    if( !iCharConv )
+        {
+        iCharConv = CCnvCharacterSetConverter::NewL( );
+        }
+		
+    if ( iCharConv->PrepareToConvertToOrFromL( KCharacterSetIdentifierUtf8, iFs )
+        != CCnvCharacterSetConverter::EAvailable )
+        {
+        User::Leave( KErrNotSupported );
+        }
+	
+    // Delete possible old headers
+    delete iHeaders;
+    iHeaders = NULL;
+    iHeaders = CMsvMimeHeaders::NewL( );
+
+    if( ( iFlags & EPostcardSaveSend ) && !( iFlags & EPostcardSaveSpecialFormat ) )
+        {
+        iHeaders->XTypeParams().AppendL( KPostcardGreetingXParameter );
+        iHeaders->XTypeParams().AppendL( TPtrC8() );	    
+        }
+
+    _LIT(KFileName, "text.txt");
+    iHeaders->SetContentLocationL( KFileName );
+    iHeaders->SetMimeCharset( KMmsUtf8 );
+
+    TInt slash = KMsgMimeTextPlain().Locate( '/' );
+    iHeaders->SetContentTypeL( KMsgMimeTextPlain().Left( slash ) );
+    iHeaders->SetContentSubTypeL( KMsgMimeTextPlain().Mid( slash + 1 ) );
+
+    iTempAtta->SetMimeTypeL( KMsgMimeTextPlain );
+
+    iText = iTempAtta->Id( );
+    RFileWriteStream writer( *iTempFile );
+    writer.PushL( );
+    delete iTempFile; // not close as write does it..
+    iTempFile = NULL;
+    
+    TPtrC16 remainderOfUnicodeText;
+    TInt numCleanupItems = 0;
+    
+    if( (iFlags & EPostcardSaveSend) && (iFlags & EPostcardSaveSpecialFormat ) )
+        {
+        // If we are here -> we are sending so we must have iContact OK
+        HBufC* wholeText = MakeSpecialFormatTextL();
+        CleanupStack::PushL( wholeText );
+        numCleanupItems++;
+
+        remainderOfUnicodeText.Set( wholeText->Des( ) );	
+        
+        // Now we remove the HasContact flag so that the recipient is not stored separately
+		iFlags &=  ~EPostcardSaveHasContact;
+		}
+    else
+        {
+        remainderOfUnicodeText.Set(
+            iPlainText.Read( 0, iPlainText.DocumentLength() ) );
+        }
+
+    // Now remainOfUnicodeText has the stuff needed to store into the greeting text
+    TBuf8<128> outputBuffer;
+    TInt returnValue;
+    do
+        {
+        returnValue = iCharConv->ConvertFromUnicode(
+            outputBuffer, remainderOfUnicodeText );
+        if ( returnValue < 0 )
+            {
+            User::Leave(
+                returnValue == CCnvCharacterSetConverter::EErrorIllFormedInput ?
+                KErrCorrupt:KErrGeneral );
+            }
+        writer.WriteL( outputBuffer );
+        remainderOfUnicodeText.Set( remainderOfUnicodeText.Right(
+            returnValue ) );
+        }
+    while( returnValue );
+
+    CleanupStack::PopAndDestroy( numCleanupItems );
+    writer.Pop( );
+    writer.Close( );	
+	}
+
+// ---------------------------------------------------------
+// MakeSpecialFormatTextL
+// ---------------------------------------------------------
+HBufC* CPostcardOperationSave::MakeSpecialFormatTextL()
+    {
+    CContactItemFieldSet& set = iContact.CardFields( );
+    // Organise the address into an array
+    CDesCArrayFlat* desarr = AddressArrayLC( set );
+
+    const TDesC& replacement =
+        iAppUi.CenRep().GetString( KPocaKeyServiceReplaceString );
+    const TDesC& separator =
+        iAppUi.CenRep().GetString( KPocaKeyServiceSeparator );
+
+    // Add the greeting text.
+    
+    TInt len = iPlainText.DocumentLength();
+    HBufC* buf = HBufC::NewLC( len );
+    *buf = iPlainText.Read( 0, len );
+
+    // Replace illegal chars
+    TPtr des = buf->Des();
+    ReplaceWith( des, separator, replacement );
+    // And add this to the desarr
+    desarr->InsertL( 0, *buf );
+
+    // Read the format string from cenrep
+    const TDesC& specialFormat =
+        iAppUi.CenRep().GetString( KPocaKeyServiceSpecialFormat );
+
+    // Construct the text to be sent. Room for each tag, strings between
+    // tags and prefix and postfix.
+    const TInt KMaxItems = TPostcardUtils::ENumSpecFormatTags * 2 + 1;
+    TPtrC textItems[KMaxItems];
+
+    TPtrC format(specialFormat);
+    TInt position = 0;
+    TInt numItems = 0;
+    while(numItems < KMaxItems - 1 && position != KErrNotFound)
+        {
+        TInt tag;
+        position = TPostcardUtils::NextTag(format, tag);
+        if ( position != KErrNotFound )
+            {
+            textItems[numItems++].Set( format.Left( position ) );
+            textItems[numItems++].Set( desarr->MdcaPoint( tag ) );
+            format.Set( format.Right( format.Length() - position - 3 ) );
+            }
+        else
+            {
+            textItems[numItems++].Set( format );
+            }
+        }
+        
+    // Calculate buffer length needed to fit the whole text
+    TInt buffSize = 0;
+    for( TInt i = 0; i < numItems; i++ )
+        {
+        buffSize += textItems[i].Length();
+        }
+
+    // Copy text into buffer
+    HBufC* dstBuf = HBufC::NewL( buffSize );
+    TPtr dst = dstBuf->Des();
+    for( TInt i = 0; i < numItems; i++ )
+        {
+        dst.Append( textItems[i] );
+        }
+
+    CleanupStack::PopAndDestroy( 2 ); // buff, desarr
+
+    return dstBuf;
+    }
+
+// ---------------------------------------------------------
+// FinalizeTextL
+// ---------------------------------------------------------
+void CPostcardOperationSave::FinalizeTextL( )
+	{
+	iHeaders->StoreL( *iTempAtta );
+	}
+	
+// ---------------------------------------------------------
+// CreateNewRecipientAttaL
+// ---------------------------------------------------------
+void CPostcardOperationSave::CreateNewRecipientAttaL( )
+	{
+    // Now lets create a new attachment
+    TFileName file( _L("recipient.vcf") );
+    StartCreatingEmptyAttachmentL( file );
+	}
+	
+// ---------------------------------------------------------
+// HandleRecipientL
+// ---------------------------------------------------------
+void CPostcardOperationSave::HandleRecipientL( )
+	{
+	if( !iCharConv )
+		{
+	    iCharConv = CCnvCharacterSetConverter::NewL( );
+		}
+    if ( iCharConv->PrepareToConvertToOrFromL( KCharacterSetIdentifierAscii, iFs ) !=
+        CCnvCharacterSetConverter::EAvailable )
+        {
+        User::Leave( KErrNotSupported );
+        }
+
+	// Delete possible old headers
+	delete iHeaders;
+	iHeaders = NULL;
+	iHeaders = CMsvMimeHeaders::NewL( );
+   	iHeaders->SetContentLocationL( _L("recipient.vcf") );
+	
+	TInt slash = KMsgMimeVCard().Locate( '/' );
+    iHeaders->SetContentTypeL( KMsgMimeVCard().Left( slash ) );
+    iHeaders->SetContentSubTypeL( KMsgMimeVCard().Mid( slash + 1 ) );
+	
+	iTempAtta->SetMimeTypeL( KMsgMimeVCard );
+
+    iRecipient = iTempAtta->Id( );    
+        
+    CParserVCard* parser = CParserVCard::NewL();
+    CleanupStack::PushL(parser);
+
+    RFileWriteStream vCardStream( *iTempFile );
+    vCardStream.PushL( );    
+    delete iTempFile; // not close as VCardStream does it..
+    iTempFile = NULL;
+
+	iCharConv->SetReplacementForUnconvertibleUnicodeCharactersL( KReplaceChar8 );
+	
+	CContactItemFieldSet& set = iContact.CardFields( );
+
+    const CContactItemField* field = TPostcardUtils::FieldOrNull( set,
+        TPostcardUtils::ContactItemNameFromId( EPostcardAddressName ) );
+
+    if( field )
+        {
+		CParserPropertyValueHBufC* val =
+		    CParserPropertyValueHBufC::NewL( field->TextStorage()->Text() );
+		CleanupStack::PushL( val );
+		CArrayPtrFlat<CParserParam>* array = new(ELeave)CArrayPtrFlat<CParserParam>(1);
+		CleanupStack::PushL( array );
+		CParserProperty* property = CParserProperty::NewL( *val, KPostcardVCardName, array);
+		CleanupStack::PushL( property );
+		parser->AddPropertyL( property );
+		CleanupStack::Pop( 3, val );				
+		}
+
+	CDesCArrayFlat* desarr = VCardAddressArrayL( set ); // Organise the address into an array
+	CleanupStack::PushL( desarr );
+	// AddressArrayL does not add empty "PO Box" descriptor into the desarr
+	desarr->InsertL( 0, KNullDesC );
+
+    CParserPropertyValue* val = new (ELeave)CParserPropertyValueCDesCArray(desarr);
+    CleanupStack::Pop(desarr);
+	CleanupStack::PushL( val );
+	CArrayPtrFlat<CParserParam>* array = new(ELeave)CArrayPtrFlat<CParserParam>(1);
+	CleanupStack::PushL( array );
+
+	CParserProperty* property = CParserProperty::NewL( *val, KPostcardVCardAddr, array);
+	CleanupStack::PushL( property );
+	parser->AddPropertyL( property );
+	CleanupStack::Pop( 3, val );				
+        
+    parser->ExternalizeL(vCardStream);
+
+    vCardStream.Pop( );
+    vCardStream.Close( );
+
+    CleanupStack::PopAndDestroy(parser);
+	}
+	
+// ---------------------------------------------------------
+// FinalizeRecipientL
+// ---------------------------------------------------------
+void CPostcardOperationSave::FinalizeRecipientL( )
+	{
+	iHeaders->StoreL( *iTempAtta );	
+	}
+
+// ---------------------------------------------------------
+// CreateNewSmilAttaL
+// ---------------------------------------------------------
+void CPostcardOperationSave::CreateNewSmilAttaL( )
+    {
+    // Now lets create a new attachment
+    TFileName file( _L("postcard.smil") );
+    StartCreatingEmptyAttachmentL( file );
+    }
+    
+// ---------------------------------------------------------
+// HandleSmilL
+// ---------------------------------------------------------
+void CPostcardOperationSave::HandleSmilL( )
+    {
+	// Delete possible old headers
+	delete iHeaders;
+	iHeaders = NULL;
+	
+	iHeaders = CMsvMimeHeaders::NewL( );
+   	iHeaders->SetContentLocationL( _L("pres.smil") );
+	
+	TInt slash = KMsgMimeSmil().Locate( '/' );
+    iHeaders->SetContentTypeL( KMsgMimeSmil().Left( slash ) );
+    iHeaders->SetContentSubTypeL( KMsgMimeSmil().Mid( slash + 1 ) );
+    
+	iTempAtta->SetMimeTypeL( KMsgMimeSmil );
+
+    iSmil = iTempAtta->Id( );
+    
+    // Get the name of the image file to be inserted into the SMIL file
+    HBufC* fileName = GetImageFileNameL( );
+    CleanupStack::PushL( fileName );
+	TInt resId = R_POSTCARD_OMA_SMIL;
+	HBufC* smil = StringLoader::LoadL( resId, *fileName );
+    CleanupStack::PopAndDestroy( fileName );
+    CleanupStack::PushL( smil );
+
+    // convert the smil to 8-bit
+    HBufC8* smil8 = HBufC8::NewLC( smil->Des( ).Length( ) );
+    smil8->Des().Copy( smil->Des( ) );
+
+    RFileWriteStream smilStream( *iTempFile );
+    smilStream.PushL( );    
+    
+    delete iTempFile; // not close as smilStream does it
+    iTempFile = NULL;
+
+    smilStream.WriteL( *smil8 );
+
+    smilStream.Close( );
+    smilStream.Pop( );
+    CleanupStack::PopAndDestroy( 2, smil ); // smil, smil8
+    }
+
+// ---------------------------------------------------------
+// FinalizeSmilL
+// ---------------------------------------------------------
+void CPostcardOperationSave::FinalizeSmilL( )
+    {
+	iHeaders->StoreL( *iTempAtta );
+    }
+	
+// ---------------------------------------------------------
+// FinalizeMessageL
+// ---------------------------------------------------------
+void CPostcardOperationSave::FinalizeMessageL( )
+	{
+	
+    const CMsvRecipientList& addresses = iDocument.Mtm( ).AddresseeList();
+    
+    if( addresses.Count( ) < 1 )
+    	{
+        iDocument.Mtm().AddAddresseeL(
+            iAppUi.CenRep().GetString( KPocaKeyServiceProvider ) );
+    	}
+	
+	// Disable delivery reports for postcards
+	iDocument.Mtm().SetDeliveryReport( EMmsNo );
+	
+    iDocument.Mtm().SaveMessageL();
+
+    TMmsMsvEntry tEntry = static_cast<TMmsMsvEntry>( iDocument.Entry() );
+	
+    // Update timestamp  
+    tEntry.iDate.UniversalTime();
+
+    if ( !tEntry.Visible() )
+        {
+        // Save from close or exit save.
+        // Message should be visible after save
+        tEntry.SetVisible( ETrue );
+        tEntry.SetInPreparation( EFalse );
+        }
+
+    if ( !tEntry.EditorOriented() )
+        {
+        tEntry.SetEditorOriented( ETrue );
+        }
+
+	tEntry.iDetails.Set( iRecipientName->Des( ) ) ;
+    tEntry.iDescription.Set(
+        iPlainText.Read( 0, Min( iPlainText.DocumentLength(), 20 ) ) );
+    
+	tEntry.iBioType = KUidMsgSubTypeMmsPostcard.iUid;
+    
+    iDocument.CurrentEntry().ChangeL( tEntry );
+	}
+
+// ---------------------------------------------------------
+// CPostcardOperationSave::StartSendingL
+// ---------------------------------------------------------
+void CPostcardOperationSave::StartSendingL( )
+	{
+    iSendOperation = iDocument.Mtm().SendL( iStatus );
+	}
+
+// ---------------------------------------------------------
+// CPostcardOperationSave::ConstructL
+// ---------------------------------------------------------
+void CPostcardOperationSave::ConstructL( )
+    {
+    if( HasContact() )
+        {
+        iFlags |=  EPostcardSaveHasContact;
+        CContactItemFieldSet& fieldSet =  iContact.CardFields();
+        const CContactItemField* field = TPostcardUtils::FieldOrNull( fieldSet,
+            TPostcardUtils::ContactItemNameFromId( EPostcardAddressName ) );
+        if( field )
+            {
+            iRecipientName = field->TextStorage()->Text().AllocL();
+            }
+        }
+    if( !iRecipientName )
+        {
+        iRecipientName = HBufC::NewL( 0 );
+        }
+    }
+
+// ---------------------------------------------------------
+// CPostcardOperationSave::DoCancel
+// ---------------------------------------------------------
+void CPostcardOperationSave::DoCancel( )
+    {
+    if( iManager )
+        {
+        if( iFlags & EPostcardSaveRequestActive )
+            { // Attachment Manager request going on -> cancel
+            iManager->CancelRequest();        
+            }
+        }
+    }
+
+// ---------------------------------------------------------
+// RunL
+// ---------------------------------------------------------
+void CPostcardOperationSave::RunL( )
+    {
+    DoLaunchStepL( );
+    }
+
+// ---------------------------------------------------------
+// RunError
+// ---------------------------------------------------------
+TInt CPostcardOperationSave::RunError( TInt aError )
+    {
+    iLaunchState = -1;
+    SetError( aError );
+    iObserver.PostcardOperationEvent(
+        EPostcardOperationSave,
+        EPostcardOperationError );     
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------
+// StartCreatingEmptyAttachmentL
+// ---------------------------------------------------------
+void CPostcardOperationSave::StartCreatingEmptyAttachmentL( const TFileName& aFileName )
+    {
+    CMsvAttachment* attachment = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
+    CleanupStack::PushL( attachment );
+    iTempFile = new ( ELeave ) RFile;
+    iManager->CreateAttachmentL( aFileName, *iTempFile, attachment, iStatus );
+    iFlags |= EPostcardSaveRequestActive;
+    CleanupStack::Pop( attachment );
+    iTempAtta = attachment;
+    iCommit = ETrue;
+    }
+    
+// ---------------------------------------------------------
+// HasContact
+// ---------------------------------------------------------
+TBool CPostcardOperationSave::HasContact( )
+	{
+	CContactItemFieldSet& set = iContact.CardFields( );
+	for( TInt i = 0; i<set.Count( ); i++ )
+		{
+		CContactItemField& field = set[i];
+		if( field.TextStorage( )->Text( ).Length( ) > 0 )
+			{
+			return ETrue;
+			}
+		}
+	return EFalse;	
+	}
+    
+// ---------------------------------------------------------
+// AddressArrayLC
+// ---------------------------------------------------------
+CDesCArrayFlat* CPostcardOperationSave::AddressArrayLC( CContactItemFieldSet& aSet )
+	{
+    // Get contact fields into an array. No ownership.
+    const CContactItemField* fields[ CPostcardAddressForm::ENumAddrFields ];
+
+    const TInt first = EPostcardAddressName;
+    const TInt last = EPostcardAddressCountry;
+    for( TInt i = first; i <= last; i++)
+        {
+        fields[i - first] = TPostcardUtils::FieldOrNull(
+            aSet, TPostcardUtils::ContactItemNameFromId( i ) );
+        }
+
+    CDesCArrayFlat* desarr =
+        new( ELeave ) CDesCArrayFlat( CPostcardAddressForm::ENumAddrFields );
+    CleanupStack::PushL(desarr);
+
+    for( TInt i = 0; i < CPostcardAddressForm::ENumAddrFields; i++ )
+        {
+        if( fields[i] )
+            {
+            if( ( iFlags & EPostcardSaveSend) &&
+                (iFlags & EPostcardSaveSpecialFormat ) )
+                { // We are sending & have the special format on
+                // so lets replace possible user added separator strings
+                TPtr string = fields[i]->TextStorage()->Text().AllocLC()->Des();
+                // Trim leading and trailing white space so it doesn't affect
+                // parsing of special format string when opened from sent
+                // folder.
+                string.Trim();
+                ReplaceWith( string,
+                    iAppUi.CenRep().GetString( KPocaKeyServiceSeparator ),
+                    iAppUi.CenRep().GetString( KPocaKeyServiceReplaceString )
+                    );
+                desarr->AppendL( string );
+                CleanupStack::PopAndDestroy(); // string
+                }
+            else
+                { // Otherwise just add the data field
+                desarr->AppendL( fields[i]->TextStorage()->Text() );
+                }
+            }
+        else
+            {
+            desarr->AppendL( KNullDesC );
+            }
+        }
+    return desarr;
+    }
+
+// ---------------------------------------------------------
+// VCardAddressArrayL
+// ---------------------------------------------------------
+CDesCArrayFlat* CPostcardOperationSave::VCardAddressArrayL( CContactItemFieldSet& aSet )
+    {
+    // Exclude name
+    const TInt KPocaVCardAddrFields = CPostcardAddressForm::ENumAddrFields - 1;
+
+    // Get contact fields into an array. No ownership.
+    const CContactItemField* fields[ KPocaVCardAddrFields ];
+
+    // Order of contact fields in array
+    static const TInt ids[KPocaVCardAddrFields] =
+        {
+        EPostcardAddressInfo, EPostcardAddressStreet, EPostcardAddressCity,
+        EPostcardAddressState, EPostcardAddressZip, EPostcardAddressCountry
+        };
+    
+    for( TInt i = 0; i < KPocaVCardAddrFields; i++)
+        {
+        fields[i] = TPostcardUtils::FieldOrNull(
+            aSet, TPostcardUtils::ContactItemNameFromId( ids[i] ) );
+        }
+
+    CDesCArrayFlat* desarr = new(ELeave) CDesCArrayFlat( KPocaVCardAddrFields );
+    CleanupStack::PushL( desarr );
+
+    for( TInt i = 0; i < KPocaVCardAddrFields; i++ )
+        {
+        desarr->AppendL(
+            fields[i] ? fields[i]->TextStorage()->Text() : KNullDesC() );
+        }
+
+	CleanupStack::Pop( desarr );
+	return desarr;
+    }
+
+// ---------------------------------------------------------
+// ReplaceWith
+// ---------------------------------------------------------
+void CPostcardOperationSave::ReplaceWith( TDes& aOrig, const TDesC& aWhat,
+    const TDesC& aWith )
+    {    
+	TInt pos = 0;
+	TInt foundPos = 0;
+	TBool found = ETrue;
+
+	while( found )
+	    {
+	    found = EFalse;
+		if( pos >= aOrig.Length( ) )
+			{
+			continue; // as found is false -> while is not true anymore
+			}	
+				
+    	foundPos =  aOrig.Mid( pos, aOrig.Length()-pos ).Find( aWhat );
+    	
+		if( foundPos == KErrNotFound )
+			{ // Not found anymore, so the rest is ok
+			continue; // as found is false -> while is not true anymore
+			}
+			
+        found = ETrue;
+		// As foundPos is related to previous sepa char, we need to add the beginning, too
+		foundPos += pos;
+		aOrig.Replace( foundPos, aWhat.Length( ), aWith );
+	    pos = foundPos + aWhat.Length( ) + ( aWith.Length( ) - aWhat.Length( ) );
+	    }    
+    }
+
+// ---------------------------------------------------------
+// GetImageFileName
+// ---------------------------------------------------------
+HBufC* CPostcardOperationSave::GetImageFileNameL( )
+    {
+    // Goes thru the attas and check how many images, text and recipients there are
+    TInt count = iManager->AttachmentCount( );
+    CMsvAttachment* atta = NULL;
+
+    HBufC* nameString = NULL;
+    for(TInt a = 0; a<count && !nameString; a++)
+        {
+        atta = iManager->GetAttachmentInfoL( a );
+        CleanupStack::PushL( atta );
+
+        switch( RecogniseFileType( atta->MimeType( ) ) )
+            {
+            case EPostcardFileImage:
+                {
+                CMsvMimeHeaders* msvMime = CMsvMimeHeaders::NewLC();
+                msvMime->RestoreL( *atta );
+                nameString = msvMime->ContentLocation().AllocL();
+                CleanupStack::PopAndDestroy( msvMime ); // msvMime, atta
+                break;
+                }
+            default:
+                break;
+            }
+        CleanupStack::PopAndDestroy( atta );
+        }
+    return nameString ? nameString : HBufC::NewL( 0 );
+    }
+    
+// EOF