email/imap4mtm/imapprotocolcontroller/src/cimapcompoundsyncservice.cpp
changeset 31 ebfee66fde93
parent 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapprotocolcontroller/src/cimapcompoundsyncservice.cpp	Fri Jun 04 10:25:39 2010 +0100
@@ -0,0 +1,1608 @@
+// Copyright (c) 2006-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 "cimapcompoundsyncservice.h"
+
+#include <imapset.h>
+
+#include "cimapfolder.h"
+#include "cimapsession.h"
+#include "cimapmailstore.h" 
+#include "cimapofflinecontrol.h"
+#include "cimapsyncmanager.h"
+#include "cimapfolder.h"
+#include "cimaplogger.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 "cimapsessionconsts.h"
+#include "imappaniccodes.h"
+#include "cimapofflinecontrol.h"
+#include "imappaniccodes.h"
+#include "cimapsyncdownloadrules.h"
+
+#include "mobilitytestmtmapi.h"
+
+CImapCompoundSyncService::CImapCompoundSyncService(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, CImapOfflineControl& aImapOfflineControl ,TBool aDoLateDeletes)
+ : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), iImapOfflineControl(aImapOfflineControl), iImapMailStore(aImapMailStore), iDoLateDeletes(aDoLateDeletes)
+	{
+	// Set default progress values here so that any progress request before
+	// the operation is started returns valid information.
+	if(aDoLateDeletes)
+		{
+		iProgressState = TImap4GenericProgress::EBusy;
+		iSyncProgressState=TImap4SyncProgress::EDeleting;
+		}
+	else
+		{
+		iProgressState = TImap4GenericProgress::ESyncing;
+		iSyncProgressState=TImap4SyncProgress::ESyncInbox;
+		}
+	}
+
+CImapCompoundSyncService::~CImapCompoundSyncService()
+	{
+	delete iOutstandingOps;
+	delete iOutstandingMoveTypeDeletes;
+	delete iOutstandingLocalDeletes;
+	delete iImapCompound;
+	delete iDoNotFetchSel;
+	}
+	
+CImapCompoundSyncService* CImapCompoundSyncService::NewL(CImapSyncManager& aSyncManager, CMsvServerEntry& aServerEntry, CImapSettings& aImapSettings, CImapMailStore& aImapMailStore, CImapOfflineControl& aImapOfflineControl ,TBool aDoLateDeletes)
+	{
+	CImapCompoundSyncService* self = new (ELeave) CImapCompoundSyncService(aSyncManager, aServerEntry, aImapSettings, aImapMailStore, aImapOfflineControl,aDoLateDeletes);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+void CImapCompoundSyncService::ConstructL()
+	{
+	iOutstandingMoveTypeDeletes = new (ELeave) CArrayFixFlat<TMsvId>(4);
+	iOutstandingLocalDeletes = new (ELeave) CArrayFixFlat<TMsvId>(4);
+	iPerformDeletes = !(iImapSettings.DeleteEmailsWhenDisconnecting());
+	
+	CActiveScheduler::Add(this);
+	}
+
+void CImapCompoundSyncService::StartOperation(TRequestStatus& aStatus, CImapSession& aSession)
+	{
+	iSession = &aSession;
+	__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::StartOperation()");
+	if(iDoLateDeletes)
+		{
+		iNextStep = EInboxLateDeletes;
+		}
+	else
+		{
+		iNextStep = ESyncService;
+		}
+	Queue(aStatus);
+	CompleteSelf();
+	}
+	
+/**
+Handles the compound operation state machine
+
+@return ETrue if compound operation is completed, 
+		EFalse otherwise (will be called again, unless active)
+*/
+TBool CImapCompoundSyncService::DoRunLoopL()
+	{
+	SetCurrentStep();
+	switch (iCurrentStep)
+		{
+		case ESyncService:  // synchronous
+			{
+			// Starts the full sync process by synching the inbox
+			iNextFolderSync = 0;
+					
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Starting the full Sync steps.");
+			CImapFolder* inbox = iSyncManager.Inbox();
+			
+			if(inbox != NULL)
+				{
+				iNextStep = EInboxSync;
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Processing offline for the inbox.");
+					// Deal with operations in the array
+				if (RefreshOutstandingOpsL(inbox->MailboxId()))
+					{
+					iNextStep = EInboxCheckPendingOps;
+					}
+				}
+			else
+				{
+				iNextStep = ESyncTree;
+				}
+			
+			CompleteSelf();
+			}
+			break;
+		case EInboxDoneOps: // synchronous
+			{
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Performing the current offline op for the inbox.");
+
+			iNextStep = EInboxCheckPendingOps;
+			DonePendingOpL();
+			CompleteSelf();
+			}
+			break;
+		case EInboxCheckPendingOps: // synchronous
+			{
+			iSyncProgressState=TImap4SyncProgress::EProcessingPendingOps;
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Find the next offline op for the inbox.");
+			if (NextPendingOpL())
+				{
+				iNextStep = EInboxSync;
+				DonePendingOpL();
+				CompleteSelf();
+				}
+			else
+				{
+				iNextStep = EInboxDoneOps;
+
+				// As there is a next pending op, we should be active so that the
+				// operation can go ahead. However under some circumstances (such
+				// as a missing shadow ID), the operation will not happen so we need
+				// to ensure the state machine does not lock up.
+				if (!IsActive())
+					{
+					CompleteSelf();
+					}
+				}
+			}
+			break;
+		case EInboxEarlyDeletes: // async, SELECTing the inbox prior to EXPUNGE. 
+			{
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Performing early deletes for the inbox.");
+
+			iSyncProgressState=TImap4SyncProgress::EDeleting;
+			// get rid of the FromLocal message sources
+			CImapFolder* inbox = iSyncManager.Inbox();
+			
+			if(inbox)
+				{
+				for (TInt i = 0; i < iOutstandingLocalDeletes->Count(); ++i)
+					{
+					TMsvId id = (*iOutstandingLocalDeletes)[i];
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Removing message %d.", id));
+					inbox->DeleteMessageL(id);
+					}
+
+				iOutstandingLocalDeletes->Reset();
+
+				// then do the inbox deletes
+				if (ProcessPendingDeleteOpsL(inbox->MailboxId(), !iPerformDeletes))
+					{
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEInboxEarlyDeletes);
+					iNextStep = EInboxEarlyExpunge;
+					inbox->SelectL(iStatus, *iSession);
+					SetActive();
+					return EFalse;
+					}
+				}
+			else
+				{
+				iOutstandingLocalDeletes->Reset();
+				}
+
+			iNextStep = ESyncTree;
+			CompleteSelf();
+			}
+	  		break;
+		case EInboxEarlyExpunge: // async - sync deletes performed by the folder object, STORE /deleted and EXPUNGE
+			{
+			iSyncProgressState=TImap4SyncProgress::EDeleting;
+
+			iNextStep = ESyncTree;
+
+			if (iStatus.Int() == KErrImapNo)
+				{
+				iFoldersNotFound++;
+				}
+			else
+				{
+				// expunge the folder
+				iFolder = iSyncManager.Inbox();
+				
+				if(iFolder)
+					{
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEInboxEarlyExpunge);
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Expunge the folder %S.", &(iFolder->FullFolderPathL())));
+					iFolder->SyncDeletesL(iStatus, *iSession);
+					SetActive();
+					return EFalse;
+					}
+				}
+
+			CompleteSelf();
+			}
+			break;
+		case EInboxSync: // async
+			{
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Calling the Syncmanger step for synching the inbox.");
+
+			// suspend operation if stop for migrate has been requested
+			if (iStopForMigrate)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Stopped for migrate");
+				iCurrentStep = ESuspendedForMigrate;
+				iNextStep = EInboxSync;
+				Complete(KErrNone);
+				return ETrue;
+				}
+
+			// Call the SyncManager inbox sync
+			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEInboxSync);			
+			iSyncProgressState=TImap4SyncProgress::ESyncInbox;
+			iSyncManager.SynchroniseInboxL(iStatus, *iSession);
+			iNextStep = EInboxEarlyDeletes;
+			}
+			break;
+		case ESyncTree:  // async
+			{
+			// suspend operation if stop for migrate has been requested
+			if (iStopForMigrate)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Stopped for migrate");
+				iCurrentStep = ESuspendedForMigrate;
+				iNextStep = ESyncTree;
+				Complete(KErrNone);
+				return ETrue;
+				}
+
+			iNextStep = ESyncSubscriptions;
+
+			// skip folder tree sync if disabled.
+			if (iImapSettings.FolderSyncDisabled())
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Folder Tree Sync Disabled....");
+				return EFalse;
+				}
+
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Calling the Syncmanger step to sync the folder tree.");
+
+			// Synching the remote folders tree to local
+			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncESyncTree);			
+			iSyncProgressState=TImap4SyncProgress::ESyncFolderTree;
+			iSyncManager.SynchroniseFolderTreeL(iStatus, *iSession);
+			}
+			break;
+		case ESyncSubscriptions:  // async
+			{
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Calling the Syncmanger step to sync the folders subscriptions.");
+			// Synching the remote folders tree to local
+			MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncESyncSubscriptions);
+			iSyncProgressState=TImap4SyncProgress::ECheckRemoteSubscription;
+			iSyncManager.SynchroniseFolderSubscriptionsL(iStatus, *iSession);
+			iNextStep = ESelectSyncFolder;
+			}
+			break;	
+		case EFolderDoneOps:  // synchronous
+			{
+			if(iNextFolderSync > 0)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Calling DonePendingOp.");
+				iNextStep = EFolderCheckPendingOPs;
+				DonePendingOpL();
+				}
+			else
+				{
+				// All folders are done.
+				iNextStep = ESyncInboxAutoFetch;
+				}
+			
+			CompleteSelf();
+			}
+			break;
+		case EFolderCheckPendingOPs: // synchronous
+			{
+			iSyncProgressState=TImap4SyncProgress::EProcessingPendingOps;
+
+			if(NextPendingOpL())
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : All offline ops are done, calling folder sync.");
+				iNextStep = ESyncFolder;
+				DonePendingOpL();
+				CompleteSelf();
+				}
+			else
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Got a pending offline op, now do it.");
+				iNextStep = EFolderDoneOps;
+
+				// As there is a next pending op, we should be active so that the
+				// operation can go ahead. However under some circumstances (such
+				// as a missing shadow ID), the operation will not happen so we need
+				// to ensure the state machine does not lock up.
+				if (!IsActive())
+					{
+					CompleteSelf();
+					}
+				}
+			}
+			break;
+		case EFolderEarlyDeletes: // asynchronous
+			{
+			iSyncProgressState=TImap4SyncProgress::EDeleting;
+
+			CImapFolder* currentFolder = iSyncManager.GetFolderL(iNextFolderSync);
+
+			if (currentFolder)
+				{
+				for (TInt i = 0; i < iOutstandingLocalDeletes->Count(); ++i)
+					{
+					TMsvId id = (*iOutstandingLocalDeletes)[i];
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Removing message %d.", id));
+					currentFolder->DeleteMessageL(id);
+					}
+
+				iOutstandingLocalDeletes->Reset();
+
+				// if we are doing deletes on connection then all deletes will
+				// be marked Delete
+				if (ProcessPendingDeleteOpsListL(!iPerformDeletes))
+					{
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Selecting the folder %S for expunge.", &(currentFolder->FullFolderPathL())));
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEFolderEarlyDeletes);
+					currentFolder->SelectL(iStatus, *iSession);
+					iNextStep = EFolderEarlyExpunge;
+					SetActive();
+					return EFalse;
+					}
+				}
+			else
+				{
+				iOutstandingLocalDeletes->Reset();
+				}
+
+			iNextStep = ESelectSyncFolder;
+			CompleteSelf();
+			}
+			break;
+		case EFolderEarlyExpunge: // asynchronous
+			{
+			iSyncProgressState=TImap4SyncProgress::EDeleting;
+
+			iNextStep = ESelectSyncFolder;
+
+			if (iStatus.Int() == KErrImapNo)
+				{
+				++iFoldersNotFound;
+				}
+			else
+				{
+				iFolder = iSyncManager.GetFolderL(iNextFolderSync);
+				if(iFolder)
+					{
+					// expunge the folder
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Expunge the folder %S.", &(iFolder->FullFolderPathL())));
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEFolderEarlyExpunge);
+					iFolder->SyncDeletesL(iStatus, *iSession);
+					SetActive();
+					return EFalse;
+					}
+				}
+
+			CompleteSelf();
+			}
+			break;
+		case ESelectSyncFolder: // synchronous
+			{
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::ESelectSyncFolder()");
+			// suspend operation if stop for migrate has been requested
+			if (iStopForMigrate)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Stopped for migrate");
+				iCurrentStep = ESuspendedForMigrate;
+				iNextStep = ESelectSyncFolder;
+				Complete(KErrNone);
+				return ETrue;
+				}
+				
+			// Call for the first time will not be active
+			iSyncManager.NextSubscribedFoldersL(iNextFolderSync);
+			iSyncProgressState=TImap4SyncProgress::ESyncOther;
+
+			if(iNextFolderSync > 0)
+				{
+				// Perform the pending offline operations first
+				iNextStep = ESyncFolder;
+				if(RefreshOutstandingOpsL(iNextFolderSync))
+					{
+					iNextStep = EFolderCheckPendingOPs;	
+					}
+				}
+			else
+				{
+				// All folders are done.
+				iNextStep = ESyncInboxAutoFetch;
+				}
+			CompleteSelf();
+			}
+			break;
+		case ESyncFolder: // async
+			{
+			if(iNextFolderSync > 0)
+				{
+				// Now sync the folder
+				MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncESyncFolder);
+				iSyncManager.SynchroniseSubscribedFoldersL(iStatus, *iSession, iNextFolderSync);
+				iNextStep = EFolderEarlyDeletes;
+				}
+			else
+				{
+				// All folders are done.
+				iNextStep = ESyncInboxAutoFetch;
+				CompleteSelf();
+				}
+			}
+			break;	
+		case EInboxLateDeletes: // async
+			{
+			iNextFolderSync = 0;
+			iSyncProgressState=TImap4SyncProgress::EDeleting;
+			CImapFolder* inbox = iSyncManager.Inbox();
+
+			if (inbox)
+				{
+				if (ProcessPendingDeleteOpsL( inbox->MailboxId(), EFalse ))
+					{
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapEInboxLateDeletes);
+					inbox->SelectL(iStatus, *iSession);
+					iNextStep = EInboxLateExpunge;
+					SetActive();
+					return EFalse;
+					}
+				}
+
+			iNextStep = EFolderLateDeletes;
+			CompleteSelf();
+			}
+			break;	
+		case EInboxLateExpunge: // async
+			{
+			iNextStep = EFolderLateDeletes;
+
+			if (iStatus.Int() == KErrImapNo)
+				{
+				iFoldersNotFound++;
+				}
+			else
+				{
+				// expunge the folder
+				iFolder = iSyncManager.Inbox();
+				
+				if(iFolder)
+					{
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncESyncFolder);
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Late Expunge the folder %S.", &(iFolder->FullFolderPathL())));
+					iFolder->SyncDeletesL(iStatus, *iSession);
+					SetActive();
+					return EFalse;
+					}
+				}
+
+			CompleteSelf();
+			}
+			break;
+		case EFolderLateDeletes: // async
+			{
+			iSyncProgressState=TImap4SyncProgress::EDeleting;
+
+			// Call for the first time will not be active
+			iSyncManager.NextSubscribedFoldersL(iNextFolderSync);
+
+			if(iNextFolderSync > 0)
+				{
+				if (ProcessPendingDeleteOpsListL( EFalse ) )
+					{
+					iNextStep = EFolderLateExpunge;
+					}		
+				else
+					{
+					CImapFolder* currentFolder = iSyncManager.GetFolderL(iNextFolderSync);
+					if(currentFolder)
+						{
+						__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Selecting the folder %S for expunge.", &(currentFolder->FullFolderPathL())));
+						MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEFolderLateDeletes);
+						iNextStep = EFolderLateExpunge;
+						currentFolder->SelectL(iStatus, *iSession);
+						SetActive();
+						return EFalse;
+						}
+
+					// back to select next folder
+					iNextStep = EFolderLateDeletes;
+					}
+				}
+			else
+				{
+				// No more subscribed folder was found
+				iNextStep = EFinished;
+				}
+
+			CompleteSelf();
+			}
+			break;
+		case EFolderLateExpunge: // async
+			{
+			iNextStep = EFolderLateDeletes;
+
+			if (iStatus.Int() == KErrImapNo)
+				{
+				iFoldersNotFound++;
+				}
+			else
+				{
+				iFolder = iSyncManager.GetFolderL(iNextFolderSync);
+				if(iFolder)
+					{
+					__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::DoRunLoopL : Late Expunge the folder %S.", &(iFolder->FullFolderPathL())));
+					// expunge the folder
+					MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmImapSyncEFolderLateExpunge);
+					iFolder->SyncDeletesL(iStatus, *iSession);
+					SetActive();
+					return EFalse;
+					}
+				}
+
+			CompleteSelf();
+			}
+			break;
+		case ESyncInboxAutoFetch:
+			{
+			// suspend operation if stop for migrate has been requested
+			if (iStopForMigrate)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Stopped for migrate");
+				iCurrentStep = ESuspendedForMigrate;
+				iNextStep = ESyncInboxAutoFetch;
+				Complete(KErrNone);
+				return ETrue;
+				}
+				
+			iSyncProgressState=TImap4SyncProgress::ESyncOther;
+			if (!iImapSettings.UseSyncDownloadRules())
+				{
+				// not using sync rules, loop to EFinished.
+				iNextStep = EFinished;
+				return EFalse;
+				}
+
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Starting Second Phase Synchronisation");
+
+			// set the next step to perform
+			iNextStep = ESyncFolderAutoFetchCheck;
+			
+			if (KErrNotFound == iImapSettings.GetSyncDownloadRuleL(CImapSyncDownloadRules::EInboxRulesType, iMailInfo))
+				{
+				// No download rules for the inbox, check other folders
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::No download rules for inbox on this bearer");
+				return EFalse;
+				}
+			
+			CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
+			CleanupStack::PushL(selection);
+			
+			TInt msgsToFetch = iSyncManager.Inbox()->GetFetchMessageChildrenL(*selection);
+			__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::Fetching %d INBOX messages.", msgsToFetch));
+			if (msgsToFetch>0)
+				{
+				delete iImapCompound;
+				iImapCompound = NULL;
+				// Create the compound operation object
+				iImapCompound = CImapCompoundCopyToLocal::NewL(iSyncManager, 
+				                    			               iServerEntry, 
+				                                			   iImapSettings,
+				                                			   iImapMailStore, 
+				                                               EFalse,
+				                                               *selection,
+				                                               KMsvNullIndexEntryId,
+				                                               iMailInfo);
+				iImapCompound->StartOperation(iStatus, *iSession);
+				if (iDoNotFetchSel)
+					{
+					((CImapCompoundCopyToLocal*)iImapCompound)->RemoveFromSelection(*iDoNotFetchSel);
+					}
+				SetActive();
+				}
+			CleanupStack::PopAndDestroy(selection);
+			return EFalse; // if not active, will re-enter this function and perform next step...
+			}
+			
+		case ESyncFolderAutoFetchCheck:
+			{
+			// suspend operation if stop for migrate has been requested
+			if (iStopForMigrate)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Stopped for migrate");
+				iCurrentStep = ESuspendedForMigrate;
+				// fetching of inbox contents may have been stopped prematurely,
+				// on resume, go back to check if any inbox messages still to fetch.
+				iNextStep = ESyncInboxAutoFetch;
+				Complete(KErrNone);
+				return ETrue;
+				}
+
+			// reset folder sync counter
+			iFolderSyncCount = 0;
+			// can only get here if download rules being used, so no need to check that.
+			// No point in continuing if no download rules defined for non-inbox folders:
+			if (KErrNotFound == iImapSettings.GetSyncDownloadRuleL(CImapSyncDownloadRules::EFolderRulesType, iMailInfo))
+				{
+				// No download rules for non-inbox folders.
+				iNextStep = EFinished;
+				return EFalse;
+				}
+			// else continue to next step...
+			}
+		case ESyncFolderAutoFetch:
+			{
+			// suspend operation if stop for migrate has been requested
+			if (iStopForMigrate)
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Stopped for migrate");
+				iCurrentStep = ESuspendedForMigrate;
+				iNextStep = ESyncFolderAutoFetchCheck;
+				// delete the compound fetch operation - we will create a new one on resume,
+				// using the download rules for the new bearer.
+				delete iImapCompound;
+				iImapCompound = NULL;
+				Complete(KErrNone);
+				return ETrue;
+				}
+
+			TInt32 numSubscribedFolders;
+			CImapFolder* folder = iSyncManager.GetSubscribedFolder(iFolderSyncCount, numSubscribedFolders);
+			if (folder)
+				{
+				CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
+				CleanupStack::PushL(selection);
+				TInt msgsToFetch = folder->GetFetchMessageChildrenL(*selection);
+				__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::Fetching %d messages in folder %d.", msgsToFetch, folder->MailboxId()));
+				if (msgsToFetch>0)
+					{
+					delete iImapCompound;
+					iImapCompound = NULL;
+					// Create the compound operation object
+					iImapCompound = CImapCompoundCopyToLocal::NewL(iSyncManager, 
+					                    			               iServerEntry, 
+					                                			   iImapSettings,
+					                                			   iImapMailStore, 
+					                                               EFalse,
+					                                               *selection,
+					                                               KMsvNullIndexEntryId,
+					                                               iMailInfo);
+					iImapCompound->StartOperation(iStatus, *iSession);
+					if (iDoNotFetchSel)
+						{
+						((CImapCompoundCopyToLocal*)iImapCompound)->RemoveFromSelection(*iDoNotFetchSel);
+						}
+					SetActive();
+					}
+				CleanupStack::PopAndDestroy(selection);
+				// when complete, check the next folder...
+				++iFolderSyncCount;
+				iNextStep = ESyncFolderAutoFetch;
+				}
+			else
+				{
+				__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Download Rules Auto Fetch Completed.");
+				iNextStep = EFinished;
+				}
+			return EFalse; // not finished yet...
+			}
+			
+		case EFinished:
+			{
+			__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Completing OK");
+			iSyncProgressState=TImap4SyncProgress::EIdle;
+			iProgressState = TImap4GenericProgress::EIdle;
+			Complete(KErrNone);
+			return ETrue;
+			}
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ESyncServiceCompoundUnexpectedState));
+			// unexpected state - complete the request
+			iSyncProgressState=TImap4SyncProgress::EIdle;
+			iProgressState = TImap4GenericProgress::EIdle;
+			return ETrue;
+			}
+		} // end of switch (iCurrentStep)
+
+	if (!IsActive())
+		{
+		SetActive();
+		}
+	return EFalse;
+	}
+	
+/**
+May be called in case of a genuine cancel or a cancel for migrate.
+Following a genuine cancel, the compound operation will be deleted.
+Following a cancel for migrate, the compound operation will be resumed,
+so the iNextState is updated here to ensure the operation is
+correctly restarted.
+
+In either case, CMsgActive::DoCancel() is called to complete the
+user request with KErrCancel.
+
+In most cases, it is possible to resume at the current step following a
+cancel-for-migration (sometimes following a SELECT on the current folder).
+
+Note that if the default CMsgActive::DoComplete() is overridden,
+then that must also be modified to handle either case described above.
+
+Note on cancelling folder select operations: the folder actually passes 
+the iStatus to the session, hence the cancel is on the session.
+*/
+void CImapCompoundSyncService::DoCancel()
+	{
+	switch (iCurrentStep)
+		{
+		case ESelectSourceMailboxRW:
+			{
+			// only set by ResumeOperation. No update to iNextStep required
+			iSession->Cancel();
+			break;
+			}
+		case EInboxEarlyDeletes:  // re-select inbox before resuming
+		case EFolderEarlyDeletes: // re-select current folder before resuming
+		case EInboxLateDeletes:   // wont be resumed, as part of disconnect
+		case EFolderLateDeletes:  // wont be resumed, as part of disconnect
+			{
+			// outstanding request on session.
+			iSession->Cancel();
+			break;
+			}
+		case EInboxEarlyExpunge:  // re-select inbox before resuming
+		case EFolderEarlyExpunge: // re-select current folder before resuming
+		case EInboxLateExpunge:   // should not be resumed, as part of disconnect
+		case EFolderLateExpunge:  // should not resumed, as part of disconnect
+			{
+			// outstanding request on folder
+			if (iFolder)
+				{
+				iFolder->Cancel();
+				}
+			iNextStep = iCurrentStep; 
+			break;
+			}
+		case EInboxSync:
+		case ESyncSubscriptions:
+		case ESyncTree:
+		case ESyncFolder:
+			{
+			// outstanding request on syncmanager
+			if (iCancelForMigrate)
+				{
+				iSyncManager.CancelForMigrate();
+				}
+			else
+				{
+				iSyncManager.Cancel();
+				}
+			// resume from the tree synchronisation
+			iNextStep = iCurrentStep;			
+			break;
+			}		
+		case EFolderCheckPendingOPs:
+		case EInboxCheckPendingOps:
+			{
+			// outstanding request on compound object
+			if (iImapCompound)
+				{
+				if (iCancelForMigrate)
+					{
+					iImapCompound->CancelForMigrate();
+					// do not update iNextStep ( == EInboxDoneOps || EFolderDoneOps )
+					// This operation will be resumed...
+					}
+				else
+					{
+					iImapCompound->Cancel();
+					}
+				}
+			break;
+			}
+		case ESyncInboxAutoFetch:
+			{
+			// outstanding request on compound object
+			if (iImapCompound)
+				{
+				// don't cancel-for-migrate - will re-create new object on restart
+				// as the download rules may well be different on the new carrier.
+				iImapCompound->Cancel();
+				}
+			iNextStep = ESyncInboxAutoFetch;
+			break;
+			}
+		case ESyncFolderAutoFetchCheck:
+		case ESyncFolderAutoFetch:
+			{
+			// outstanding request on compound object
+			if (iImapCompound)
+				{
+				// don't cancel-for-migrate - will re-create new object on restart
+				// as the download rules may well be different on the new carrier.
+				iImapCompound->Cancel();
+				}
+			// restart in the check state to update the download rules being used.
+			iNextStep = ESyncFolderAutoFetchCheck;
+			break;
+			}
+		case EInboxDoneOps:
+		case ESyncService:
+		case EFolderDoneOps:
+		case ESelectSyncFolder:
+		case EFinished:
+		case ESuspendedForMigrate:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ESyncServiceCompoundCancelUnexpectedState));
+			iNextStep = EFinished;
+			break;
+			}
+		} // end of switch (iCurrentStep)
+
+	if (!iCancelForMigrate)
+		{
+		// genuine cancel - update progress
+		iProgressErrorCode = KErrCancel;
+		}
+	CMsgActive::DoCancel();	
+	}
+
+
+void CImapCompoundSyncService::Progress(TImap4CompoundProgress& aCompoundProgress)
+	{
+	aCompoundProgress.iGenericProgress.iOperation = TImap4GenericProgress::ESync;
+	aCompoundProgress.iGenericProgress.iState = iProgressState;
+
+	iSyncManager.Progress(aCompoundProgress.iSyncProgress);
+	aCompoundProgress.iSyncProgress.iState = iSyncProgressState;
+
+	// Put error into progress buffer
+	if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone )
+		{
+		aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode;
+		}
+	}
+
+/**
+Handles NO/BAD responses according to current step.
+Negative server responses are not fatal - the error is saved in
+the message currently being operated on and the state machine pushed
+on to process the next message in the requested selection.
+
+@return KErrNone if the error has been handled
+		Completion error code otherwise.
+*/
+TInt CImapCompoundSyncService::ProcessNegativeServerResponse()
+	{
+	TInt err = iStatus.Int();
+	switch (iCurrentStep)
+		{
+	case ESyncTree:
+	case ESyncSubscriptions:
+	case EInboxDoneOps:
+	case EFolderDoneOps:
+	case EFolderCheckPendingOPs:
+	case EInboxCheckPendingOps:
+	case EInboxSync:
+	case EInboxEarlyDeletes:
+	case EFolderEarlyDeletes:
+	case EInboxEarlyExpunge:
+	case EFolderEarlyExpunge:
+	case EInboxLateDeletes:
+	case EFolderLateDeletes:
+	case EInboxLateExpunge:
+	case EFolderLateExpunge:
+	case ESyncFolder:
+	case ESyncService:
+	case ESyncInboxAutoFetch:
+	case ESyncFolderAutoFetchCheck:
+	case ESyncFolderAutoFetch:
+		{
+		// Just store the error code in the progress response.
+		break;
+		}
+	case ESuspendedForMigrate:
+	case EFinished:
+	default:
+		{
+		// positive error code not expected,
+		// self-completed states or no outstanding request.
+		TImapServerPanic::ImapPanic(TImapServerPanic::ESyncServiceCompoundUnexpectedState);
+		break;
+		}
+		} // end of switch (iCurrentStep)
+	iProgressErrorCode = err;
+	return KErrNone;
+	}
+
+
+/**
+Given an id of a folder or the service, restores the offline
+operation array. Returns the number of operations in the array.
+Any non-zero return value indicates an action that needs to be addressed.
+*/
+TBool CImapCompoundSyncService::RefreshOutstandingOpsL(TMsvId aId)
+	{
+	if (iOutstandingOps)
+		{
+		delete iOutstandingOps;
+		iOutstandingOps = NULL;
+		}
+	
+	iOutstandingOps = iImapOfflineControl.OffLineOpArrayL(aId);
+	iOutstandingOpsFolder = aId;
+
+	// reset the count
+	iMsgsToDo = iOutstandingOps->CountOperations();
+	
+	iMsgsDone = 0;
+	
+	iMovedId = KMsvNullIndexEntryId;
+	iShadowId = KMsvNullIndexEntryId;
+
+	return iMsgsToDo;
+	}
+
+/**
+ This routine sets up iShadowId which will be deleted when the
+operation completes successfully
+
+*/
+void CImapCompoundSyncService::DoOpL(const CImOffLineOperation& aOp)
+	{
+	// clean the disconnected op flags and ensure its visible and get
+	// an entry copy
+	SetEntryL(aOp.MessageId());
+	TMsvEmailEntry entry = iServerEntry.Entry();
+	entry.SetVisible(ETrue);
+	entry.SetDisconnectedOperation(ENoDisconnectedOperations);
+	ChangeEntryL(entry);
+
+	// check and see if there is a shadow and whether it has been
+	// removed or marked for deletion
+	TBool shadowOK = ETrue;
+	if ( aOp.OpType() != CImOffLineOperation::EOffLineOpMtmSpecific &&
+		 aOp.OpType() != CImOffLineOperation::EOffLineOpDelete )
+		{
+		iShadowId = iImapOfflineControl.FindShadowIdL(aOp);
+
+		shadowOK = iShadowId != KMsvNullIndexEntryId &&
+			iServerEntry.SetEntry(iShadowId) == KErrNone &&
+			((TMsvEmailEntry)iServerEntry.Entry()).DisconnectedOperation() != EDisconnectedDeleteOperation;
+		}
+
+	delete iImapCompound;
+	iImapCompound = NULL;
+
+	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL(selection);
+	
+	// Deal with operation
+	switch(aOp.OpType())
+		{
+	case CImOffLineOperation::EOffLineOpCopyToLocal:
+		// do the copy, if not a message
+		if (entry.iType != KUidMsvMessageEntry ||
+			// or the shadow exists
+			shadowOK )
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EOffLineOpCopyToLocal : %d %d", aOp.MessageId(), aOp.TargetMessageId()));
+
+			//Create Copy to Local Compound object
+		
+			selection->AppendL(aOp.MessageId());
+			
+			iImapCompound = CImapCompoundCopyToLocal::NewL(iSyncManager, 
+														   iServerEntry, 
+														   iImapSettings,
+														   iImapMailStore,
+														   EFalse,
+		                                               	   *selection,
+		                                               	   aOp.TargetMessageId());
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			}
+		break;
+		
+	case CImOffLineOperation::EOffLineOpCopyFromLocal:
+		if (shadowOK)
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EOffLineOpCopyFromLocal : %d %d", aOp.MessageId(), aOp.TargetMessageId()));
+
+			//Create Copy from Local Compound object
+
+			selection->AppendL(aOp.MessageId());
+			
+			iImapCompound = CImapCompoundCopyFromLocal::NewL(iSyncManager, 
+														     iServerEntry, 
+														     iImapSettings,
+														     EFalse,
+		                                               	     *selection,
+		                                               	     aOp.TargetMessageId());
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			}
+		break;
+
+	case CImOffLineOperation::EOffLineOpCopyWithinService:
+		if (shadowOK)
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EOffLineOpCopyWithinService : %d %d", aOp.MessageId(), aOp.TargetMessageId()));
+
+			//Create Copy within service Compound object
+
+			selection->AppendL(aOp.MessageId());
+			
+			iImapCompound = CImapCompoundCopyWithinService::NewL(iSyncManager, 
+														   		 iServerEntry, 
+														   		 iImapSettings,
+														   		 EFalse,
+														   		 *selection,
+														   		 aOp.TargetMessageId());
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			}
+		break;
+		
+	case CImOffLineOperation::EOffLineOpMoveToLocal:
+		if (shadowOK)
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EOffLineOpMoveToLocal : %d %d", aOp.MessageId(), aOp.TargetMessageId()));
+
+			selection->AppendL(aOp.MessageId());
+			
+			iImapCompound = CImapCompoundCopyToLocal::NewL(iSyncManager, 
+														   iServerEntry, 
+														   iImapSettings,
+														   iImapMailStore,
+														   EFalse,
+		                                               	   *selection,
+		                                               	   aOp.TargetMessageId());
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			}
+		// even if the shadow has been removed we still want to delete
+		// the original
+		iMovedId = aOp.MessageId();
+		break;
+
+	case CImOffLineOperation::EOffLineOpMoveFromLocal:
+		if (shadowOK)
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EOffLineOpMoveFromLocal : %d %d", aOp.MessageId(), aOp.TargetMessageId()));
+
+			//Create Copy from Local Compound object
+
+			selection->AppendL(aOp.MessageId());
+			
+			iImapCompound = CImapCompoundCopyFromLocal::NewL(iSyncManager, 
+														     iServerEntry, 
+														     iImapSettings,
+														     EFalse,
+		                                               	     *selection,
+		                                               	     aOp.TargetMessageId());
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			}
+		// even if the shadow has been removed we still want to delete
+		// the original
+		iMovedId = aOp.MessageId();
+		break;
+
+	case CImOffLineOperation::EOffLineOpMoveWithinService:
+		if (shadowOK)
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EOffLineOpMoveWithinService : %d %d", aOp.MessageId(), aOp.TargetMessageId()));
+
+			//Create Copy within service Compound object
+
+			selection->AppendL(aOp.MessageId());
+			
+			iImapCompound = CImapCompoundCopyWithinService::NewL(iSyncManager, 
+														   		 iServerEntry, 
+														   		 iImapSettings,
+														   		 EFalse,
+														   		 *selection,
+														   		 aOp.TargetMessageId());
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			}
+		// even if the shadow has been removed we still want to delete
+		// the original, unless the folder itself has been removed
+		if (iServerEntry.SetEntry(aOp.TargetMessageId()) == KErrNone)
+			{
+			iMovedId = aOp.MessageId();
+			}
+		break;
+
+	case CImOffLineOperation::EOffLineOpMtmSpecific:
+		switch (aOp.MtmFunctionId())
+			{
+		case EFnOffLineOpPopulate:
+			{
+			__LOG_FORMAT((iSession->LogId(), "CImOffLineOperation::EFnOffLineOpPopulate : %d", aOp.MessageId()));
+
+			//Create Copy to Local Compound object
+		
+			selection->AppendL(aOp.MessageId());
+			
+			// aOp.MtmParameters() for EFnOffLineOpPopulate is a package that contains the partial mail info.
+			//
+			// 1) Create an unitialised dummy partial mail object.  This will NOT receive the partial mail data, but is needed for the build.
+			TImImap4GetPartialMailInfo dummyPartialMailInfo;
+			// 2) Create a package object that WILL point at the package data.
+			//    but for now we need to point it at our unitialised partial mail object, in order for it to compile.
+			TPckgC<TImImap4GetPartialMailInfo> partialMailInfoPackage(dummyPartialMailInfo);
+			// 3) Immediately point our package object at the actual package data.
+			partialMailInfoPackage.Set(aOp.MtmParameters());
+					
+			iImapCompound = CImapCompoundCopyToLocal::NewL(iSyncManager, 
+														   iServerEntry, 
+														   iImapSettings,
+														   iImapMailStore,
+														   EFalse,
+		                                               	   *selection,
+		                                               	   KMsvNullIndexEntryId,
+		                                               	   partialMailInfoPackage()); // pass the actual package data as an object
+			
+			iImapCompound->StartOperation(iStatus, *iSession);
+			SetActive();
+			break;
+			}
+		default:
+			break;
+			}
+		break;
+
+	case CImOffLineOperation::EOffLineOpDelete:
+	default:
+		break;
+		}
+
+	CleanupStack::PopAndDestroy(selection);
+	}
+
+/**
+Tidies up after performing an offline operation. 
+In the case of move operations, this means storing an array of messages that are to
+be deleted on the server later in the sync phase (early deletes) or as the service 
+is disconnected (late deletes).
+
+*/
+void CImapCompoundSyncService::DonePendingOpL()
+	{
+	// if we've done one then...
+	if (iMsgsDone != 0)
+		{
+		// if this was a move then append a delete
+		if (iMovedId != KMsvNullIndexEntryId)
+			{
+			SetEntryL(iMovedId);
+			if (iServerEntry.Entry().Parent() == iOutstandingOpsFolder)
+				{
+				__LOG_FORMAT((iSession->LogId(), "Append MoveDelete for %x", iMovedId));
+				iOutstandingMoveTypeDeletes->AppendL(iMovedId);
+				}
+			else
+				{
+				// if this id was from a MoveFrom (ie its parent is not
+				// this folder) then put it in a different pending array
+				__LOG_FORMAT((iSession->LogId(), "Append LocalDelete for %x", iMovedId));
+				iOutstandingLocalDeletes->AppendL(iMovedId);
+				}
+			}
+		
+		// delete the shadowid if there is one, ignore errors
+		if (iShadowId != KMsvNullIndexEntryId &&
+			iServerEntry.SetEntry(iShadowId) == KErrNone &&
+			iServerEntry.SetEntry(iServerEntry.Entry().Parent()) == KErrNone)
+			{
+			iServerEntry.DeleteEntry(iShadowId);
+			}
+		}
+	}
+
+/**
+Checks current folder's pending offline operation list. If no more pending operations
+exist, returns ETrue, otherwise calls DoOpL() and returns EFalse.
+
+DoOpL() creates a compound operation object to perform the requested operation, 
+and launches it, setting this class active.
+
+Note in some circumstances it is possible that the operation cannot be performed, for
+example if the folder in question has subsequently become unsubscribed. If this occurs,
+this function shall return EFalse (as though an operation had been started) however,
+the class will not have been set active.
+
+@return ETrue if no more pending operations to be performed at this time.
+*/
+TBool CImapCompoundSyncService::NextPendingOpL()
+	{
+	TBool finished = EFalse;
+
+	iMovedId = KMsvNullIndexEntryId;
+	iShadowId = KMsvNullIndexEntryId;
+
+	// Any operations in outstanding list?
+	if (iOutstandingOps->CountOperations())
+		{
+		__LOG_FORMAT((iSession->LogId(), "Outstanding operations on this folder %d", iOutstandingOps->CountOperations()));
+
+		// Fetch operation
+		CImOffLineOperation* thisop = new(ELeave)CImOffLineOperation();
+		CleanupStack::PushL(thisop);
+		
+		thisop->CopyL(iOutstandingOps->Operation(0));
+
+		// when we get to one of the Delete operations then it is time
+		// to stop
+		if (thisop->OpType() == CImOffLineOperation::EOffLineOpDelete ||
+			(thisop->OpType() == CImOffLineOperation::EOffLineOpMtmSpecific &&
+			 thisop->MtmFunctionId() == EFnOffLineOpMoveDelete))
+			{
+			__LOG_TEXT(iSession->LogId(),"Reached delete op. Finished");
+			++iMsgsDone;
+			finished = ETrue;
+			}
+		else
+			{
+			// remove from list and save back
+			iOutstandingOps->Delete(0);
+			iImapOfflineControl.SetOffLineOpArrayL(iOutstandingOpsFolder, *iOutstandingOps);
+
+			// and execute
+			DoOpL(*thisop);
+			++iMsgsDone;
+			}
+		
+		CleanupStack::PopAndDestroy(thisop);
+		}
+	else
+		{
+		// No more operations to do, return to what we should be doing next
+		finished = ETrue;
+		}
+
+	// when we are about to finish this folder then tidy up
+	if (finished)
+		{
+		// add the list of pending deletes to the front of the
+		// offline op array
+		for (TInt i = 0; i < iOutstandingMoveTypeDeletes->Count(); i++)
+			{
+			TMsvId id = (*iOutstandingMoveTypeDeletes)[i];
+			CImOffLineOperation* thisop = new(ELeave)CImOffLineOperation();
+			CleanupStack::PushL(thisop);
+
+			// if we are doing deletes on connection then store this
+			// as a delete and we will do all deletes at the end of
+			// the sync.
+
+			// if we are doing deletes on disconnection then store
+			// this as a special code - it will still get done at end
+			// of sync, but real deletes will be done on
+			// disconnection.
+			if (!iImapSettings.DeleteEmailsWhenDisconnecting()) // iPerformDeletes
+				{
+				thisop->SetDelete(id);
+				}				
+			else
+				{
+				thisop->SetMtmSpecificCommandL(id, EFnOffLineOpMoveDelete, 0, KNullDesC8());
+				}
+
+			iOutstandingOps->InsertOperationL(*thisop, 0);
+			CleanupStack::PopAndDestroy(thisop);
+			}
+
+		// if there were outstanding move type deletes then clear
+		// their array and save back the main outstanding ops list
+		if (iOutstandingMoveTypeDeletes->Count())
+			{
+			iOutstandingMoveTypeDeletes->Reset();
+
+			iImapOfflineControl.SetOffLineOpArrayL(iOutstandingOpsFolder, *iOutstandingOps);
+			}
+		}
+
+	return finished;
+	}
+
+TBool CImapCompoundSyncService::MatchDeleteOp(const CImOffLineOperation& aOp , TBool aMoveDeletesOnly )
+	{
+	return (aOp.OpType() == CImOffLineOperation::EOffLineOpMtmSpecific && aOp.MtmFunctionId() == EFnOffLineOpMoveDelete) ||
+		(!aMoveDeletesOnly && aOp.OpType() == CImOffLineOperation::EOffLineOpDelete);
+	}
+
+TBool CImapCompoundSyncService::ProcessPendingDeleteOpsL( TMsvId aFolder, TBool aMoveDeletesOnly )
+	{
+	TBool hadDeletes = EFalse;
+
+	__LOG_FORMAT((iSession->LogId(), "CImapCompoundSyncService::ProcessPendingDeleteOpsL: Folder %x aMoveDeletesOnly=%d", aFolder, aMoveDeletesOnly));
+		
+	// get the current offline operations of this folder
+	if (RefreshOutstandingOpsL(aFolder))
+		{
+		// Fetch operation
+		CImOffLineOperation* thisop = new(ELeave)CImOffLineOperation();
+		CleanupStack::PushL(thisop);
+		
+		thisop->CopyL(iOutstandingOps->Operation(0));
+
+		// check delete type
+		if (MatchDeleteOp(*thisop, aMoveDeletesOnly))
+			{
+			do
+				{
+				// if can't find the entry then just skip it so it is
+				// removed from array
+				if (iServerEntry.SetEntry(thisop->MessageId()) == KErrNone)
+					{
+					// set its server deleted flag
+					TMsvEmailEntry entry=iServerEntry.Entry();
+					entry.SetDeletedIMAP4Flag(ETrue);
+					ChangeEntryL(entry);
+					}
+
+				// remove from the array and write back immediately
+				iOutstandingOps->Delete(0);
+				iImapOfflineControl.SetOffLineOpArrayL(iOutstandingOpsFolder, *iOutstandingOps);
+
+				// check for finish
+				if (iOutstandingOps->CountOperations() == 0)
+					break;
+			
+				// get next op
+				thisop->CopyL(iOutstandingOps->Operation(0));
+				}
+			while (MatchDeleteOp(*thisop, aMoveDeletesOnly));
+					
+			hadDeletes = ETrue;
+			}
+			
+		CleanupStack::PopAndDestroy(thisop);
+		}
+
+	return hadDeletes;
+	}
+
+TBool CImapCompoundSyncService::ProcessPendingDeleteOpsListL( TBool aMoveDeletesOnly )
+	{
+	if(iNextFolderSync > 0)
+		{
+		if (ProcessPendingDeleteOpsL( iNextFolderSync, aMoveDeletesOnly ))
+			{
+			return ETrue;
+			}				
+		}
+
+	return EFalse;
+	}
+
+/**
+Sets the context's index entry to the specified values on the Message server
+
+@param  aEntry
+The new details for the entry.
+@leave KErrAccessDenied - the entry is read only (deleted entry, standard folder, or locked); 
+KErrNotSupported - aEntry is invalid or the ID specified in aEntry is not the same as the context ID
+or no context has been set for the object; 
+KErrNoMemory - a memory allocation failed. 
+*/
+void CImapCompoundSyncService::ChangeEntryL(const TMsvEntry& aEntry)
+	{
+	User::LeaveIfError(iServerEntry.ChangeEntry(aEntry));
+	}
+
+/**
+Called when operation has completed and we are about to complete the caller.
+
+@param aErr Error code that we will complete with.
+*/
+void CImapCompoundSyncService::DoComplete(TInt& aErr)
+	{
+	CImapCompoundBase::DoComplete(aErr);
+
+	if (!iCancelForMigrate)
+		{
+		// Reset the flags which show if the new flags should be cleared on the
+		// next sync.
+		iSyncManager.ResetInboxClearNewFlags();
+		iSyncManager.ResetNonInboxClearNewFlags();
+		}
+	}
+
+
+/**
+Resumes the operation following a migration.
+*/
+void CImapCompoundSyncService::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession)
+	{
+	iSession = &aSession;
+	__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::Resuming");
+	__ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::ESyncServiceCompoundUnexpectedState));
+	iStopForMigrate = EFalse;
+	
+	switch (iNextStep)
+		{
+		case EInboxEarlyExpunge:
+		case EInboxEarlyDeletes:
+			{
+			// re-select the inbox, and then re-enter state machine
+			iSyncManager.Inbox()->SelectL(iStatus, *iSession);
+			SetActive();
+			iCurrentStep = ESelectSourceMailboxRW;
+			break;
+			}
+		case EFolderEarlyDeletes:
+		case EFolderEarlyExpunge:
+			{
+			// re-select the inbox, and then re-enter state machine
+			iSyncManager.GetFolderL(iNextFolderSync)->SelectL(iStatus, *iSession);
+			iCurrentStep = ESelectSourceMailboxRW;
+			break;
+			}
+		case EInboxLateDeletes:
+		case EFolderLateDeletes:
+		case EInboxLateExpunge:
+		case EFolderLateExpunge:
+			{
+			// resume not expected since these states are only entered during
+			// a disconnection operation. Migrations are not accepted during
+			// diconnection - a downgrade results in the operation being
+			// cancelled and any sessions deleted.
+			iNextStep=EFinished;
+			CompleteSelf();
+			break;
+			}
+		case EInboxSync:  			// select is called by the sync manager in this state
+		case ESyncSubscriptions:	// select not required
+		case ESyncTree:				// select not required
+		case ESyncFolder:			// select is called by the sync manager in this state
+		case ESelectSyncFolder:		// self-completed state, however can suspend in this state
+		case ESyncInboxAutoFetch:   // select not required prior to resuming 2nd phase
+		case ESyncFolderAutoFetchCheck:// select not required prior to resuming 2nd phase
+			{
+			// resume the state machine in this state.
+			CompleteSelf();
+			break;
+			}		
+		case EInboxDoneOps:
+			{
+			if (iImapCompound)
+				{
+				// restart the compound object performing the offline-requested operation.
+				iCurrentStep = EInboxCheckPendingOps;
+				iImapCompound->ResumeOperationL(iStatus, *iSession);
+				SetActive();
+				}
+			else
+				{
+				// Resume with check for further pending inbox operations
+				iNextStep = EInboxCheckPendingOps;
+				CompleteSelf();
+				}
+			break;
+			}
+		case EFolderDoneOps:
+			{
+			if (iImapCompound)
+				{
+				// resume the compound object performing the offline-requested operation.
+				iCurrentStep = EFolderCheckPendingOPs;
+				iImapCompound->ResumeOperationL(iStatus, *iSession);
+				SetActive();
+				}
+			else
+				{
+				// Resume with check for further pending folder operations
+				iNextStep = EFolderCheckPendingOPs;
+				CompleteSelf();
+				}
+			break;
+			}
+		case ESyncFolderAutoFetch:
+		case EFolderCheckPendingOPs: // not expected...
+		case EInboxCheckPendingOps:  // not expected...
+		case ESyncService:           // self-completed state
+		case EFinished:
+		default:
+			{
+			__ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ESyncServiceCompoundCancelUnexpectedState));
+			// something unexpected has happened. Complete the operation.
+			iNextStep = EFinished;
+			break;
+			}
+		} // end of switch (iNextStep)
+	Queue(aStatus);
+	}
+
+/**
+Provides the sync class with an array of messages that are not to be fetched according
+to the download rules. This function is called by the background sync controller if a
+fetch command is received by the server MTM is performing a background synchronise, and
+allows the sync operation to prevent attempts to simultaneously fetch the same message
+via multiple imap sessions.
+
+If message content fetch is already underway, then the array is passed to the 
+CImapCompoundCopyToLocal object for immediate checking. The array is then stored
+within this class and each subsequent copy to local operation is checked for matching
+message id's.
+
+Note that it is possible for a message ID to be remove from the passed entry selection
+in the case that that message is currently being fetched at the time of calling this
+function.
+
+@param aSelection - the selection of messages that should not be autofetched
+*/
+void CImapCompoundSyncService::RemoveFromSelectionL(CMsvEntrySelection& aDeleteSel)
+	{
+	if (iCurrentStep == ESyncFolderAutoFetch ||
+	    iCurrentStep == ESyncInboxAutoFetch  ||
+	    iCurrentStep == ESyncFolderAutoFetchCheck)
+		{
+		((CImapCompoundCopyToLocal*)iImapCompound)->RemoveFromSelection(aDeleteSel);
+		}
+
+	// keep a copy of this selection to prevent future fetch clashes.
+	delete iDoNotFetchSel;
+	iDoNotFetchSel = aDeleteSel.CopyL();
+	}
+
+/**
+Indicates that the compound operation should complete as soon as it is possible
+for the operation to be re-started without requiring a significant amount of
+repeated communication.
+The operation will be restarted by a call to ResumeOperationL()
+Overrides the base-class implementation.
+*/
+void CImapCompoundSyncService::StopForMigrate()
+	{
+	__LOG_TEXT(iSession->LogId(), "CImapCompoundSyncService::StopForMigrate()");
+	
+	if (iCurrentStep==ESyncFolderAutoFetch || iCurrentStep==ESyncInboxAutoFetch)
+		{
+		// if doing a download-rules based fetch, instruct it to stop.
+		if(iImapCompound)
+			{
+			iImapCompound->StopForMigrate();
+			}
+		}
+	iStopForMigrate = ETrue;
+	}
+