email/pop3andsmtpmtm/smtpservermtm/src/IMSMSEND.CPP
changeset 0 72b543305e3a
child 76 60a8a215b0ec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/smtpservermtm/src/IMSMSEND.CPP	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1143 @@
+// 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:
+// Implementation file for class CImStmpSession -- representing
+// a connection to a remote SMTP server.
+// 
+//
+
+#include <miutpars.h>           // TImMessageField
+#include <imcvsend.h>			// CImSendConvert
+#include <logwrap.h>
+#include <logwraplimits.h>
+
+#include "SMTS.H"
+#include "IMSM.H"				// CImSmtpSession
+#include "IMSMSEND.H"			// CImSmtpFile
+#include "SMTSUTIL.H"			// forward declarations for utility fns
+#include <imutcon.h>
+#include "mobilitytestmtmapi.h"
+
+const TInt KSmtpAuthCodeSuccessful=235;
+const TInt KSmtpAuthCodeFailed=535;
+const TInt KSmtpUnableToAuthAtPresent=435;
+const TInt KSmtpAuthCodeReadyResponse=334;
+const TInt KSmtpBadParameter=501;
+
+/**
+Factory constructor
+
+@param aServerEntry Server entry
+@param aSettings SMTP settings
+@param aServ Socket server
+@param aConnect Network connection
+@return Constructed class
+*/
+CImSmtpSession* CImSmtpSession::NewL(CMsvServerEntry& aServerEntry, CSmtpSettings& aSettings, RSocketServ& aServ, CImConnect& aConnect, TMsvId aServiceId)
+	{
+	CImSmtpSession* self = new (ELeave) CImSmtpSession(aServerEntry, aSettings, aServiceId);
+	CleanupStack::PushL(self);
+	self->ConstructL(aServ, aConnect);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+Connect the session to the server
+
+@param aClientStatus Client status to be completed
+*/
+void CImSmtpSession::ConnectL(TRequestStatus& aClientStatus)
+	{
+	__ASSERT_DEBUG(!iSocketIsConnected, gPanic(EImsmSocketAlreadyConnected));
+
+	if (!iSocketIsConnected)
+		{
+		if(iSettings.SecureSockets())
+			iThisSession = ESecureSession;		 // if the user has chosen to use TLS then we have to use ESMTP+TLS
+		else if(iSettings.SMTPAuth())
+			iThisSession = EEnhancedSmtpSession; // if the user has chosen to use AUTH, when we have to use ESMTP
+		else
+			iThisSession = ESmtpSession;		 // use SMTP session for now
+
+		iCompleted = KErrNone;
+		iState = EConnectingToSmtp;		// Initialise to 1st state of state machine
+		DoStateL();						// Here we go...
+		Queue(aClientStatus);		
+		}
+	else
+		{
+		// Nothing to do so just complete the caller
+		RequestComplete(aClientStatus, KErrNone);
+		}
+	}
+
+/**
+Send all the emails to the server
+
+@param aSendFiles Files to send
+@param aClientStatus Client status to be completed
+*/
+void CImSmtpSession::SendFilesL(CMsgImOutboxSend& aSendFiles, TRequestStatus& aClientStatus)
+	{
+	__ASSERT_DEBUG(iSocketIsConnected, gPanic(EImsmSocketNotConnected1));
+	
+	TInt error = aSendFiles.NextFile();
+	if (iSocketIsConnected &&  error == KErrNone)
+		{
+		iSendFiles = &aSendFiles;
+		iState = ESendingImail;
+		DoStateL();
+		Queue(aClientStatus);
+		}
+	else if(error == KErrNotFound)
+		{
+		// Nothing to do so just complete the caller
+		RequestComplete(aClientStatus, KErrNotFound);
+		}
+	else
+		{
+		// Nothing to do so just complete the caller
+		RequestComplete(aClientStatus, KErrNone);
+		}
+	}
+
+/**
+Quit from the server
+
+@param aClientStatus Client status to be completed
+*/
+void CImSmtpSession::QuitL(TRequestStatus& aClientStatus)
+	{
+	__ASSERT_DEBUG(iSocketIsConnected, gPanic(EImsmSocketNotConnected2));
+	
+	if (iSocketIsConnected)
+		{
+		iState = EClosingSmtp;
+		DoStateL();
+		Queue(aClientStatus);
+		}
+	else
+		{
+		// Nothing to do so just complete the caller
+		RequestComplete(aClientStatus, KErrNone);
+		}
+	}
+
+/**
+Constructor
+
+@param aServerEntry Server entry
+@param aSettings SMTP settings
+*/
+CImSmtpSession::CImSmtpSession(CMsvServerEntry& aServerEntry, CSmtpSettings& aSettings, TMsvId aServiceId)
+			  :	CMsgActive(KImSmtpSessionPriority),
+			    iServerEntry(aServerEntry),
+				iSettings(aSettings),
+				iServiceId(aServiceId),
+				iCurrentAuthProfile(CSmtpAuthMechanismHelper::EUndefined) // always start with undefined
+	{
+	__DECLARE_NAME(_S("CImSmtpSession"));
+	}
+
+/**
+Second phase constructor
+
+@param aServ Socket server
+@param aConnect Network connection
+*/
+void CImSmtpSession::ConstructL(RSocketServ& aServ, CImConnect& aConnect)
+	{
+	iSocket = CImTextServerSession::NewL(aServ, aConnect);
+	//if log fails, ignore the error and continue.
+	TRAP_IGNORE(iLogMessage = CImLogMessage::NewL(iServerEntry.FileSession()));
+	CActiveScheduler::Add(this);					// Add SmtpSession to scheduler's queue
+	}
+
+/**
+Destructor
+*/
+CImSmtpSession::~CImSmtpSession()
+	{
+	Cancel();
+	if (iSocketIsConnected)
+		{
+		iSocket->Disconnect();
+		}
+	delete iSmtpFile;			// should be dead already
+	delete iSocket;
+	delete iSmtpAuthHelper;
+	delete iLogMessage;
+	}
+
+//
+//  CreateFormattedAddressL()
+//
+//  Creates a new HDesC* containing the concateneatd alias and address strings
+//
+HBufC* CImSmtpSession::CreateFormattedAddressLC(const TDesC& aString, const TDesC& aAlias)
+    {
+    TImMessageField msgField;
+
+    //  Make a buffer..
+    TInt strLen     = aString.Length();
+    TInt aliasLen   = aAlias.Length();
+
+    HBufC* outString = HBufC::NewLC(strLen+aliasLen+5);
+    HBufC* buffer = HBufC::NewL(strLen);
+
+    *buffer = msgField.GetValidInternetEmailAddressFromString(aString);
+
+	if (aliasLen)
+        {
+		outString->Des().Format(KSmtpEmailAddressAndAliasFmtStr,&aAlias,buffer);
+        }
+    else
+        {
+        *outString = *buffer;	        // just copy email address; as there's no alias to be added
+        }
+
+	delete buffer;
+    return outString;
+    }
+
+
+TImImailFileProgress CImSmtpSession::FileProgress()
+	{
+	// Return info about how much of message has been sent so far
+	TImImailFileProgress progress;
+
+	if (iSmtpFile && (iState==ESendingImail))
+		{
+		// get progress from CImSmtpFile object as it is doing the work at the moment
+        iSmtpFile->GetProgress(progress);
+
+		// If there is more than 1 message being sent(ie when sending to BCC's), then
+		// update the Progress to take account of the Total Num of Messages sent
+		if (iNumMsgsToSend > 1)
+			{
+			// Calc the Total number of Bytes to send
+			TInt fileByteSize = iSmtpFile->BytesToSend();
+			TInt bytesSentBefore = (iNumMsgsSent-1)*fileByteSize;  // iNumMsgsSent-1, as this msg is not yet complete
+			TInt totalBytesSent = bytesSentBefore + progress.iBytesSent;
+
+			// Update the Progress
+			progress.iBytesToSend = iTotalBytesToSend;
+			progress.iBytesSent = totalBytesSent;
+			}
+		}
+	else
+		{
+		progress.iSessionState = EClosingSmtp;
+		progress.iBytesSent = 0;
+		progress.iBytesToSend = 0;
+		}
+	return progress;
+	}
+
+
+void CImSmtpSession::SendFileL()
+	{
+	// Start the 'file mailer' object
+	__ASSERT_DEBUG(!iSmtpFile,gPanic(EImsmSmtpFileObjectAlreadyExists));
+	// log the info of the message to be sent
+	if (iLogMessage)
+		iLogMessage->Reset();
+	//  Update the header info for this message..
+	CImHeader* header = CImHeader::NewLC();
+	CMsvStore* store = iServerEntry.EditStoreL();
+	CleanupStack::PushL(store);
+	header->RestoreL(*store);
+
+	TBool isBccRcpt = EFalse;
+	CDesCArray* rcptArray = NULL;
+	TBool sendBccNow = iToRcptHeaderUpdated;
+
+	if(!iToRcptHeaderUpdated)
+		{
+		//  Set the new info...
+		//set from address
+		HBufC* formatedAddress = CreateFormattedAddressLC(iSettings.EmailAddress(), iSettings.EmailAlias());//Format the address fields
+		header->SetFromL(*formatedAddress);
+		CleanupStack::PopAndDestroy(formatedAddress);
+		formatedAddress = NULL;
+
+		// set ReceiptAddress if one exists in the settings
+		if( header->ReceiptAddress().Length() == 0 && iSettings.ReceiptAddress().Length() > 0 )
+			{
+			formatedAddress=CreateFormattedAddressLC(iSettings.ReceiptAddress(),iSettings.EmailAlias());//Format the address fields
+			header->SetReceiptAddressL(*formatedAddress);
+			CleanupStack::PopAndDestroy(formatedAddress);
+			formatedAddress = NULL;
+			}
+
+		if(header->ToRecipients().Count()==0 && header->CcRecipients().Count()==0)
+			{
+			sendBccNow=ETrue;
+			}
+
+		// ReplyToAddress
+		if ((header->ReplyTo().Length() == 0 || header->ReplyTo() == iSettings.ReplyToAddress()) &&
+		    (iSettings.ReplyToAddress().Length() > 0))
+			{
+			formatedAddress= CreateFormattedAddressLC(iSettings.ReplyToAddress(),iSettings.EmailAlias());//Format the address fields
+			header->SetReplyToL(*formatedAddress);
+			CleanupStack::PopAndDestroy(formatedAddress);
+			formatedAddress = NULL;
+			}
+		
+		//make a copy of 'Bcc' recipients then remove from Msg header
+		rcptArray= &(header->BccRecipients());
+		if (iSettings.SendCopyToSelf()==ESendCopyAsBccRecipient)
+			{
+			if ( header->ReceiptAddress().Length() )
+				rcptArray->AppendL(header->ReceiptAddress());
+			else if ( header->ReplyTo().Length() )
+				rcptArray->AppendL(header->ReplyTo());
+			}
+
+		if(rcptArray->Count() > 0)
+			{
+			iBccRcptFound=ETrue; // 'Bcc' recipients exists
+			iBccRcptIndex=0;	  //reset the counter
+			iFinishedWithBccRcpts=EFalse;
+
+			iSendFiles->ResetBccRcptArrayL();
+
+			TInt numberRcpts = rcptArray->Count();
+			if(numberRcpts)
+			{
+			iSendFiles->BccRcptArray().AppendL((*rcptArray)[0]);
+			CDesCArray& tempArr = iSendFiles->BccRcptArray();
+			for(TInt counter=1; counter<numberRcpts; counter++)
+				{
+				TInt aPos = 0;
+				tempArr.Find((*rcptArray)[counter],aPos,ECmpFolded16);			
+				if(aPos > 0)	
+				iSendFiles->BccRcptArray().AppendL((*rcptArray)[counter]);
+				}
+				}
+			rcptArray->Reset();
+			}
+
+		iToRcptHeaderUpdated=ETrue;
+		isBccRcpt=EFalse;
+		}
+
+	// Reset the number of Sent Messages, if all previous messages have been sent
+	if (iNumMsgsSent >= iNumMsgsToSend)
+		{
+		iNumMsgsSent = 0;
+		}
+
+	// Calc the num of messages we're sending. If there is a To or CC,
+	// recipient then there will be a message sent
+	if(header->ToRecipients().Count()>0 || header->CcRecipients().Count()>0)
+		iNumMsgsToSend = 1;
+	else
+		iNumMsgsToSend = 0;
+
+	// If there are BCC recipients, then we will be sending a message to each
+	if (iBccRcptFound)
+		iNumMsgsToSend += iSendFiles->BccRcptArray().Count();
+
+	// Make sure there are recipients to send to.
+	__ASSERT_ALWAYS(iNumMsgsToSend,gPanic(EImsmZeroRecipientsInMessage));
+
+	// sending Msg to 'Bcc' recipients so update header
+	if (sendBccNow && iBccRcptFound)
+		{
+		rcptArray= &(header->BccRecipients());
+		rcptArray->Reset();
+		header->BccRecipients().AppendL(iSendFiles->BccRcptArray()[iBccRcptIndex]);
+
+		++iBccRcptIndex;//for the next recipient
+		if(iBccRcptIndex < iSendFiles->BccRcptArray().Count())
+			iFinishedWithBccRcpts=EFalse;
+		else
+			iFinishedWithBccRcpts=ETrue;
+
+		isBccRcpt=ETrue;
+		}
+
+	header->StoreL(*store);
+	store->CommitL();
+	if (iLogMessage)
+		{
+		iLogMessage->LogEvent().SetEventType(KLogMailEventTypeUid);
+		if ((header->ToRecipients().Count()
+ 			+ header->CcRecipients().Count()
+ 			+ header->BccRecipients().Count()) > 1)
+ 		// If there are multiple recipients then set the recipient to 'multiple'
+ 			{
+ 			TBuf<KLogMaxSharedStringLength> multipleRecipientsString;
+ 			iLogMessage->GetString(multipleRecipientsString, R_LOG_REMOTE_MULTIPLE);
+ 			iLogMessage->LogEvent().SetRemoteParty(multipleRecipientsString);
+ 			}
+ 		else
+ 		// If there is only one recipient then set the recipient name
+ 			{
+			if (header->ToRecipients().Count())
+				iLogMessage->LogEvent().SetRemoteParty(header->ToRecipients()[0]);
+
+			else if (header->CcRecipients().Count())
+				iLogMessage->LogEvent().SetRemoteParty(header->CcRecipients()[0]);
+
+			else if (header->BccRecipients().Count())
+				iLogMessage->LogEvent().SetRemoteParty(header->BccRecipients()[0]);
+			}
+
+		iLogMessage->LogEvent().SetSubject(header->Subject());
+
+		TBuf<KLogMaxSharedStringLength> outString;
+		iLogMessage->GetString(outString, R_LOG_DIR_OUT);
+		iLogMessage->LogEvent().SetDirection(outString);
+
+		iLogMessage->LogEvent().SetLink(iServerEntry.Entry().Id());
+		}
+
+	CleanupStack::PopAndDestroy(2, header);
+
+	iTimeNow.UniversalTime();	// Set time/date of sending this message file
+	if (iLogMessage)
+		iLogMessage->LogEvent().SetTime(iTimeNow);
+	TRAPD(error,iSmtpFile = CImSmtpFile::NewL(	*iSocket,
+                                                iServerEntry,
+												iTimeNow,
+												iSmtpBuffer,
+												iSettings,
+												isBccRcpt)); // Create the file mailing object
+
+	if(error == KErrNone)
+		{
+		// If this is the first message to send, then calc the total bytes to send
+		if (iNumMsgsSent == 0)
+			{
+			iTotalBytesToSend =  iSmtpFile->TotalMsgSizeL() * iNumMsgsToSend;
+			}
+
+		// Tell the file how many bytes it will be sending.  This is just a
+		// rough estimate for progress
+		iSmtpFile->SetBytesToSend(iTotalBytesToSend/iNumMsgsToSend);
+
+		// Increment the number of messages sent
+		++iNumMsgsSent;
+
+		iSmtpFile->StartL(iStatus);		// start sending the message
+		MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpSendFile);
+		}
+	else
+		{
+		iCompleted=error;
+		RequestComplete(iStatus,0-error);	// Failed to create File object, but don't want to stop session prematurely, so negate error code
+		}
+	}
+
+void CImSmtpSession::DoCancel()
+	{
+	if (iSmtpFile)
+		{
+		iSmtpFile->Cancel();	//which calls iSocket->Cancel()
+		delete iSmtpFile;		// this is expected to be deleted
+		iSmtpFile=NULL;
+		}
+	else if (iSocket)
+		{
+		iSocket->Cancel();
+		}
+
+	if (iLogMessage)
+		{
+		iLogMessage->Cancel();
+		}
+	CMsgActive::DoCancel();		// this MUST be the last statement in this function
+	}
+
+
+void CImSmtpSession::DoRunL()
+	{
+	// Received a KErrNone signal completing last pending asynchronous operation.
+	// So now decide what to do next...
+
+	delete iSmtpFile;			// delete mailing object; (new one created later if need to mail another message)
+	iSmtpFile=NULL;
+
+	if (iStatus.Int() != KErrNone && iState!=ELogDataEvent)
+		{
+		iCompleted=iStatus.Int();	// retain any non-zero completion code for future reference when DoComplete() is called
+		}
+
+	iState = NextStateL();
+		
+	if (!iOperationComplete)
+		{
+		if (iSmtpMultiLineResponse)
+			{
+			// Don't change state, just read next line of multi-line
+			// response from remote SMTP server...
+			iSmtpLastMultiLineResponse=EFalse;
+			iSocket->QueueReceiveNextTextLine(iStatus);
+			SetActive();
+			}
+		else
+			{
+			DoStateL();		// move to state that was chosen by this function
+			}
+		}
+	else
+		{
+		iOperationComplete = EFalse;
+		}
+	}
+
+
+
+void CImSmtpSession::DoComplete(TInt& aStatus)
+	{
+	if (aStatus==KErrDisconnected && iState==EClosingSmtp)
+		{
+		// ignore sudden disconnect if in closing state
+		aStatus = KErrNone;
+		}
+
+	if (iCompleted && aStatus==KErrNone)		// last non-zero error in aStatus takes preference over older error stored in iCompleted
+		{
+		aStatus = iCompleted;
+		}
+
+	if (aStatus>KErrNone)
+		{
+		switch (aStatus)
+			{
+			case ESmtpMailboxNoAccess:
+			case ESmtpMailboxName:
+			case ESmtpTransactionFailed:
+				aStatus=KSmtpLoginRefused;
+				break;
+			case -KSmtpBadMailFromAddress:
+			case -KSmtpBadRcptToAddress:
+			case -KSmtpNoMailFromErr:
+			case -KSmtpLoginRefused:
+				// Negate the status because it was negated earlier so that it wouldn't be fatal
+				aStatus = -aStatus;
+				break;
+			default:
+				aStatus=KSmtpUnknownErr;
+			}
+		}
+	}
+
+
+TInt CImSmtpSession::NextStateL()
+// this returns the next state according to the current state and the response text received from the server
+// if the text has not been accepted by the server, then we close our session with it, i.e. return EClosingSmtp.
+	{
+	switch(iState)
+		{
+		case EConnectingToSmtp:
+			return EWaitingForReply;
+
+		case EWaitingForReply:
+			return NextStateFromWaitingForReply();
+
+		case EAuthorisingSmtp:
+			return NextStateFromAuthorisingSmtpL();
+
+		case ESendingStarttls:
+			return NextStateFromSendStarttls();
+
+		case ESettingSecurity:
+			return NextStateFromSettingSecurityL();
+
+		case ESendingImail:
+			if (iBccRcptFound && !iFinishedWithBccRcpts)
+				{
+				return ESendingImail;
+				}
+			else
+				{
+				// 'Bcc' recipients are dealt with at this point
+				iSendFiles->SetLastMessageStatusL(iTimeNow,(TSmtpSessionError)iStatus.Int());
+				// ... which unlocks the server entry (except the first time)
+
+				// now move on to the next Email message
+				iToRcptHeaderUpdated=EFalse; // for the new msg
+				iBccRcptFound=EFalse;
+				iFinishedWithBccRcpts=ETrue;
+				return ELogDataEvent;
+				}
+
+		case ELogDataEvent:
+			return NextStateFromNextFile();
+
+		case EClosingSmtp:
+			{
+			iOperationComplete = ETrue;
+			return iState;
+			}
+
+		case EAuthInProgress:
+			// for SMTP auth, call specific function, to stop cluttering up this one
+			return NextStateFromAuthInProgressL();
+
+		case EResetSmtp:
+			return NextStateFromResetSmtp();
+
+		default:
+			return iState;
+		}
+	}
+
+TInt CImSmtpSession::NextStateFromWaitingForReply()
+	{
+	if (CommandAccepted())
+		{
+		if (!iEsmtpSpokenHere)
+			{
+			iEsmtpSpokenHere = (iSmtpBuffer.Match(KEsmtpMatchString) != KErrNotFound);
+			if (iEsmtpSpokenHere && iThisSession == ESmtpSession)
+				iThisSession = EEnhancedSmtpSession;
+			}
+
+		if (iSmtpMultiLineResponse)
+			{
+			// If this is not last reply of a mullti-line response... request next
+			// line and don't change state
+			return EWaitingForReply;
+			}
+		return EAuthorisingSmtp;
+		}
+	else
+		{
+		// SMTP server didn't return "220" to "HELO" command, so quit session
+		// without sending any Emails.
+		return  EClosingSmtp;
+		}
+	}
+
+TInt CImSmtpSession::NextStateFromAuthorisingSmtpL()
+	{
+	if (CommandAccepted())
+		{
+		UpdateAuthorisingInfo();
+
+		if (iSmtpMultiLineResponse)
+			{
+			// If this is not last reply of a mullti-line response... request next
+			// line and don't change state.
+			return EAuthorisingSmtp;
+			}
+
+		if (iThisSession==ESecureSession)
+			{
+			if(iStartTlsAcceptedHere)
+				{
+				return ESendingStarttls;
+				}
+			else
+				{
+				// The SMTP server doesn't support TLS.
+				// Save the error and close down the session.
+				iCompleted = KErrSmtpTLSNegotiateFailed;
+				return EClosingSmtp;
+				}
+			}
+		else if (iThisSession==EEnhancedSmtpSession)
+			{
+			if (iSettings.SMTPAuth() && SelectNextSMTPAuthProfileL())
+				{
+				// if possible, perform SMTP AUTH
+				return EAuthInProgress;
+				}
+			
+			iOperationComplete = ETrue;
+			return iState;
+			}
+		else
+			{
+			__ASSERT_DEBUG(iThisSession==ESmtpSession, gPanic(EImsmBadSessionState));
+			iOperationComplete = ETrue;
+			return iState;
+			}
+		}
+	else
+		{
+		// EHLO or HELO rejected by server
+		if( iSettings.SecureSockets() )
+			{
+			// Cannot continue if we're trying for secure?
+			iCompleted = KErrSmtpTLSNegotiateFailed;
+			return EClosingSmtp;
+			}
+		else if((iLastSmtpCode == KSmtpBadParameter) && (iRetryAuthWithHostname==EFalse))
+			{
+			// The server did not like the supplied IP parameter, reset and retry with a named
+			// string as paramter using the same EHLO or HELO command if it hasn't already been
+			// attempted
+			iRetryAuthWithHostname = ETrue;
+			iCompleted = KErrNone;
+			return EResetSmtp;
+			}
+		else if(iThisSession==EEnhancedSmtpSession)
+			{
+			// EHLO has failed, reset and retry with HELO
+			iCompleted = KErrNone;
+			iRetryAuthWithHostname = EFalse;
+			iThisSession = ESmtpSession;
+			return EResetSmtp;
+			}
+		else
+			{
+			// Every attempt has failed, iCompleted has been set with an appropriate
+			// error code in the CommandAccepted() call, so simply close the smtp session
+			return EClosingSmtp;
+			}
+		}
+	}
+
+TInt CImSmtpSession::NextStateFromSendStarttls()
+	{
+	if (CommandAccepted() && iSmtpMultiLineResponse)
+		{
+		// If this is not last reply of a mullti-line response... request next
+		// line and don't change state
+		return ESendingStarttls;
+		}
+
+	if(iCompleted)
+		return EClosingSmtp;
+	else
+		return ESettingSecurity;
+	}
+
+TInt CImSmtpSession::NextStateFromSettingSecurityL()
+	{
+	if ( CommandAccepted() )
+		{
+		UpdateAuthorisingInfo();
+
+		if (iSmtpMultiLineResponse)
+			{
+			// If this is not last reply of a mullti-line response... request next
+			// line and don't change state
+			return ESettingSecurity;
+			}
+		}
+
+	if(iCompleted)
+		return EClosingSmtp;
+
+	if (iSettings.SMTPAuth() && SelectNextSMTPAuthProfileL())
+		return EAuthInProgress;
+	else
+		{
+		iOperationComplete = ETrue;
+		return iState;
+		}
+	}
+
+TInt CImSmtpSession::NextStateFromAuthInProgressL()
+	{
+	TInt smtpCode=ESmtpNoReturnCode;
+	TBool commandAccepted=GetCurrentTextLine();
+	if (commandAccepted)
+		{
+		// parse out SMTP code from text response
+		smtpCode=SmtpResponseCode(iSmtpBuffer,iSmtpMultiLineResponse,iSmtpLastMultiLineResponse);
+		if (iSmtpMultiLineResponse)
+			{
+			// Check for response code 535
+			if (smtpCode == KSmtpAuthCodeFailed)
+				{
+				User::Leave(KSmtpLoginRefused);
+				}
+			iSmtpAuthHelper->SetLastServerMessageL(iSmtpBuffer,iSmtpMultiLineResponse);
+			return iState;
+			}
+		if(smtpCode==KSmtpAuthCodeSuccessful || smtpCode==KSmtpAuthCodeFailed || smtpCode==KSmtpUnableToAuthAtPresent)
+			{
+			//if KSmtpAuthCodeSuccessful then SmtpAuth concluded - move onto next state.
+			//if KSmtpAuthCodeFailed then SmtpAuth failed, but try to send anyway
+			iOperationComplete = ETrue;
+			return iState;
+			}
+		else if(smtpCode==ESmtpParamNotImplemented)
+			{
+			// this mechanism not recognised by the server.
+			delete iSmtpAuthHelper;
+			iSmtpAuthHelper=NULL;
+			if (SelectNextSMTPAuthProfileL())
+				{
+				return EAuthInProgress;
+				}
+			else
+				{
+				// all AUTH attempts fail, try to send anyway
+				iOperationComplete = ETrue;
+				return iState;
+				}
+			}
+		else if(smtpCode==KSmtpAuthCodeReadyResponse)
+			{
+			iSmtpAuthHelper->SetLastServerMessageL(iSmtpBuffer,iSmtpMultiLineResponse);
+			return EAuthInProgress;
+			}
+			// if here, we will execute default error case, unless someone adds
+			// a clause not resulting in a "return" statement
+		}
+	// default error case
+	iCompleted=SmtpSessionError(smtpCode);
+	return EClosingSmtp;
+
+	}
+
+TInt CImSmtpSession::NextStateFromResetSmtp()
+	{
+	if( CommandAccepted() )
+		{
+		if (iSmtpMultiLineResponse)
+			{
+			// If this is not last reply of a mullti-line response... request next
+			// line and don't change state
+			return EResetSmtp;
+			}
+		// The reset was accepted - send the HELO command.
+		return EAuthorisingSmtp;
+		}
+
+	// The reset was not accepted - didn't get a 2xx response. Might as
+	// well end the SMTP session.
+	return EClosingSmtp;
+	}
+
+void CImSmtpSession::DoStateL()
+// performs the operation required by iState
+    {
+    switch (iState)
+        {
+		case EConnectingToSmtp:
+			iEsmtpSpokenHere = EFalse;
+			i8BitMimeAcceptedHere = EFalse;
+			iSizeAcceptedHere = EFalse;
+			iStartTlsAcceptedHere = EFalse;
+			if(iSettings.SSLWrapper())
+				{
+				// Open secure socket on port 465 for SMTP session
+				iSocket->SSLQueueConnectL(iStatus, iSettings.ServerAddress(), iSettings.Port(), iSettings.IapPrefs(), iSettings.TlsSslDomain());
+				}
+			else
+				{
+				// Open socket on port 25 for SMTP session
+				iSocket->QueueConnectL(iStatus, iSettings.ServerAddress(), iSettings.Port(), iSettings.IapPrefs(), iSettings.TlsSslDomain());
+				}
+
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpConnectingToSmtp);
+			break;
+
+		case EWaitingForReply:
+			iSocketIsConnected=ETrue;
+			iSocket->QueueReceiveNextTextLine(iStatus);
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpWaitingForReply);
+			break;
+
+		case EAuthorisingSmtp:
+			{
+			TBuf8<KImskIPAddressLen> address;
+			GetIpAddress(address);
+			
+			if(iRetryAuthWithHostname)
+				{
+				// Try sending a text string hostname instead of IP address
+				_LIT(KDefaultNameString, "localhost");
+				address.Copy(KDefaultNameString);
+				}
+
+			if (iThisSession!=ESmtpSession)
+				iSmtpBuffer.Format(KSmtpEhloCommand,&address);
+			else
+				iSmtpBuffer.Format(KSmtpHeloCommand,&address);
+
+			iSocket->SendQueueReceive(iStatus, iSmtpBuffer);
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpAuthorisingSmtp);
+			break;
+			}
+		case EAuthInProgress:
+			{
+			iSmtpAuthHelper->GetNextClientMessageL(iSmtpBuffer);
+			iSocket->SendQueueReceive(iStatus, iSmtpBuffer);
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpAuthInProgress);
+			break;
+			}
+		case ESendingStarttls:
+			{
+			// User has chosen to use Tls - send the STARTTLS cmd.
+			iSocket->SetSSLTLSResponseL(KSmtpTlsResponse);
+			iSocket->SendQueueReceive(iStatus, KSmtpStartTlsCommand());
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpSendingStartTls);
+			break;
+			}
+		case ESettingSecurity:
+			{
+			// Reset the Auth profiles and flags.
+			i8BitMimeAcceptedHere	= EFalse;
+			iSizeAcceptedHere		= EFalse;
+			iStartTlsAcceptedHere	= EFalse;
+			iSupportedAuthProfiles	= ENone;
+			
+			//  Set Security, send a NOOP
+			//	and then queues asynch read request for a response
+			//	from the remote SMTP server.
+			TBuf8<KImskIPAddressLen> address;
+			GetIpAddress(address);
+
+			iSmtpBuffer.Format(KSmtpEhloCommand, &address);
+			iSocket->SendQueueReceive(iStatus,iSmtpBuffer);
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpSettingSecurity);
+			break;
+			}
+		case ESendingImail:	// send one Imail message
+ 			SendFileL();
+			break;
+
+		case ELogDataEvent:
+			{
+			if (iLogMessage)
+				{
+				const TInt logError = iServerEntry.Entry().iError;
+				if (logError == KErrNone)
+					{
+					TBuf<KLogMaxStatusLength> status;
+					if (iLogMessage->GetString(status, R_LOG_DEL_SENT) == KErrNone)
+						iLogMessage->LogEvent().SetStatus(status);
+					}
+				iLogMessage->Start(logError, iStatus);
+				}
+			else
+				{
+				TRequestStatus* status = &iStatus;
+				User::RequestComplete(status, KErrNone);
+				}
+			break;
+			}
+		case EResetSmtp:
+			iSocket->SendQueueReceive(iStatus, KSmtpResetCommand());
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpResetSmtp);
+			break;
+
+		case EClosingSmtp:	//finished running - end SMTP session
+			iSocket->SendQueueReceive(iStatus, KSmtpQuitCommand());
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpClosingSmtp);
+			break;
+
+		default:     // Unknown state
+			gPanic(EImsmBadSessionState);
+			break;
+        }
+ 	SetActive();
+    }
+
+
+TInt CImSmtpSession::SmtpSessionError(const TInt aSmtpErrorCode)
+	{
+	// This function is called when no socket/memory error occurred whilst completing
+	// last state, but the remote SMTP server did not return the expected 3 digit
+	// completion code.
+	// Select an appropriate error code determined from the current state of CImSmtpSession.
+	switch (iState)
+		{
+		case EAuthorisingSmtp:
+			if (aSmtpErrorCode==ESmtpSyntaxError)
+				return (KSmtpLoginRefused);
+			break;
+
+		case EWaitingForReply:
+			// assume that ANY failure in this state means I have been refused connection to SMTP server
+			return (KSmtpLoginRefused);
+
+		case ESendingStarttls:
+		case ESettingSecurity:
+		case EConnectingToSmtp:
+		case ESendingImail:
+		case EResetSmtp:
+		case EClosingSmtp:
+			break;
+
+		case EAuthInProgress:
+			if (aSmtpErrorCode==KSmtpAuthCodeFailed || aSmtpErrorCode==KSmtpUnableToAuthAtPresent)
+				return (KSmtpLoginRefused);
+			break;
+
+		default:     // Unknown state
+			gPanic(EImsmBadSessionState);
+			break;
+        }
+
+	return IdentifySmtpError(aSmtpErrorCode);
+	}
+
+TBool CImSmtpSession::GetCurrentTextLine()
+	{
+	TInt result=iSocket->GetCurrentTextLine(iSmtpBuffer);
+	__ASSERT_DEBUG(result==ECRLFTerminated,gPanic(EImsmBadSmtpBuffer));
+
+	if(result==ECRLFTerminated)
+		return ETrue;
+	else
+		return EFalse;
+	}
+
+void CImSmtpSession::GetIpAddress(TDes8& aAddress)
+/** Gets the local IP address from the socket without any trailing scope identifiers
+@param aAddress Buffer in which the IP address of the socket will be returned */
+	{
+    aAddress.Copy(iSocket->LocalName());
+
+    // Remove the %nn scope identifier if present Eg. x.x.x.x.x.x%1 becomes x.x.x.x.x.x
+	//  This only occurs with IPv6 and was a requirement of multi-homing.
+	TInt pos = aAddress.Locate(TChar('%'));
+	if (pos>0)
+		{
+		aAddress.SetLength(pos);
+		}
+	}
+
+/*
+From knowledge of the last profile used, and the profiles available, select a next profile
+to try, create the helper class
+otherwise return false
+*/
+TBool CImSmtpSession::SelectNextSMTPAuthProfileL()
+	{
+	CSmtpAuthMechanismHelper::TSmtpAuthProfileFlag nextProfile=NextSMTPAuthProfile(iCurrentAuthProfile);
+	while (!(nextProfile&iSupportedAuthProfiles) && nextProfile!=CSmtpAuthMechanismHelper::ENoProfile)	// while "nextProfile" is not supported
+																				// and there are profiles to try
+		nextProfile=NextSMTPAuthProfile(nextProfile); // try the next one
+	iCurrentAuthProfile=nextProfile;
+	if (iCurrentAuthProfile==CSmtpAuthMechanismHelper::ENoProfile)  // run out of profiles to try
+		return EFalse;
+	else
+		{
+		__ASSERT_DEBUG(!iSmtpAuthHelper, gPanic(EImsmSmtpAuthHelperAlreadyExists));
+		iSmtpAuthHelper=CreateSMTPAuthHelperL(iCurrentAuthProfile,iSettings);
+		return ETrue;
+		}
+	}
+
+// Get the value of the connecting IAP from CImTextServerSession
+//  Returns the value of the currently connecting IAP or a system-wide error code.
+TInt CImSmtpSession::GetConnectionIAP()
+	{
+	TUint32 iap;
+	TInt err = iSocket->GetIAPValue(iap);
+	if (err == KErrNone)
+		{
+		return iap;
+		}
+	else
+		{
+		return err;
+		}
+	}
+
+CSmtpAuthMechanismHelper::TSmtpAuthProfileFlag  CImSmtpSession::NextSMTPAuthProfile(CSmtpAuthMechanismHelper::TSmtpAuthProfileFlag aPreviousProfile)
+	{
+	// we try the following profiles in this order, CRAM-MD5, LOGIN, PLAIN
+	switch(aPreviousProfile)
+		{
+		case CSmtpAuthMechanismHelper::EUndefined:
+			return CSmtpAuthMechanismHelper::ECramMD5;
+
+		case CSmtpAuthMechanismHelper::ECramMD5:
+			return CSmtpAuthMechanismHelper::ELogin;
+
+		case CSmtpAuthMechanismHelper::ELogin:
+			return CSmtpAuthMechanismHelper::EPlain;
+
+		case CSmtpAuthMechanismHelper::EPlain:
+			return CSmtpAuthMechanismHelper::ENoProfile;
+
+		default:
+			gPanic(EImsmBadSmtpAuthProfile1);
+		}
+	return CSmtpAuthMechanismHelper::ENoProfile;
+	}
+
+TInt CImSmtpSession::NextStateFromNextFile()
+	{
+	if (iSendFiles->NextFile() == KErrNone)
+		{
+		return ESendingImail;
+		}
+	else
+		{
+		iOperationComplete = ETrue;
+		return iState;
+		}
+	}
+
+TBool CImSmtpSession::CommandAccepted()
+	{
+	iLastSmtpCode = ESmtpNoReturnCode;
+	TBool commandAccepted = GetCurrentTextLine();
+	if (commandAccepted)
+		{
+		iLastSmtpCode = SmtpResponseCode(iSmtpBuffer, iSmtpMultiLineResponse, iSmtpLastMultiLineResponse);   // parse out SMTP code from text response
+
+		commandAccepted = LastSmtpCommandAccepted(iLastSmtpCode, 2);      // was response accepted by remote server?
+		}
+
+	if (!commandAccepted)
+		{
+		// overrride completion code with suitable error number
+		iCompleted = SmtpSessionError(iLastSmtpCode);
+		}
+	return commandAccepted;
+	}
+
+void CImSmtpSession::UpdateAuthorisingInfo()
+	{
+	if (!i8BitMimeAcceptedHere)
+		{
+		// i.e. don't do test again if flag is already set
+		i8BitMimeAcceptedHere = (iSmtpBuffer.Match(K8BitMimeMatchString) != KErrNotFound);
+		}
+	if (!iSizeAcceptedHere)
+		{
+		iSizeAcceptedHere = (iSmtpBuffer.Match(KSizeMatchString) != KErrNotFound);
+		// ESMTP: V1.1 parse string to extract max message size from string here...
+		}
+	if (!iStartTlsAcceptedHere)
+		{
+		iStartTlsAcceptedHere = (iSmtpBuffer.Match(KStartTlsMatchString) != KErrNotFound);
+		}
+	//Support added for Domino server.
+	if (iSmtpBuffer.Match(KAuthMatchString) != KErrNotFound || 
+    				iSmtpBuffer.Match(KAuthDominoMatchString) != KErrNotFound) 
+		{
+		// check for each supported profile
+		if (iSmtpBuffer.Match(KAuthPlainMatchString) != KErrNotFound)
+			iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::EPlain;
+		if (iSmtpBuffer.Match(KAuthLoginMatchString) != KErrNotFound)
+			iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ELogin;
+		if (iSmtpBuffer.Match(KAuthCramMD5MatchString) != KErrNotFound)
+			iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ECramMD5;
+   		// Special case for Domino servers tagging "=" after "AUTH" 
+   		if (iSmtpBuffer.Match(KAuthDominoPlainMatchString) != KErrNotFound) 
+       		iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::EPlain; 
+   		if (iSmtpBuffer.Match(KAuthDominoLoginMatchString) != KErrNotFound) 
+       		iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ELogin; 
+   		if (iSmtpBuffer.Match(KAuthDominoCramMD5MatchString) != KErrNotFound) 
+   			iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ECramMD5;
+
+ 		}
+	}