email/pop3andsmtpmtm/smtpservermtm/src/IMSM.CPP
changeset 0 72b543305e3a
child 40 224522e33db9
child 42 1367103c24e2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/smtpservermtm/src/IMSM.CPP	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1312 @@
+// 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:
+// Internet SMTP Transport Driver
+// 
+//
+
+#include <msvstd.h>		// TMsvEntry, CMsvEntrySelection
+#include <msventry.h>	// CMsvServerEntry
+#include <msvstore.h>	// needed for CMsvStore::~CMsvStore
+#include <imcvsend.h>	// CImSendConvert
+
+#include "IMSM.H"		
+#include "IMSMSEND.H"	
+#include "SMTSUTIL.H"	// forward declarations for utility fns
+#include "csmtpsessionmanager.h"
+#include "csmtpsettings.h"
+#include "cimmobilitymanager.h"
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+#include "csmtpupsresponsewaiter.h"
+#endif
+
+
+const TInt KBccArraySegment  = 100; 
+const TUid KUidSmtpServerMtm = {0x10003C79};
+
+/**
+Constructor
+
+@param aEntrySelection Selection of messages to send
+@param aServerEntry SMTP server entry
+*/
+CMsgImOutboxSend::CMsgImOutboxSend(const CMsvEntrySelection& aEntrySelection,CMsvServerEntry& aServerEntry)
+        : CActive(KMsgImOutboxSendPriority), iEntrySelection(aEntrySelection),iServerEntry(aServerEntry)
+	{
+	}
+
+/**
+Factory constructor
+
+@param aEntrySelection Selection of messages to send
+@param aServerEntry SMTP server entry
+@param aService SMTP service ID
+
+@return Constructed class
+*/
+CMsgImOutboxSend* CMsgImOutboxSend::NewLC(const CMsvEntrySelection& aEntrySelection,CMsvServerEntry& aServerEntry, TMsvId aService)
+	{
+	CMsgImOutboxSend* self = new (ELeave) CMsgImOutboxSend(aEntrySelection,aServerEntry);
+	CleanupStack::PushL(self);
+	self->ConstructL(aService);
+	return self;
+	}
+
+/**
+Factory constructor
+
+@param aEntrySelection Selection of messages to send
+@param aServerEntry SMTP server entry
+@param aService SMTP service ID
+
+@return Constructed class
+*/
+CMsgImOutboxSend* CMsgImOutboxSend::NewL(const CMsvEntrySelection& aEntrySelection,CMsvServerEntry& aServerEntry, TMsvId aService)
+	{
+	CMsgImOutboxSend* self=CMsgImOutboxSend::NewLC(aEntrySelection,aServerEntry,aService);
+	CleanupStack::Pop();
+	return self;
+	}
+
+void CMsgImOutboxSend::ConstructL(TMsvId aService)
+	{
+	iTotalMessages = iEntrySelection.Count();
+    iCurrentMessageNo = -1;
+	iProgress.SetServiceId(aService);
+
+	//Load service info iServerEntry context..
+	User::LeaveIfError(iServerEntry.SetEntry(aService));	
+	__ASSERT_DEBUG(iServerEntry.Entry().iType==KUidMsvServiceEntry,gPanic(EImsmNoServiceInfo));	//	Assert is ServiceEntry..	
+
+	iSettings = CSmtpSettings::NewL(iServerEntry);
+
+	iBccRcptArray = new (ELeave) CDesCArrayFlat(KBccArraySegment);
+
+	//	Assert descriptors != empty
+	__ASSERT_DEBUG (iSettings->ServerAddress().Length() >0 , gPanic(EImsmEmptyPostOffice));
+	__ASSERT_DEBUG (iSettings->EmailAddress().Length()   >0 , gPanic(EImsmEmptyEmailAddress));
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)	
+	iWaiter = CSmtpUpsResponseWaiter::NewL();
+#endif
+
+	if (iSettings->BearerMobility())
+		{
+		iMobilityManager = CImMobilityManager::NewL(KUidSmtpServerMtm, aService, *this);
+		}
+
+	CActiveScheduler::Add(this);
+	}
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)	
+void CMsgImOutboxSend::StartL(TRequestStatus& aStatus, TThreadId aClientThreadId, TBool aHasCapability) 
+	{
+	SetupStartL();
+
+	iState = EStateUserPrompting;
+	iWaiter->AuthoriseAndConnectL(iSettings->SmtpSettings(), aHasCapability, aClientThreadId, iStatus);
+	SetActive();
+
+	aStatus = KRequestPending;
+	iReport = &aStatus;
+	}
+#endif
+
+void CMsgImOutboxSend::StartL(TRequestStatus& aStatus) 
+	{
+	SetupStartL();
+	StartConnectingL();
+
+	aStatus = KRequestPending;
+	iReport = &aStatus;
+	}
+
+void CMsgImOutboxSend::SetupStartL()
+	{
+	iSetDisconnected=EFalse;
+
+	//	Get count messages.. leave if none.
+	if (iTotalMessages == 0)
+		{
+		//	No messages leave..
+		User::Leave(KErrEof);			
+		}
+	
+	//  Set the connected flag messages..
+	User::LeaveIfError(iServerEntry.SetEntry(KMsvGlobalOutBoxIndexEntryIdValue));
+	User::LeaveIfError(iServerEntry.ChangeAttributes( iEntrySelection, KMsvConnectedAttribute, 0));
+	iServerEntry.SetEntry(KMsvNullIndexEntryId);
+
+	//	Initialise the progress object..Create and start a new sesion..
+	iProgress.InitialiseTotal(iTotalMessages);	//	Sets status=EMsgOutboxProgressWaiting etc.
+	iProgress.SetStatus(EMsgOutboxProgressConnecting);
+	iProgress.SetMsgNo(-1);
+	iCurrentMessageNo = -1;
+	}
+
+void CMsgImOutboxSend::StartConnectingL()
+	{
+	iState = EStateConnectingSession;
+	iMobilityOperation = EMobilityOperationIdle;
+	iSession = NULL;
+	if (!iSessionManager)
+		{
+		iSessionManager = CSmtpSessionManager::NewL(iMobilityManager, iProgress.ServiceId());
+		}
+	iSessionManager->GetSessionL(iServerEntry, *iSettings, iSession, iStatus);
+	SetActive();
+	}
+
+/**
+A bearer switch has been requested and we should try to switch over to it.
+The action parameter indicates what should happen to the current operation.
+
+@param aAction What should happen to the current operation.
+@param aIsSeamless Is this a seamless switchover.
+*/
+void CMsgImOutboxSend::PrepareForNewCarrier(TImMobilityAction aAction, TBool /*aIsSeamless*/)
+	{
+	switch (aAction)
+		{
+		case KAcceptImmediately:
+			{
+			// Just kill the current operation, and then signal that we are
+			// ready to migrate
+			CarrierLost();
+			SignalMigrate();
+			break;
+			}
+
+		case KAcceptStopCurrent:
+			{
+			PrepareForNewCarrierAfterOperation(EMobilityOperationStoppingCurrent);
+			break;
+			}
+
+		case KAcceptCompleteCurrent:
+			{
+			PrepareForNewCarrierAfterOperation(EMobilityOperationCompletingCurrent);
+			break;
+			}
+
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedMobilityAction));
+			break;
+			}
+		}
+	}
+
+/**
+A bearer switch has been requested and the current bearer should be dropped
+immediately.
+*/
+void CMsgImOutboxSend::CarrierLost()
+	{
+	switch (iState)
+		{
+		case EStateIdle:
+		case EStateWaitingNewCarrier:
+		case EStateMobilityError:
+			{
+			// do nothing
+			break;
+			}
+
+		case EStateConnectingSession:
+			{
+			CancelForMigrate();
+			StartWaitingNewCarrier();
+			break;
+			}
+
+		case EStateSendingFiles:
+			{
+			CancelForMigrate();
+			delete iSession;
+			iSession = NULL;
+			StartWaitingNewCarrier();
+			break;
+			}
+
+		case EStateClosingSession:
+			{
+			CancelForMigrate();
+
+			// If we have stopped the current operation and there are still some
+			// messages left to send then we need to wait for the migration to
+			// occur.
+			// If we have completed the operation, or we have sent all the messages
+			// then we need to exit now. We do this by pretending that no bearer
+			// migration is currently taking place, and self completing. Execution
+			// will then pass to the RunL which will just assume that the session
+			// has closed at the end of the operation and we will then exit.
+			if (PreparingForMigration() && iCurrentMessageNo < iTotalMessages)
+				{
+				StartWaitingNewCarrier();
+				}
+			else
+				{
+				iMobilityOperation = EMobilityOperationIdle;
+				TRequestStatus* status = &iStatus;
+				User::RequestComplete(status, KErrNone);
+				SetActive();
+				}
+
+			break;
+			}
+
+		case EStateUserPrompting:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState2));
+			break;
+			}
+		}
+	}
+
+/**
+The new bearer is now active. Try to start using it.
+
+@param aNewAp New access point
+@param aIsSeamless Is this a seamless switchover
+*/
+void CMsgImOutboxSend::NewCarrierActive(TAccessPointInfo /*aNewAp*/, TBool /*aIsSeamless*/)
+	{
+	if (iState == EStateWaitingNewCarrier)
+		{
+		// Self complete to get the active object running again
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		}
+	else
+		{
+		if (iMobilityManager)
+			{
+			iMobilityManager->NewCarrierAccepted();
+			}
+		}
+	}
+
+/**
+An error has occured during a bearer mobility switch
+
+@param aError Error code
+*/
+void CMsgImOutboxSend::MobilityError(TUint /*aError*/)
+	{
+	CancelForMigrate();
+
+	TInt err = KErrNone;
+
+	// If the mobility error has occurred when we are closing the connection
+	// after sending all the messages then we can just self complete to make
+	// it look like the session close completed successfully.
+	// If the mobility error occurs in other states, or while closing the
+	// session for a migration, then we should self complete with an error
+	// so that the RunL routine cleans up and exits.
+	if ((iState != EStateClosingSession) ||
+	    (PreparingForMigration() && iCurrentMessageNo < iTotalMessages))
+		{
+		err = KErrDisconnected;	
+		}
+
+	iMobilityOperation = EMobilityOperationIdle;
+	iState = EStateMobilityError;
+
+	// Self complete with error code
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, err);
+	SetActive();
+	}
+
+/**
+Get progress information for mobility plugin
+
+@return Packaged progress information
+*/
+const TDesC8& CMsgImOutboxSend::MobilityProgress()
+	{
+	iMobilityProgressBuffer = Progress();
+	return iMobilityProgressBuffer;
+	}
+
+/**
+A bearer switch has been requested and we should either stop the current
+operation or complete it before indicating that the migration can proceed.
+
+@param aMobilityOperation Type of mobility operation that has been requested
+*/
+void CMsgImOutboxSend::PrepareForNewCarrierAfterOperation(TMobilityOperation aMobilityOperation)
+	{
+	switch (iState)
+		{
+		case EStateIdle:
+		case EStateWaitingNewCarrier:
+		case EStateMobilityError:
+			{
+			SignalMigrate();
+			break;
+			}
+
+		case EStateConnectingSession:
+			{
+			CancelForMigrate();
+			SignalMigrate();
+			StartWaitingNewCarrier();
+			break;
+			}
+
+		case EStateSendingFiles:
+		case EStateClosingSession:
+			{
+			// Just need to store what mobility operation has been requested.
+			// Note that if we are sending files, the file sending operation will
+			// see this and halt at the requested time.
+			iMobilityOperation = aMobilityOperation;
+			break;
+			}
+
+		case EStateUserPrompting:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState1));
+			break;
+			}
+		}
+	}
+
+void CMsgImOutboxSend::RunL()
+	{
+	TInt status = iStatus.Int();
+
+	if (status == KErrNone)
+		{
+		switch (iState)
+			{
+			case EStateUserPrompting:
+				{
+				StartConnectingL();
+				break;
+				}
+
+			case EStateConnectingSession:
+				{
+				SessionConnectedL();
+				break;
+				}
+
+			case EStateSendingFiles:
+				{
+				SentFiles();
+				break;
+				}
+
+			case EStateClosingSession:
+				{
+				SessionClosed();
+				break;
+				}
+
+			case EStateWaitingNewCarrier:
+				{
+				MigratedL();
+				break;
+				}
+
+			case EStateMobilityError:
+				{
+				// A mobility error occurred while we were closing the session after
+				// sending all the messages.
+				// do nothing
+				break;
+				}
+
+			default:
+				{
+				__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState3));
+				break;
+				}
+			}
+		}
+	else
+		{
+		switch (iState)
+			{
+			case EStateUserPrompting:
+				{
+				// do nothing
+				break;
+				}
+
+			case EStateConnectingSession:
+				{
+				SessionConnectionFailed();
+				break;
+				}
+
+			case EStateSendingFiles:
+				{
+				// closing Session with server 	
+				SentFiles();
+				break;
+				}
+
+			case EStateClosingSession:
+				{
+				// Failure to close a session can be ignored as it will have been
+				// tidied up.
+				// Continue by assuming the close was successful.
+				SessionClosed();
+				break;
+				}
+
+			case EStateMobilityError:
+				{
+				// do nothing
+				break;
+				}
+
+			case EStateWaitingNewCarrier:
+			default:
+				{
+				__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState4));
+				}
+			}
+		}
+
+	// If we are not active then this means we should complete
+	if (!IsActive())
+		{
+		if (iSession)
+			{
+			iProgress.iSendFileProgress = iSession->FileProgress();
+			}
+		else
+			{
+			iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+			iProgress.iSendFileProgress.iBytesSent = 0;
+			iProgress.iSendFileProgress.iBytesToSend = 0;
+			}
+
+		Complete(status);
+		}
+	}
+
+/**
+Handles leaves during RunL
+
+@param aError Error code
+*/
+TInt CMsgImOutboxSend::RunError(TInt aError)
+	{
+	switch (iState)
+		{
+		case EStateConnectingSession:
+			{
+			if (iMobilityOperation != EMobilityOperationMigrating)
+				{
+				iProgress.iSendFileProgress.iSessionState = EConnectingToSmtp;
+				iProgress.iSendFileProgress.iBytesSent = 0;
+				iProgress.iSendFileProgress.iBytesToSend = 0;
+				iProgress.SetMsgNo(KErrNotFound);
+				iProgress.SetConnectionIAP(KErrNotFound);
+				}
+			break;
+			}
+
+		case EStateSendingFiles:
+			{
+			if (iSession)
+				{
+				iProgress.iSendFileProgress = iSession->FileProgress();
+				}
+			iProgress.iSendFileProgress.iSessionState = ESendingImail;
+			break;
+			}
+
+		case EStateUserPrompting:
+		case EStateClosingSession:
+		case EStateWaitingNewCarrier:
+		case EStateMobilityError:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState6));
+			break;
+			}
+		}
+
+	Complete(aError);
+
+	return KErrNone;
+	}
+
+/**
+A new session has been connected
+*/
+void CMsgImOutboxSend::SessionConnectedL()
+	{
+	if (iMobilityOperation == EMobilityOperationMigrating)
+		{
+		if (iMobilityManager)
+			{
+			iMobilityManager->NewCarrierAccepted();
+			}
+
+		if (iDecrementMessageCountAfterMigration)
+			{
+			--iCurrentMessageNo;
+			iDecrementMessageCountAfterMigration = EFalse;
+			}
+		}
+
+	iMobilityOperation = EMobilityOperationIdle;
+
+	iState = EStateSendingFiles;
+	iSession->SendFilesL(*this, iStatus);
+	SetActive();
+	}
+
+/**
+We have completed sending the files
+*/
+void CMsgImOutboxSend::SentFiles()
+	{
+	iProgress.iSendFileProgress = iSession->FileProgress();
+
+	iState = EStateClosingSession;
+	iSessionManager->DeleteSession(*iSession, iStatus);
+	iSession = NULL;
+	SetActive();
+	}
+
+/**
+The session has been closed
+*/
+void CMsgImOutboxSend::SessionClosed()
+	{
+	// If the session has been closed for a bearer migration, and there are still more
+	// messages to send, then start waiting for the migration.
+	if (iMobilityManager)
+		{
+		if (PreparingForMigration() && iCurrentMessageNo < iTotalMessages)
+			{
+			SignalMigrate();
+			StartWaitingNewCarrier();
+			}
+		}
+	}
+
+/**
+The mobility framework has told us that the new bearer is now active. We need
+to conect to it.
+*/
+void CMsgImOutboxSend::MigratedL()
+	{
+	iState = EStateConnectingSession;
+	iSession = NULL;
+	iSessionManager->GetSessionL(iServerEntry, *iSettings, iSession, iStatus);
+	SetActive();
+	}
+
+/**
+Failed to connect a new session
+*/
+void CMsgImOutboxSend::SessionConnectionFailed()
+	{
+	if (iMobilityOperation != EMobilityOperationMigrating)
+		{
+		iSessionManager->ConnectionProgress(iProgress);				
+		}
+
+	// If we support bearer mobility, then we should tell the mobility manager
+	// to reject the carrier and then wait for a new one.
+	// If however we are doing the initial (non migration) session connection,
+	// and the failure is because the network connection did not start then we
+	// won't have registed with the mobility manager so we should just exit.
+	if (iMobilityManager &&
+	   (iMobilityOperation == EMobilityOperationMigrating ||
+	    iSessionManager->IsConnectionStarted()))
+		{
+		iMobilityManager->NewCarrierRejected();
+		StartWaitingNewCarrier();
+		}
+	}
+
+/**
+Tell the mobility manager that we are ready for the migration to proceed
+*/
+void CMsgImOutboxSend::SignalMigrate()
+	{
+	if (iMobilityManager)
+		{
+		iMobilityManager->MigrateToNewCarrier();
+		}
+	}
+
+/**
+Start waiting for the bearer migration to complete
+*/
+void CMsgImOutboxSend::StartWaitingNewCarrier()
+	{
+	iState = EStateWaitingNewCarrier;
+	iMobilityOperation = EMobilityOperationMigrating;
+	iStatus = KRequestPending;
+	SetActive();
+	}
+
+/**
+Cancel the current operations prior to a migration taking place. This
+performs the same as a normal cancel, except that it does not complete
+the caller.
+*/
+void CMsgImOutboxSend::CancelForMigrate()
+	{
+	iCancellingForMigrate = ETrue;
+	Cancel();
+	iCancellingForMigrate = EFalse;
+	}
+
+void CMsgImOutboxSend::DoCancel()
+	{
+	switch (iState)
+		{
+		case EStateUserPrompting:
+			{
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+			iWaiter->Cancel();
+#endif
+			break;
+			}
+
+		case EStateConnectingSession:
+			{
+			if (iMobilityOperation != EMobilityOperationMigrating)
+				{
+				iSessionManager->ConnectionProgress(iProgress);
+				}
+			iSessionManager->Cancel();
+			break;
+			}
+
+		case EStateSendingFiles:
+			{
+			__ASSERT_DEBUG(iSession, gPanic(EImsmSessionNotDefined));
+			if (iSession)
+				{
+				// We should not update the file progress information if we are
+				// cancelling for a bearer migration. For a bearer migration we
+				// collected the progress information at the end of the last
+				// successfully completed file and we don't want to overwrite it
+				// with the information about the file whose send we have cancelled.
+				if (!iCancellingForMigrate)
+					{
+					iProgress.iSendFileProgress = iSession->FileProgress();
+					}
+
+				iSession->Cancel();
+				delete iSession;
+				iSession = NULL;
+
+				// If we are cancelling for bearer migration, make sure that the
+				// message being cancelled is restored to its original state.
+				// We should also decrease our message count so that it indicates
+				// the last sent message so that we restart from the correct place
+				// after the migration.
+				if (iCancellingForMigrate)
+					{
+					TRAP_IGNORE(RestoreBccRecipientsToHeaderL());
+					--iCurrentMessageNo;
+					}
+				}
+			else
+				{
+				TRequestStatus* status = &iStatus;
+				User::RequestComplete(status, KErrCancel);
+				}
+			break;
+			}
+
+		case EStateClosingSession:
+			{
+			// If the session is being closed because all the emails have been sent, set
+			// the final progress information. If the session is closing for a bearer
+			// migration we don't want to overwrite the existing progress values.
+			if (!PreparingForMigration())
+				{
+				iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+				iProgress.iSendFileProgress.iBytesSent = 0;
+				iProgress.iSendFileProgress.iBytesToSend = 0;
+				}
+
+			iSessionManager->Cancel();
+			break;
+			}
+
+		case EStateWaitingNewCarrier:
+			{
+			// There is no outstanding async request so we need to self complete
+			// to keep the active object going.
+			TRequestStatus* status = &iStatus;
+			User::RequestComplete(status, KErrCancel);
+			break;
+			}
+
+		case EStateMobilityError:
+		case EStateIdle:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState5));
+			TRequestStatus* status = &iStatus;
+			User::RequestComplete(status, KErrCancel);
+			break;
+			}
+		}
+
+	if (!iCancellingForMigrate)
+		{
+		//  Make sure that all message not dealt with
+		//  already are set connected EFalse...
+		TRAP_IGNORE(DisconnectUnsentMessagesL());
+		iSetDisconnected=ETrue;
+
+		Complete(KErrCancel);
+		}
+	}
+
+/**
+The sending of all the emails has completed.
+Tidy up and complete the caller.
+*/
+void CMsgImOutboxSend::Complete(TInt status)
+	{
+	if (iState != EStateMobilityError)
+		{
+		// Respond to any outstanding bearer mobility requests as we are about
+		// to exit and we don't need to hold up the migration.
+		if (PreparingForMigration())
+			{
+			SignalMigrate();
+			}
+		else if (iMobilityOperation == EMobilityOperationMigrating &&
+		         iState == EStateConnectingSession)
+			{
+			if (iMobilityManager)
+				{
+				iMobilityManager->NewCarrierAccepted();
+				}
+			}
+		}
+
+	delete iSession;
+	iSession = NULL;
+	delete iSessionManager;
+	iSessionManager = NULL;
+
+	iState = EStateIdle;
+	iMobilityOperation = EMobilityOperationIdle;
+	iDecrementMessageCountAfterMigration = EFalse;
+
+	iProgress.SetStatus(EMsgOutboxProgressDone);
+	iProgress.SetError(status);
+
+	User::RequestComplete(iReport, status);
+	}
+
+//
+//	Progress()
+//
+//	Args: None
+//
+//	Return Value:	Reference to a TImSmtpProgress object maintained
+//					in CImOutboxSend...
+//
+//	Remarks:		Returns the current iProgress object...
+//
+const TImSmtpProgress& CMsgImOutboxSend::Progress()
+	{
+	switch (iState)
+		{
+		case EStateUserPrompting:
+			{
+			iProgress.iSendFileProgress.iSessionState = EConnectingToSmtp;
+			iProgress.iSendFileProgress.iBytesSent = 0;
+			iProgress.iSendFileProgress.iBytesToSend = 0;
+			iProgress.SetMsgNo(KErrNotFound);
+			iProgress.SetConnectionIAP(KErrNotFound);
+			break;
+			}
+
+		case EStateConnectingSession:
+			{
+			if (iMobilityOperation != EMobilityOperationMigrating)
+				{
+				if (iSessionManager)
+					{
+					iSessionManager->ConnectionProgress(iProgress);
+					}
+				}
+			break;
+			}
+
+		case EStateSendingFiles:
+			{
+			if (iSession)
+				{
+				iProgress.iSendFileProgress = iSession->FileProgress();
+				}
+			break;
+			}
+
+		case EStateClosingSession:
+			{
+			if (iMobilityOperation != EMobilityOperationMigrating)
+				{
+				iProgress.SetStatus(EMsgOutboxProgressDone);
+				iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+				}
+			break;
+			}
+
+		case EStateWaitingNewCarrier:
+			{
+			// do nothing
+			break;
+			}
+
+		case EStateIdle:
+		case EStateMobilityError:
+		default:
+			{
+			iProgress.SetStatus(EMsgOutboxProgressDone);
+			iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+			break;	
+			}
+		}
+
+	if (iProgress.MsgNo() > iProgress.SendTotal())
+		{
+		iProgress.SetMsgNo(iProgress.SendTotal());
+		}
+
+	if (iProgress.MsgNo() < 0)
+		{
+		iProgress.SetMsgNo(0);
+		}
+
+	return iProgress;
+	}
+
+//
+//	NextFile()
+//
+//	Args:None.
+//
+//	Return Value TInt kErrXXX
+//
+//	Remarks:	Called from CImStmpSession::SelectNextState() 
+//				Moves iServerEntry and iProgress::MsgNo() to point
+//				to the next (or first entry to send)
+//				Locking done implicitly in SetEntry() call.
+//
+TInt CMsgImOutboxSend::NextFile()
+	{
+	if(++iCurrentMessageNo >= iTotalMessages) 	//	Stepped past the messages..return
+		{
+		return KErrNotFound;
+		}
+
+	// If we are stopping for migration, exit now
+	if (iMobilityOperation == EMobilityOperationStoppingCurrent)
+		{
+		iDecrementMessageCountAfterMigration = ETrue;
+		return KErrNotFound;
+		}
+
+	//	Set the ServerEntry context to the next message.. and check if the message is suspended.
+	//if suspended, don't send and try to send the next message
+	TInt err = iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+	
+	if(err == KErrNotFound)
+		{
+		return KErrNotFound;
+		}
+
+	TMsvEntry entry=iServerEntry.Entry();
+	while(entry.SendingState()==KMsvSendStateSuspended && iServerEntry.Entry().iType==KUidMsvMessageEntry)
+		{
+		if (++iCurrentMessageNo >= iTotalMessages)
+			{
+			err=KErrNotFound;
+			iProgress.UpdateFailedToSend(); //this is not the correct update.  There should be UpdateNotSent() method.
+			break;
+			}
+		iProgress.UpdateFailedToSend();
+		err = iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+		entry=iServerEntry.Entry();
+		}			
+
+	if(err == KErrNone && iServerEntry.Entry().iType != KUidMsvMessageEntry)
+		{
+		//	Next context is not a SMTP message type..
+		err = KErrBadHandle;
+		}
+	if(err == KErrNone)
+		{
+		iProgress.SetMsgNo(iCurrentMessageNo);	//	Update iProgress.. 
+		
+		//if this is the first message we are sending then reset the iProgress.iSent member.
+		if (iCurrentMessageNo==0)
+			{
+			iProgress.SetConnectionIAP(0);	
+			}
+
+		iProgress.SetStatus(EMsgOutboxProgressSending);
+		}
+	return(err);
+	} 
+
+//
+//	SetLastMessageStatusL()
+//
+//	Args:				TTime -- time stamp..	
+//						TInt  -- completion reason from the last send..
+//
+//	Return Value:		void.
+//
+//	Remarks:			Called by CImSmtpSession::SelectNextState() to set CMsvServeEntry
+//						data for last file sent.. Either updates iDate etc.. or if 1st call
+//						with messages to be sent resets iProgress.SetStatus().
+//
+
+void CMsgImOutboxSend::SetLastMessageStatusL(const TTime& aTimeNow, TInt aCompletionReason)
+	{
+	//	If its the first message && there are messages to be sent change status..
+	if (iTotalMessages>0) 
+		{ 
+		iProgress.SetStatus(EMsgOutboxProgressSending); 
+		} 
+
+	// Store the file progress for the last file sent. This will be used in
+	// the situation where we cancel the operation to do a bearer migration
+	// so that the progress information is for the last file completed as
+	// opposed to the one we have just cancelled.
+	if (iSession)
+		{
+		iProgress.iSendFileProgress = iSession->FileProgress();
+		}
+
+	//	Fill in the iServerEntry details with data from the last message
+    //  IMCV may had left this inconsistent... so reset the iServerEntry
+    //  explicitly....
+	if (iCurrentMessageNo != -1)
+		{     
+		TInt err;
+        err = iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+		__ASSERT_DEBUG( err == KErrNone, gPanic(EImsmServerError));
+		TMsvEntry entry = iServerEntry.Entry();
+
+		//	Set date info and completion data..update the iServerEntry with this data..
+		entry.iDate=aTimeNow;
+		if(aCompletionReason!=KErrCancel)
+			{
+			entry.SetFailed(aCompletionReason != KErrNone);
+			entry.SetSendingState(aCompletionReason==KErrNone? KMsvSendStateSent: KMsvSendStateWaiting);  //set it to send agian.
+			if (aCompletionReason)
+				entry.iError=CalculateError(aCompletionReason);
+			}
+		else
+			{
+			entry.SetFailed(EFalse);
+			entry.SetSendingState(KMsvSendStateSuspended);
+			entry.iError=KErrNone;
+			}
+
+//		if (aCompletionReason<=KErrNone)
+	//		{
+			// ignore any +ve errors which may leak from the SMTP code
+//			entry.iError=aCompletionReason;
+//			entry.SetSendingState(aCompletionReason==KErrNone? KMsvSendStateSent: KMsvSendStateWaiting);  //set it to send agian.
+//			}
+		RestoreBccRecipientsToHeaderL();
+
+        entry.SetConnected(EFalse);
+		err = iServerEntry.ChangeEntry(entry);
+		__ASSERT_DEBUG( err == KErrNone, gPanic(EImsmServerError));
+		UpdateSummaryInfo(aCompletionReason);
+
+        //  If it went move to the "Sent" folder..
+        if(!entry.Failed() && aCompletionReason!=KErrCancel)
+            {
+            TMsvId id = entry.Id();
+            err = iServerEntry.SetEntry(KMsvSentEntryIdValue);
+            if(err == KErrNone)
+                {
+                err = iServerEntry.SetEntry(KMsvGlobalOutBoxIndexEntryId);
+                if(err == KErrNone)
+                    {
+                    // Move it....
+                    err = iServerEntry.MoveEntryWithinService(id, KMsvSentEntryIdValue);
+                    }
+                }
+            }
+		}
+	}
+
+TInt CMsgImOutboxSend::CalculateError(TInt aCompletionReason)
+	{
+	switch (aCompletionReason)
+		{
+	case ESmtpMailboxNoAccess:
+	case ESmtpMailboxName:
+	case ESmtpTransactionFailed:
+		return KErrAccessDenied; //KSmtpLoginRefused;
+	default:
+		break;
+		}
+	return KErrUnknown;		   
+	}
+
+//
+//	UpdateSummaryInfo()
+//
+//	Args:				TInt -- KErrXXX from calling function
+//
+//	Return value:		None
+//
+//	Remarks:			Called from SetLastMessage Status just increments
+//						iProgress count for sent or not sent messages...
+//
+void CMsgImOutboxSend::UpdateSummaryInfo(TInt& aReason)
+	{
+	if (aReason==KErrNone)	// sent message successfully
+		{
+		iProgress.UpdateSent();
+		}
+	//	MRG 26/08/98 -- Switch statement in IdentifySmtpError() returns no
+	//					error information at the moment.. 
+	//					else if edited accordingly..
+	else /*if (IdentifySmtpError(aReason))*/
+		{
+		iProgress.UpdateFailedToSend();
+		}
+	}
+
+
+//
+//  SessionIsConnected()
+//
+//  Ask for state of iSessions iSocket...
+//
+//
+TBool CMsgImOutboxSend::SessionIsConnected()
+	{
+	if (iState == EStateConnectingSession)
+		{
+		return iSessionManager->IsSessionConnected();
+		}
+	else if(iSession)
+		{
+		return iSession->IsConnected();
+		}
+	else
+		{
+		return EFalse;
+		}
+	}
+
+
+void CMsgImOutboxSend::DisconnectUnsentMessagesL()
+    {
+    //  Pos errors
+    TInt err = KErrNone;
+    //  Temp entry selection...
+    CMsvEntrySelection* unsentSelection = new (ELeave) CMsvEntrySelection();
+    CleanupStack::PushL(unsentSelection);
+    //  Append unsent messages into the temp array..
+    for(TInt i=0; i<iEntrySelection.Count(); i++)
+        {
+        err = iServerEntry.SetEntry((iEntrySelection)[i]);
+        //  If not found carry on along the array...
+        if(err != KErrNone)
+            continue;
+        if(iServerEntry.Entry().Parent() == KMsvGlobalOutBoxIndexEntryIdValue)
+            {
+            unsentSelection->AppendL((iEntrySelection)[i]);
+            }
+        }
+
+    //  Reset the iConnected flag on the lot.
+	 User::LeaveIfError(iServerEntry.SetEntry(KMsvGlobalOutBoxIndexEntryIdValue));
+    if(unsentSelection->Count() > 0)
+        {
+		  User::LeaveIfError(iServerEntry.ChangeAttributes(*unsentSelection, 0, KMsvConnectedAttribute));
+        }
+	iServerEntry.SetEntry(KMsvNullIndexEntryId);
+    CleanupStack::PopAndDestroy();  //  unsentSelection..
+    }
+
+void CMsgImOutboxSend::CleanUpOnDestructL()
+    {
+	if(!iSetDisconnected)
+		{
+		DisconnectUnsentMessagesL();
+		}
+
+	if (iStatus.Int() != KErrNone)
+		{
+		if ((iCurrentMessageNo != -1) && (iCurrentMessageNo<iTotalMessages))
+			{
+			TInt err=iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+			if(err == KErrNotFound)
+				{
+				User::Leave(KErrNotFound);
+				}
+			__ASSERT_DEBUG(err == KErrNone, gPanic(EImsmServerError));
+			TMsvEntry entry = iServerEntry.Entry();
+
+			RestoreBccRecipientsToHeaderL();
+
+			//	Set date info and completion data..update the iServerEntry with this data..
+			TInt errorCode = (TSmtpSessionError)iStatus.Int();
+			entry.iDate.UniversalTime();
+			if(errorCode!=KErrCancel)
+				{
+				entry.SetFailed(errorCode != KErrNone);
+				entry.SetSendingState(errorCode==KErrNone? KMsvSendStateSent: KMsvSendStateWaiting);  //set it to send agian.
+				if (errorCode)
+					entry.iError=CalculateError(errorCode);
+				}
+			else
+				{
+				entry.SetFailed(EFalse);
+				entry.SetSendingState(KMsvSendStateSuspended);
+				entry.iError=KErrNone;
+				}
+			entry.SetConnected(EFalse);
+		#ifdef _DEBUG
+			err = 
+		#endif
+			iServerEntry.ChangeEntry(entry);
+			__ASSERT_DEBUG(err == KErrNone, gPanic(EImsmServerError));
+			}
+		}
+	}
+
+//
+//	~CMsgImOutboxSend -- destructor.
+//
+CMsgImOutboxSend::~CMsgImOutboxSend( )
+	{
+	Cancel();
+	TRAP_IGNORE(CleanUpOnDestructL());
+
+	delete iSession;				//should be dead
+	delete iSettings;
+
+	if (iBccRcptArray)
+		{
+		iBccRcptArray->Reset();
+		delete iBccRcptArray;
+		}
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)		
+	delete iWaiter;	
+#endif
+
+	delete iSessionManager;
+	delete iMobilityManager;
+	}
+
+CDesCArray& CMsgImOutboxSend::BccRcptArray()
+	{
+	return *iBccRcptArray;
+	}
+
+void CMsgImOutboxSend::ResetBccRcptArrayL()
+	{
+	if(iBccRcptArray)
+		{
+		iBccRcptArray->Reset();
+		delete iBccRcptArray;
+		iBccRcptArray=0;
+		}
+	iBccRcptArray = new (ELeave) CDesCArrayFlat(KBccArraySegment);
+	}
+
+
+void CMsgImOutboxSend::RestoreBccRecipientsToHeaderL()
+	{
+	if (!iServerEntry.HasStoreL() || !iBccRcptArray || iBccRcptArray->Count()==0)
+		return; // no recipients to restore.
+
+	CMsvStore* store = iServerEntry.EditStoreL();
+	CleanupStack::PushL(store);
+	
+	// Must have an rfc822 header.
+	CImHeader* header = CImHeader::NewLC();
+	if (store->IsPresentL( KUidMsgFileIMailHeader) )
+		{
+		header->RestoreL(*store);
+		header->BccRecipients().Reset();
+
+		TInt ii = iBccRcptArray->Count(); 
+		while (ii-- > 0)
+			header->BccRecipients().InsertL(0, (*iBccRcptArray)[ii]);
+		header->StoreL(*store);	
+		store->CommitL();
+		}
+
+	// To stop the array growing, delete and recreate.
+	iBccRcptArray->Reset();
+	delete iBccRcptArray;
+	iBccRcptArray=0;
+	iBccRcptArray = new (ELeave) CDesCArrayFlat(KBccArraySegment);
+
+	CleanupStack::PopAndDestroy(header);
+	CleanupStack::PopAndDestroy(store); 
+	}
+
+/**
+Indicates if we are preparing to do a migration based on the mobility
+operation value.
+
+@return ETrue if preparing to do a migration, EFalse otherwise
+*/
+TBool CMsgImOutboxSend::PreparingForMigration()
+	{
+	if (iMobilityOperation == EMobilityOperationStoppingCurrent ||
+	    iMobilityOperation == EMobilityOperationCompletingCurrent)
+		{
+		return ETrue;
+		}
+
+	return EFalse;
+	}
+
+/**
+Gets the access point ID in use for the connection to the server
+
+@param aAccessPointId On return stores the access point ID value
+
+@return KErrNone if successful, or a system wide error code
+*/
+TInt CMsgImOutboxSend::GetAccessPointIdForConnection(TUint32& aAccessPointId) const
+	{
+	if (iSessionManager)
+		{
+		return iSessionManager->GetAccessPointIdForConnection(aAccessPointId);
+		}
+
+	return KErrNotFound;
+	}