email/pop3andsmtpmtm/imapservermtm/src/IMAPSYNC.CPP
changeset 25 84d9eb65b26f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/imapservermtm/src/IMAPSYNC.CPP	Mon May 03 12:29:07 2010 +0300
@@ -0,0 +1,1604 @@
+// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// IMAP4 connect & synchronise operation control
+// This class deals with stepping through a synchronise operation - this
+// involves:
+// 1. Select inbox
+// 2. Perform any outstanding operations on inbox
+// 3. Synchronise inbox
+// 4. Synchronise folder list
+// 5. Build list of folders to synchronise, sorted by 'last sync' date
+// (oldest first), and outstanding operations (folders with outstanding
+// operations are first, whatever their date).
+// 6. Mirror subscription flags as necessary
+// 7. For each folder in the 'to synchronise' list...
+// i)   Select folder
+// ii)  Perform any outstanding operations
+// iii) Synchronise folder
+// 8. Reselect inbox
+// 
+//
+
+#include <e32base.h>
+#include <e32cons.h>
+#include <mentact.h>
+#include "impspan.h"
+#include "imapsess.h"
+#include "imapsync.h"
+#include "fldsync.h"
+#include <imapset.h>
+#include <offop.h>
+#include "imapcomp.h"
+#include "imapoffl.h"
+#include "impsutil.h"
+
+#ifdef _DEBUG
+#define DBG(a) iSession->LogText a
+#define PRINTING
+#else
+#define DBG(a)
+#undef PRINTING
+#endif
+
+// Inbox name for newly created one
+_LIT(KInbox, "Inbox");
+#ifdef PRINTING
+LOCAL_C const TText8* SynchroniseStateString(CImImap4Synchronise::TSynchroniseState aState)
+	{
+	switch (aState)
+		{
+	case CImImap4Synchronise::ESyncStateIdle:
+		return _S8("Idle");
+	case CImImap4Synchronise::EInboxSelect:
+		return _S8("InboxSelect");
+	case CImImap4Synchronise::EInboxPendingOps:
+		return _S8("InboxPendingOps");
+	case CImImap4Synchronise::EInboxSync:
+		return _S8("InboxSync");
+	case CImImap4Synchronise::ESynchroniseFolderTree:
+		return _S8("SynchroniseFolderTree");
+	case CImImap4Synchronise::ECheckRemoteSubscription:
+		return _S8("CheckRemoteSubscription");
+	case CImImap4Synchronise::EProcessRemoteSubscription:
+		return _S8("ProcessRemoteSubscription");
+	case CImImap4Synchronise::EUpdateRemoteSubscription:
+		return _S8("UpdateRemoteSubscription");
+	case CImImap4Synchronise::EFolderSelect:
+		return _S8("FolderSelect");
+	case CImImap4Synchronise::EFolderPendingOps:
+		return _S8("FolderPendingOps");
+	case CImImap4Synchronise::EFolderSynchronise:
+		return _S8("FolderSynchronise");
+	case CImImap4Synchronise::EInboxEarlyDeletes:
+		return _S8("InboxEarlyDeletes");
+	case CImImap4Synchronise::EFolderEarlyDeletes:
+		return _S8("FolderEarlyDeletes");
+	case CImImap4Synchronise::EFolderEarlyExpunge:
+		return _S8("FolderEarlyExpunge");
+	case CImImap4Synchronise::EInboxLateExpunge:
+		return _S8("InboxLateExpunge");
+	case CImImap4Synchronise::EFolderLateDeletes:
+		return _S8("FolderLateDeletes");
+	case CImImap4Synchronise::EFolderLateExpunge:
+		return _S8("FolderLateExpunge");
+#if 0
+	case CImImap4Synchronise::EInboxLateNewSync:
+		return _S8("InboxLateNewSync");
+	case CImImap4Synchronise::EFolderLateNewSync:
+		return _S8("FolderLateNewSync");
+#endif
+	case CImImap4Synchronise::EEndSelectInbox:
+		return _S8("EndSelectInbox");
+	case CImImap4Synchronise::EInboxLogMessage:
+		return _S8("InboxLogMessage");
+	case CImImap4Synchronise::EFolderLogMessage:
+  		return _S8("FolderLogMessage");
+	case CImImap4Synchronise::EStartIdle:
+		return _S8("StartIdle");
+	case CImImap4Synchronise::ESynchronise:
+		return _S8("Synchronise");	
+	case CImImap4Synchronise::ESynchroniseTree:
+		return _S8("SynchroniseTree");		
+	case CImImap4Synchronise::ESynchroniseDeletes:
+		return _S8("SynchroniseDeletes");		
+		}
+	return _S8("Unknown");
+	}
+#endif
+
+CImImap4Synchronise::CImImap4Synchronise()
+	: CMsgActive(1), iState(ESyncStateIdle)
+	{
+	__DECLARE_NAME(_S("CImImap4Synchronise"));
+	}
+
+CImImap4Synchronise::~CImImap4Synchronise()
+	{
+	// Global bits
+	delete iFolderList;
+	delete iFolderListDest;
+	delete iFolderSync;
+	delete iSubscribeList;
+	delete iUnsubscribeList;
+	delete iOutstandingOps;
+	delete iCompound;
+	delete iOutstandingMoveTypeDeletes;
+	delete iOutstandingLocalDeletes;
+
+	// We don't delete iSession as we don't own it, we were just
+	// passed it to use.  similarly iOffLineControl
+	}
+
+CImImap4Synchronise* CImImap4Synchronise::NewLC(CImImap4Session *aSession)
+	{
+	CImImap4Synchronise* self=new (ELeave) CImImap4Synchronise();
+	CleanupStack::PushL(self);
+
+	// Non-trivial constructor
+	self->ConstructL(aSession);
+	return self;
+	}
+
+CImImap4Synchronise* CImImap4Synchronise::NewL(CImImap4Session *aSession)
+	{
+	CImImap4Synchronise* self=NewLC(aSession);
+	CleanupStack::Pop();
+	return self;
+	}
+
+// The non-trivial constructor
+void CImImap4Synchronise::ConstructL(CImImap4Session *aSession)
+	{
+	// Save session
+	iSession=aSession;
+
+	// We're an active object...
+	CActiveScheduler::Add(this);
+
+	// One-off bits
+	iFolderList=new (ELeave) CArrayFixFlat<TImImap4SyncList>(16);
+	iFolderListDest=new (ELeave) CArrayFixFlat<TImImap4SyncList>(16);
+	iFolderSync=CImImap4FolderSync::NewL(iSession);
+	iSubscribeList=new (ELeave) CArrayFixFlat<TMsvId>(4);
+	iUnsubscribeList=new (ELeave) CArrayFixFlat<TMsvId>(4);
+	iOutstandingMoveTypeDeletes=new (ELeave) CArrayFixFlat<TMsvId>(4);
+	iOutstandingLocalDeletes=new (ELeave) CArrayFixFlat<TMsvId>(4);
+
+	// Compound operation
+	iCompound=CImImap4Compound::NewL(iSession);
+
+	// Make a thread-local timer
+	iCheckMailbox.CreateLocal();
+
+	iProgress.iType=EImap4SyncProgressType;
+
+	iIdleBeforeCommand = EFalse;
+	}
+
+void CImImap4Synchronise::SetOffLineControl(CImap4OffLineControl* aOffLineControl)
+	{
+	iOffLineControl = aOffLineControl;
+	}
+
+void CImImap4Synchronise::SetUtils(CImap4Utils* aUtils)
+	{
+	iUtils = aUtils;
+	}
+
+// Set the entry to use to talk to the server
+void CImImap4Synchronise::SetEntry(CMsvServerEntry* aEntry)
+	{
+	// Save it
+	iEntry=aEntry;
+	
+	// Tell compound about it
+	iCompound->SetEntry(iEntry);
+	}
+
+// Do setentry, leave if there is an error
+void CImImap4Synchronise::SetEntryL(const TMsvId aId)
+	{
+	User::LeaveIfError(iEntry->SetEntry(aId));
+	}
+
+// Change entry, leave if error
+void CImImap4Synchronise::ChangeEntryL(const TMsvEntry& aEntry)
+	{
+	User::LeaveIfError(iEntry->ChangeEntry(aEntry));
+	}
+
+// Change entry in bulk mode (i.e no index file commit. no notify),
+// leave if error
+void CImImap4Synchronise::ChangeEntryBulkL(const TMsvEntry& aEntry)
+	{
+	User::LeaveIfError(iEntry->ChangeEntryBulk(aEntry));
+	}
+// Get children, leave if error
+void CImImap4Synchronise::GetChildrenL(CMsvEntrySelection& aSelection)
+	{
+	User::LeaveIfError(iEntry->GetChildren(aSelection));
+	}
+
+// Given an id of a folder or the service then restore the offline
+// operation array out of it. Returns the number of operations in the
+// array
+TBool CImImap4Synchronise::RefreshOutstandingOpsL(TMsvId aId)
+	{
+	if (iOutstandingOps)
+		{
+		delete iOutstandingOps;
+		iOutstandingOps=NULL;
+		}
+	
+	iOutstandingOps = iOffLineControl->OffLineOpArrayL(aId);
+	iOutstandingOpsFolder = aId;
+
+	// reset the count
+	iProgress.iMsgsToDo = iOutstandingOps->CountOperations();
+	iProgress.iMsgsDone = 0;
+	
+	iMovedId = KMsvNullIndexEntryId;
+	iShadowId = KMsvNullIndexEntryId;
+
+	return iProgress.iMsgsToDo;
+	}
+
+// Called when async child completes with an error
+#ifdef PRINTING
+void CImImap4Synchronise::DoComplete(TInt& aStatus)
+#else
+void CImImap4Synchronise::DoComplete(TInt& /*aStatus*/)
+#endif
+	{
+	DBG((_L8("CImImap4Synchronise::DoComplete(state=%s, istatus=%d)"),
+		 SynchroniseStateString(iState),aStatus));
+
+	// No alteration of the error code
+	}
+
+// This routine sets up iShadowId which will be deleted when the
+// operation completes successfully
+void CImImap4Synchronise::DoOpL(const CImOffLineOperation& aOp)
+	{
+	iShadowId = iMovedId = KMsvNullIndexEntryId;
+	
+	// clean the disconnected op flags and ensure its visible and get
+	// an entry copy
+	SetEntryL(aOp.MessageId());
+	TMsvEmailEntry entry = iEntry->Entry();
+	entry.SetVisible(ETrue);
+	entry.SetDisconnectedOperation(ENoDisconnectedOperations);
+	ChangeEntryBulkL(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 = iOffLineControl->FindShadowIdL(aOp);
+
+		shadowOK = iShadowId != KMsvNullIndexEntryId &&
+			iEntry->SetEntry(iShadowId) == KErrNone &&
+			((TMsvEmailEntry)iEntry->Entry()).DisconnectedOperation() != EDisconnectedDeleteOperation;
+		}
+
+	iUtils->ClearLogMessage();
+	
+	// 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 )
+			{
+			iUtils->SetUpLogMessageL(aOp.MessageId());
+			SetActive();
+			iCompound->SyncCopyToLocalL(iStatus,aOp.MessageId(),aOp.TargetMessageId());
+			}
+		break;
+		
+	case CImOffLineOperation::EOffLineOpCopyFromLocal:
+		if (shadowOK)
+			{
+			SetActive();
+			iSession->Append(iStatus,aOp.MessageId(),aOp.TargetMessageId());
+			}
+		break;
+
+	case CImOffLineOperation::EOffLineOpCopyWithinService:
+		if (shadowOK)
+			{
+			SetActive();
+			iSession->Copy(iStatus,aOp.MessageId(),aOp.TargetMessageId(),EFalse);
+			}
+		break;
+		
+	case CImOffLineOperation::EOffLineOpMoveToLocal:
+		if (shadowOK)
+			{
+			iUtils->SetUpLogMessageL(aOp.MessageId());
+			SetActive();
+			iCompound->SyncCopyToLocalL(iStatus,aOp.MessageId(),aOp.TargetMessageId());
+			}
+		// even if the shadow has been removed we still want to delete
+		// the original
+		iMovedId=aOp.MessageId();
+		break;
+
+	case CImOffLineOperation::EOffLineOpMoveFromLocal:
+		if (shadowOK)
+			{
+			SetActive();
+			iSession->Append(iStatus,aOp.MessageId(),aOp.TargetMessageId());
+			}
+		// even if the shadow has been removed we still want to delete
+		// the original
+		iMovedId=aOp.MessageId();
+		break;
+
+	case CImOffLineOperation::EOffLineOpMoveWithinService:
+		if (shadowOK)
+			{
+			SetActive();
+			iSession->Copy(iStatus,aOp.MessageId(),aOp.TargetMessageId(),EFalse);
+			}
+		// even if the shadow has been removed we still want to delete
+		// the original, unless the folder itself has been removed
+		if (iEntry->SetEntry(aOp.TargetMessageId()) == KErrNone)
+			iMovedId=aOp.MessageId();
+		break;
+
+	case CImOffLineOperation::EOffLineOpMtmSpecific:
+		switch (aOp.MtmFunctionId())
+			{
+		case EFnOffLineOpPopulate:
+			{
+			TImap4GetMailOptions options;
+			TPckgC<TImap4GetMailOptions> package(options);
+			package.Set(aOp.MtmParameters());
+			
+			// Copy TImImap4GetMailOptions information into TImImap4GetPartialMailInfo
+			TImImap4GetPartialMailInfo imap4GetPartialMailInfo;	
+			imap4GetPartialMailInfo.iGetMailBodyParts = package();
+			// Set to default
+			imap4GetPartialMailInfo.iMaxEmailSize = KMaxTInt;
+			// Set the remaining members to default so that the server can check 
+			// if these are defaults, then this package is for TImImap4GetMailInfo
+			imap4GetPartialMailInfo.iTotalSizeLimit	= KMaxTInt;
+			imap4GetPartialMailInfo.iBodyTextSizeLimit = KMaxTInt;
+			imap4GetPartialMailInfo.iAttachmentSizeLimit = KMaxTInt;
+			imap4GetPartialMailInfo.iPartialMailOptions = ENoSizeLimits;
+			
+			TPckgBuf<TImImap4GetPartialMailInfo> partialPackage(imap4GetPartialMailInfo);
+	
+			iUtils->SetUpLogMessageL(aOp.MessageId());
+			SetActive();
+			iSession->FetchBody(iStatus,aOp.MessageId(),partialPackage());
+			break;
+			}
+		default:
+			break;
+			}
+		break;
+
+	case CImOffLineOperation::EOffLineOpDelete:
+	default:
+		break;
+		}
+	}
+
+void CImImap4Synchronise::MakeVisibleL(TMsvId aId)
+	{
+	DBG((_L8("  This folder isn't selectable, just making it visible")));
+
+	// Just make it visible
+	SetEntryL(aId);
+	TMsvEmailEntry mbcheck=iEntry->Entry();
+				
+	do
+		{
+		// Ensure visibility
+		if (!mbcheck.Visible())
+			{
+			mbcheck.SetVisible(ETrue);
+			ChangeEntryBulkL(mbcheck);
+			}
+
+		// Move up one
+		SetEntryL(mbcheck.Parent());
+		mbcheck=iEntry->Entry();
+		}
+	while(mbcheck.iType!=KUidMsvServiceEntry && mbcheck.Id()!=KMsvRootIndexEntryId);
+	}
+
+void CImImap4Synchronise::DonePendingOpL()
+	{
+	// if we've done one then...
+	if (iProgress.iMsgsDone != 0)
+		{
+		// if this was a move then append a delete
+		if (iMovedId != KMsvNullIndexEntryId)
+			{
+			SetEntryL(iMovedId);
+			if (iEntry->Entry().Parent() == iOutstandingOpsFolder)
+				{
+				DBG((_L8("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
+				DBG((_L8("Append LocalDelete for %x"), iMovedId));
+				iOutstandingLocalDeletes->AppendL(iMovedId);
+				}
+			}
+		
+		// delete the shadowid if there is one, ignore errors
+		if (iShadowId != KMsvNullIndexEntryId &&
+			iEntry->SetEntry(iShadowId) == KErrNone &&
+			iEntry->SetEntry(iEntry->Entry().Parent()) == KErrNone)
+			{
+			iEntry->DeleteEntry(iShadowId);
+			}
+
+		if (iUtils->SendLogMessageL(KErrNone,iStatus))
+			SetActive();
+		}
+	}
+
+TBool CImImap4Synchronise::NextPendingOpL()
+	{
+	TBool finished = EFalse;
+	
+	// Any operations in outstanding list?
+	if (iOutstandingOps->CountOperations())
+		{
+		DBG((_L8("Outstanding operations on this folder (%d)"),
+			 iOutstandingOps->CountOperations()));
+
+		// Fetch operation
+		CImOffLineOperation 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))
+			{
+			DBG((_L8("Reached delete op. Finished")));
+			finished = ETrue;
+			}
+		else
+			{
+			// remove from list and save back
+			iOutstandingOps->Delete(0);
+			iOffLineControl->SetOffLineOpArrayL(iOutstandingOpsFolder, *iOutstandingOps);
+
+			// and execute
+			DoOpL(thisop);
+			iProgress.iMsgsDone++;
+			}
+		}
+	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];
+			TBuf8<128> paramBuf(_L8(""));
+			CImOffLineOperation 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 (iPerformDeletes)
+				thisop.SetDelete(id);
+			else
+				thisop.SetMtmSpecificCommandL(id, EFnOffLineOpMoveDelete, 0, paramBuf);
+
+			iOutstandingOps->InsertOperationL(thisop, 0);
+			}
+
+		// if there were outstanding move type deletes then clear
+		// their array and save back the main outstanding ops list
+		if (iOutstandingMoveTypeDeletes->Count())
+			{
+			iOutstandingMoveTypeDeletes->Reset();
+
+			iOffLineControl->SetOffLineOpArrayL(iOutstandingOpsFolder, *iOutstandingOps);
+			}
+		}
+
+	return finished;
+	}
+
+TBool CImImap4Synchronise::MatchDeleteOp(const CImOffLineOperation& aOp , TBool aMoveDeletesOnly )
+	{
+	return (aOp.OpType() == CImOffLineOperation::EOffLineOpMtmSpecific && aOp.MtmFunctionId() == EFnOffLineOpMoveDelete) ||
+		(!aMoveDeletesOnly && aOp.OpType() == CImOffLineOperation::EOffLineOpDelete);
+	}
+
+TBool CImImap4Synchronise::ProcessPendingDeleteOpsL( TMsvId aFolder, TBool aMoveDeletesOnly )
+	{
+	TBool hadDeletes = EFalse;
+
+	DBG((_L8("CImImap4Synchronise::ProcessPendingDeleteOpsL: Folder %x aMoveDeletesOnly=%d"),
+		 aFolder, aMoveDeletesOnly));
+		
+	// get the current offline operations of this folder
+	if (RefreshOutstandingOpsL(aFolder))
+		{
+		// Fetch operation
+		CImOffLineOperation 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 (iEntry->SetEntry(thisop.MessageId()) == KErrNone)
+					{
+					// set its server deleted flag
+					TMsvEmailEntry entry=iEntry->Entry();
+					entry.SetDeletedIMAP4Flag(ETrue);
+					iEntry->ChangeEntry(entry);
+					}
+
+				// remove from the array and write back immediately
+				iOutstandingOps->Delete(0);
+				iOffLineControl->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;
+			}
+		}
+
+	return hadDeletes;
+	}
+
+TBool CImImap4Synchronise::ProcessPendingDeleteOpsListL( TBool aMoveDeletesOnly )
+	{
+	while (iProgress.iFoldersDone < iProgress.iFoldersToDo)
+		{
+		TImImap4SyncList next=(*iFolderList)[iProgress.iFoldersDone++];
+
+		if (ProcessPendingDeleteOpsL( next.iFolder, aMoveDeletesOnly ))
+			{
+			iSession->SelectL(iStatus, next.iFolder, ETrue);
+			SetActive();
+
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+// Called when async child completes with >=KErrNone
+void CImImap4Synchronise::DoRunL()
+	{
+	DBG((_L8("CImImap4Synchronise::DoRunL(istatus=%d)"), iStatus.Int()));
+	
+	while (!IsActive() && !DoRunLoopL())
+		{
+		// do nothing in the body of this
+		}
+	}
+
+TBool CImImap4Synchronise::DoRunLoopL()
+	{
+	DBG((_L8("CImImap4Synchronise::DoRunLoopL(state=%s)"),
+		SynchroniseStateString(iState)));
+
+	TBool done = EFalse;
+	
+	// DoRunL is only called
+	switch(iState)
+		{
+	case EInboxSelect:
+		iProgress.iState=TImap4SyncProgress::ESyncInbox;
+
+		// if select failed then skip past INBOX ops
+		if (iStatus.Int() == KErrIMAPNO)
+			{
+			iState = ESynchroniseFolderTree;
+			iProgress.iFoldersNotFound++;
+			}
+		else
+			{
+			// Deal with operations in the array
+			if (RefreshOutstandingOpsL(iInbox))
+				iState=EInboxPendingOps;
+			else
+				iState=EInboxSync;
+			}
+		break;
+
+	case EInboxPendingOps:
+		iProgress.iState=TImap4SyncProgress::EProcessingPendingOps;
+		
+		iState = EInboxLogMessage;
+		DonePendingOpL();
+		break;
+		
+	case EInboxLogMessage:
+		if (NextPendingOpL())
+			iState = EInboxSync;
+		else
+			iState = EInboxPendingOps;
+		break;
+
+	case EInboxSync:
+		iProgress.iState=TImap4SyncProgress::ESyncInbox;
+		
+		//Once pending operations are done on folder/s, sync the Inbox 
+		if(iPendingOpOnFolder)
+			{
+			// reset iPendingOpOnFolder flag
+			iPendingOpOnFolder=EFalse;
+			if (iSession->ImapIdleSupported() && iIdleBeforeCommand)
+				{
+				// change the state to EStartIdle, if ImapIdle supported
+				iState = EStartIdle;
+				}
+			else
+				{
+				// else  change the state to EEndSelectInbox
+				iState = EEndSelectInbox;
+				}
+			}
+		else
+			{
+			// After inbox sync, start folder tree sync
+			iState=ESynchroniseFolderTree;
+			}
+
+		// mark inbox as having been done
+		iProgress.iFoldersDone++;
+
+		// Start the sync of the current folder (ie, the inbox)
+		DBG((_L8("CImImap4Sync::DoRunLoopL(): Calling iSession->SynchroniseL()")));
+                iSession->SynchroniseL(iStatus,EFalse);
+		SetActive();
+		break;
+
+	case ESynchroniseFolderTree:
+		iProgress.iState=TImap4SyncProgress::ESyncFolderTree;
+
+		// After this, check the remote folder subscription if needed
+		iState=ECheckRemoteSubscription;
+		
+		// Update folder tree
+		iFolderSync->SetEntry(iEntry);
+		iFolderSync->SynchroniseTreeL(iStatus,iServiceId,iNewFoldersAreInvisible);
+		SetActive();
+		break;
+
+	case ECheckRemoteSubscription:
+		iProgress.iState=TImap4SyncProgress::ECheckRemoteSubscription;
+
+		// Check the remote subscription/build local folder list,
+		// dependent on strategy in use
+		iState=EProcessRemoteSubscription;
+
+		// Do we need to know what folders are subscribed remotely?
+		if (iSynchroniseStrategy==EUseLocal && 
+			(iSubscribeStrategy==EUpdateNeither || iSubscribeStrategy==EUpdateLocal))
+			{
+			// No we don't: just add the folders (done in this state)
+			}
+		else
+			{
+			// Update our list of remotely subscribed folders
+			iSession->LsubL(iStatus);
+			SetActive();
+			}
+		break;
+
+	case EProcessRemoteSubscription:
+		iProgress.iState=TImap4SyncProgress::ECheckRemoteSubscription;
+
+		// Build (from local folders) and sort the list
+		SortFolderListL();
+
+		iProgress.iFoldersDone=0;
+		iProgress.iFoldersToDo=iFolderList->Count();
+
+		// Any remote subscribing/unsubscribing to do?
+		if (iSubscribeList->Count() || iUnsubscribeList->Count())
+			{
+			// Yes, do them
+			iState=EUpdateRemoteSubscription;
+			}
+		else
+			{
+			// Start folder selection
+			iState=EFolderSelect;
+			}
+		break;
+
+	case EUpdateRemoteSubscription:
+		iProgress.iState=TImap4SyncProgress::EUpdateRemoteSubscription;
+		
+		// Any subscription to do?
+		if (iSubscribeList->Count())
+			{
+			// Take it off the head
+			TMsvId folder=(*iSubscribeList)[0];
+			iSubscribeList->Delete(0,1);
+
+			// Subscribe to it
+			iSession->RemoteSubscribeL(iStatus,folder,ETrue);
+			SetActive();
+			}
+		// ...or unsubscription?
+		else if (iUnsubscribeList->Count())
+			{
+			// Take it off the head
+			TMsvId folder=(*iUnsubscribeList)[0];
+			iUnsubscribeList->Delete(0,1);
+
+			// Unsubscribe from it
+			iSession->RemoteSubscribeL(iStatus,folder,EFalse);
+			SetActive();
+			}
+		else
+			{
+			// All done, select the first folder
+			iState=EFolderSelect;
+			}
+		break;
+
+	case EFolderSelect:
+		iProgress.iState=TImap4SyncProgress::ESyncOther;
+
+		// Anything to do...
+		while (iProgress.iFoldersDone < iProgress.iFoldersToDo)
+			{
+			// Select the next folder
+			// Get next folder to-do
+			TImImap4SyncList next=(*iFolderList)[iProgress.iFoldersDone++];
+
+			DBG((_L8("Next folder to select is %x"),next.iFolder));
+
+			// Is this a selectable folder?
+			if (next.iNotSelectable)
+				{
+				MakeVisibleL(next.iFolder);
+				}
+			else
+				{
+				// Select it
+                                DBG((_L8("CImImap4Sync::DoRunLoopL(): Calling iSession->SelectL()")));
+ 				iSession->SelectL(iStatus,next.iFolder,ETrue);
+				SetActive();
+
+				// get the current offline operations of this folder
+				if (RefreshOutstandingOpsL(next.iFolder))
+					{
+					iState=EFolderPendingOps;
+					// set iPendingOpOnFolder flag, according this flag will syncing the Inbox.
+  	  				iPendingOpOnFolder=ETrue;					
+					}
+				else
+					{
+					iState=EFolderSynchronise;
+					}
+
+				// stop loop
+				break;
+				}
+			}
+
+		// Run out of folders to sync?
+		if (iState == EFolderSelect)
+			iState = EInboxEarlyDeletes;
+		break;
+
+	case EFolderPendingOps:
+		iProgress.iState=TImap4SyncProgress::EProcessingPendingOps;
+
+		if (iStatus.Int() == KErrIMAPNO)
+			{
+			iState = EFolderSelect;
+			iProgress.iFoldersNotFound++;
+			}
+		else
+			{
+			iState = EFolderLogMessage;
+			DonePendingOpL();
+			}
+		break;
+
+	case EFolderLogMessage:
+		if (NextPendingOpL())
+			{
+			// Where a pending operation involves a folder that is also the currently
+			// selected mailbox, the IMAP session may have deselected the mailbox
+			// during the operation. Just in case this has happened, we need to select it
+			// again now.
+			DBG((_L8("CImImap4Sync::DoRunLoopL(): Calling select after pending ops")));
+			TImImap4SyncList next=(*iFolderList)[iProgress.iFoldersDone - 1];
+			iSession->SelectL(iStatus,next.iFolder,ETrue);
+			SetActive();
+
+			iState = EFolderSynchronise;
+			}
+		else
+			iState = EFolderPendingOps;
+		break;
+
+	case EFolderSynchronise:
+		iProgress.iState=TImap4SyncProgress::ESyncOther;
+
+		if (iStatus.Int() == KErrIMAPNO)
+			iProgress.iFoldersNotFound++;
+		else
+			{
+			iSession->SynchroniseL(iStatus,EFalse);
+			SetActive();
+			}
+
+		// back to select next folder
+		iState=EFolderSelect;
+		break;
+
+	case EInboxEarlyDeletes:
+		{
+		iProgress.iState=TImap4SyncProgress::EDeleting;
+
+		// get rid of the FromLocal message sources
+		for (TInt i = 0; i < iOutstandingLocalDeletes->Count(); i++)
+			{
+			TMsvId id = (*iOutstandingLocalDeletes)[i];
+			iSession->DeleteMessageL(id);
+			}
+
+		iOutstandingLocalDeletes->Reset();
+
+		// then do the inbox deletes
+		iProgress.iFoldersDone = 0;
+		if (ProcessPendingDeleteOpsL(iInbox, !iPerformDeletes))
+			{
+			iState = EFolderEarlyExpunge;
+
+			iSession->SelectL(iStatus, iInbox, ETrue);
+			SetActive();
+			}
+		else
+			{
+	        // DEF045009
+            if (iSession->ServiceSettings()->DeleteEmailsWhenDisconnecting())
+                 iState = EFolderLateDeletes;
+            else
+                 iState = EFolderEarlyDeletes;
+			}
+  		break;
+		}
+
+	case EFolderEarlyDeletes:
+		iProgress.iState=TImap4SyncProgress::EDeleting;
+
+		// if we are doing deletes on connection then all deletes will
+		// be marked Delete
+		if (ProcessPendingDeleteOpsListL(!iPerformDeletes))
+			{
+			iState = EFolderEarlyExpunge;
+			}
+		else
+			{
+			if (iSession->ImapIdleSupported() && iIdleBeforeCommand)
+				{
+				iState = EStartIdle;
+				}
+			else
+				{
+				iState = EEndSelectInbox;
+				}
+
+			// All done: reselect inbox r/w
+			iSession->SelectL(iStatus,iInbox,ETrue);
+			SetActive();
+			}
+		break;
+
+	case EFolderEarlyExpunge:
+		iProgress.iState=TImap4SyncProgress::EDeleting;
+
+		if (iStatus.Int() == KErrIMAPNO)
+			iProgress.iFoldersNotFound++;
+		else
+			{
+			// expunge the folder
+			iSession->Close(iStatus, ETrue);
+			SetActive();
+			}
+
+		// back to select next folder
+		iState=EFolderEarlyDeletes;
+		break;
+		
+	case EEndSelectInbox:
+		iProgress.iState=TImap4SyncProgress::EIdle;
+
+		// Finish sync operation
+		iState=ESyncStateIdle;
+		Complete(KErrNone);
+		done = ETrue;
+		break;
+
+	case EInboxLateExpunge:
+		iProgress.iState=TImap4SyncProgress::EDeleting;
+
+		if (iStatus.Int() == KErrIMAPNO)
+			iProgress.iFoldersNotFound++;
+		else
+			{
+			iSession->Close(iStatus, ETrue);
+			SetActive();
+			}
+
+		iState = EFolderLateDeletes;
+		iProgress.iFoldersDone = 0;
+		break;
+
+	case EFolderLateDeletes:
+		iProgress.iState=TImap4SyncProgress::EDeleting;
+
+		if (ProcessPendingDeleteOpsListL( EFalse ) )
+			{
+			iState = EFolderLateExpunge;
+			}			
+		else
+			{
+			iProgress.iState=TImap4SyncProgress::EIdle;
+			
+			if(iPendingOpOnFolder)
+				{
+				iSession->SelectL(iStatus,iInbox,ETrue);
+				iState=EInboxSync;
+				SetActive();
+				}
+			else if (iSession->ImapIdleSupported() && iIdleBeforeCommand)
+				{
+				iSession->SelectL(iStatus,iInbox,ETrue);
+				iState = EStartIdle;			
+				SetActive();
+				}
+			else
+				{
+				iState=ESyncStateIdle;
+				Complete(KErrNone);
+				done = ETrue;
+				}
+			}
+		break;
+
+	case EFolderLateExpunge:
+		iProgress.iState=TImap4SyncProgress::EDeleting;
+
+		if (iStatus.Int() == KErrIMAPNO)
+			{
+			iState = EFolderLateDeletes;
+			iProgress.iFoldersNotFound++;
+			}
+		else
+			{
+			iSession->Close(iStatus, ETrue);
+			SetActive();
+
+			iState = EFolderLateDeletes;
+			}
+		break;
+
+	case EStartIdle:
+		iState = EEndSelectInbox;
+
+		iSession->StartIdleL(iStatus);
+		SetActive();
+		break;
+
+	case ESynchronise:
+		// Synchronise with the inbox
+		iSession->SelectL(iStatus,iInbox,ETrue);
+		iState=EInboxSelect;
+		SetActive();
+		break;
+
+	case ESynchroniseTree:
+		if (iSession->ImapIdleSupported())
+			{
+			iState = EStartIdle;
+			}
+		else
+			{
+			iState = EEndSelectInbox;
+			}
+
+		iFolderSync->SetEntry(iEntry);
+		iFolderSync->SynchroniseTreeL(iStatus, iServiceId, iNewFoldersAreInvisible);
+		SetActive();
+		break;
+
+	case ESynchroniseDeletes:
+		if (ProcessPendingDeleteOpsL( iInbox, EFalse ))
+			{
+			iSession->SelectL(iStatus,iInbox,ETrue);
+
+			iState = EInboxLateExpunge;
+			SetActive();
+			}
+		else if (ProcessPendingDeleteOpsListL( EFalse ) )
+			{
+			iState = EFolderLateExpunge;
+			}
+		else
+			{
+			iProgress.iState=TImap4SyncProgress::EIdle;
+
+			iState=ESyncStateIdle;
+			Complete(KErrNone);
+			done = ETrue;
+			}
+		break;
+
+	default:
+		gPanic(EUnknownState);
+		done = ETrue;
+		break;
+		}
+
+	return done;
+	}
+
+void CImImap4Synchronise::GetInboxL()
+	{
+	// First of all, synchronise the inbox
+	CMsvEntrySelection *findinbox=new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL(findinbox);
+	SetEntryL(iServiceId);
+	GetChildrenL(*findinbox);
+	
+	// Find inbox	
+	TMsvId inboxid=0;
+	for(TInt a=0;a<findinbox->Count();a++)
+		{
+		SetEntryL((*findinbox)[a]);
+		if (iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0)
+			{
+			inboxid=(*findinbox)[a];
+			TMsvEmailEntry e=iEntry->Entry();
+
+			DBG((_L8("INBOX found with id %x (mailbox=%d)"),inboxid,e.Mailbox()));
+
+			// Mailbox flag not set? It always should be. This will 'repair' it.
+			if (!e.Mailbox())
+				{
+				// Set it
+				e.SetMailbox(ETrue);
+				User::LeaveIfError(iEntry->ChangeEntry(e));
+				}
+
+			break;
+			}
+		}
+
+	// Clean up
+	CleanupStack::PopAndDestroy();
+
+	// Not found/no entries?
+	if ((iInbox=inboxid)==0)
+		{
+		// Create an inbox entry below the service. This is a local-only
+		// creation as the server *WILL* have an inbox.
+		SetEntryL(iServiceId);
+		TMsvEmailEntry entry;
+
+		entry.iType=KUidMsvFolderEntry;
+		entry.iMtm=KUidMsgTypeIMAP4;
+		entry.iServiceId=iServiceId;
+		entry.SetMtmData1(0);
+		entry.SetMtmData2(0);
+		entry.SetMtmData3(0);
+		entry.iSize=0;
+		entry.SetUID(0);
+		entry.SetValidUID(EFalse);
+		entry.SetMailbox(ETrue);
+		entry.SetLocalSubscription(ETrue);
+		entry.SetComplete(ETrue);
+		entry.iDetails.Set(KInbox);
+		User::LeaveIfError(iEntry->CreateEntryBulk(entry));
+
+		// Set inbox ID
+		iInbox=entry.Id();
+
+		DBG((_L8("INBOX created with id %x"),iInbox));
+		}
+	}
+
+void CImImap4Synchronise::SynchroniseTreeL(TRequestStatus& aStatus, TMsvId aServiceId, TBool aNewFoldersAreInvisible)
+	{
+	DBG((_L8("CImImap4Synchronise::SynchroniseTreeL")));
+
+	__ASSERT_DEBUG(aServiceId!=KMsvNullIndexEntryId, gPanic(EInvalidService));
+
+	Queue(aStatus);
+
+	iServiceId = aServiceId;
+	iNewFoldersAreInvisible = aNewFoldersAreInvisible;
+
+	GetInboxL();
+	ResetStats();
+	iSession->SetInbox(iInbox);
+
+	iProgress.iState=TImap4SyncProgress::ESyncFolderTree;
+	
+	if (iSession->IsIdling())
+		{
+		iState=ESynchroniseTree;
+		iSession->SyncStopIdleL(iStatus);
+		SetActive();
+		}
+	else
+		{
+		iFolderSync->SetEntry(iEntry);
+		iFolderSync->SynchroniseTreeL(iStatus, iServiceId, aNewFoldersAreInvisible);
+
+		iState=EEndSelectInbox;
+		SetActive();
+		}
+	}
+
+void CImImap4Synchronise::ResetStats()
+	{
+	iProgress.iFoldersToDo = iProgress.iFoldersDone = 0;
+	iProgress.iMsgsToDo = iProgress.iMsgsDone = 0;
+	iProgress.iHeadersFetched = iProgress.iOrphanedFolders = 0;
+	iProgress.iNewFolders = iProgress.iOrphanedMessages = 0;
+	iProgress.iRemoteMessagesDeleteTagged = 0;
+
+	iProgress.iMessagesFetchedOK = iProgress.iMessagePartsFetchedOK = 0;
+	iProgress.iMessagePartsNotFound = iProgress.iFoldersNotFound = 0;
+	iProgress.iErrorCode = KErrNone;
+
+	// also reset the counts in the ImapSession
+	iSession->ResetStats();
+	iFolderSync->ResetStats();
+	}
+
+void CImImap4Synchronise::SynchroniseDeletesL(TRequestStatus& aStatus, TMsvId aServiceId)
+	{
+	DBG((_L8("CImImap4Synchronise::SynchroniseDeletesL")));
+
+	__ASSERT_DEBUG(aServiceId!=KMsvNullIndexEntryId, gPanic(EInvalidService));
+
+	iServiceId = aServiceId;
+
+	ResetStats();
+
+	// Get the list of folders which need to be synchronized
+	iProgress.iFoldersToDo=iFolderList->Count();
+
+	GetInboxL();
+	iSession->SetInbox(iInbox);
+	
+	Queue(aStatus);
+
+	iProgress.iState=TImap4SyncProgress::EDeleting;
+	
+	if (iSession->IsIdling())
+		{
+		iState=ESynchroniseDeletes;
+
+		iSession->SyncStopIdleL(iStatus);
+		SetActive();
+		}
+	else
+		{
+		if (ProcessPendingDeleteOpsL( iInbox, EFalse ))
+			{
+			iSession->SelectL(iStatus,iInbox,ETrue);
+
+			iState = EInboxLateExpunge;
+			SetActive();
+			}
+		else if (ProcessPendingDeleteOpsListL( EFalse ) )
+			{
+			iState = EFolderLateExpunge;
+			}
+		else
+			{
+			iProgress.iState=TImap4SyncProgress::EIdle;
+			Complete(KErrNone);
+			}
+		}
+	}
+
+// Synchronise mirror tree with remote server
+void CImImap4Synchronise::SynchroniseL(TRequestStatus& aStatus, TMsvId aService, TBool aNewInvisible, TBool aPerformDeletes, TBool aConnectAndSync)
+	{
+	DBG((_L8("CImImap4Synchronise::SynchroniseL(service=%x,newInvisible=%d,performDeletes=%d)"),
+		 aService, aNewInvisible, aPerformDeletes));
+
+	Queue(aStatus);
+
+	// Note where this list came from
+	iServiceId=aService;
+	iFolderId=iServiceId;
+
+	// Note the invisibility
+	iNewFoldersAreInvisible=aNewInvisible;
+	iPerformDeletes = aPerformDeletes;
+
+	iIdleBeforeCommand = aConnectAndSync;
+	
+	// set iPendingOpOnFolder flag
+	iPendingOpOnFolder=EFalse;
+	
+	// Get the synchronise settings
+	iSynchroniseStrategy=iSession->ServiceSettings()->Synchronise();
+	iSubscribeStrategy=iSession->ServiceSettings()->Subscribe();
+
+	DBG((_L8("iSynchroniseStrategy=%d, iSubscribeStrategy=%d"),
+		iSynchroniseStrategy,iSubscribeStrategy));
+
+	GetInboxL();
+	ResetStats();
+	iSession->SetInbox(iInbox);
+
+	// Reset stats: 1 folder to do (inbox)
+	iProgress.iFoldersToDo=1;
+	iProgress.iState=TImap4SyncProgress::ESyncInbox;
+	
+	if (iSession->IsIdling())
+		{
+		iIdleBeforeCommand=ETrue;
+		iState=ESynchronise;
+
+		iSession->SyncStopIdleL(iStatus);
+		SetActive();
+		}
+	else
+		{
+		// Synchronise with the inbox
+		iSession->SelectL(iStatus,iInbox,ETrue);
+		iState=EInboxSelect;
+		SetActive();
+		}
+	}
+
+void CImImap4Synchronise::SortFolderListL()
+	{
+	// Clear list of folders to sync, and subscribe/unsubscribe lists
+	iFolderList->Reset();
+	iFolderListDest->Reset();
+	iSubscribeList->Reset();
+	iUnsubscribeList->Reset();
+
+	// Add folders
+	AddLocalFoldersL(iServiceId);
+
+	// Sort it by last-sync date (oldest first)
+	TKeyArrayFix timeKey(_FOFF(TImImap4SyncList,iLastSync),ECmpTInt64);
+
+	// Perform the sort on each of the lists separately
+	iFolderList->Sort(timeKey);
+	iFolderListDest->Sort(timeKey);
+
+	// Merge the two lists and clear the second one
+	for(TInt i=0; i<iFolderListDest->Count(); i++) 
+		iFolderList->AppendL((*iFolderListDest)[i]);
+	iFolderListDest->Reset();
+	}
+
+void CImImap4Synchronise::AddIfNotThereL(TMsvId aFolder, CArrayFix<TImImap4SyncList>* aFolderList)
+	{
+	// first see if we already have this folder and return if we do
+	for (TInt i=0; i < aFolderList->Count(); i++)
+		{
+		if ((*aFolderList)[i].iFolder == aFolder)
+			return;
+		}
+
+	// don't add the inbox or service
+	if (aFolder == iInbox || aFolder == iServiceId)
+		return;
+	
+	// visit folder
+	SetEntryL(aFolder);
+	TMsvEmailEntry entry=iEntry->Entry();
+
+	// not there so add it
+	TImImap4SyncList add;
+	add.iFolder=aFolder;
+	add.iNotSelectable=EFalse;
+	add.iLastSync=entry.iDate;
+	// previously we adjusted the date of source folders to bring them
+	// earlier in the list. No need to do this now as they
+	// automatically appear before subscriptions/destinations
+
+	// If this is a \NoSelect marked folder, then we need to note this, so
+	// that at sync time we just make the folder visible, as opposed to
+	// trying to select it
+	if (!entry.Mailbox())
+		add.iNotSelectable=ETrue;
+	
+	DBG((_L8("Adding folder '%S' to synchronise todo list (mailbox=%d)"),
+					   &entry.iDetails, entry.Mailbox()?1:0));
+
+	aFolderList->AppendL(add);
+	}
+
+// Add local (subscribed) folders to the 'to do' iFolderList. Recursive.
+void CImImap4Synchronise::AddLocalFoldersL(const TMsvId aFolder)
+	{
+	// Select the entry
+	SetEntryL(aFolder);
+	TMsvEmailEntry entry=iEntry->Entry();
+
+	// Is it actually a folder or service? If not, ignore it. 
+	if (entry.iType!=KUidMsvServiceEntry &&
+		entry.iType!=KUidMsvFolderEntry)
+		return;
+
+	DBG((_L8("CImImap4Synchronise::AddLocalFolders(%x), iSync=%d iSubs=%d"),
+					   aFolder,iSynchroniseStrategy,iSubscribeStrategy));
+
+	// What we do now depends on the strategy
+	TBool addthisone=EFalse;
+	switch(iSynchroniseStrategy)
+		{
+	case EUseLocal:
+		// Is it locally subscribed?
+		if (entry.LocalSubscription())
+			addthisone=ETrue;
+		break;
+
+	case EUseRemote:
+		// Is it remotely subscribed?
+		if (entry.Subscribed())
+			addthisone=ETrue;
+		break;
+
+	case EUseCombination:
+		// Either will do
+		if (entry.LocalSubscription() || entry.Subscribed())
+			addthisone=ETrue;
+		break;
+		}
+
+	// Any outstanding operations?
+	RefreshOutstandingOpsL(aFolder);
+
+	if (!entry.Orphan())
+		{
+		// check each and add the folder and destination folders. Add
+		// source folders to one list, destination and just subscribed
+		// ones to the second list, the two lists are merged
+		// together before use
+		if (iOutstandingOps->CountOperations())
+			{
+			AddIfNotThereL(aFolder, iFolderList);
+		
+			for(TInt a=0; a<iOutstandingOps->CountOperations(); a++)
+				{
+				TMsvId dest = iOutstandingOps->Operation(a).TargetMessageId();
+				if (dest != KMsvNullIndexEntryId)
+					AddIfNotThereL(dest, iFolderListDest);
+				}
+			}
+
+		// add subscribed afterwards
+		if (addthisone)
+			AddIfNotThereL(aFolder, iFolderListDest);
+		}	
+
+	// Any children?
+	CMsvEntrySelection *children=new (ELeave) CMsvEntrySelection;
+	CleanupStack::PushL(children);
+	GetChildrenL(*children);
+	TInt noofchildren=children->Count();
+
+	if (!entry.Orphan() && aFolder != iInbox && aFolder != iServiceId)
+		{
+		if (addthisone)
+			{
+			// Do updating
+			switch(iSubscribeStrategy)
+				{
+			case EUpdateNeither:
+				break;
+
+			case EUpdateLocal:
+			case EUpdateBoth:
+				if (!entry.LocalSubscription())
+					{
+					// Update local subscription flag
+					entry.SetLocalSubscription(ETrue);
+					SetEntryL(aFolder);
+					ChangeEntryBulkL(entry);
+					}
+
+				// Doing both?
+				if (iSubscribeStrategy!=EUpdateBoth)
+					break;
+
+				// Fall through...
+
+			case EUpdateRemote:
+				if (!entry.Subscribed())
+					{
+					// Queue subscribe command
+					iSubscribeList->AppendL(aFolder);
+
+					DBG((_L8("Adding folder '%S' to remote subscribe todo list"),&entry.iDetails));
+					}
+				break;
+				}
+			}
+		else // if (!addthisone)
+			{
+			// Do updating
+			switch(iSubscribeStrategy)
+				{
+			case EUpdateNeither:
+				break;
+
+			case EUpdateLocal:
+			case EUpdateBoth:
+				if (entry.LocalSubscription())
+					{
+					// Update local subscription flag
+					entry.SetLocalSubscription(EFalse);
+					SetEntryL(aFolder);
+					ChangeEntryBulkL(entry);
+					}
+
+				// Doing both?
+				if (iSubscribeStrategy!=EUpdateBoth)
+					break;
+
+				// Fall through...
+
+			case EUpdateRemote:
+				if (entry.Subscribed())
+					{
+					// Queue subscribe command
+					iUnsubscribeList->AppendL(aFolder);
+
+					DBG((_L8("Adding folder '%S' to remote unsubscribe todo list"),&entry.iDetails));
+					}
+				break;
+				}
+
+			// This folder is not subscribed, but has children. 
+			// If any children are messages, then delete.
+			if (noofchildren)
+				{
+				DBG((_L8("Checking unsubscribed folder (%S) for messages"),&entry.iDetails));
+
+				// This folder is not subscribed to, so check if it has any messages.
+				// Do each in turn
+				TInt child = children->Count();
+				while (child--)
+					{
+					SetEntryL((*children)[child]);
+					TMsvEmailEntry entry=iEntry->Entry();
+
+					// Is it a message?
+					if (entry.iType==KUidMsvMessageEntry)
+						{
+						DBG((_L8("Deleting unsubscribed folder message(%x)"),(*children)[child]));
+
+						// Yup its a message - delete it!
+						iSession->DeleteMessageL((*children)[child]);
+						}
+					}
+
+				// If we've some children then get the children list
+				// again
+				SetEntryL(aFolder);
+				GetChildrenL(*children);
+				noofchildren=children->Count();
+				}
+			}
+		}
+	
+	// Any children?
+	if (noofchildren)
+		{
+		// Do each in turn
+		for(TInt child=0;child<children->Count();child++)
+			AddLocalFoldersL((*children)[child]);
+		}
+
+	CleanupStack::PopAndDestroy(children);
+	}
+
+// Report progress
+TImap4SyncProgress CImImap4Synchronise::Progress()
+	{
+	TImap4SyncProgress progress = iProgress;
+	
+	// Make sure we don't get stuck in sync folder mode. Fix for DEF053846.
+	if (iProgress.iState == TImap4SyncProgress::ESyncFolderTree && !iFolderSync->IsActive())
+		{
+		iProgress.iState = TImap4SyncProgress::EIdle;
+		}
+	
+	// get the info from the folder sync
+	// currently OrphanedFolders and NewFolders
+	if (iState >= ESynchroniseFolderTree)
+		iFolderSync->IncProgress(progress);
+
+	// get info from the session (note overwrites the msgs counts).
+	iSession->IncSyncStats(progress);
+
+	// when synchronising (ie getting new headers) then we use the
+	// counts from the session otherwise use our own count
+	if (iProgress.iState != TImap4SyncProgress::ESyncOther &&
+		iProgress.iState != TImap4SyncProgress::ESyncInbox )
+		{
+		progress.iMsgsDone=iProgress.iMsgsDone;
+		progress.iMsgsToDo=iProgress.iMsgsToDo;
+		}
+
+	// Return the modified progress
+	return(progress);
+	}
+
+// Called when parent wants to cancel current operation
+void CImImap4Synchronise::DoCancel()
+	{
+	DBG((_L8("CImImap4Synchronise::DoCancel() called. Cancelling session")));
+
+	// Cancel any outstanding ops
+	iFolderSync->Cancel();
+	iCompound->Cancel();
+	iSession->Cancel();
+
+	// Not doing nuffink
+	iState=ESyncStateIdle;
+
+	// Parent
+	CMsgActive::DoCancel();
+	}