email/pop3andsmtpmtm/clientmtms/src/SMTCMTM.CPP
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:44:11 +0200
changeset 0 72b543305e3a
permissions -rw-r--r--
Revision: 200949 Kit: 200951

// Copyright (c) 1998-2009 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:
// Client MTM for the SMTP protocol
// 
//

#include <bautils.h>	// BaflUtils::NearestLanguageFile()
#include <txtrich.h>	// CRichText

#include <barsc.h>		//RResourceFile
#include <barsread.h>

#include <msvstore.h>	// CMsvStore
#include <mtmdef.h>		//KUidMtmQueryxxx & TMsvPartList flags
#include <imcm.rsg>
#include <mmsvattachmentmanager.h>
#include <cmsvmimeheaders.h>
#include <cemailaccounts.h>
#include "CImAttachmentWaiter.h"
#include "cmsvsmtpsendoperation.h"
#include "MIUTHDR.H"	//CImHeader
#include "SMTCMTM.H"
#include "MIUT_ERR.H"
#include "IMCMMAIN.H"
#include "IMCMUTIL.H"
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS 
#include <mtmuidsdef.hrh>
#include "timrfc822datefield.h" 
#include "miut_errconsts.h"
#include "msvconsts.h"
#include "cimmessagepart.h"
#endif

#include <msvenhancesearchsortutil.h>

#if defined (_UNICODE)
	#define KUidMsgInternetMailEditorDLL	0x10003C53  // (268450899)
#else
	#define KUidMsgInternetMailEditorDLL	0x100011AC  // (268439980)
#endif

/**
@internalTechnology
*/

EXPORT_C CSmtpClientMtm* CSmtpClientMtm::NewL(CRegisteredMtmDll& aRegisteredMtmDll, CMsvSession& aMsvSession)
	{
	CSmtpClientMtm* self = new(ELeave) CSmtpClientMtm(aRegisteredMtmDll, aMsvSession);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
	}

CSmtpClientMtm::CSmtpClientMtm(CRegisteredMtmDll& aRegisteredMtmDll, CMsvSession& aMsvSession)
	: CBaseMtm(aRegisteredMtmDll, aMsvSession)
	{
	__DECLARE_NAME(_S("CSmtpClientMtm"));
	}

void CSmtpClientMtm::ConstructL()
	{
	iWait=CMsvOperationActiveSchedulerWait::NewLC();
	CleanupStack::Pop();	// iWait

	iImSmtpSettings = new (ELeave) CImSmtpSettings;
	iHeader = CImHeader::NewLC();
	CleanupStack::Pop();	// iHeader

	//open the resource file
	RResourceFile resourceFile;
	OpenResourceFileL(resourceFile, Session().FileSession());
	CleanupClosePushL(resourceFile);

	HBufC8* buf = resourceFile.AllocReadLC(EMAIL_ADDRESS_FORMATTING_STRING);
	TResourceReader reader;
	reader.SetBuffer(buf);
	iEmailAddressFormatString = (reader.ReadTPtrC()).AllocL();
	CleanupStack::PopAndDestroy(2); // resourceFile (Close resourceFile), buf
	}

CSmtpClientMtm::~CSmtpClientMtm()
/** Destructor. */
	{
	delete iWait;
	delete iImSmtpSettings;
	delete iHeader;
	delete iSubject;
	delete iEmailAddressFormatString;
	delete iEntrySelection;
	delete iImEmailOperation;
	delete iAttachmentWaiter;
	delete iEmailMessage;
	}

void CSmtpClientMtm::ResetData()
	{
	// The client MTM owns a number of objects which are used to store data
	// from service settings and messages.
	iAddresseeList->Reset();
	iImSmtpSettings->Reset();
	
	delete iSubject;
	iSubject = NULL;

	iHeader->Reset();
	Body().Reset();	// discard old contents of CRichText object
	}

void CSmtpClientMtm::SaveMessageL()
/** Commits cached changes to the current message context to the storage controlled 
by the Message Server. */
	{
	__ASSERT_DEBUG(iMsvEntry!=NULL,gPanic(ESmtcMTMNoCMsvEntrySet));
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvMessageEntryValue, gPanic(ESmtcMTMNotAMessageEntry));
	
	StoreEmailMessageL();
	}

void CSmtpClientMtm::StoreEmailMessageL()
	{
	__ASSERT_DEBUG(KUidMsvMessageEntryValue==iMsvEntry->Entry().iType.iUid, gPanic(ESmtcMTMNotAMessageEntry));
	CMsvStore* store = iMsvEntry->EditStoreL();
	CleanupStack::PushL(store);

	// Save the Email header stream...
	iHeader->SetSubjectL(SubjectL());
	const TInt numberOfRecipients=iAddresseeList->Count();
	// put all addressees in the To: field...
	for (TInt n=0 ; n < numberOfRecipients ; ++n)
		{
		// validation done by ValidateMessage()
		switch(iAddresseeList->Type(n))
			{
		case EMsvRecipientTo:
			iHeader->ToRecipients().AppendL(AddresseeList()[n]);
			break;
		case EMsvRecipientCc:
			iHeader->CcRecipients().AppendL(AddresseeList()[n]);
			break;
		case EMsvRecipientBcc:
			iHeader->BccRecipients().AppendL(AddresseeList()[n]);
			break;
			}
		}

	iHeader->StoreL(*store);
	store->CommitL();

	// NB the From field will be set by server MTM when the email message is sent

	CleanupStack::PopAndDestroy();

	TMsvEntry messageEntry=iMsvEntry->Entry();
	CImEmailMessage* emailMessage = CImEmailMessage::NewLC(*iMsvEntry);

	// now store the body text
	emailMessage->StoreBodyTextL(messageEntry.Id(), Body(), iWait->iStatus);
	iWait->Start();		// wait for the asynch operation to complete
	
	// 
	// I have stored away all the email data,
	// now update the TMsventry that represents
	// the email to ensure that it is up to date
	//
	TInt32 totalSizeOfAllAttachments = GetAttachmentSizeL(*emailMessage, messageEntry.Id());

	messageEntry.iSize = iHeader->DataSize() + Body().DocumentLength() + totalSizeOfAllAttachments;
	messageEntry.iDescription.Set(iHeader->Subject());
	messageEntry.iDate.UniversalTime();

	// fix for DEF051564 - SMTP client MTM CreateMessageL not creating message in correct state
	//
	// since CreateMessageL creates message as in preparation and non-visible, the SaveMessageL
	// must now make it visible and not in preparation
	messageEntry.SetVisible(ETrue);
	messageEntry.SetInPreparation(EFalse);

	if (iHeader->ToRecipients().Count()>0)
		messageEntry.iDetails.Set(iHeader->ToRecipients()[0]);
	else if (iHeader->CcRecipients().Count())
		messageEntry.iDetails.Set(iHeader->CcRecipients()[0]);
	else if (iHeader->BccRecipients().Count())
		messageEntry.iDetails.Set(iHeader->BccRecipients()[0]);
	//else do nothing as there are no recipients yet!

	if (totalSizeOfAllAttachments>0)
		messageEntry.SetAttachment(ETrue);

	// if there are multiple recipients then set the flag
	if ((iHeader->ToRecipients().Count() + iHeader->CcRecipients().Count() + iHeader->BccRecipients().Count()) >=2)
		messageEntry.SetMultipleRecipients(ETrue);

	// update the contents of the message entry
	iMsvEntry->ChangeL(messageEntry);

	CleanupStack::PopAndDestroy();	// emailMessage
	}

EXPORT_C void CSmtpClientMtm::StoreSettingsL()
/** Stores the current service settings from the object's cache in to the Central 
Repository for the current entry.

The current entry must be a service. */
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvServiceEntryValue, gPanic(ESmtcMTMNotAServiceEntry));

	CEmailAccounts* account = CEmailAccounts::NewLC();
  	TSmtpAccount id;
	account->GetSmtpAccountL(iMsvEntry->Entry().Id(), id);
	account->SaveSmtpSettingsL(id, *iImSmtpSettings);
	CleanupStack::PopAndDestroy(account);    
	}

EXPORT_C void CSmtpClientMtm::RestoreSettingsL()
/** Loads into the object's cache the service settings from the Central Repository
for the current entry. */
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvServiceEntryValue, gPanic(ESmtcMTMNotAServiceEntry));

	CEmailAccounts* account = CEmailAccounts::NewLC();
  	TSmtpAccount id;
	account->GetSmtpAccountL(iMsvEntry->Entry().Id(), id);
	account->LoadSmtpSettingsL(id, *iImSmtpSettings);
	CleanupStack::PopAndDestroy(account);    
	}

void CSmtpClientMtm::LoadMessageL()
/** Loads the cache with the message data for the current context. */
	{
	__ASSERT_DEBUG(iMsvEntry!=NULL,gPanic(ESmtcMTMNoCMsvEntrySet));
	switch (iMsvEntry->Entry().iType.iUid)
		{
		case KUidMsvServiceEntryValue:
			RestoreSettingsL();
			break;
		case KUidMsvMessageEntryValue:
			RestoreEmailMessageL();
			break;
		};
	}	

void CSmtpClientMtm::RestoreEmailMessageL()
	{

	// Get a reference to TMsvEnhanceSearchSortUtil  instance set by CMsvSearchsortOpOnHeaderBody class
	// If advanced search and sort is being performed than do not load the message header and body.
	// These are loaded when the search criteria is known in DoFindL()
	// For API's other than CMsvSearchsortOpOnHeaderBody-> FindInHeaderBodyL(), a call to LoadMessageL()
	// loads the body and the header.

	TMsvEnhanceSearchSortUtil* searchsortutil = (TMsvEnhanceSearchSortUtil*)(GetExtensionData());
	if ( searchsortutil == NULL )
		{
		CMsvStore* msvStore = iMsvEntry->ReadStoreL();
		CleanupStack::PushL(msvStore);

		// message must have a CImHeader stream...if it's not there leave as there's something wrong
		iHeader->RestoreL(*msvStore);

		TPtrC subject = iHeader->Subject();
		TPtrC to;
		if (iHeader->ToRecipients().Count())
			to.Set(iHeader->ToRecipients()[0]);
		else if (iHeader->CcRecipients().Count())
			to.Set(iHeader->CcRecipients()[0]);
		else if (iHeader->BccRecipients().Count())
			to.Set(iHeader->BccRecipients()[0]);
	//else do nothing as there are no recipients!

		SetSubjectL(subject);
		SetAddresseeListL();

		// Get the attachments and the body text...
		TMsvEntry messageEntry=iMsvEntry->Entry();

		CleanupStack::PopAndDestroy(); // msvStore
		CImEmailMessage* emailMessage = CImEmailMessage::NewLC(*iMsvEntry);

		GetBodyTextL(*emailMessage, messageEntry.Id());
		TInt32 totalSizeOfAllAttachments = GetAttachmentSizeL(*emailMessage, messageEntry.Id());

		messageEntry.iSize = iHeader->DataSize() + Body().DocumentLength() + totalSizeOfAllAttachments;
		messageEntry.iDescription.Set(subject);
		messageEntry.iDetails.Set(to);

	// update the contents of the message entry
		iMsvEntry->ChangeL(messageEntry);
	
		CleanupStack::PopAndDestroy(); // emailMessage
	
		}
	}


void CSmtpClientMtm::GetBodyTextL(CImEmailMessage& aMessage, TMsvId aMsvId)
	{
	CRichText* messageText = CRichText::NewL(iParaFormatLayer, iCharFormatLayer);
	CleanupStack::PushL(messageText);

	aMessage.GetBodyTextL(aMsvId, CImEmailMessage::EThisMessageOnly, *messageText,*iParaFormatLayer,*iCharFormatLayer);

	Body().Reset();
	Body().AppendTakingSolePictureOwnershipL(*messageText);

	CleanupStack::PopAndDestroy();
	}

TInt32 CSmtpClientMtm::GetAttachmentSizeL(CImEmailMessage& aMessage, TMsvId aMsvId)
	{
	// Calculate the total size of all attachments associated with this message
	TInt total=0;
	aMessage.GetAttachmentsListL(iWait->iStatus, aMsvId, CImEmailMessage::EAllAttachments,CImEmailMessage::EThisMessageOnly);
	iWait->Start();		// wait for the asynch operation to complete
	TInt numAttachments=aMessage.AttachmentManager().AttachmentCount();
	for (TInt n=0 ; n<numAttachments ; ++n)
		{
		CMsvAttachment* attachment = aMessage.AttachmentManager().GetAttachmentInfoL(n);
		total+=attachment->Size();
		delete attachment;
		}
	return total;
	}

CMsvOperation* CSmtpClientMtm::ReplyL(TMsvId aDestination, TMsvPartList aPartList, TRequestStatus& aCompletionStatus)
/** Creates a reply message to the current message context. 

@return	If successful, this is an asynchronously completing reply operation. 
If failed, this is a completed operation, with status set to the relevant error code. 
@param aDestination The entry to which to assign the reply 
@param aPartList Defines the parts that are to be copied from the original message into the reply 
@param aCompletionStatus The request status to be completed when the operation has finished 
*/
	{
	TMsvEmailTypeList msvEmailTypeList = 0;
	TUid messageType = KUidMsgTypeSMTP;
	return CImEmailOperation::CreateReplyL(aCompletionStatus, Session(), iMsvEntry->EntryId(), aDestination, aPartList, msvEmailTypeList, messageType);
	}

CMsvOperation* CSmtpClientMtm::ForwardL(TMsvId aDestination, TMsvPartList aPartList, TRequestStatus& aCompletionStatus)
/** Creates a forwarded message from the current message context. 

@return	If successful, this is an asynchronously completing forward message operation. 
If failed, this is a completed operation, with status set to the relevant error code. 		
@param aDestination The entry to which to assign the forwarded message 
@param aPartList Defines the parts that are to be copied from the original message into the forwarded message 
@param aCompletionStatus The request status to be completed when the operation has finished 
*/
	{
	TMsvEmailTypeList msvEmailTypeList = 0;
	TUid messageType = KUidMsgTypeSMTP;
	return CImEmailOperation::CreateForwardL(aCompletionStatus, Session(), iMsvEntry->EntryId(), aDestination, aPartList, msvEmailTypeList, messageType);
	}

EXPORT_C void CSmtpClientMtm::SetSubjectL(const TDesC& aSubject)
/** Sets the message context's subject text.

@param aSubject Subject text */
	{
	HBufC* newSubject= aSubject.AllocL();
	delete iSubject;
	iSubject = newSubject;	
	}

EXPORT_C const TPtrC CSmtpClientMtm::SubjectL() const
/** Gets the message context's subject text.

@return Subject text */
	{
	// NB won't ever leave
	return (iSubject) ? TPtrC(*iSubject) : TPtrC();
	}

TMsvPartList CSmtpClientMtm::ValidateMessage(TMsvPartList aPartList)
/** Validates the current message context.

The addresses for the message are checked to be well-formed email addresses.

@param aPartList Indicates the message parts for which validation is requested 
@return If valid, KErrNone If invalid, identifies the invalid part(s). The 
error value is the bitmask of the TMsvPartList IDs for each invalid part */
	{
	__ASSERT_DEBUG(iMsvEntry!=NULL,gPanic(ESmtcMTMNoCMsvEntrySet));

	TMsvPartList failed(0);
	if (aPartList & KMsvMessagePartRecipient)
		{
		if (iAddresseeList->Count() == 0)
			failed |= KMsvMessagePartRecipient;
		else
			{
			// check the recipient list for valid 'addresses'
			for (TInt ii=0; ii < iAddresseeList->Count(); ++ii)
				{
				if (((*iAddresseeList)[ii].Length() == 0) || !ValidateAddress((*iAddresseeList)[ii]))
					{
					failed |= KMsvMessagePartRecipient;
					break;
					}
				}
			}
		}
	return failed;
	}

TBool CSmtpClientMtm::ValidateAddress(const TPtrC& anAddress)
	{
	return iTImMessageField.ValidInternetEmailAddress(anAddress);
	}

TMsvPartList CSmtpClientMtm::DoFindL(const TDesC& aTextToFind, TMsvPartList aPartList)
	{
	CImClientMTMUtils* clientMTMUtils = CImClientMTMUtils::NewL();
	CleanupStack::PushL(clientMTMUtils);

	TMsvPartList retList = KMsvMessagePartNone;
	
	// Get a reference to TMsvEnhanceSearchSortUtil instance set by CMsvSearchsortOpOnHeaderBody class	
	TMsvEnhanceSearchSortUtil* searchsortutil = (TMsvEnhanceSearchSortUtil*)(GetExtensionData());
	
	// searchsortuitl variable will not be NULL for Advanced Search and Sort called from CMsvSearchsortOpOnHeaderBody
 	// For the old implementation, it will be NULL
	if(searchsortutil != NULL)
		{
		// Get the searchsort setting flags
		TUint32 searchsortsetting=searchsortutil->GetSearchSortSetting();
		
		// The body was not loaded in LoadMessageL()
 		// If 2 search query options are on the body or on the header, than it sets EMessagePartBodyLoaded flag
 		// or EMessagePartHeaderLoaded of searchsortsetting
		if(aPartList & KMsvMessagePartBody && !(searchsortsetting & EMessagePartBodyLoaded))  	
   			{
   			// Restore the body
   			
			Body().Reset();	
			// Get the attachments and the body text...
			TMsvEntry messageEntry=iMsvEntry->Entry();
			CImEmailMessage* emailMessage = CImEmailMessage::NewLC(*iMsvEntry);
			GetBodyTextL(*emailMessage, messageEntry.Id());
			CleanupStack::PopAndDestroy(); // emailMessage
			searchsortutil->SetSearchSortSetting(EMessagePartBodyLoaded);
			}
		else if (!(searchsortsetting & EMessagePartHeaderLoaded))
			{
			// Restore the header
			CMsvStore* msvStore = iMsvEntry->ReadStoreL();
			CleanupStack::PushL(msvStore);

			// message must have a CImHeader stream...if it's not there leave as there's something wrong
			iHeader->RestoreL(*msvStore);

			TPtrC subject = iHeader->Subject();
			TPtrC to;
			if (iHeader->ToRecipients().Count())
				to.Set(iHeader->ToRecipients()[0]);
			else if (iHeader->CcRecipients().Count())
				to.Set(iHeader->CcRecipients()[0]);
			else if (iHeader->BccRecipients().Count())
				to.Set(iHeader->BccRecipients()[0]);
			//else do nothing as there are no recipients!

			SetSubjectL(subject);
			SetAddresseeListL();

			// Get the attachments and the body text...
			TMsvEntry messageEntry=iMsvEntry->Entry();

			CleanupStack::PopAndDestroy(); // msvStore
			searchsortutil->SetSearchSortSetting(EMessagePartHeaderLoaded);
			}
			
		// Issue a request to FindL
		clientMTMUtils->FindL(aTextToFind, Body(), *iHeader, aPartList, retList);
		
		/* Copy the sort data if sorting is specified.
		 The operations being performed could be only be sort or it could be search and sort
		 If the operation is search and sort than copy the sort data only if the
		 search operation succeeded	*/
		 
		if ((searchsortsetting & EMessagePartSort ) || (((searchsortsetting & EMessagePartSearchSort) && (searchsortsetting & EMessagePartLastQueryOption) && (retList))))
   			{
   			/* Copy the data to be sorted from the header stream.
   			   This done by setting iExtensionData to point to the field being copied */
   			   
	   		if (iHeader)
   				{
   				if (searchsortsetting & EMessagePartToSort )
   					{
   					SetExtensionData((TAny*)&iHeader->ToRecipients());
	   				}
   				else if(searchsortsetting & EMessagePartCcSort)
   					{
   					SetExtensionData((TAny*)&iHeader->CcRecipients());
   					}
   				else if(searchsortsetting & EMessagePartBccSort)
 	 	  			{
    				SetExtensionData((TAny*)&iHeader->BccRecipients());
    				}
    			else if(searchsortsetting & EMessagePartFromSort)
    				{
    				SetExtensionData((TAny*)(iHeader->From().Ptr()));
    				}
				else if(searchsortsetting & EMessagePartSubjectSort)
					{
					SetExtensionData((TAny*)(iHeader->Subject().Ptr()));
					}
    			}
   			}
		}
	else
		{

		clientMTMUtils->FindL(aTextToFind, Body(), *iHeader, aPartList, retList);	
		}
    CleanupStack::PopAndDestroy(clientMTMUtils); 
	return retList;
	}

TMsvPartList CSmtpClientMtm::Find(const TDesC& aTextToFind, TMsvPartList aPartList)
/** Searches the specified message part(s) for the plain-text version of the text 
to be found.

@param aTextToFind The plain-text version of the text to be found. 
@param aPartList Indicates the message parts which should be searched. 
@return If the text was not found, or searching is unsupported, 0. If the text 
was found, a bitmask of the TMsvPartList IDs for each part in which the text 
was present. */
	{
 	TMsvPartList retList = KMsvMessagePartNone;
 	TRAPD(ret, retList = DoFindL(aTextToFind, aPartList));
   	return retList;
	}

void CSmtpClientMtm::AddAddresseeL(const TDesC& aRealAddress)
/** Adds an addressee for the current context.

@param aRealAddress String representing an address to be added to the list 
for the current message */
	{
	iAddresseeList->AppendL(EMsvRecipientTo, aRealAddress);
	}

void CSmtpClientMtm::SetAddresseeListL()
	{
	// fill in the addressee list from the contents of iHeader
	// copy all of the recipient addresses into the addressee list

	iAddresseeList->Reset();
	TInt numberOfRecipients=iHeader->ToRecipients().Count();
	TInt n;
	for (n=0 ; n < numberOfRecipients ; ++n)
		{
		TPtrC addressWithoutAlias = iTImMessageField.GetValidInternetEmailAddressFromString(iHeader->ToRecipients()[n]);
		AddAddresseeL(EMsvRecipientTo, addressWithoutAlias);
		}

	numberOfRecipients=iHeader->CcRecipients().Count();
	for (n=0 ; n < numberOfRecipients ; ++n)
		{
		TPtrC addressWithoutAlias = iTImMessageField.GetValidInternetEmailAddressFromString(iHeader->CcRecipients()[n]);
		AddAddresseeL(EMsvRecipientCc, addressWithoutAlias);
		}

	// there shouldn't be any Bcc: addresses in an incoming email
	// but add them anyway?
	numberOfRecipients=iHeader->BccRecipients().Count();
	for (n=0 ; n < numberOfRecipients ; ++n)
		{
		TPtrC addressWithoutAlias = iTImMessageField.GetValidInternetEmailAddressFromString(iHeader->BccRecipients()[n]);
		AddAddresseeL(EMsvRecipientBcc, addressWithoutAlias);
		}
	}

void CSmtpClientMtm::AddAddresseeL(const TDesC& aRealAddress, const TDesC& aAlias)
/** Adds an addressee with an alias for the current context.

@param aRealAddress String representing an address to be added to the list 
for the current message 
@param aAlias Alias information */
	{
	HBufC* emailAddress = HBufC::NewLC(aRealAddress.Length()+aAlias.Length()+iEmailAddressFormatString->Length()-4);
	emailAddress->Des().Format(*iEmailAddressFormatString,&aAlias,&aRealAddress);
	iAddresseeList->AppendL(EMsvRecipientTo, *emailAddress);
	CleanupStack::PopAndDestroy();	// emailAddress
	}

void CSmtpClientMtm::RemoveAddressee(TInt aIndex)
/** Removes an address from the current address list.

@param aIndex Index of address to be removed */
	{
	if (iAddresseeList->Count() > aIndex)
		iAddresseeList->Delete(aIndex);
	}

void CSmtpClientMtm::AddAddresseeL(TMsvRecipientType aType, const TDesC& aRealAddress)
	{
	iAddresseeList->AppendL(aType, aRealAddress);
	}

void CSmtpClientMtm::AddAddresseeL(TMsvRecipientType aType, const TDesC& aRealAddress, const TDesC& aAlias)
	{
	HBufC* emailAddress = HBufC::NewLC(aRealAddress.Length()+aAlias.Length()+iEmailAddressFormatString->Length()-4);
	emailAddress->Des().Format(*iEmailAddressFormatString,&aAlias,&aRealAddress);
	iAddresseeList->AppendL(aType, *emailAddress);
	CleanupStack::PopAndDestroy(emailAddress);
	}
	
void CSmtpClientMtm::ContextEntrySwitched()
	{
	ResetData();
	}

TInt CSmtpClientMtm::QueryCapability(TUid aCapability, TInt& aResponse)
/** Queries if the MTM supports a particular capability, specified by a UID.

@param aCapability UID of capability to be queried 
@param aResponse Response value. The format of the response varies according 
to the capability. 
@return KErrNone: aCapability is a recognised value and a response is returned 
KErrNotSupported: aCapability is not a recognised value */
	{
	TInt error = KErrNone;
	switch (aCapability.iUid)
		{
	// Supported:
	case KUidMtmQueryMaxBodySizeValue:
	case KUidMtmQueryMaxTotalMsgSizeValue:
		aResponse = KMaxTInt;
		break;
	case KUidMsvMtmQueryEditorUidValue:
		aResponse = KUidMsgInternetMailEditorDLL;
		break;
	case KUidMtmQuerySupportedBodyValue:
		aResponse = KMtm7BitBody + KMtm8BitBody + KMtm16BitBody + KMtmBinaryBody;
		break;
	case KUidMtmQuerySupportSubjectValue:
	case KUidMtmQuerySupportAttachmentsValue:
	case KUidMtmQueryCanSendMsgValue:
	case KUidMtmQuerySupportsRecipientTypeValue:
	case KUidMtmQuerySendAsMessageSendSupportValue:
		break;
	// All others - Not Supported:
	default:
		error = KErrNotSupported;
		}
	return error;
	}

EXPORT_C void CSmtpClientMtm::SetSettingsL(const CImSmtpSettings& aSettings)
/** Copies the specified service settings to the cached service settings.

@param aSettings New service settings */
	{
	iImSmtpSettings->CopyL(aSettings);
	}

EXPORT_C const CImSmtpSettings& CSmtpClientMtm::Settings() const
/** Gets the current cached service settings.

@return The current cached service settings */
	{
	__ASSERT_DEBUG(iImSmtpSettings!=NULL, User::Invariant());
	return *iImSmtpSettings;
	}

void CSmtpClientMtm::InvokeSyncFunctionL(TInt aFunctionId, const CMsvEntrySelection& aSelection, TDes8& aParameter)
/** Invokes a synchronous SMTP-specific operation.

@param aFunctionId Specifies which operation to perform. The only valid ID is KSMTPMTMIsConnected.
@param aSelection A selection of messages for the operation.
@param aParameter Not used
*/
	{
	__ASSERT_DEBUG(iMsvEntry!=NULL,gPanic(ESmtcMTMNoCMsvEntrySet));

	TInt error = KErrNone;
	switch (aFunctionId)
		{
		case KSMTPMTMIsConnected:
			{
			TPckgBuf<TImSmtpProgress> progress;
			Session().TransferCommandL(aSelection, KSMTPMTMIsConnected, aParameter, progress);
			aParameter.Copy(progress);
			return;
			}
		default:
			error=KErrNotSupported;
			__ASSERT_DEBUG(EFalse,gPanic(ESmtcUnknownSyncFunction));
		}

	User::LeaveIfError(error);
	}

CMsvOperation* CSmtpClientMtm::InvokeAsyncFunctionL(TInt aFunctionId, const CMsvEntrySelection& aSelection,
													TDes8& aParameter, TRequestStatus& aCompletionStatus)
/** Invokes asynchronous SMTP-specific operations.

@param aFunctionId Specifies which operation to perform e.g. connect, copy 
new mail etc. The specific operations are defined by the TSmtpCmds enumeration. 
@param aSelection A selection of messages that need to be copied/moved to a 
local folder. The first entry in this selection MUST be the service. 
@param aParameter Not used
@param aCompletionStatus The status when the operation completes. 
@leave KErrNotFound The selection of email to be moved or copied is empty
@leave KErrNotSupported The specified operation is not recognised
@return If successful, this is an asynchronously completing operation. If failed, 
this is a completed operation, with status set to the relevant error code. 
@see TSmtpCmds */
	{
	switch(aFunctionId)
		{
	case KMTMStandardFunctionsSendMessage:
		return CMsvSmtpProgressOperation::NewL(Session(), aSelection, KSMTPMTMSendOnNextConnection, aParameter, aCompletionStatus);
	case KSMTPMTMSendOnNextConnection:
		return (Session().TransferCommandL(aSelection, aFunctionId, aParameter, aCompletionStatus));
	case KSMTPMTMCreateNewEmailMessage:
	case KSMTPMTMCreateReplyEmailMessage:
	case KSMTPMTMCreateForwardEmailMessage:
	case KSMTPMTMCreateForwardAsAttachmentEmailMessage:
	case KSMTPMTMCreateReceiptEmailMessage:
		{
		TImCreateMessageOptions createMessageOptions;	
		TPckgC<TImCreateMessageOptions> paramPack(createMessageOptions);
		paramPack.Set(aParameter);
		switch (aFunctionId)
			{
		case KSMTPMTMCreateNewEmailMessage:
			return iImEmailOperation->CreateNewL(aCompletionStatus, iMsvEntry->Session(), aSelection[0], paramPack().iMsvPartList, paramPack().iMsvEmailTypeList, paramPack().iMessageType);
		case KSMTPMTMCreateReplyEmailMessage:
			return iImEmailOperation->CreateReplyL(aCompletionStatus, iMsvEntry->Session(), aSelection[1], aSelection[0], paramPack().iMsvPartList, paramPack().iMsvEmailTypeList, paramPack().iMessageType);
		case KSMTPMTMCreateForwardEmailMessage:
			return iImEmailOperation->CreateForwardL(aCompletionStatus, iMsvEntry->Session(), aSelection[1], aSelection[0], paramPack().iMsvPartList, paramPack().iMsvEmailTypeList, paramPack().iMessageType);
		case KSMTPMTMCreateForwardAsAttachmentEmailMessage:
			return iImEmailOperation->CreateForwardAsAttachmentL(aCompletionStatus, iMsvEntry->Session(), aSelection[1], aSelection[0], paramPack().iMsvPartList, paramPack().iMsvEmailTypeList, paramPack().iMessageType);
		case KSMTPMTMCreateReceiptEmailMessage:
			return iImEmailOperation->CreateReceiptL(aCompletionStatus, iMsvEntry->Session(), aSelection[1], aSelection[0], paramPack().iMsvPartList, paramPack().iMsvEmailTypeList, paramPack().iMessageType);
			}
		}
	break;
	default:
		User::Leave(KErrNotSupported);
		};
	return NULL;
	}

// Attachment functions to support the SendAs API

EXPORT_C void CSmtpClientMtm::AddAttachmentL(const TDesC& aFilePath, const TDesC8& aMimeType, TUint aCharset, TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvMessageEntryValue, gPanic(ESmtcMTMNotAMessageEntry));

	if( iAttachmentWaiter == NULL )
		{
		iAttachmentWaiter = CImAttachmentWaiter::NewL();
		}	

	if (iEmailMessage == NULL)
		{		
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	else if (iEmailMessage->EmailEntryId() != iMsvEntry->EntryId())
		{
		delete iEmailMessage;
		iEmailMessage = NULL;
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}

	CMsvAttachment* attachmentInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
	CleanupStack::PushL(attachmentInfo);
	
	attachmentInfo->SetMimeTypeL(aMimeType);
	TParse fileNameParser;
	User::LeaveIfError(fileNameParser.Set(aFilePath, NULL, NULL));
	attachmentInfo->SetAttachmentNameL(fileNameParser.NameAndExt());
	if( aCharset!=0 )
		{
		CMsvMimeHeaders* headers = CMsvMimeHeaders::NewLC();
		headers->SetMimeCharset(aCharset);
		headers->StoreL(*attachmentInfo);
		CleanupStack::PopAndDestroy(headers);
		}

	iEmailMessage->AttachmentManager().AddAttachmentL(aFilePath, attachmentInfo, iAttachmentWaiter->iStatus);
	CleanupStack::Pop(attachmentInfo);// ownership passed to attachment manager
	
	iAttachmentWaiter->StartWaitingL(aStatus, iEmailMessage, EFalse);
	}
	
EXPORT_C void CSmtpClientMtm::AddAttachmentL(RFile& aFile, const TDesC8& aMimeType, TUint aCharset, TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvMessageEntryValue, gPanic(ESmtcMTMNotAMessageEntry));

	if( iAttachmentWaiter == NULL )
		{
		iAttachmentWaiter = CImAttachmentWaiter::NewL();
		}
	
	if (iEmailMessage == NULL)
		{		
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	else if (iEmailMessage->EmailEntryId() != iMsvEntry->EntryId())
		{
		delete iEmailMessage;
		iEmailMessage = NULL;
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}

	CMsvAttachment* attachmentInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
	CleanupStack::PushL(attachmentInfo);
	
	attachmentInfo->SetMimeTypeL(aMimeType);
	TFileName fileName;	
	aFile.Name(fileName);
	attachmentInfo->SetAttachmentNameL(fileName);
	if( aCharset!=0 )
		{
		CMsvMimeHeaders* headers = CMsvMimeHeaders::NewLC();
		headers->SetMimeCharset(aCharset);
		headers->StoreL(*attachmentInfo);
		CleanupStack::PopAndDestroy(headers);
		}
	
	iEmailMessage->AttachmentManager().AddAttachmentL(aFile, attachmentInfo, iAttachmentWaiter->iStatus);
	CleanupStack::Pop(attachmentInfo);// ownership passed to attachment manager	

	iAttachmentWaiter->StartWaitingL(aStatus, iEmailMessage, EFalse);
	}
	
EXPORT_C void CSmtpClientMtm::AddLinkedAttachmentL(const TDesC& aFilePath, const TDesC8& aMimeType, TUint aCharset, TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvMessageEntryValue, gPanic(ESmtcMTMNotAMessageEntry));

	if( iAttachmentWaiter == NULL )
		{
		iAttachmentWaiter = CImAttachmentWaiter::NewL();
		}

	if (iEmailMessage == NULL)
		{		
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	else if (iEmailMessage->EmailEntryId() != iMsvEntry->EntryId())
		{
		delete iEmailMessage;
		iEmailMessage = NULL;
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	
	CMsvAttachment* attachmentInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvLinkedFile);
	CleanupStack::PushL(attachmentInfo);
	
	attachmentInfo->SetMimeTypeL(aMimeType);
	
	TParse fileNameParser;
	User::LeaveIfError(fileNameParser.Set(aFilePath, NULL, NULL));
	attachmentInfo->SetAttachmentNameL(fileNameParser.NameAndExt());

	if( aCharset!=0 )
		{
		CMsvMimeHeaders* headers = CMsvMimeHeaders::NewLC();
		headers->SetMimeCharset(aCharset);
		headers->StoreL(*attachmentInfo);
		CleanupStack::PopAndDestroy(headers);
		}
	
	iEmailMessage->AttachmentManager().AddLinkedAttachmentL(aFilePath, attachmentInfo, iAttachmentWaiter->iStatus);
	CleanupStack::Pop(attachmentInfo);// ownership passed to attachment manager	

	iAttachmentWaiter->StartWaitingL(aStatus, iEmailMessage, EFalse);
	}
	
EXPORT_C void CSmtpClientMtm::AddEntryAsAttachmentL(TMsvId aAttachmentId, TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvMessageEntryValue, gPanic(ESmtcMTMNotAMessageEntry));

	if( iAttachmentWaiter == NULL )
		{
		iAttachmentWaiter = CImAttachmentWaiter::NewL();
		}

	if (iEmailMessage == NULL)
		{		
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	else if (iEmailMessage->EmailEntryId() != iMsvEntry->EntryId())
		{
		delete iEmailMessage;
		iEmailMessage = NULL;
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	
	CMsvAttachment* attachmentInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvMessageEntry);
	CleanupStack::PushL(attachmentInfo);

	iEmailMessage->AttachmentManager().AddEntryAsAttachmentL(aAttachmentId, attachmentInfo, iAttachmentWaiter->iStatus);
	CleanupStack::Pop(attachmentInfo);// ownership passed to attachment manager	
	
	iAttachmentWaiter->StartWaitingL(aStatus, iEmailMessage, EFalse);
	}

EXPORT_C void CSmtpClientMtm::CreateAttachmentL(const TDesC& aFileName, RFile& aAttachmentFile, const TDesC8& aMimeType, TUint aCharset, TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iMsvEntry->Entry().iType.iUid==KUidMsvMessageEntryValue, gPanic(ESmtcMTMNotAMessageEntry));
	
	if( iAttachmentWaiter == NULL )
		{
		iAttachmentWaiter = CImAttachmentWaiter::NewL();
		}

	if (iEmailMessage == NULL)
		{		
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}
	else if (iEmailMessage->EmailEntryId() != iMsvEntry->EntryId())
		{
		delete iEmailMessage;
		iEmailMessage = NULL;
		iEmailMessage = CImEmailMessage::NewL(*iMsvEntry);
		}

	CMsvAttachment* attachmentInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
	CleanupStack::PushL(attachmentInfo);

	attachmentInfo->SetAttachmentNameL(aFileName);
	attachmentInfo->SetMimeTypeL(aMimeType);
	
	if( aCharset!=0 )
		{
		CMsvMimeHeaders* headers = CMsvMimeHeaders::NewLC();
		headers->SetMimeCharset(aCharset);
		headers->StoreL(*attachmentInfo);
		CleanupStack::PopAndDestroy(headers);
		}

	iEmailMessage->AttachmentManager().CreateAttachmentL(aFileName, aAttachmentFile, attachmentInfo, iAttachmentWaiter->iStatus);
	CleanupStack::Pop(attachmentInfo);// ownership passed to attachment manager	
	
	iAttachmentWaiter->StartWaitingL(aStatus, iEmailMessage, EFalse);
	}


EXPORT_C void CSmtpClientMtm::CreateMessageL(TMsvId aServiceId)
/** Creates a new message entry as a child of the current context.

@param aServiceId ID of the service to own the entry. */
	{
	// fix for DEF051564 - SMTP client MTM CreateMessageL not creating message in correct state
	TMsvEmailTypeList emailTypeList = KMsvEmailTypeListInvisibleMessage | KMsvEmailTypeListMessageInPreparation;

	CEmailAccounts* account = CEmailAccounts::NewLC();
	
	CImSmtpSettings* smtpSettings = new (ELeave) CImSmtpSettings;
	CleanupStack::PushL(smtpSettings);
  	TSmtpAccount id;
	account->GetSmtpAccountL(aServiceId, id);
	account->LoadSmtpSettingsL(id, *smtpSettings);
	
	switch (smtpSettings->BodyEncoding())
		{
		case EMsgOutboxMHTMLAsMIME:
		case EMsgOutboxMHTMLAlternativeAsMIME:
			emailTypeList |= KMsvEmailTypeListMHTMLMessage;
			break;
		case EMsgOutboxDefault:
		case EMsgOutboxNoAlgorithm:
		case EMsgOutboxMIME:
			break;
		}
	CleanupStack::PopAndDestroy(2, account); // smtpSettings, account
	
	// Now invoke the create new mail operation.
	// Note that it is wrapped up to make the asynchronous call synchronous.
	CMsvOperationActiveSchedulerWait* waiter=CMsvOperationActiveSchedulerWait::NewLC();
	CImEmailOperation* createNewMailOp = CImEmailOperation::CreateNewL(waiter->iStatus, iMsvEntry->Session(), iMsvEntry->Entry().Id(), aServiceId, KMsvMessagePartBody|KMsvMessagePartAttachments, emailTypeList, KUidMsgTypeSMTP);
	CleanupStack::PushL(createNewMailOp);
	waiter->Start();

	// The the entry is expected to be set to the new message.
	TMsvId temp;	
	TPckgC<TMsvId> paramPack(temp);
	const TDesC8& progBuf = createNewMailOp->ProgressL();	
	paramPack.Set(progBuf);
	TMsvId messageId = paramPack();
	iMsvEntry->SetEntryL(messageId);
	
	CMsvStore* store = iMsvEntry->ReadStoreL();
	CleanupStack::PushL(store);
	iHeader->RestoreL(*store);

	CleanupStack::PopAndDestroy(3, waiter); // waiter, createNewMailOp, store	
	}
	
/**
Gets the default SMTP service.

@return
The default service

@leave 
KErrNotFound If default service setting does not exist.
*/
EXPORT_C TMsvId CSmtpClientMtm::DefaultServiceL() const
	{
	// Get default service Id from CenRep
	CEmailAccounts* account = CEmailAccounts::NewLC();
	TSmtpAccount id;
	TInt error = account->DefaultSmtpAccountL(id);
	if (error == KErrNotFound)
		{
		User::Leave(error);
		}
	
	CleanupStack::PopAndDestroy(account); 	   
	return id.iSmtpService;		
	}

/**
Removes the default SMTP service.
*/		
EXPORT_C void CSmtpClientMtm::RemoveDefaultServiceL()
	{
	User::Leave(KErrNotSupported);
	}

/**
Sets the default SMTP service.

@param	aService
The default service
*/		
EXPORT_C void CSmtpClientMtm::ChangeDefaultServiceL(const TMsvId& aService)
	{
	CEmailAccounts* account = CEmailAccounts::NewLC();
	TSmtpAccount id;
	account->GetSmtpAccountL(aService, id);
	account->SetDefaultSmtpAccountL(id);
	CleanupStack::PopAndDestroy(account); 	   		
	}
	
/**
Cancels the current attachment operation.
*/	
EXPORT_C void CSmtpClientMtm::CancelAttachmentOperation()
	{
 	if ( iAttachmentWaiter)
		 {
		 iAttachmentWaiter->Cancel();
		 }

	if ( iWait )
		 {
		 iWait->Cancel();
		 }
	}