email/imap4mtm/imapprotocolcontroller/src/cimapprotocolcontroller.cpp
changeset 0 72b543305e3a
child 18 a9c7e5670d17
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapprotocolcontroller/src/cimapprotocolcontroller.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,3526 @@
+// Copyright (c) 2008-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:
+//
+
+#include <imapset.h>
+#include <mentact.h>
+#include <miut_err.h>
+
+#include "cimapprotocolcontroller.h"
+#include "cimapsettings.h"
+#include "cimapsessionmanager.h"
+#include "cimapsession.h"
+#include "cimapsessionconsts.h"
+#include "cimapmailstore.h" 
+#include "cimapofflinecontrol.h"
+#include "cimapsyncmanager.h"
+#include "cimapfolder.h"
+
+#include "cimapcompoundcopyfromlocal.h"
+#include "cimapcompoundcopytolocal.h"
+#include "cimapcompoundcopywithinservice.h"
+#include "cimapcompoundcreate.h"
+#include "cimapcompounddelete.h"
+#include "cimapcompounddeletefolder.h"
+#include "cimapcompounddisconnect.h"
+#include "cimapcompoundrename.h"
+#include "cimapcompoundselect.h"
+#include "cimapcompoundsyncfolder.h"
+#include "cimapcompoundsynctree.h"
+#include "cimapcompoundsyncservice.h"
+
+#include "cimapopbackgroundsync.h"
+#include "cimapidlecontroller.h"
+#include "cimapupdateflagoperation.h"
+
+#include "cimaplogger.h"
+
+#include "mobilitytestmtmapi.h"
+
+// at the moment we do not need more than 2 sessions
+const TInt KImapSessionArrayGranularity = 2;
+
+const TUid KUidImapServerMtm = {0x10003C4E};
+
+CImapProtocolController::CImapProtocolController(CMsvServerEntry& aEntry,CImapOfflineControl& aImapOfflineControl):
+	CMsgActive(EPriorityStandard),
+	iEntry(aEntry),
+	iImapSessionArray(KImapSessionArrayGranularity),
+	iImapOfflineControl(aImapOfflineControl)
+	{
+	}
+	
+CImapProtocolController::~CImapProtocolController()
+	{
+	Cancel();
+
+	// The mail store needs to be deleted before DisconnectAll() as its destructor
+	// uses the session that DisconnectAll() destroys.
+	delete iImapMailStore;	
+
+	//serviceid indicates that we are online
+	if(iServiceId!=0)
+		{
+		DisconnectAll();
+		}
+
+	delete iBackgroundSyncOp;
+	delete iImapCompound;
+	delete iMigrateCompound;
+	delete iImapSyncManager;
+	delete iImapIdleController;
+	delete iImapSettings;
+	
+	delete iMobilityManager;
+
+	// Need to destroy the sessions before the session manager is deleted.
+	// This is because session manager will cleanup transport handler code,
+	// and it is important to ensure that the sockets are not being used
+	// by the session when that happens.
+	iImapSessionArray.ResetAndDestroy();
+	delete iImapSessionManager;
+	}
+
+EXPORT_C CImapProtocolController* CImapProtocolController::NewL(CMsvServerEntry& aEntry,CImapOfflineControl& aImapOfflineControl)
+	{
+	CImapProtocolController* self = new (ELeave) CImapProtocolController(aEntry,aImapOfflineControl);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+Part of two phase construction
+Note that syncmanager is not constructed until a connection is made.
+*/
+void CImapProtocolController::ConstructL()
+	{
+	iImapSettings       = CImapSettings::NewL(iEntry);
+	iImapMailStore 		= CImapMailStore::NewL(iEntry);
+	iImapSessionManager = CImapSessionManager::NewL(*iImapSettings, *iImapMailStore);
+
+	iFlushPrimary = EFalse;
+	// We're an active object...
+	CActiveScheduler::Add(this);
+	}
+
+/**
+Query connection status - return true if at least one IMAP Session exists and is 
+connected. Also return true if migrating.
+This is not a compound operation.
+
+@return 
+*/
+EXPORT_C TBool CImapProtocolController::Connected() const
+	{
+	if (iImapSessionArray.Count() > 0 || iMigrateState != ENotMigrating)
+		{
+		// Sessions are deleted if a disconnect is observed,
+		// therefore the existance of any sessions indicates 
+		// that the Protocol Controller is connected.
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+/**
+If a connected session already exists, this API shall complete immediately with 
+KErrServerBusy. Otherwise, this API uses the IMAP Session Manager to create and connect 
+an IMAP Session object that can be used for subsequent commands.
+On successful connection, the passed request status is completed. This API can not 
+be used to instantiate further IMAP Session objects.
+
+Sequence For Connect
+	CreateSession
+	SelectInboxRW
+	Finished
+	
+@param aStatus
+@param aSelection
+@leave KErrServerBusy if a connected session already exists
+*/
+EXPORT_C void CImapProtocolController::ConnectL( TRequestStatus& aStatus,
+												 CMsvEntrySelection& aSelection )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::ConnectL()");
+	
+	ResetProgress();
+	
+	if (Connected())
+		{
+		// Do not call Complete() - this would result in the Protocol
+		// Controller disconnecting all sessions.
+		TRequestStatus* pStatus = &aStatus;
+		User::RequestComplete(pStatus, KErrServerBusy);
+		}
+	else
+		{
+		// Load the Service Settings
+		iServiceId = aSelection[0];
+		iImapSettings->LoadSettingsL(iServiceId);
+
+		// We're going online - create a session manager if none exists
+		if (iImapSessionManager==NULL)
+			{
+			// session manage may have been deleted due to a mobility error
+			iImapSessionManager = CImapSessionManager::NewL(*iImapSettings, *iImapMailStore);
+			}
+	
+		// create a sync manager
+		delete iImapSyncManager;
+		iImapSyncManager = NULL;
+		iImapSyncManager = CImapSyncManager::NewL( iEntry, *iImapSettings);
+		
+		// create the mobility manager if mobility supported
+		delete iMobilityManager;
+		iMobilityManager = NULL;
+
+		if (iImapSettings->BearerMobility())
+			{
+			iMobilityManager = CImMobilityManager::NewL(KUidImapServerMtm, iServiceId, *this);
+			}
+	
+		// Create the primary session pointer
+		CImapSession* imapSession = NULL;
+		iImapSessionArray.AppendL(imapSession);
+
+		// Request the session manager to connect the session
+		iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[0]);
+
+		iForegroundSession = 0;
+		iRequestedOp = EConnect;
+		iCurrentOp = iRequestedOp;
+
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+This method allows the client to connect to an IMAP account and initialise a 
+background synchronisation of the account once connection has completed. If a 
+connected IMAP Session already exists, the API shall complete immdediately with 
+KErrServerBusy. 
+On successful connection, a background synchronisation is started and the passed 
+request status is completed. The background synchronisation is processed by a 
+CImapBackgroundSyncOp object, which will notify the Protocol Controller that 
+the process has completed via a callback function void 
+BackgroundSyncCompleted( TInt aError )
+
+Sequence For ConnectAndSynchronise
+	CreateSession
+	StartBackgroundSyncOp
+	Finished
+
+@param aStatus
+@param aSelection
+@leave KErrServerBusy if a connected session already exists
+*/
+EXPORT_C void CImapProtocolController::ConnectAndSynchroniseL( TRequestStatus& aStatus, 
+															   CMsvEntrySelection& aSelection )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::ConnectAndSynchroniseL()");
+
+	// Issue the connect request (this will cause ResetProgress() to be called)
+	ConnectL(aStatus, aSelection);
+
+	// Create the Background Sync Operation object
+	if (IsActive())
+		{
+		__ASSERT_DEBUG(iBackgroundSyncOp==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EConnectAndSyncBgSyncOpIsNotNull));
+		iBackgroundSyncOp = CImapOpBackgroundSync::NewL(*this, iEntry, *iImapSyncManager, *iImapSettings, *iImapMailStore, iImapOfflineControl);
+		}
+	}
+
+/**
+Logs out and disconnects all connected sessions with the remote server.
+
+Disconnect request is handled and acted upon during migration, unlike other 
+client-requested operations which are rejected (with KErrServerBusy).
+
+The Bearer Mobility Manager is deleted prior to acting on this request,
+effectively de-registering the server MTM from the mobility framework
+
+@param aStatus
+*/
+EXPORT_C void CImapProtocolController::DisconnectL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectL()");
+
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectCompoundIsNotNull));
+	
+	// de-register for mobility notifications
+	delete iMobilityManager;
+	iMobilityManager = NULL;
+	
+	// Disconnect allowed to happen even if we are migrating...
+	switch (iMigrateState)
+		{
+		case ENotMigrating:
+		case EHandlingConnectError:
+			{
+			// normal behaviour...
+			DoDisconnectL(aStatus);
+			break;
+			}
+		case EConnectingAfterMigrate: // Cancel connecting of new session
+		case EStartingReconnect:	  // already disconnected. Cancel dummy request
+		case EWaitingForNewCarrier:   // already disconnected. Cancel dummy request
+		case EWaitingInitialCarrierRejected: // already disconnected. Cancel dummy request
+			{
+			Cancel();
+			// mark offline and complete
+			DisconnectAll();
+			Queue(aStatus);
+			Complete(KErrNone);
+			break;
+			}
+		case EDisconnectingForMigrate:
+			{
+			// already doing a graceful disconnect, switch to main state machine.
+			iMigrateState = ENotMigrating;
+			iRequestedOp  = EDisconnect;
+			iCurrentOp    = EDisconnect;
+			// however, using the iMigrateCompound pointer...
+			iImapCompound = iMigrateCompound;
+			iMigrateCompound = NULL;
+			Queue(aStatus);
+			break;
+			}
+		case EWaitingForOpToComplete:
+		case EWaitingForOpToStop:
+		default:
+			{
+			__ASSERT_DEBUG(iBackgroundSyncOp==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDisconnectUnexpectedMigrateState));
+			break;
+			}
+		}
+	}
+
+void CImapProtocolController::DoDisconnectL(TRequestStatus& aStatus)
+	{
+	// Unlike the other async methods, DisconnectL() will wait for any flush operation to finish
+	// and then initiate the disconnect.
+	// Consequently there is no need to call CompleteIfBackgroundOpInProgress()	
+	ResetProgress();
+	
+	iImapCompound = CImapCompoundDisconnect::NewL( *iImapSyncManager, 
+												   iEntry, 
+												   *iImapSettings,
+												   *iImapSessionManager,
+												   *iImapMailStore,
+												   iImapSessionArray,
+												   iImapOfflineControl,
+												   ETrue );
+	iRequestedOp = EDisconnect;
+	
+	if (iCurrentOp != ECancelRecoverPrimary)
+		{
+		// Cancel any outstanding background sync												   
+		if (iBackgroundSyncOp!=NULL)
+			{
+			iBackgroundSyncOp->Cancel();
+			delete iBackgroundSyncOp;
+			iBackgroundSyncOp=NULL;
+
+			// Flush the session, now that it has been cancelled
+			iCurrentOp = ECancelRecoverPrimary;
+			CImapSession* session = iImapSessionArray[0];
+			session->FlushCancelledCommand(iStatus);
+			SetActive();
+			}
+		else
+			{
+			StartPrimaryOperation();	
+			SetActive();	
+			}
+		}
+	
+	// NOTE: In the case of iCurrentOp == ECancelRecoverPrimary, DoRunL() will start the async 
+	// iImapCompound operation after the flush has completed.
+	// The iImapCompound will expect aStatus to have been queued so that it can be Completed later.
+	
+	Queue(aStatus);
+	}
+
+/**
+Checks the status of the CImapSessions
+
+@return ETrue if a backgroundsync is in progress and the second session is busy,
+		EFalse otherwise.
+*/
+EXPORT_C TBool CImapProtocolController::Busy() const
+	{
+	// second session is busy if a compound operation exists.
+	if (BackgroundSyncInProgress() && iImapCompound!=NULL)
+		{
+		return ETrue;
+		}
+	return EFalse;	
+					
+	}
+
+/**
+Gets the access point ID in use for the connection to the server
+
+@param aIap On return stores the access point ID value
+
+@return KErrNone if successful, or a system wide error code
+*/
+EXPORT_C TInt CImapProtocolController::GetAccessPointIdForConnection(TUint32& aAccessPointId) const
+	{
+	if (iImapSessionManager)
+		{
+		return iImapSessionManager->GetAccessPointIdForConnection(aAccessPointId);
+		}
+
+	return KErrNotFound;
+	}
+
+/**
+Returns whether a Background synchronisation is in progress
+
+@return ETrue if a Background synchronisation is in progress, EFalse otherwise
+*/
+EXPORT_C TBool CImapProtocolController::BackgroundSyncInProgress() const
+	{
+	return (iBackgroundSyncOp != NULL);
+	}
+
+/**
+Cancels an outstanding background synchronise operation. Calls 
+CImapOpBackgroundSync::Cancel() to propagate the cancel and kicks off
+an asynchronous cleanup operation.
+The cancel request is completed immediately with KErrNone if no background
+sync was in operation, otherwise it is completed with KErrCancel when
+the cleanup process is complete.
+*/	
+EXPORT_C void CImapProtocolController::CancelBackgroundSync(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CancelBackgroundSync()");
+	
+	ResetProgress();
+	
+	Queue(aStatus);
+	if (BackgroundSyncInProgress())
+		{
+		if (iMigrateState != ENotMigrating && iBackgroundSyncOp->IsSuspendedForMigrate())
+			{
+			delete iBackgroundSyncOp;
+			iBackgroundSyncOp = NULL;
+			Complete(KErrCancel);
+			}
+		else
+			{
+			iBackgroundSyncOp->CancelAndCleanup();
+			iWaitForBackgroundSync = ETrue;
+			}
+		}
+	else
+		{
+		Complete(KErrNone);
+		}
+	}
+
+/**
+Wait for background sync operation to complete. This effectively makes a background 
+synchronise a foreground operation blocking subsequent Server MTM requests until the 
+synchonise is completed.
+
+@param aStatus
+*/
+EXPORT_C void CImapProtocolController::WaitForBackground(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::WaitForBackground()");
+	
+	ResetProgress();
+	
+	Queue(aStatus);
+	// Wait for background operation to complete: is one running?
+	if (!BackgroundSyncInProgress())
+		{
+		// No, complete immediately
+		Complete(KErrNone);
+		}
+	else
+		{
+		// Otherwise, wait for completion
+		iWaitForBackgroundSync = ETrue;
+		}
+	}
+
+
+/**
+Completes the passed request status if a background operation is in progress,
+or if the primary session is being recovered following a cancel operation.
+This should be called prior to starting a compound operation that requires the
+primary session to be performed.
+
+This method must be called before assigning iRequestedOp, 
+for any operation that requires the primary session.
+
+@return ETrue if the user request has been completed
+		EFalse otherwise.
+*/
+TBool CImapProtocolController::CompleteIfBackgroundOpInProgress(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CompleteIfBackgroundOpInProgress()");
+	if (BackgroundSyncInProgress() || iCurrentOp==ECancelRecoverPrimary || iMigrateState!=ENotMigrating)
+		{
+		// Complete the user request
+		// Do not call Complete() - this would result in the Protocol
+		// Controller disconnecting all sessions.
+		TRequestStatus* status = &aStatus;
+		User::RequestComplete(status, KErrServerBusy);
+		return ETrue;
+		}
+	return EFalse;
+	}
+	
+/**
+Synchronise Folder Tree - calls CImapSyncManager::SynchroniseTreeL()
+Sequence For SynchroniseTree
+	StopIdle
+	SynchroniseTree // CImapSyncManager Operation
+
+@param aStatus
+@leave
+*/	
+EXPORT_C void CImapProtocolController::SynchroniseTreeL(TRequestStatus& aStatus)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::SynchroniseTreeL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESynchroniseTreeCompoundIsNotNull));
+
+	ResetProgress();
+
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundSyncTree::NewL( *iImapSyncManager, 
+													 iEntry, 
+													 *iImapSettings );
+		
+		iRequestedOp = ESync;
+		StartPrimaryOperation();	
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+
+/**
+Perform a full account synchronisation - calls CImapSyncManager::SynchroniseL()
+Sequence For SynchroniseAll
+	StopIdle
+	SynchroniseL  // CImapSyncManager Operation
+
+@param aStatus
+@leave
+*/	
+EXPORT_C void CImapProtocolController::SynchroniseAllL( TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::SynchroniseAllL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESynchroniseAllCompoundIsNotNull));
+
+	ResetProgress();
+
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundSyncService::NewL( *iImapSyncManager, 
+												    	iEntry, 
+												    	*iImapSettings,
+												    	*iImapMailStore,
+												    	iImapOfflineControl,
+												    	EFalse);
+	
+		iRequestedOp = ESync;
+		StartPrimaryOperation();	
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Callback function to notify Protocol Controller that a background sync 
+operation has completed.
+Deletes the CImapOpBackgroundComplete object that has just completed.
+If WaitForBackground has been previously called, completes the user request.
+
+@param aError
+*/	
+EXPORT_C void CImapProtocolController::BackgroundSyncComplete(TInt aError)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::BackgroundSyncComplete()");
+	__ASSERT_DEBUG(iBackgroundSyncOp!=NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ENoBackgroundSyncInProgress));
+
+	// get final sync progress... 
+	// unless a foreground operation is being performed
+	if (iImapCompound == NULL)
+		{
+		iBackgroundSyncOp->Progress(iProgress);
+		}
+		
+	if (iMigrateState != ENotMigrating)
+		{
+		// If the protocol controller is not active, then there is no
+		// operation currently in progress. Therefore we are now ready to
+		// migrate. Complete the request here, and then Disconnect current sockets in DoMigrateRunL(). 
+		// We cannot just test existance of a compound operation object, as there may be a "paused" operation.
+		if (!IsActive())
+			{
+			// Complete the request so that, we migrate from DoMigrateRunL()
+			SetActive();
+			TRequestStatus* pStatus = &iStatus;
+			User::RequestComplete(pStatus, KErrNone);
+			}
+		
+		// the background sync may have been completed early to allow
+		// a migration to occur. If that is the case, then we do not want
+		// to delete the background sync operation object - it will need
+		// to be restarted once the migrate has occurred. Otherwise, just
+		// process the completion of the background sync normally.
+		if (iBackgroundSyncOp->IsSuspendedForMigrate())
+			{
+			return;
+			}
+		}
+
+	// Deleting the background sync op.
+	// CImapOpBackgroundComplete must finish immediately after calling this API
+	delete iBackgroundSyncOp;
+	iBackgroundSyncOp = NULL;
+	
+	if (aError>0)
+		{
+		// translate positive error codes, weed out non-fatal errors
+		ThranslateSessionError(aError);
+		}
+
+	TBool foregroundOpCancelled = EFalse;
+	if (aError==KErrNone || aError==KErrCancel)
+		{
+		if (iImapCompound==NULL && iMigrateState == ENotMigrating)
+			{
+			// only start IDLE if foreground operation not in progress
+			StartIdle();
+			MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateImapIdle);
+			}
+		}
+	else
+		{
+		// Cancel any foreground operation - the primary session has
+		// failed, so all sessions are to be torn down.
+		if (iImapCompound != NULL)
+			{
+			iImapCompound->Cancel();
+			foregroundOpCancelled = ETrue;
+			delete iImapCompound;
+			iImapCompound=NULL;
+			}
+
+		// DisconnectAll will be process by DoComplete if a client request is outstanding.
+		if (!iWaitForBackgroundSync && !foregroundOpCancelled)
+			{
+			// Drop all session connections and mark the service offline.
+			DisconnectAll();
+			}
+		}
+
+	// Store the error in the progress object
+	iProgress.iGenericProgress.iErrorCode=aError;
+	iProgress.iGenericProgress.iState=TImap4GenericProgress::EIdle;
+		
+	// Complete the client request if it was waiting for notification of 
+	// bg sync complete, or if we have cancelled an foreground operation
+	if (iWaitForBackgroundSync || foregroundOpCancelled)
+		{
+		iCancelInProgress = ETrue;
+		Complete(aError);
+		iCancelInProgress = EFalse;
+		}
+
+	iWaitForBackgroundSync = EFalse;
+	}
+
+/**
+CopyToLocalL() is used to fetch a selection of messages from the remote 
+server as a single operation, without return to idle state between handling each
+message. The email is fetched to the local mirror of the containing folder. Optionally 
+this function can also copy the message to a local folder, specified in aDestination. 
+A fetch to mirror only is performed by calling with aDestination set to 
+KMsvNullIndexEntryId
+
+Sequence For CopyToLocal
+	StopIdle
+	SelectSourceMailboxRO
+	FetchMessage(s)
+	InboxDuplicateCopy
+
+@param aStatus
+@param aSourceSel
+@param aDestination
+*/
+EXPORT_C void CImapProtocolController::CopyToLocalL(TRequestStatus& aStatus, 
+													const CMsvEntrySelection& aSourceSel, 
+													const TMsvId aDestination)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CopyToLocalL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyToLocalCompoundIsNotNull));
+
+	ResetProgress();
+
+	// Instead of calling CompleteIfBackgroundOpInProgress(), just check whether a flush is occurring 
+	// on the primary session (always index 0).
+	// If a background op is in progress, then the copy will be performed on a secondary session.
+	if (iCurrentOp == ECancelRecoverPrimary || iMigrateState != ENotMigrating)
+		{
+		// Do not call Complete() - this would result in the Protocol
+		// Controller disconnecting all sessions.
+		TRequestStatus* pStatus = &aStatus;
+		User::RequestComplete(pStatus, KErrServerBusy);
+
+		return;
+		}
+	
+	// must be set before calling SelectSession
+	iRequestedOp = ECopyToLocal;
+
+	// request the session to use.
+	TBool startNow = SelectSessionL(iForegroundSession);
+
+	if (iBackgroundSyncOp && iImapSettings->UseSyncDownloadRules())
+		{
+		// we need a non-const copy of the message selection to operate on.
+		CMsvEntrySelection* selection = aSourceSel.CopyLC();
+		
+		// show the list to the background sync controller
+		iBackgroundSyncOp->RemoveFromSelectionL(*selection);
+		// Create the copy to local operation object using the (possibly) updated selection
+		iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, 
+		                    			               iEntry, 
+		                                			   *iImapSettings,
+		                                			   *iImapMailStore,
+		                                               EFalse,
+		                                               *selection,
+		                                               aDestination);
+		CleanupStack::PopAndDestroy(selection);
+		}
+	else
+		{
+		// Create the compound operation object
+		iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, 
+		                    			               iEntry, 
+		                                			   *iImapSettings,
+		                                			   *iImapMailStore,
+		                                               EFalse,
+		                                               aSourceSel,
+		                                               aDestination);
+		}
+
+	if (startNow)
+		{
+		// A session is ready - start the command
+		// otherwise a request to connect a secondary session has been issued
+		if (iForegroundSession==0)
+			{
+			// Primary session
+			StartPrimaryOperation();
+			}
+		else
+			{
+			// Secondary session
+			iCurrentOp = iRequestedOp;
+			iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]);
+			}
+		}	
+	
+	Queue(aStatus);
+	SetActive();
+	}
+
+
+/**
+PopulateL() performs the same action as CopyToLocalL(), however uses the passed 
+TImImap4GetPartialMailInfo object instead of using the default parameters. This 
+allows the client application to specify the parts of the message that are to be 
+fetched, and to impose limits to on the amount of data that can be transferred.
+
+Sequence For Populate
+	StopIdle
+	SelectSourceMailboxRO
+	FetchMessage(s)
+	InboxDuplicateCopy
+
+@param aStatus
+@param aSourceSel
+@param aGetPartialMailInfo
+*/
+EXPORT_C void CImapProtocolController::PopulateL(TRequestStatus& aStatus, 
+                                                 const CMsvEntrySelection& aSourceSel, 
+                                                 TImImap4GetPartialMailInfo aGetPartialMailInfo)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::PopulateL() - START - iCurrentOp: %d, iRequestedOp: %d", iCurrentOp, iRequestedOp ));
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EPopulateCompoundIsNotNull));
+
+	// Instead of calling CompleteIfBackgroundOpInProgress(), just check whether a flush is occurring 
+	// on the primary session (always index 0).
+	// If a background op is in prgoress, then the copy will be performed on a secondary session.
+	ResetProgress();
+
+	// If a flush is occurring on the primary session (always index 0) then we are busy
+	if (iCurrentOp == ECancelRecoverPrimary || iMigrateState != ENotMigrating)
+		{
+		// Do not call Complete() - this would result in the Protocol
+		// Controller disconnecting all sessions.
+		TRequestStatus* pStatus = &aStatus;
+		User::RequestComplete(pStatus, KErrServerBusy);
+	    __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - END - KErrServerBusy...");
+		return;
+		}
+
+	// must be set before calling SelectSessionL()
+	iRequestedOp = EPopulate;		
+
+	// request the session to use.
+	TBool startNow = SelectSessionL(iForegroundSession);
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::PopulateL() - iCurrentOp: %d, iRequestedOp: %d, startNow: %d", iCurrentOp, iRequestedOp, startNow ));
+	
+	if (iBackgroundSyncOp && iImapSettings->UseSyncDownloadRules())
+		{
+		// we need a non-const copy of the message selection to operate on.
+		CMsvEntrySelection* selection = aSourceSel.CopyLC();
+		
+		// show the list to the background sync controller
+		iBackgroundSyncOp->RemoveFromSelectionL(*selection);
+		// Create the copy to local operation object using the (possibly) updated selection
+		iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, 
+			                			               iEntry, 
+		    	                        			   *iImapSettings,
+		        	                    			   *iImapMailStore, 
+		            	                               EFalse,
+		                	                           *selection,
+		                    	                       KMsvNullIndexEntryId,
+		                        	                   aGetPartialMailInfo);
+		CleanupStack::PopAndDestroy(selection);
+		}
+	else
+		{
+		// Create the compound operation object
+		iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, 
+	    	                			               iEntry, 
+	        	                        			   *iImapSettings,
+	            	                    			   *iImapMailStore, 
+	                	                               EFalse,
+	                    	                           aSourceSel,
+	                        	                       KMsvNullIndexEntryId,
+	                            	                   aGetPartialMailInfo);
+		}
+
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - done creation of CompoundCopyToLocal...");
+	if (startNow)
+		{
+		// A session is ready - start the command
+		// otherwise a request to connect a secondary session has been issued
+		if (iForegroundSession==0)
+			{
+			// Primary session
+	        __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - starting primary op...");
+			StartPrimaryOperation();
+			}
+		else
+			{
+			// Secondary session
+	        __LOG_TEXT(KDefaultLog, "CImapProtocolController::PopulateL() - starting compound op...");
+			iCurrentOp = iRequestedOp;
+			iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]);
+			}
+		}	
+	
+	Queue(aStatus);
+	SetActive();
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::PopulateL() - END - iCurrentOp: %d, iRequestedOp: %d, startNow: %d", iCurrentOp, iRequestedOp, startNow ));
+	}
+
+/**
+MoveToCopyL() shall enable a selection of messages to be moved as a single operation, 
+without return to idle state between handling each message. If a message has not been 
+previously fetched (ie is not complete on the local mirror of the IMAP folder) the 
+message is fetched to the local mirror and a copy is then made in the local service 
+folder. The message is then deleted from the IMAP folder on the remote server and the 
+local mirror.
+
+Sequence For MoveToLocal
+	StopIdle
+	SelectSourceMailbox 
+	FetchMessage
+	InboxDuplicateCopy
+	DeleteMessage
+
+@param aStatus
+@param aSourceSel
+@param aDestination
+*/	
+EXPORT_C void CImapProtocolController::MoveToLocalL(TRequestStatus& aStatus, 
+													const CMsvEntrySelection& aSourceSel, 
+													const TMsvId aDestination)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::MoveToLocalL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EMoveToLocalCompoundIsNotNull));
+
+	ResetProgress();
+
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		// We can't do a 'movetolocal' with the destination being the same as the
+		// source, as then the mirror will be out of sync. We must be moving to somewhere
+		// outside this service. Check this.
+		if (!IdIsLocalL(aDestination))
+			{
+			// Complete the user request
+			// Do not call Complete() - this would result in the Protocol
+			// Controller disconnecting all sessions.
+			TRequestStatus* status = &aStatus;
+			User::RequestComplete(status, KErrNotSupported);
+			}
+		else
+			{
+			iImapCompound = CImapCompoundCopyToLocal::NewL(*iImapSyncManager, 
+														   iEntry, 
+														   *iImapSettings,
+														   *iImapMailStore,
+														   ETrue,
+		                                               	   aSourceSel,
+		                                               	   aDestination);
+			
+			iRequestedOp = EMoveToLocal;
+			StartPrimaryOperation();
+			Queue(aStatus);
+			SetActive();
+			}
+		}
+	}
+
+/**
+Copy within service is the operation of moving a message or selection of messages from 
+one mailbox  to another on the remote IMAP server. This is done by issuing a COPY IMAP 
+command to the remote server, followed by a sync operation on the destination folder.
+
+Sequence For CopyWithinService
+	StopIdle
+	SelectSourceMailboxRO
+	CopyMessage
+	SelectDestinationMailboxRO
+	NewSyncFolder
+
+@param aStatus
+@param aSourceSel
+@param aDestination
+*/
+EXPORT_C void CImapProtocolController::CopyWithinServiceL( TRequestStatus& aStatus, 
+							 					  		   const CMsvEntrySelection& aSourceSel, 
+							 					  		   const TMsvId aDestination )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CopyWithinServiceL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyWithinServiceCompoundIsNotNull));
+	
+	ResetProgress();
+
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{	
+		iImapCompound = CImapCompoundCopyWithinService::NewL( *iImapSyncManager, 
+															  iEntry, 
+															  *iImapSettings,
+															  EFalse,
+															  aSourceSel,
+															  aDestination);
+		
+		iRequestedOp = ECopyWithinService;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Move within service is the same as Copy Within Service, however the message is deleted 
+from the original location following the copy operation.
+
+Sequence For MoveWithinService
+	StopIdle
+	SelectSourceMailboxRW
+	CopyMessage
+	DeleteMessage
+	SelectDestinationMailboxRO
+	NewSyncFolder
+	
+@param aStatus
+@param aSourceSel
+@param aDestination
+*/
+EXPORT_C void CImapProtocolController::MoveWithinServiceL( TRequestStatus& aStatus, 
+							 					  		   const CMsvEntrySelection& aSourceSel, 
+							 					  		   const TMsvId aDestination )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::MoveWithinServiceL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EMoveWithinServiceCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{	
+		iImapCompound = CImapCompoundCopyWithinService::NewL( *iImapSyncManager, 
+															  iEntry, 
+															  *iImapSettings,
+															  ETrue,
+															  aSourceSel,
+															  aDestination );
+
+		iRequestedOp = EMoveWithinService;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Copy from local is the action of copying an email from a local service folder (for 
+example the local inbox) to a folder on the remote IMAP service. This is done using 
+the IMAP APPEND command.
+
+Sequence For CopyFromLocal
+	AppendMessage
+	SelectDestinationMailboxRO
+	NewSyncFolder
+
+@param aStatus
+@param aSourceSel
+@param aDestination
+*/	
+EXPORT_C void CImapProtocolController::CopyFromLocalL( TRequestStatus& aStatus, 
+						 					  		   const CMsvEntrySelection& aSourceSel, 
+						 					  		   const TMsvId aDestination )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CopyFromLocalL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyFromLocalCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{	
+		iImapCompound = CImapCompoundCopyFromLocal::NewL( *iImapSyncManager, 
+														  iEntry, 
+														  *iImapSettings,
+														  EFalse,
+														  aSourceSel,
+														  aDestination );
+		
+		iRequestedOp = ECopyFromLocal;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Move from local is the action of moving an email from a local service folder (for 
+example the local inbox) to a folder on the remote IMAP service. It is performed 
+by following the same steps as for a Copy From Local operation, using the IMAP APPEND 
+command, however the local copy of the message is deleted after the APPEND has 
+successfully completed.
+
+Sequence For MoveFromLocal
+	StopIdle
+	AppendMessage
+	DeleteLocalMessage
+	SelectDestinationMailboxRO
+	NewSyncFolder
+
+@param aStatus
+@param aSourceSel
+@param aDestination
+*/
+EXPORT_C void CImapProtocolController::MoveFromLocalL( TRequestStatus& aStatus, 
+						 					  		   const CMsvEntrySelection& aSourceSel, 
+						 					  		   const TMsvId aDestination )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::MoveFromLocalL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECopyFromLocalCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundCopyFromLocal::NewL( *iImapSyncManager, 
+														  iEntry, 
+														  *iImapSettings,
+														  ETrue,
+														  aSourceSel,
+														  aDestination );
+	
+		iRequestedOp = EMoveFromLocal;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Deletes the specified messages from the remote server.
+
+Sequence For Delete
+	StopIdle
+	SelectSourceMailboxRW
+	Store /delete flags for each message
+	Expunge remote messages
+	Delete local messages
+
+@param aStatus
+@param aSourceSel
+*/
+EXPORT_C void CImapProtocolController::DeleteL(TRequestStatus& aStatus, 
+									   		   const CMsvEntrySelection& aSourceSel)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DeleteL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		// Create the compound object for message delete
+		iImapCompound = CImapCompoundDelete::NewL(*iImapSyncManager,
+												  iEntry,
+												  *iImapSettings,
+												  aSourceSel);
+
+		iRequestedOp = EDelete;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Deletes the specified folder(s) on the remote IMAP Server.
+
+Sequence For DeleteFolder
+	StopIdle
+	SelectSourceMailboxRW
+	DeleteAllMessages
+	CloseFolder
+	DeleteLocalFolder
+
+@param aStatus
+@param aSelection
+*/
+EXPORT_C void CImapProtocolController::DeleteFolderL( TRequestStatus& aStatus, 
+											 		  const CMsvEntrySelection& aSelection )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DeleteFolderL()");
+	__ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EDeleteFolderCompoundIsNotNull));
+
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundDeleteFolder::NewL( *iImapSyncManager,
+													     iEntry,
+													     *iImapSettings,
+													     aSelection );
+													     
+		iRequestedOp = EDelete;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}	
+	}
+
+/**
+NewOnlySyncL() is used to synchronise recent messages in the specified folder, 
+i.e. any messages that have arrived in the remote IMAP mailbox since the last 
+synchronisation. This is done by requesting (FETCHing) the header details for 
+messages with UID's that are greater than the highest UID of the messages present 
+in the local mirror of the folder. The message's header summary information, 
+flags etc are stored as new entries in the Message Server entry array under the 
+folder entry, and the header information is streamed to the Mailstore using the 
+Mailstore API. 
+
+Sequence For NewSyncFolder
+	StopIdle
+	SelectSourceMailboxRW
+	NewSyncFolder
+
+@param aStatus
+*/
+EXPORT_C void CImapProtocolController::NewOnlySyncFolderL( TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::NewOnlySyncFolderL()");
+	__ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ENewOnlySyncFolderCompoundIsNotNull));
+
+	ResetProgress();
+	
+	if (iMigrateState!=ENotMigrating)
+		{
+		// Complete the user request if migrating.
+		// Do not call Complete() - this would result in the Protocol
+		// Controller disconnecting all sessions.
+		TRequestStatus* status = &aStatus;
+		User::RequestComplete(status, KErrServerBusy);
+		return;
+		}
+	
+	// If we support idle, and the server we are talking to supports idle,
+	// then we don't need to do the new only sync as IMAP idle will take
+	// care of it for us.
+	// Just complete the user request with KErrNone.
+	if (iImapSettings->ImapIdle() && iImapSessionArray[0]->ImapIdleSupported())
+		{
+		__LOG_TEXT(KDefaultLog, "CImapProtocolController::NewOnlySyncFolderL() - Immediate complete as idle is in use");
+		Queue(aStatus);
+		Complete(KErrNone);
+		return;
+		}
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{	
+		CImapFolder* folder = iImapSyncManager->Inbox();
+		TMsvId inboxId = folder->MailboxId();
+		
+		iImapCompound = CImapCompoundSyncFolder::NewL( *iImapSyncManager,
+													   iEntry,
+													   *iImapSettings, 
+													   *iImapMailStore, 
+													   ETrue,
+													   inboxId);
+
+		iRequestedOp = ESync;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+The full synchronisation of a folder involves the synchronisation of messages newly 
+received at the IMAP server, as described for NewOnlySyncL, above, and the 
+synchronisation of "old messages", ie messages that have previously been synchronised 
+to the local mirror of the folder.
+When synchronising old messages, messages that have been marked for delete locally 
+are marked such on the remote folder (the actual delete occurs either at the end of 
+the synchronisation process, or is deferred until the connection is cancelled), 
+messages that have been removed from the remote server (by another client) are 
+deleted locally and any outstanding offline operations are performed, for example 
+move operations may be outstanding, etc.
+
+Sequence For FullSyncFolder
+	StopIdle
+	SelectSourceMailboxRW
+	SyncFolder
+
+@param aStatus
+@param aFolder
+*/
+EXPORT_C void CImapProtocolController::FullSyncFolderL( TRequestStatus& aStatus, 
+											   			const TMsvId aFolder )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::FullSyncFolderL()");
+	__ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EFullSyncFolderCompoundIsNotNull));
+	
+	ResetProgress();
+
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundSyncFolder::NewL( *iImapSyncManager,
+													   iEntry,
+													   *iImapSettings,
+													   *iImapMailStore,
+													   EFalse,
+													   aFolder );
+
+		iRequestedOp = ESync;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+SELECT is made available to the client application directly via an IMAP MTM command 
+which is handled by this function. It allows the client to specifically SELECT a 
+mailbox and then, using the Synchronise MTM command, to specifically request the 
+selected mailbox is synchronised.
+
+Sequence For Select
+	StopIdle
+	SelectSourceMailboxRW
+
+@param aStatus
+@param aFolder
+*/	
+EXPORT_C void CImapProtocolController::SelectL(TRequestStatus& aStatus, const TMsvId aFolder)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::SelectL()");
+	__ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESelectCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundSelect::NewL( *iImapSyncManager, 
+												   iEntry, 
+												   *iImapSettings,
+												   aFolder );
+
+		iRequestedOp = ESelect;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Performs a full sync on the currently selected mailbox.
+
+Sequence For FullSyncSelectedFolder
+	StopIdle
+	SyncFolder
+
+@param aStatus
+*/	
+EXPORT_C void CImapProtocolController::FullSyncSelectedFolderL( TRequestStatus& aStatus )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::FullSyncSelectedFolderL()");
+	__ASSERT_DEBUG( iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EFullSyncSelectedFolderCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundSyncFolder::NewL( *iImapSyncManager,
+													   iEntry,
+													   *iImapSettings,
+													   *iImapMailStore, 
+													   EFalse );
+		
+		iRequestedOp = ESync;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+	
+/**
+Creates a new folder on the remote IMAP service with the given name.
+
+Sequence For Create
+	StopIdle
+	Create
+
+@param aStatus
+@param aParent
+@param aLeafName
+@param aFolder
+*/
+EXPORT_C void CImapProtocolController::CreateL( TRequestStatus& aStatus, 
+				  					   			const TMsvId aParent, 
+				  					   			const TDesC& aLeafName, 
+				  					   			const TBool aFolder )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CreateL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECreateCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundCreate::NewL( *iImapSyncManager,
+												   iEntry,
+												   *iImapSettings,
+												   aParent, 
+												   aLeafName, 
+												   aFolder );
+
+		iRequestedOp = ECreate;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+/**
+Renames the specified folder. Note that although this does cause a write to the 
+remote folder, it does not change the contents of the folder and hence does not 
+force a re-synchronisation of the folder.
+
+Sequence For Rename
+	StopIdle
+	RenameRemote
+	RenameLocal
+
+@param aStatus
+@param aTarget
+@param aNewName
+*/	
+EXPORT_C void CImapProtocolController::RenameL( TRequestStatus& aStatus, 
+				  					   			const TMsvId aTarget, 
+				  					   			const TDesC& aNewName )
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::RenameL()");
+	__ASSERT_DEBUG(iImapCompound == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundIsNotNull));
+	
+	ResetProgress();
+	
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapCompoundRename::NewL( *iImapSyncManager, 
+												   iEntry, 
+												   *iImapSettings,
+												   aTarget,
+												   aNewName );
+												   
+		iRequestedOp = ERename;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+
+
+EXPORT_C void CImapProtocolController::DoRunL()
+	{
+	// Handle all migration operations in a separate state machnine.
+	if (iMigrateState != ENotMigrating)
+		{
+		DoMigrateRunL();
+		return;
+		}
+
+	// ProcessError completes and returns ETrue if an error occured
+	if (ProcessError(iStatus.Int()))
+		{
+		return;
+		}
+
+	switch (iCurrentOp)
+		{
+	case EConnect:
+		{
+		TRAP_IGNORE(MarkOnOrOfflineL(ETrue));
+
+		// Collect the final connect progress information		
+		iImapSessionManager->Progress(iProgress.iGenericProgress);
+
+		// Set last socket activity timeout to iMtmData1. This is used by Imcm.
+		User::LeaveIfError( iEntry.SetEntry( iServiceId ) );
+		TMsvEntry entry=iEntry.Entry();
+		entry.SetMtmData1(iImapSessionManager->LastSocketActivityTimeout());
+		User::LeaveIfError( iEntry.ChangeEntry( entry ) );
+				
+		// Create an IMAP IDLE controller
+		delete iImapIdleController;
+		iImapIdleController=NULL;
+		iImapIdleController = CImapIdleController::NewL(*this, iImapSessionArray[0], *iImapSyncManager, iEntry, *iImapSettings, *iImapMailStore);
+		
+		// Register the connection with the mobility manager, 
+		// if we are a mobile service
+		if (iMobilityManager)
+			{
+			iMobilityManager->SetConnection(iImapSessionManager->GetConnectionL());
+			}
+
+		// kick off a background sync if it was requested.
+		if (iBackgroundSyncOp != NULL)
+			{
+			iBackgroundSyncOp->StartSync(*iImapSessionArray[0]);
+			iCurrentOp = EIdle;
+			}
+		else
+			{
+			StartIdle();
+			}
+
+		// complete the connect request
+		Complete(iStatus.Int());
+		break;
+		}
+
+	case EConnectSecondary:
+		{
+		// perform the user-requested operation using the
+		// newly created imap session
+		iCurrentOp = iRequestedOp;
+		if(iImapCompound->Suspended())
+			{
+			// if the operation was previously suspended for migration, resume it...
+			iImapCompound->ResumeOperationL(iStatus, *iImapSessionArray[iForegroundSession]);
+			}
+		else
+			{
+			// otherwise, just start the operation.
+			iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]);
+			}
+		SetActive();
+		break;
+		}
+	
+	case EStopIdle:
+		{
+		// Idle has been cancelled, the primary session is available
+		// Kick off the requested operation
+		iCurrentOp = iRequestedOp;
+		iImapCompound->StartOperation(iStatus, *iImapSessionArray[0]);
+		SetActive();
+		break;
+		}
+
+	case EDisconnect:
+		{
+		TRAP_IGNORE( MarkOnOrOfflineL( EFalse ) );
+
+		iImapSessionArray.ResetAndDestroy();
+
+		// update last operation progress state
+		iImapCompound->Progress(iProgress);
+
+		delete iImapCompound;
+		iImapCompound = NULL;
+		iServiceId=0;
+		
+		Complete(iStatus.Int());
+		break;
+		}
+	
+	case ECancelRecoverPrimary:
+		{
+		// Primary session is successfully recovered
+		// Start the next requested operation
+		
+		if (iRequestedOp == EIdle)
+			{
+			// We should only go into Idle if no async operations have been requested.
+			// Consequently, CMsgActive::iReport should be NULL.
+			// We can't ASSERT this here as it is a private member of CMsgActive, but it is worth checking while debugging.
+			StartIdle();
+			}
+		else if (iRequestedOp == EDisconnect)
+			{
+			// We should only start a primary operation if an async operation has been requested, and an aStatus Queue()ed.
+			// Consequently, CMsgActive::iReport should NOT be NULL.
+			// We can't ASSERT this here as it is a private member of CMsgActive, but it is worth checking while debugging.
+			StartPrimaryOperation();
+			SetActive();
+			}
+		else
+			{
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerUnexpectedRequestedOp));
+			}
+		
+		break;
+		}
+
+	case ESync:
+	case ESelect:
+	case ECopyToLocal:
+	case ECopyWithinService:
+	case ECopyFromLocal:
+	case EMoveToLocal:
+	case EMoveWithinService:
+	case EMoveFromLocal:
+	case EPopulate:
+	case EDelete:
+	case EDeleteFolder:
+	case ECreate:
+	case ERename:
+	case EUpdateFlag:
+		{
+		// update last operation progress state
+		iImapCompound->Progress(iProgress);
+
+		delete iImapCompound;
+		iImapCompound = NULL;
+		
+		if ((iRequestedOp==ESelect) && (iProgress.iGenericProgress.iErrorCode==KErrNone))
+			{
+			// Do not start IDLE following a select request
+			Complete(iStatus.Int());
+			}
+		else
+			{
+			// Start idle and complete the server mtm request.
+			StartIdle();
+			Complete(iStatus.Int());
+			}
+		break;
+		}
+
+	case EIdle:	// shouldn't happen
+	default:
+		{
+		Complete(iStatus.Int());
+		break;
+		}
+		
+		} // end of switch (iCurrentOp)
+	}
+
+/**
+This variation on DoRunL() presents a super-state machine, that replaces 
+the default state machine during a migration process.
+*/
+void CImapProtocolController::DoMigrateRunL()
+	{
+	// waiting for existing operation to stop to allow migration to occur.
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::DoMigrateRunL(iMigrateState = %d) CurrentOp = %d", iMigrateState, iCurrentOp));
+	switch (iMigrateState)
+		{
+ 		case EWaitingForOpToStop: 
+		case EWaitingForOpToComplete:
+			{
+			// operation has completed (is either finished, or has "paused"
+			// to allow migration to complete. Do any tidying up necessary.
+			ProcessOpCompleteForMigrate();
+			
+			// is there also a background sync op in progress? if not (or if the
+			// background sync op is suspended, we are now ready to migrate, 
+			// otherwise wait for the background op to complete.
+			 // 
+			if (!iBackgroundSyncOp || iBackgroundSyncOp->IsSuspendedForMigrate())
+				{
+				// Asynch disconnect current sockets.
+				DisconnectForMigrateL();
+				}
+			break;
+			}
+		case EDisconnectingForMigrate:
+			{
+			// The disconnect operation has completed. Delete IMAP Sessions.
+			iImapSessionArray.ResetAndDestroy();
+
+			// delete the migration compound object
+			delete iMigrateCompound;
+			iMigrateCompound = NULL;
+			
+			// Notify the mobility framework that we are ready to migrate.
+			iMigrateState = EWaitingForNewCarrier;
+			iMobilityManager->MigrateToNewCarrier();
+			
+			// do not set waiting if NewCarrierActive has been called synchronously.
+			if (iMigrateState == EWaitingForNewCarrier)
+				{
+				// Now in a waiting state, set self active
+				iStatus = KRequestPending;
+				SetActive();
+				}
+			break;
+			}
+		case EHandlingConnectError:
+			{
+			// register with the mobility framework
+			iMobilityManager->SetConnection(iImapSessionManager->GetConnectionL());
+						
+			// empty the session array
+			TInt numSessions = iImapSessionArray.Count();
+			for (TInt i=0; i<numSessions; ++i)
+				{
+				if (iImapSessionArray[i])
+					{
+					iImapSessionManager->Disconnect(*(iImapSessionArray[i]));
+					}
+				}
+			iImapSessionArray.ResetAndDestroy();
+
+			// reject the initial carrier
+			iMigrateState = EWaitingInitialCarrierRejected;
+			iMobilityManager->NewCarrierRejected();
+			
+			if (iMigrateState == EWaitingInitialCarrierRejected)
+				{
+				iStatus = KRequestPending;
+				SetActive();
+				}
+			break;
+			}
+		case EStartingReconnect:
+			{
+			NewPrimarySessionL();
+			break;
+			}
+		case EConnectingAfterMigrate:
+			{
+			if (iStatus.Int()!=KErrNone)
+				{
+				// An error has occurred while attempting to re-connect
+				// - reject this new carrier, wait to see if a new one turns up.
+				iMigrateState = EWaitingForNewCarrier;
+				iMobilityManager->NewCarrierRejected();
+
+				// Now in a waiting state, set self active
+				if (iMigrateState == EWaitingForNewCarrier)
+					{
+					iStatus = KRequestPending;
+					SetActive();
+					}
+				}
+			else
+				{
+				iMobilityManager->NewCarrierAccepted();
+				RestartAfterMigrateL();
+				}
+			break;
+			}
+		case ENotMigrating:
+		case EWaitingForNewCarrier:
+		case EWaitingInitialCarrierRejected:
+			// DoRunMigrateL() Should never be called in this state.
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerUnexpectedMigrateState));
+			break;
+			}
+		}
+	}
+
+
+/** 
+Called when completing an outstanding client request.
+Handles negative (system-wide) error codes returned
+on completion of asynchronous service requests.
+*/
+void CImapProtocolController::DoComplete(TInt& aErr)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::DoComplete() - START - aErr = %d, CurrentOp = %d, iCancelInProgress: %d", aErr, iCurrentOp, iCancelInProgress));
+
+	// Requested operation is completed.
+	iRequestedOp = EIdle;
+
+	// return if everything is OK
+	if (aErr==KErrNone)
+		{
+	    __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - END - KErrNone" );
+		return;
+		}
+		
+	// First retrieve the progress information
+	this->Progress();
+	
+	// log that a cancel has occurred and return
+	if (aErr==KErrCancel)
+		{
+	    __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - dealing with cancel case..." );
+		iProgress.iGenericProgress.iErrorCode=aErr;
+		if(!iCancelInProgress)
+			{
+	        __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - no cancel in progress, so disconnecting all..." );
+			DisconnectAll();
+			}
+			
+	    __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - END - KErrCancel" );
+		return;
+		}
+
+	// Non-fatal errors should have been handled prior to this.
+	__LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - fatal error - disconnect all..." );
+	// iServiceId = 0, indicates we are already disconnected.
+	if(iServiceId!=0)
+		{
+		DisconnectAll();
+		}
+	
+	// Save error code in progress and flag the disconnect
+	iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected;
+	iProgress.iGenericProgress.iErrorCode=aErr;
+
+    __LOG_TEXT( KDefaultLog, "CImapProtocolController::DoComplete() - END - fatal error" );
+	}
+	
+
+/**
+Cancels any outstanding client requested asynchronous operations and recovers
+the primary session if required.
+
+This function is exclusively for the use of the CImapServerMtm, and only calls
+Cancel() if the PC is currently active performing the requested operation. 
+If the PC is active for other reasons (for example, waiting for a bearer
+migration to complete), the appropriate action is taken.
+*/
+EXPORT_C void CImapProtocolController::CancelAndCleanup()
+	{
+	__LOG_FORMAT( (KDefaultLog, "CImapProtocolController::CancelAndCleanup() - START - iCurrentOp: %d, iRequestedOp: %d, iFlushPrimary: %d", iCurrentOp, iRequestedOp, iFlushPrimary ) );
+	if ( iMigrateState == ENotMigrating ||
+	     iMigrateState == EWaitingForOpToComplete ||
+	     iMigrateState == EWaitingForOpToStop )
+		{
+		// In these states the protocol controller is currently doing something
+		// at the clients request.
+		// iCancelInProgress
+		iCancelInProgress = ETrue;
+		Cancel();
+
+	    __LOG_TEXT(KDefaultLog, "CImapProtocolController::CancelAndCleanup() - done DoCancel");
+
+		// DoCancel() will set iFlushPrimary to ETrue for operations that require 
+		// a flush after being cancelled. This will cause DoRunL() to be called when the
+		// session has been flushed. In the case of bearer migration, the next step will
+		// be started in DoRunL.
+		if (iFlushPrimary) 
+			{
+			iRequestedOp = EIdle; // We want to return to idle when the flush has completed.
+			iCurrentOp = ECancelRecoverPrimary;
+			CImapSession* session = iImapSessionArray[0];
+			session->FlushCancelledCommand(iStatus);
+			SetActive();
+			iFlushPrimary = EFalse;
+			}
+		// clear the cancelling flag
+		iCancelInProgress = EFalse;
+		}
+	else			
+		{
+		// in other migration states, we don't want to cancel because we 
+		// are active for migration purposes. However, we do need to clean
+		// compound operation objects and complete the user.
+		iRequestedOp = iCurrentOp = EIdle;
+		
+		// Update the progress object
+		iProgress.iGenericProgress.iState = TImap4GenericProgress::EIdle;
+		iProgress.iGenericProgress.iErrorCode = KErrCancel;
+
+		delete iImapCompound;
+		iImapCompound = NULL;
+		CMsgActive::DoCancel(); // completes the user request.
+		}
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::CancelAndCleanup() - END");
+	}
+
+
+/**
+DoCancel - called by CMsgActive::Cancel() to cancel asychronous services:
+
+Cancel() must not be called on this class to cancel a requested async
+service - CancelAndCleanup() should be used instead. CancelAndCleanup()
+provides support for cancelling requested operations while this class
+has become active for migration purposes.
+
+However, Cancel() must always be called eventually as the iStatus needs 
+to be cleared. Hence this function must support all cancel operations.
+
+This class is allows for Cancel() to be called internally to cancel
+async operations that have been launched internally for migration 
+purposes, without completing the CImapServerMtm.
+*/
+void CImapProtocolController::DoCancel()
+	{
+	__LOG_FORMAT( (KDefaultLog, "CImapProtocolController::DoCancel() - START - iCurrentOp: %d, iRequestedOp: %d, iFlushPrimary: %d", iCurrentOp, iRequestedOp, iFlushPrimary ) );
+
+	if (iMigrateState==ENotMigrating)
+		{
+		DoCancelClientOp();
+		}
+	else
+		{
+		switch (iMigrateState)
+			{
+			case ESuspendingForMigrate:
+				{
+				// Special handling for cancelling current operation to allow
+				// migration to occur.
+				DoCancelForMigrate();
+				break;
+				}
+			case EWaitingForOpToComplete:
+			case EWaitingForOpToStop:
+				{
+				// we have an outstanding request on the compound operation
+				// call the default DoCancelClientOp(). This will also
+				// complete the client's iStatus with KErrCancel
+				DoCancelClientOp();
+				break;
+				}
+			case EDisconnectingForMigrate:
+				{
+				// outstanding op is on the iMigrateCompound
+				iMigrateCompound->Cancel();
+				delete iMigrateCompound;
+				iMigrateCompound = NULL;
+				break;
+				}
+			case EConnectingAfterMigrate:
+				{
+				// This state represents re-connection of the primary session,
+				// it is safe to simply clear the imap session array.
+				iImapSessionManager->Cancel();
+				iImapSessionArray.ResetAndDestroy();
+				break;
+				}
+			case EWaitingForNewCarrier:
+			case EWaitingInitialCarrierRejected:
+				{
+				// in these states, we are in a self-induced active state
+				// Cancel it:
+				TRequestStatus* status = &iStatus;
+				User::RequestComplete(status, KErrCancel);
+				break;
+				}
+
+			case EStartingReconnect: // nothing to cancel
+			case EHandlingConnectError: // nothing to cancel
+			case ENotMigrating:		 // somethings gone wrong
+			default:
+				{
+				__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerCancelBadMigrateState));
+				}
+			} // switch (iMigrateState)
+		}
+
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancel() - END");
+	}
+
+void CImapProtocolController::DoCancelClientOp()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - START");
+
+	// Update the requested and current operations
+	// the implication is that a return to EIdle is required
+	// current op may be updated later if an async op is issued.
+	TImapProtocolOp cancelledOp = iCurrentOp;
+	iRequestedOp = iCurrentOp = EIdle;
+
+	switch (cancelledOp)
+		{
+		case EConnect:
+			{
+			__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - EConnect");
+			iImapSessionManager->Cancel();
+			
+			// As this state represents connection of the primary session,
+			// it is safe to simply clear the imap session array.
+			iImapSessionArray.ResetAndDestroy();
+
+			// delete the background sync operation if it exists.
+			if (iBackgroundSyncOp != NULL)
+				{
+				iBackgroundSyncOp->Cancel();
+				delete iBackgroundSyncOp;
+				iBackgroundSyncOp=NULL;
+				}
+
+			// delete the sync manager		
+			delete iImapSyncManager;
+			iImapSyncManager = NULL;
+
+			break;
+			}
+		
+		case EConnectSecondary:
+			{
+	        __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - EConnectSecondary");
+			// Cancel the connect request
+			iImapSessionManager->Cancel();
+			iForegroundSession=0;
+			
+			// Remove the session pointer from the array
+			// The session has already been deleted - see note above.
+			TInt numSessions = iImapSessionArray.Count();
+			iImapSessionArray.Remove(numSessions-1);
+			
+			if (iImapCompound!=NULL)
+				{
+				// The requested op hasn't actually started yet
+				// so don't cancel on the compound operation..
+				// update last operation progress state
+				iImapCompound->Progress(iProgress);
+				
+				delete iImapCompound;
+				iImapCompound = NULL;
+				}
+			
+			// The progress will not report cancel at this stage - force it here.
+			iProgress.iGenericProgress.iState = TImap4GenericProgress::EIdle;
+			iProgress.iGenericProgress.iErrorCode = KErrCancel;
+
+			break;
+			}
+
+		case EDisconnect:
+			{
+	        __LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - EDisconnect");
+			// Cancel the disconnect compound operation
+			iImapCompound->Cancel();
+
+			delete iImapCompound;
+			iImapCompound = NULL;
+			
+			// A cancelled disconnect will leave the sessions disconnected
+			// but not deleted. Reset and destroy the session array.
+			iImapSessionArray.ResetAndDestroy();
+			
+			break;
+			}
+		
+		case EIdle:
+			{
+			// This is unexpected - in this state there should be no
+			// outstanding asynconous requests on the Protocol Controller.
+			// Protocol Controller has no outstanding requests in this state
+			// however a sync may be running in the background. This is
+			// cancelled when the background sync object's destructor is called.
+			// So.. Nothing to do.
+			break;
+			}
+		
+		case EStopIdle:
+			{
+			if (iImapCompound!=NULL)
+				{
+				// The requested op hasn't actually started yet
+				// so don't cancel on the compound operation..
+				// update last operation progress state
+				iImapCompound->Progress(iProgress);
+				
+				delete iImapCompound;
+				iImapCompound = NULL;
+				}
+			
+			// The progress will not report cancel at this stage - force it here.
+			iProgress.iGenericProgress.iState = TImap4GenericProgress::EIdle;
+			iProgress.iGenericProgress.iErrorCode = KErrCancel;
+			
+			// Cancel the idle controller.
+			iImapIdleController->Cancel();
+			
+			// Cancelling the idle controller is likely to leave the IMAP Session
+			// with data left on the input stream to be dealt with. We need to 
+			// flush the session if we wish to re-use it.
+			iFlushPrimary = ETrue;
+
+			break;
+			}
+
+		case ESync:
+		case ESelect:
+		case ECopyToLocal:
+		case ECopyWithinService:
+		case ECopyFromLocal:
+		case EMoveToLocal:
+		case EMoveWithinService:
+		case EMoveFromLocal:
+		case EPopulate:
+		case EDelete:
+		case EDeleteFolder:
+		case ECreate:
+		case ERename:
+	case EUpdateFlag:
+			{
+			__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - ESync/ESelect/ECopyToLocal/ECopyWithinService/ECopyFromLocal/EMoveToLocal/EMoveWithinService/EMoveFromLocal/EPopulate/EDelete/EDeleteFolder/ECreate/ERename/");
+			// This is the normal case, a requested operation is
+			// being performed by a compound operation object.
+			// First of all, check one exists.
+			if (iImapCompound!=NULL)
+				{
+				// Cancel the compound operation
+				__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - cancelling iImapCompound...");
+				iImapCompound->Cancel();
+
+				// update last operation progress state
+				__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - getting progres...");
+				iImapCompound->Progress(iProgress);
+
+				// Delete the compound object
+				__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - deleting compound...");
+				delete iImapCompound;
+				iImapCompound = NULL;
+				
+				// Cancelling the compound operation is likely to leave 
+				// the IMAP Session in an incomplete state. We need to 
+				// flush the session if we wish to re-use it.
+				if (iForegroundSession==0)
+					{
+					__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - flush primary = ETrue...");
+					iFlushPrimary = ETrue;
+					}
+				else
+					{
+					__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - drop session...");
+					DropSession(iForegroundSession);
+					iForegroundSession=0;
+					}
+
+				__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - main iImapCompound handling complete...");
+				}
+			break;
+			}
+	
+		case ECancelRecoverPrimary:
+			{
+			// The recover must be taking too long.
+			// cancel the recover request..
+			(iImapSessionArray[0])->Cancel();
+			// Delete all sessions, go offline etc.
+			DisconnectAll();
+			break;
+			}
+		default:
+			{
+			// nothing to do..
+			break;
+			}
+		} // end of switch (cancelledOp)
+
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - about to call CMsgActive::DoCancel()...");
+	CMsgActive::DoCancel();
+
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelClientOp() - END");
+	}
+
+
+/**
+Performs a fast disconnect on all sessions
+
+Ensures that the compound, background sync and sync manager objects are
+deleted following the disconnect.
+@param aMarkOffline - indicates if the service is to be marked offline.
+*/
+void CImapProtocolController::DisconnectAll()
+	{
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - START");
+
+	// Delete the background sync op
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - deleting iBackgroundSyncOp");
+	delete iBackgroundSyncOp;
+	iBackgroundSyncOp=NULL;
+
+	// Delete the imap compound operation
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - deleting iImapCompound");
+	delete iImapCompound;
+	iImapCompound=NULL;
+	
+	// Delete the migrate compound operation
+	delete iMigrateCompound;
+	iMigrateCompound = NULL;
+
+	// delete the sync manager
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - deleting iImapSyncManager");
+	delete iImapSyncManager;
+	iImapSyncManager = NULL;
+	
+	// delete the mobility manager (de-register for mobility notifications)
+	delete iMobilityManager;
+	iMobilityManager = NULL;
+
+	// delete the imap idle controller
+	delete iImapIdleController;
+	iImapIdleController = NULL;
+
+	// Drop connection on each of the imap sessions
+	TInt numSessions = iImapSessionArray.Count();
+	for (TInt i=0; i<numSessions; ++i)
+		{
+		if (iImapSessionArray[i])
+			{
+	        __LOG_FORMAT( (KDefaultLog, "CImapProtocolController::DisconnectAll() - disconnecting session: %d/%d", i, numSessions ) );
+			iImapSessionManager->Disconnect(*(iImapSessionArray[i]));
+			}
+		}
+		
+	// The current network connection will be closed
+ 	iImapSessionManager->CloseNetworkConnection();
+
+
+	// empty the session array
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - resetting and destroying sessions...");
+	iImapSessionArray.ResetAndDestroy();
+	
+	// force the state to IDLE
+	iRequestedOp = EIdle;
+	iCurrentOp = EIdle;
+	
+	// Clear serviceid and mark the service as offline
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - about to MarkOnOrOffline...");
+	TRAP_IGNORE(MarkOnOrOfflineL(EFalse));
+	iServiceId=0;
+
+    __LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectAll() - END");
+	}
+
+/**
+Performs an immediate disconnect of specifed session and removes it from
+the session array.
+
+@param sessionId - the index in the session array of the session to drop.
+*/
+void CImapProtocolController::DropSession(TInt sessionId)
+	{
+	if (sessionId < iImapSessionArray.Count())
+		{
+		CImapSession* tempSession = iImapSessionArray[sessionId];
+		if (tempSession)
+			{
+			iImapSessionManager->Disconnect(*tempSession);
+			delete tempSession;
+			}
+		iImapSessionArray.Remove(sessionId);
+		}
+	}
+
+/**
+Private method to select session from the session array for use for operations
+that are supported while background operations are in progress.
+Issues a request to connect a secondary session if the primary session is busy
+and a secondary session is not already available. In this case the requested op
+is updated to show a secondary connect request, and the original requested op
+is saved.
+
+@param  aIndexToUse updated to indicate the index of the session to be used
+@return ETrue if the session is connected and ready to use immediately.
+		EFalse if a request to connect a secondary session has been issued.
+*/
+TBool CImapProtocolController::SelectSessionL(TInt& aIndexToUse)
+	{
+	__ASSERT_DEBUG(iCurrentOp != ECancelRecoverPrimary, TImapServerPanic::ImapPanic(TImapServerPanic::EProtocolControllerUnexpectedCurrentOp));
+	
+	aIndexToUse = 0; // default to primary session.
+	TBool sessionReady = ETrue;
+	if (iBackgroundSyncOp != NULL)
+		{
+		TInt sessionCount = iImapSessionArray.Count();
+		if (sessionCount>=2)
+			{
+			// already a secondary session available - use this
+			aIndexToUse = sessionCount-1;
+			}
+		else
+			{
+			// Create a new session and get it connected.
+			iCurrentOp = EConnectSecondary;
+			
+			CImapSession* imapSession = NULL;
+			iImapSessionArray.AppendL(imapSession);
+			iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[sessionCount]);
+			aIndexToUse = sessionCount;
+			sessionReady = EFalse;
+			}
+		}
+	return sessionReady;
+	}
+
+/**
+Called to kick off a foreground operation, ie a user requested
+operation that is being performed on the primary IMAP session.
+
+If the session is in IMAP IDLE, a request is issued to stop this
+and the requested operation is started when the stop-idle operation
+has completed. Otherwise the operation is started immediately.
+*/
+void CImapProtocolController::StartPrimaryOperation()
+	{
+	// Make sure the foreground session id is correct
+	iForegroundSession = 0;
+	// imap idle controller is active in any state after
+	// idle has been requested.
+	if (iImapIdleController->IsActive())
+		{
+		iCurrentOp = EStopIdle;
+		iImapIdleController->StopIdle(iStatus);
+		}
+	else
+		{
+		iCurrentOp = iRequestedOp;
+		iImapCompound->StartOperation(iStatus, *iImapSessionArray[iForegroundSession]);
+		}
+	}
+
+/**
+Starts the background IDLE process.
+If IDLE is not supported by the server, or if IDLE is disabled via the
+account settings, a "dummy read" is issued, to manage any unsolicited
+server messages.
+
+This is called when user-requested compound operations have completed
+to return the primary session to IDLE state. Note that some operations
+require that IDLE is not re-issued, eg "select". It is also called
+when a background sync operation has completed, if there is no foreground
+operation currently in progress
+
+Idle is only issued on the primary session.
+
+Idle is not started if there is a background operation in progress 
+*/
+void CImapProtocolController::StartIdle()
+	{
+	iCurrentOp = EIdle;
+	if (iBackgroundSyncOp==NULL)
+		{
+		iImapIdleController->StartIdle();
+		}
+	}
+
+/**
+Called by the CImapIdleController in the case of an error being observed
+while in IDLE state.
+
+@param aError - the observed error
+*/
+void CImapProtocolController::OnIdleError(TInt aError)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::OnIdleError(aError = %d)", aError));
+	if (aError>0)
+		{
+		aError = KErrImapServerFail;
+		}
+	iProgress.iGenericProgress.iErrorCode = aError;
+	iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected;
+	DisconnectAll();
+	// Nothing to complete.
+	}
+
+/**
+Returns the progress information for the outstanding command.
+
+@return progress information
+*/
+EXPORT_C TImap4CompoundProgress CImapProtocolController::Progress()
+	{
+	// Progress of foreground operation is returned	over that of a
+	// background sync operation.
+	// Do not refresh the progress while migrating.
+	if (iCurrentOp!=EIdle && iMigrateState == ENotMigrating)
+		{
+		// do not update if IDLEing - return the stored progress object
+		// this should have been populated correctly on completion of
+		// requested operation, whether successful or otherwise.
+		switch (iCurrentOp)
+			{
+		case EConnect:
+			{
+			iImapSessionManager->Progress(iProgress.iGenericProgress);
+			break;
+			}
+		case EStopIdle:
+		case ESync:
+		case ESelect:
+		case ECopyToLocal:
+		case ECopyWithinService:
+		case ECopyFromLocal:
+		case EMoveToLocal:
+		case EMoveWithinService:
+		case EMoveFromLocal:
+		case EPopulate:
+		case EDelete:
+		case EDeleteFolder:
+		case ECreate:
+		case ERename:
+		case EDisconnect:
+		case EUpdateFlag: 
+			{
+			// Obtain progress from compound object if it exists
+			// Otherwise the last progress obtained on completion
+			// of the compound object is returned.
+			if (iImapCompound!=NULL)
+				{
+				iImapCompound->Progress(iProgress);
+				}
+			break;
+			}
+		case EIdle:
+		default:
+			break;
+			}
+		}
+		else if (iBackgroundSyncOp!=NULL)
+		{
+		// update sync progress if background sync is being performed.
+		iBackgroundSyncOp->Progress(iProgress);
+		}
+
+	return iProgress;
+	}
+
+/**
+changes local subscription flag on a folder immediately.
+If unsubscribing, the folder is marked as invisible, and the invisible
+flag is propagated to any parent folders that are not themselves subscribed
+or contain subscribed folders.
+
+@param aFolder
+@param aSubscribed
+@return Error code
+*/
+EXPORT_C TInt CImapProtocolController::SetLocalSubscription(const TMsvId aFolder, 
+															TBool aSubscribed)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::SetLocalSubscription()");
+
+	TInt err;
+
+	// Move to the entry
+	if ((err=iEntry.SetEntry(aFolder))!=KErrNone)
+		return(err);
+
+	// Check it's a folder
+	if (iEntry.Entry().iType!=KUidMsvFolderEntry)
+		return(KErrNotSupported);
+
+	// update subscription flag
+	TMsvEmailEntry entry=iEntry.Entry();
+	entry.SetLocalSubscription(aSubscribed);
+	entry.SetVisible(aSubscribed);
+	
+	err = iEntry.ChangeEntry(entry);
+		
+	// Return if error or if setting subscribed
+	if (err!=KErrNone || !aSubscribed)
+		{
+		TRAP(err, PropagateUnsubscribeL(aFolder));
+		return err;
+		}
+	else
+		{
+		return KErrNone;
+		}
+	}
+
+/**
+Marks aFolder as invisible and propagates the invisible flag to parent folder.
+
+@param aFolder the unsubscribed folder.
+*/
+void CImapProtocolController::PropagateUnsubscribeL(const TMsvId aFolder)
+	{
+	// Settings may not yet be loaded.
+	if (iImapSettings->SettingsLoaded()==EFalse)
+		{
+		iImapSettings->LoadSettingsL(aFolder);
+		}
+
+	// if synchronisation setting is not remote only then
+	// update the invisibility flags
+	if (iImapSettings->Synchronise() != EUseRemote)
+		{
+		PropagateInvisibleFlagL(aFolder);
+		ChangeVisibilityL(aFolder,ETrue,EFalse,KUidMsvMessageEntry);
+		}
+	}
+
+
+// Mark service as on or offline
+void CImapProtocolController::MarkOnOrOfflineL( const TBool aOnline )
+	{
+	// Mark service entry as on/offline	
+	User::LeaveIfError( iEntry.SetEntry( iServiceId ) );
+
+	TMsvEntry entry=iEntry.Entry();
+	entry.SetConnected( aOnline );
+	User::LeaveIfError( iEntry.ChangeEntry( entry ) );
+
+	// Release the service entry
+	User::LeaveIfError( iEntry.SetEntry( KMsvNullIndexEntryId ) );
+
+	// Going offline?
+	if ( !aOnline && iImapSettings->DisconnectedUserMode() )
+		{
+		// We're an expert user going offline: don't touch anything
+		return;
+		}
+
+	// Mark all immediate children of the service as invisible
+	if ( !aOnline )
+		ChangeVisibilityL( iServiceId, !aOnline );
+	}
+	
+void CImapProtocolController::ChangeVisibilityL(TMsvId aParent, TBool aInvisible)
+	{
+	ChangeVisibilityL(aParent, aInvisible, ETrue, KUidMsvFolderEntry);
+	}
+
+
+void CImapProtocolController::ChangeVisibilityL(TMsvId aParent, TBool aInvisible, TBool aRecurse, TUid aType)
+	{
+	// Get children at this level
+	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL( selection );
+
+	CMsvEntrySelection* folders = new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL( folders );
+
+	User::LeaveIfError( iEntry.SetEntry( aParent ) );
+	User::LeaveIfError( iEntry.GetChildren( *selection ) );
+
+	if ( selection->Count() )
+		{
+		for( TInt child=0; child < selection->Count(); child++ )
+			{
+			// Move to this child
+			User::LeaveIfError( iEntry.SetEntry( (*selection)[child] ) );
+			
+			TMsvEntry message=iEntry.Entry();
+			
+			// Is this the type we want to change?
+			if (message.iType==aType)
+				{
+				// Add to selection to do bulk change on, if necessary
+				if ((message.Visible() && aInvisible) ||
+					(!message.Visible() && !aInvisible))
+					{
+					folders->AppendL(message.Id());
+					}
+				}
+			
+			// Recurse downwards
+			if (aRecurse && message.iType==KUidMsvFolderEntry)
+				ChangeVisibilityL(message.Id(),aInvisible,aRecurse,aType);
+			}
+
+		// Change its visibility off all children if necessary
+		if (folders->Count())
+			{
+			// Do the change to the invisible flag (actual constant for the
+			// flag we want is private:()
+			
+			User::LeaveIfError( iEntry.SetEntry( aParent ) );
+			User::LeaveIfError( iEntry.ChangeAttributes(*folders,
+								aInvisible?0:KMsvVisibilityAttribute,
+								aInvisible?KMsvVisibilityAttribute:0));
+			}
+		}
+
+	// Release the service entry
+	User::LeaveIfError( iEntry.SetEntry( KMsvNullIndexEntryId ) );
+
+	// Get rid of selection
+	CleanupStack::PopAndDestroy(2);
+	}
+
+/**
+Propagates invisible flag for unsubscribed folders.
+aId has been unsubscribed. If it has no visible child folders then
+it is made invisible and its parent checked with the same test
+
+@param aId the folder that has been unsubscribed
+*/
+void CImapProtocolController::PropagateInvisibleFlagL(TMsvId aId)
+	{
+	__LOG_FORMAT((KDefaultLog, "PropagateInvisibleFlagL: 0x%x", aId));
+
+	// finish if we've reached the top
+	if (aId == KMsvRootIndexEntryId)
+		{
+		return;
+		}
+	
+	User::LeaveIfError(iEntry.SetEntry(aId));
+
+	// finish if we've reached a service
+	if (iEntry.Entry().iType == KUidMsvServiceEntry)
+		{
+		return;
+		}
+
+	// return if we've found a subscribed folder since we can't make
+	// it invisible
+	if (((TMsvEmailEntry)iEntry.Entry()).LocalSubscription())
+		{
+		return;
+		}
+	
+	// check the children of this unsubscribed folder
+	CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL(selection);
+
+	User::LeaveIfError(iEntry.GetChildren(*selection));
+
+	TBool visible=EFalse;
+	TInt count = selection->Count();
+	for (TInt i=0; i < count; ++i)
+		{
+		User::LeaveIfError(iEntry.SetEntry((*selection)[i]));
+
+		// look for a visible folder
+		TMsvEmailEntry entry = (TMsvEmailEntry)iEntry.Entry();
+		if (entry.iType == KUidMsvFolderEntry && entry.Visible())
+			{
+			visible=ETrue;
+			break;
+			}
+		}
+	
+	CleanupStack::PopAndDestroy(selection);
+
+	// if no child folders were visible then make this folder
+	// invisible and continue up
+	if (!visible)
+		{
+		User::LeaveIfError(iEntry.SetEntry(aId));
+		
+		// make this invisible
+		TMsvEntry entry = iEntry.Entry();
+		entry.SetVisible(EFalse);
+		User::LeaveIfError(iEntry.ChangeEntry(entry));
+
+		// go up
+		PropagateInvisibleFlagL(entry.Parent());
+		}
+	}
+
+
+/**
+Is this id in the local service?
+@param  aId   the id of the entry to check
+@return true, if the message id belongs to the local service.
+*/
+TBool CImapProtocolController::IdIsLocalL(TMsvId aId)
+	{
+	return ServiceOfL(aId) == KMsvLocalServiceIndexEntryIdValue;
+	}
+
+/**
+Returns the id of the service containing this id
+@param  aId   the id of the entry to check
+@return       the id of the containing service
+*/
+TMsvId CImapProtocolController::ServiceOfL(TMsvId aId)
+	{
+	TMsvId current=aId;
+	while(current!=KMsvRootIndexEntryIdValue)
+		{
+		// Visit this entry
+		User::LeaveIfError(iEntry.SetEntry(current));
+
+		TMsvEmailEntry entry = iEntry.Entry();
+		
+		// if service then searched far enough
+		if (entry.iType==KUidMsvServiceEntry)
+			break;
+
+		// Go upwards
+		current=entry.Parent();
+		}
+
+	return current;
+ 	}
+ 	
+/**
+Processes positive error codes returned by asynchronous service requests.
+Specifically, these will be error codes returned by the CImapSession,
+indicating error conditions in communication with the remote server.
+
+See cimapsessionconsts.h for full definition of error codes, summarised here:
+
+KErrImapNo 		 	 IMAP server returned a tagged NO response
+KErrImapBad 	 	 IMAP server returned a tagged BAD response
+KErrImapClosed		 indicates one of the streams has closed
+KErrImapFlushTimeout an attempt to flush a cancelled command has timed out
+KErrImapCorrupt		 corrupt data was enountered during parsing of IMAP server data
+
+@param  aError - the positive error code received on completion 
+                 of an asynchronous service
+
+@return ETrue  - if this function has called Complete() due to a fatal error.
+		EFalse - otherwise
+*/
+TBool CImapProtocolController::ProcessError(TInt aError)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::ProcessError(iCurrentOp = %d, aError = %d)", iCurrentOp, aError));
+	
+	// Bearer mobility support for initially connect failure 
+	if (iMobilityManager && aError == KErrImapConnectError && iImapSessionManager->HasConnection())
+		{
+		// Change migration state, so the connect error can be dealt with, 
+		// using the migration state machine.
+		iMigrateState = EHandlingConnectError;
+
+		SetActive();
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+
+		return ETrue;
+		}
+	
+
+	// weed out non-fatal server errors
+	ThranslateSessionError(aError);
+	if (aError == KErrNone)
+		{
+		return EFalse;
+		}
+	
+	if (iForegroundSession>0)
+		{
+		// error has occurred on a secondary session.
+		// This is not fatal to the whole protocol controller's
+		// existance, but it is fatal to the requested operation
+		
+		// get final progress state...
+		if (iCurrentOp==EConnectSecondary)
+			{
+			iImapSessionManager->Progress(iProgress.iGenericProgress);
+			}
+		else
+			{
+			iImapCompound->Progress(iProgress);
+			}
+		
+		//... and update reported error code
+		iProgress.iGenericProgress.iErrorCode=aError;
+		
+		// delete the compound operation object
+		delete iImapCompound;
+		iImapCompound = NULL;
+		
+		// drop the secondary session
+		DropSession(iForegroundSession);
+		
+		// complete the user request with KErrNone..
+		Complete(KErrNone);
+		}
+	else
+		{
+		// DoComplete normally handles the session tidyup, but
+		// is not called if the error has occurred during a background
+		// operation, for example ECancelRecoverPrimary
+		if (iCurrentOp==ECancelRecoverPrimary)
+			{
+			// Non-fatal errors should have been handled prior to this.
+			DisconnectAll();
+	
+			// Save error code in progress and flag the disconnect
+			// this is defensive as the client should not be watching progress
+			iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected;
+			iProgress.iGenericProgress.iErrorCode=aError;
+			}
+		Complete(aError);
+		}
+	return ETrue;
+	}
+
+/**
+Translates session error codes for reporting to the Server MTM.
+KErrImapNo and KErrImapBad are non-fatal to the session, and 
+should have been fielded by the compound operation, sync manager
+or session manager (during session connect), however they are
+translated to KErrNone here.
+
+All other session errors are fatal to the connection. If an
+error occurs on the primary session, all connected sessions 
+should be disconnected and the client completed.
+*/
+void CImapProtocolController::ThranslateSessionError(TInt& errCode)
+	{
+	if ( errCode == KErrNone 
+	  || errCode == KErrImapNo 
+	  || errCode == KErrImapBad )
+		{
+		// No and bad should have been handled by the compound 
+		// operation and are non-fatal to the connection.
+		errCode = KErrNone;
+		}
+	else
+		{
+		errCode = KErrImapServerFail;
+		}
+	}
+
+/**
+Resets the progress object
+*/
+void CImapProtocolController::ResetProgress()
+	{
+	//initialise the TImap4SyncProgress part
+	iProgress.iSyncProgress.iFoldersToDo = 0;
+	iProgress.iSyncProgress.iFoldersDone = 0;
+	iProgress.iSyncProgress.iMsgsToDo = 0;
+	iProgress.iSyncProgress.iMsgsDone = 0;
+	iProgress.iSyncProgress.iHeadersFetched = 0;
+	iProgress.iSyncProgress.iOrphanedFolders = 0;
+	iProgress.iSyncProgress.iNewFolders = 0;
+	iProgress.iSyncProgress.iOrphanedMessages = 0;
+	iProgress.iSyncProgress.iRemoteMessagesDeleteTagged = 0;
+	iProgress.iSyncProgress.iMessagesFetchedOK = 0;
+	iProgress.iSyncProgress.iMessagePartsFetchedOK = 0;
+	iProgress.iSyncProgress.iMessagePartsNotFound = 0;
+	iProgress.iSyncProgress.iFoldersNotFound = 0;
+	iProgress.iSyncProgress.iErrorCode = KErrNone;	
+	iProgress.iSyncProgress.iType = EImap4SyncProgressType;
+	
+	//initialise the TImap4GenericProgress part
+	iProgress.iGenericProgress.iMsgsToDo = 0;
+	iProgress.iGenericProgress.iMsgsDone = 0;
+	iProgress.iGenericProgress.iPartsToDo = 0;
+	iProgress.iGenericProgress.iPartsDone = 0;
+	iProgress.iGenericProgress.iBytesToDo = 0;
+	iProgress.iGenericProgress.iBytesDone = 0;
+	iProgress.iGenericProgress.iReturnedMsvId = 0;
+	iProgress.iGenericProgress.iTotalSize = 0;
+	iProgress.iGenericProgress.iErrorCode = KErrNone;
+	iProgress.iGenericProgress.iType = EImap4GenericProgressType;
+
+	}
+
+/**
+Notice that a preferred carrier has become available, and migration to that bearer has been accepted.
+The protocol controller shall either pause or allow any current operation to complete
+according to the action specified in parameter aAction. Once the current operation 
+is paused or complete, the protocol controller shall close any existing sockets
+and finally notify the mobility framework that it is ready to migrate to the new
+carrier.
+
+@param aAction      - indicates the action that should be taken re: current operations
+@param aIsSeamless  - indicates if the 
+*/
+void CImapProtocolController::PrepareForNewCarrier(TImMobilityAction aAction, TBool /*aIsSeamless*/)
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::PrepareForNewCarrier(iMigrateState=%d, aAction = %d)", iMigrateState, aAction));
+	// Handle being called in an already-migrating state before handling usual case
+	if (iMigrateState!=ENotMigrating)
+		{
+		switch (iMigrateState)
+			{
+			case EDisconnectingForMigrate:
+			case EWaitingForOpToStop:
+				{
+				// Do not allow change from EWaitingForOpToStop to EWaitingForOpToComplete
+				// but do allow an operation to be stopped suddenly if necessary.
+				if (aAction==KAcceptImmediately)
+					{
+					CarrierLost();
+					iMigrateState = EWaitingForNewCarrier;
+					iMobilityManager->MigrateToNewCarrier();
+		
+					// do not set waiting if NewCarrierActive has been called synchronously.
+					if (iMigrateState == EWaitingForNewCarrier)
+						{
+						// Now in a waiting state, set self active
+						iStatus = KRequestPending;
+						SetActive();
+						}
+					}
+				// else already preparing for new carrier, nothing to do.
+				return;
+				}
+
+			case EWaitingForOpToComplete:	// async wait on compound op/bground op/idle controller
+				{
+				// Allow change from EWaitingForOpToComplete to EWaitingForOpToStop
+				// also allow an operation to be stopped suddenly if necessary.
+				if (aAction==KAcceptImmediately)
+					{
+					CarrierLost();
+					iMigrateState = EWaitingForNewCarrier;
+					iMobilityManager->MigrateToNewCarrier();
+		
+					// do not set waiting if NewCarrierActive has been called synchronously.
+					if (iMigrateState == EWaitingForNewCarrier)
+						{
+						// Now in a waiting state, set self active
+						iStatus = KRequestPending;
+						SetActive();
+						}
+					}
+				else if (aAction==KAcceptStopCurrent)
+					{
+					// change from EWaitingForOpToComplete to EWaitingForOpToStop
+					StopCurrentForMigrate();
+					}
+				// else already preparing for new carrier, nothing to do.
+				return;
+				}
+			case EHandlingConnectError:
+				{
+				// We have a Connect Error, from previous connect attempt
+				// We are ready to (migrate) reconnect
+				iMigrateState = EWaitingForNewCarrier;
+				iMobilityManager->MigrateToNewCarrier();
+				
+				// do not set waiting if NewCarrierActive has been called synchronously.
+				if (iMigrateState == EWaitingForNewCarrier)
+					{
+					// Now in a waiting state, set self active
+					iStatus = KRequestPending;
+					SetActive();
+					}
+				break;
+				}
+			case EConnectingAfterMigrate:	// async wait on session manager
+				{
+				// cancelling in this state tidies up the session array, so
+				// nothing extra to do here - now ready for migration.
+				Cancel();
+				iMigrateState = EWaitingForNewCarrier;
+				iMobilityManager->MigrateToNewCarrier();
+	
+				// do not set waiting if NewCarrierActive has been called synchronously.
+				if (iMigrateState == EWaitingForNewCarrier)
+					{
+					// Now in a waiting state, set self active
+					iStatus = KRequestPending;
+					SetActive();
+					}
+				return;				
+				}
+
+			case EWaitingInitialCarrierRejected:	// async wait state on mobility engine
+			case EWaitingForNewCarrier:		// async wait state on mobility engine
+			case EStartingReconnect:		// intermediate state
+				{
+				// already ready and waiting for a new carrier - Cancel dummy request
+				// and tell the Mobility framework we are ready
+				Cancel();
+				iMigrateState = EWaitingForNewCarrier;
+				iMobilityManager->MigrateToNewCarrier();
+	
+				// do not set waiting if NewCarrierActive has been called synchronously.
+				if (iMigrateState == EWaitingForNewCarrier)
+					{
+					// Now in a waiting state, set self active
+					iStatus = KRequestPending;
+					SetActive();
+					}
+				return;
+				}
+			case ESuspendingForMigrate:     // intermediate state
+			case ENotMigrating:				// already checked this isn't true
+			default:
+				{
+				__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProConPreForNewCarBadMigrateState));
+				}
+			}
+		}
+
+	// Do the requested action...
+	switch (aAction)
+		{
+		case KAcceptCompleteCurrent:
+			{
+			// Any current operation shall be allowed to complete prior
+			// to indicating that the server MTM is ready to migrate to
+			// the new bearer.
+			CompleteCurrentForMigrate();
+			break;
+			}
+		case KAcceptStopCurrent:
+			{
+			// Any current operation shall be allowed to continue until
+			// such a point that it may be resumed without re-sending or
+			// re-receiveing a significant amount to data prior to 
+			// indicating that the server MTM is ready to migrate to the 
+			// new bearer.
+			StopCurrentForMigrate();
+			break;
+			}
+		case KAcceptImmediately:
+		default:
+			{
+			// accept immediately is an instruction to immediately suspend any
+			// current operation (cancel any outstanding server communication)
+			// and close any open sockets. This is the same behaviour as required
+			// for a downgrade situation.
+			CarrierLost();
+			iMigrateState = EWaitingForNewCarrier;
+			iMobilityManager->MigrateToNewCarrier();
+
+			// do not set waiting if NewCarrierActive has been called synchronously.
+			if (iMigrateState == EWaitingForNewCarrier)
+				{
+				// Now in a waiting state, set self active
+				iStatus = KRequestPending;
+				SetActive();
+				}
+			break;
+			}
+		} // end switch (aAction)
+	}
+
+/**
+Configures the protocol controller (PC) to allow any current operation in progress
+to complete prior to indicating to the mobility framework that it is ready to
+migrate to the new carrier.
+
+Note the protocol controller may be in any of the following states
+ - IDLE (only one session exists)
+ - background sync in progress only (only one session exists)
+ - foreground op in progress only (only one session exists)
+ - background sync and foreground operation in progress (2 sessions exist)
+ - background sync in progress and connecting second session for foreground op
+
+*/ 	
+void CImapProtocolController::CompleteCurrentForMigrate()	
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::CompleteCurrentForMigrate()");
+	// set the migrating flag
+	iMigrateState = EWaitingForOpToComplete;
+	
+	if (BackgroundSyncInProgress())
+		{
+		// 2-phase background sync is always delayed till after migration
+		iBackgroundSyncOp->StopForMigrate();
+		}
+
+	// If no operation in progress, stop the IDLE
+	if (iCurrentOp == EIdle && iImapIdleController->IsActive())
+		{
+		iCurrentOp = EStopIdle;
+		iImapIdleController->StopIdle(iStatus);
+		SetActive();
+		return;
+		}
+
+	// cancel a secondary session connect
+	if (iCurrentOp == EConnectSecondary)
+		{
+		CancelForMigrate();
+		iMigrateState = EWaitingForOpToComplete;
+		
+		// defensive: it is possible that the background sync has completed
+		// while the secondary connect is still in progress. In this case
+		// neither DoRunL() nor BackgroundSyncComplete() will not be called.
+		if (!BackgroundSyncInProgress())
+			{
+			// fake a stop-idle op
+			iCurrentOp = EStopIdle;
+			SetActive();
+			TRequestStatus* status = &iStatus;
+			User::RequestComplete(status, KErrNone);
+			}
+		}
+	}
+
+/**
+CancelForMigrate() - cancels any outstanding service without deleting the
+compound operation or completing the user request.
+
+This does not cancel a background sync.
+
+iMigrateState must be set to a new state immediately after calling this.
+*/
+void CImapProtocolController::CancelForMigrate()
+	{
+	iMigrateState = ESuspendingForMigrate;
+	Cancel();
+	}
+
+/**
+*/
+void CImapProtocolController::DoCancelForMigrate()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DoCancelForMigrate()");
+	switch (iCurrentOp)
+		{
+		case EConnectSecondary:
+			{
+			// Cancel the connect request
+			iImapSessionManager->Cancel();
+			// Remove the session pointer from the array
+			// The session has already been deleted - see note above.
+			TInt numSessions = iImapSessionArray.Count();
+			iImapSessionArray.Remove(numSessions-1);
+			break;
+			}
+
+		case EIdle:
+		case EStopIdle:
+			{
+			// Cancel the IDLE controller.
+			iImapIdleController->Cancel();
+			break;
+			}
+
+		case ESync:
+		case ESelect:
+		case ECopyToLocal:
+		case ECopyWithinService:
+		case ECopyFromLocal:
+		case EMoveToLocal:
+		case EMoveWithinService:
+		case EMoveFromLocal:
+		case EPopulate:
+		case EDelete:
+		case EDeleteFolder:
+		case ECreate:
+		case ERename:
+			{
+			// Cancel the compound operation
+			iImapCompound->CancelForMigrate();
+			break;
+			}
+
+		case ECancelRecoverPrimary:
+			{
+			// Cancel the session recovery.
+			(iImapSessionArray[0])->Cancel();
+			}
+
+		case EConnect:
+//		case EConnectAndSync:
+		case EDisconnect:
+		default:
+			{
+			// nothing to do..
+			break;
+			}
+			
+		} // end of switch (iCurrentOp)
+
+	// Do NOT call CMsgActive::DoCancel()
+	// - the client requested operation is not being cancelled
+	}
+
+
+/**
+Configures the protocol controller (PC) to stop any current operation in 
+progress at the next convenient point - the meaning of this depends on the 
+specific compound operations. For example, for simple operations such as 
+creating a folder, this will be when the operation has completed but for
+operations for which multiple messages are processed in sequence (eg 
+fetching several messages), it shall be once the current message has been
+processed (eg fetched). At this point the operation shall complete the
+protocol controller's iStatus
+
+Note the protocol controller may be in any of the following states
+ - IDLE (only one session exists)
+ - background sync in progress only (only one session exists)
+ - foreground op in progress only (only one session exists)
+ - background sync and foreground operation in progress (2 sessions exist)
+ - background sync in progress and connecting second session for foreground op
+
+*/ 
+void CImapProtocolController::StopCurrentForMigrate()	
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::StopCurrentForMigrate()");
+	// set the migrating flag
+	iMigrateState = EWaitingForOpToStop;
+	
+	if (BackgroundSyncInProgress())
+		{
+		iBackgroundSyncOp->StopForMigrate();
+		}
+	
+	if (iCurrentOp == EIdle && iImapIdleController->IsActive())
+		{
+		// If no operation in progress, stop the IDLE
+		iCurrentOp = EStopIdle;
+		iImapIdleController->StopIdle(iStatus);
+		SetActive();
+		return;
+		}
+	else if (iCurrentOp == EConnectSecondary)
+		{
+		// cancel a secondary session connect
+		Cancel();
+		
+		// defensive: it is possible that the background sync has completed
+		// while the secondary connect is still in progress. In this case
+		// neither DoRunL() nor BackgroundSyncComplete() will not be called.
+		if (!BackgroundSyncInProgress())
+			{
+			// fake a stop-idle op
+			iCurrentOp = EStopIdle;
+			SetActive();
+			TRequestStatus* status = &iStatus;
+			User::RequestComplete(status, KErrNone);
+			}
+		}
+	else if (iCurrentOp != ECancelRecoverPrimary)
+		{
+		// nothing to do for ECancelRecoverPrimary - just wait for the
+		// session to flush.
+		// all other operations are told to stop:
+		iImapCompound->StopForMigrate();		
+		}
+	}		
+
+/**
+This API is called by the Bearer Mobility Manager. It is typically called in the 
+case that a downgrade is occuring, and the original sockets are no longer valid
+for use. It may also be called if an immediate migration to an preferred bearer is
+required, without graceful closing of the original sockets.
+
+Any operations are cancelled immediately using the CancelForMigrate() API. This
+cancels the operations in such a way that they may be restarted following migration
+to a new carrier.
+*/
+void CImapProtocolController::CarrierLost()
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::CarrierLost(iMigrateState=%d)", iMigrateState));
+
+	// Handle being called in an already-migrating state before handling usual case
+	if (iMigrateState!=ENotMigrating)
+		{
+		switch (iMigrateState)
+			{
+			case EWaitingForOpToStop:
+			case EWaitingForOpToComplete:
+				{
+				// break to default behaviour
+				break;
+				}
+			
+			case EStartingReconnect:		// intermediate state
+			case EDisconnectingForMigrate:
+			case EConnectingAfterMigrate:
+				{
+				// cancel the disconnect/connect, then break to default behaviour
+				// (calling CancelForMigrate() shouldn't do anything as any compound
+				// operations will not themselves be active)
+				Cancel();
+				break;
+				}
+				
+			case EHandlingConnectError:
+				{
+				// empty the session array
+				TInt numSessions = iImapSessionArray.Count();
+				for (TInt i=0; i<numSessions; ++i)
+					{
+					if (iImapSessionArray[i])
+						{
+						iImapSessionManager->Disconnect(*(iImapSessionArray[i]));
+						}
+					}
+				iImapSessionArray.ResetAndDestroy();
+
+				// Set the migration state:
+				iMigrateState = EWaitingForNewCarrier;
+				
+				// Now in an waiting state, set self active
+				iStatus = KRequestPending;
+				SetActive();
+				return;
+				}
+					
+			case EWaitingInitialCarrierRejected:
+			case EWaitingForNewCarrier:
+				{
+				// already ready and waiting for a new carrier - nothing to do.
+				return;
+				}
+			
+			case ESuspendingForMigrate:     // intermediate state
+			case ENotMigrating:				// normal state
+			default:
+				{
+				__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProConKillCurrentBadMigrateState));
+				break;
+				}
+			}
+		}
+
+
+	// stop any background operation
+	if (iBackgroundSyncOp)
+		{
+		iBackgroundSyncOp->CancelForMigrate();
+		}
+
+	// Cancel foreground operations
+	CancelForMigrate();
+		
+	// delete the imap idle controller - this will be re-created on reconnect.
+	delete iImapIdleController;
+	iImapIdleController=NULL;
+		
+	// Drop connection on each of the imap sessions
+	TInt numSessions = iImapSessionArray.Count();
+	for (TInt i=0; i<numSessions; ++i)
+		{
+		if (iImapSessionArray[i])
+			{
+			iImapSessionManager->Disconnect(*(iImapSessionArray[i]));
+			}
+		}
+
+	// empty the session array
+	iImapSessionArray.ResetAndDestroy();
+	
+	// Set the migration state:
+	iMigrateState = EWaitingForNewCarrier;
+	
+	// Now in an waiting state, set self active
+	iStatus = KRequestPending;
+	SetActive();
+	}
+	
+/**
+Called to indicate the migration has completed and the RConnection is ready to
+provide new sockets via the new bearer.
+
+Initiates the creation of a new IMAP session, following which any suspended
+operation shall be restarted.
+*/
+void CImapProtocolController::NewCarrierActive(TAccessPointInfo /*aNewAp*/, TBool /*aIsSeamless*/)
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::NewCarrierActive()");
+	__ASSERT_DEBUG((iMigrateState == EWaitingForNewCarrier || iMigrateState == EWaitingInitialCarrierRejected),
+	               TImapServerPanic::ImapPanic(TImapServerPanic::ENewCarrierActiveUnexpectedMigrateState));
+
+	// Cancel the dummy active state
+	Cancel();
+
+	// set the new migration state
+	iMigrateState = EStartingReconnect;
+
+	// complete self - this requires some memory allocation, do it within the RunL.
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	SetActive();	
+	}
+
+	
+void CImapProtocolController::NewPrimarySessionL()
+	{
+	// Create the primary session pointer
+	CImapSession* imapSession = NULL;
+	iImapSessionArray.AppendL(imapSession);
+
+	// Request the session manager to connect the session
+	iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[0]);
+	iMigrateState = EConnectingAfterMigrate;
+
+	SetActive();
+	}
+	
+/**
+Indicates that the RConnection is no longer valid.
+Therefore disconnect and mark the service as offline.
+*/
+#ifdef __IMAP_LOGGING
+void CImapProtocolController::MobilityError(TUint aError)
+#else //__IMAP_LOGGING
+void CImapProtocolController::MobilityError(TUint /*aError*/)
+#endif //__IMAP_LOGGING
+	{
+	__LOG_FORMAT((KDefaultLog, "CImapProtocolController::MobilityError(iMigrateState=%d, aError=%d)", iMigrateState, aError));
+	
+	// Cancel any async request
+	Cancel();
+	
+	if (iRequestedOp != EIdle)
+		{
+		// Complete the user request - calls DisconnectAll()
+		Complete(KErrDisconnected);
+		}
+	else
+		{
+		// Disconnect all sessions and mark the service as offline
+		DisconnectAll();
+		}
+	
+	// the RConnection is no longer valid - delete the session manager
+	// (will be recreated on ConnectL())
+	delete iImapSessionManager;
+	iImapSessionManager = NULL;
+	
+	// no longer migrating...
+	iMigrateState = ENotMigrating;
+
+	// Save error code in progress and flag the disconnect
+	// this is defensive as the client should not be watching progress
+	iProgress.iGenericProgress.iState=TImap4GenericProgress::EDisconnected;
+	iProgress.iGenericProgress.iErrorCode=KErrDisconnected;
+	}
+
+void CImapProtocolController::ProcessOpCompleteForMigrate()
+	{
+	// Any tidying up to do for the current user-requested operation
+	switch (iCurrentOp)
+		{
+		case ECancelRecoverPrimary:
+		case EIdle:
+		case EStopIdle:
+			{
+			// engine was IDLE or recovering from a cancel when the 
+			// migrate notice arrived... nothing to do before disconnecting.
+			break;
+			}
+
+		case ESync:
+		case ESelect:
+		case ECopyToLocal:
+		case ECopyWithinService:
+		case ECopyFromLocal:
+		case EMoveToLocal:
+		case EMoveWithinService:
+		case EMoveFromLocal:
+		case EPopulate:
+		case EDelete:
+		case EDeleteFolder:
+		case ECreate:
+		case ERename:
+			{
+			// update last operation progress state
+			iImapCompound->Progress(iProgress);
+			
+			// if the user requested operation has finished, 
+			// tidyup and inform the client 
+			if (iProgress.iGenericProgress.iState == TImap4GenericProgress::EIdle)
+				{
+				delete iImapCompound;
+				iImapCompound = NULL;
+		
+				// Complete the server mtm request.
+				Complete(iStatus.Int());
+				}
+			break;
+			}
+
+		case EConnect:
+		case EDisconnect:
+		case EConnectSecondary:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::EProcessOpCompleteForMigrateUnexpectedState));
+			}
+		} // end of switch (iCurrentOp)
+	}
+
+/**
+Logs out and disconnects all connected sessions with the remote server.
+*/
+void CImapProtocolController::DisconnectForMigrateL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::DisconnectForMigrateL()");
+	__ASSERT_DEBUG(iMigrateCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EMigrateCompoundIsNotNull));
+	
+	// Background operations will have been taken care of already - just disconnect.
+	// Do not perform the late-delete offline operations.
+	delete iMigrateCompound;
+	iMigrateCompound = NULL;
+	
+	iMigrateCompound = CImapCompoundDisconnect::NewL( *iImapSyncManager, 
+												      iEntry, 
+												      *iImapSettings,
+												      *iImapSessionManager,
+												      *iImapMailStore,
+												      iImapSessionArray,
+												      iImapOfflineControl,
+												      EFalse );
+
+	iMigrateState = EDisconnectingForMigrate;
+	iMigrateCompound->StartOperation(iStatus, *iImapSessionArray[0]);
+	SetActive();	
+	}
+
+
+/**
+Resumes operations that were stopped to allow migration to occur,
+or operations that were requested while migration was in progress.
+*/
+void CImapProtocolController::RestartAfterMigrateL()
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::RestartAfterMigrateL()");
+	
+	// The migration has completed. Restart anything that was going on
+	// before the migration started, or has been requested in the meantime.
+	iMigrateState = ENotMigrating;
+	
+	// was the initial carrier rejected during the connect operation?
+	// if so, mark as online and get the final connection progress info.
+	if (iCurrentOp == EConnect)
+		{
+		TRAP_IGNORE(MarkOnOrOfflineL(ETrue));
+
+		// Collect the final connect progress information		
+		iImapSessionManager->Progress(iProgress.iGenericProgress);
+
+		// Set last socket activity timeout to iMtmData1. This is used by Imcm.
+		User::LeaveIfError( iEntry.SetEntry( iServiceId ) );
+		TMsvEntry entry=iEntry.Entry();
+		entry.SetMtmData1(iImapSessionManager->LastSocketActivityTimeout());
+		User::LeaveIfError( iEntry.ChangeEntry( entry ) );
+				
+		// complete the connect request
+		Complete(iStatus.Int());		
+		}
+	
+	// Create a new IDLE controller - the old one was deleted.
+	delete iImapIdleController;
+	iImapIdleController=NULL;
+	iImapIdleController = CImapIdleController::NewL(*this, iImapSessionArray[0], *iImapSyncManager, iEntry, *iImapSettings, *iImapMailStore);
+
+	//	was there a background sync in progress prior to migration?
+	if (iBackgroundSyncOp)
+		{
+		// restart any background operation, using the new primary session.
+		iBackgroundSyncOp->ResumeOperationL(*iImapSessionArray[0]);
+		
+		// was there a forground operation paused as well?
+		if (iImapCompound)
+			{
+			// Create a second connected session.
+			iCurrentOp = EConnectSecondary;
+			// iRequestedOp should not have been changed...
+			CImapSession* imapSession = NULL;
+			iImapSessionArray.AppendL(imapSession);
+			TInt sessionCount = iImapSessionArray.Count();
+			iImapSessionManager->GetSessionL(iStatus, iImapSessionArray[sessionCount-1]);
+			SetActive();
+			}
+		else
+			{
+			iCurrentOp = EIdle;
+			}
+		return;
+		}
+
+	if (iImapCompound)
+		{
+		// Idle has not been restarted after the migration, so the requested 
+		// operation can be re-started without calling StopIdle()
+		// resume the suspended operation
+		iCurrentOp = iRequestedOp;
+		iImapCompound->ResumeOperationL(iStatus, *iImapSessionArray[0]);
+		SetActive();		
+		}
+	else
+		{
+		// otherwise just start Idle
+		iCurrentOp = EIdle;
+		iRequestedOp = EIdle;
+		StartIdle();
+		}
+	}
+
+/**
+Returns a packaged copy of the current progress for passing to the
+mobility policy plugin when a preferred carrier available notice
+is received.
+*/
+const TDesC8& CImapProtocolController::MobilityProgress()
+	{
+	Progress();
+	iProgressBuffer = TImap4ProgressBuf(iProgress);
+	return iProgressBuffer;
+	}
+/**
+Updates  the specified messages read/unread status from the remote server.
+@param aStatus
+*/	
+EXPORT_C void CImapProtocolController::UpdateFlagL( TRequestStatus& aStatus)
+ 
+	{
+	__LOG_TEXT(KDefaultLog, "CImapProtocolController::UpdateFlagL()");
+	__ASSERT_DEBUG(iImapCompound==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECreateCompoundIsNotNull));
+	ResetProgress();
+	if (!CompleteIfBackgroundOpInProgress(aStatus))	
+		{
+		iImapCompound = CImapUpdateFlagOperation::NewL(*iImapSyncManager,
+												   iEntry,
+												   *iImapSettings
+												   );
+		iRequestedOp = EUpdateFlag;
+		StartPrimaryOperation();
+		Queue(aStatus);
+		SetActive();
+		}
+	}
+	
+