email/pop3andsmtpmtm/popservermtm/src/POPSOFFL.CPP
changeset 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/popservermtm/src/POPSOFFL.CPP	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1024 @@
+
+// Copyright (c) 2007-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 <msventry.h>
+#include <offop.h>
+#include "POPSOFFL.H"
+#include "popstran.h"
+#include "POPS.PAN"
+
+// API
+// ===
+// The CImPop3OfflineOperationFinder class is used to find and order offline
+// operations. It can also be used to obtain progress information eg. the
+// number of operations queued of a particular type.
+
+// Offline operations are retrieved by first calling the asynchronous
+// FindFirstL function. After completetion call the OperationFound function
+// to find out whether or not an offline operation has been found, if it has
+// then the OfflineOperation function should be called to retrive it.
+//
+// The FindNextL function should be called to find other offline operations
+// (in conjunction with the OperationFound and OfflineOperations functions.)
+// It should be noted that the FindNextL function is synchronous.
+
+// The DeleteCurrentOperationL() function deletes the current entry from the
+// message store.
+
+// OperationDetails returns TOperationDetails which can be used for providing
+// progress information.
+
+
+// Implementaiton
+// ==============
+// The asynchronous FindFirstL function builds up a list of
+// CImOffLineOperationArray objects, each array holds a list of operations
+// of a particular type.
+
+// The operations are stored this way to facilitate easy ordering based on
+// operation type as well as being useful for generating progress
+// information.
+
+// The operations are retrieved by first visiting each message under the
+// service, this potentially slow operation is handled by the asynchronous
+// function.
+
+CImPop3OfflineOperationFinder* CImPop3OfflineOperationFinder::NewL(CMsvServerEntry& aEntry)
+	{
+	CImPop3OfflineOperationFinder* self = NewLC(aEntry);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+CImPop3OfflineOperationFinder* CImPop3OfflineOperationFinder::NewLC(CMsvServerEntry& aEntry)
+	{
+	CImPop3OfflineOperationFinder* self = new (ELeave) CImPop3OfflineOperationFinder(aEntry);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+void CImPop3OfflineOperationFinder::FindFirstL(TMsvId aServiceId, TBool aQuitting, TRequestStatus &aStatus)
+// Asynchronous function for finding all the offline operations and setting
+// the current operation (iOperationArrayIndex and iOperationIndex) to point
+// to the first one.
+
+// Use OperationFound(), OfflineOperation() and OperationDetails() to access
+// the results of this search.
+	{
+	TInt index = 0;
+	// Delete each of the CImOffLineOperationArrays
+	while (index < iOfflineOperations->Count())
+		{
+		delete (*iOfflineOperations)[index];
+		index++;
+		}
+	iOfflineOperations->Reset();
+
+	iServiceId = aServiceId;
+	iState = EPopsOffOpLookingForMessages;
+	iQuitting = aQuitting;
+
+	if (iChildMessages)
+		{
+		delete iChildMessages;
+		iChildMessages = 0;
+		}
+
+	iChildMessages = new (ELeave) CMsvEntrySelection();
+
+	User::LeaveIfError(iEntry.GetChildren(*iChildMessages));
+	iMessageIndex = 0;
+	Queue(aStatus);
+	if (iChildMessages->Count())
+		{
+		// Check the first message for offline operations.
+		CheckNextChildMessageL();
+		}
+	else
+		{
+		// There are no messages found so complete and stop searching here.
+		delete iChildMessages;
+		iChildMessages = 0;
+		Complete(KErrNone);
+		}
+	}
+
+void CImPop3OfflineOperationFinder::CheckNextChildMessageL()
+// Check the current message (iMessageIndex) and append any offline operations
+// to the offline operations list.
+	{
+	if (iMessageIndex < iChildMessages->Count())
+		{
+		iEntry.SetEntry((*iChildMessages)[iMessageIndex]);
+		TMsvEmailEntry entry = iEntry.Entry();
+		if (entry.DisconnectedOperation() != ENoDisconnectedOperations)
+			// If the entry has offline operations queued then add it to the list.
+			{
+			AppendOfflineOpsL();
+			}
+		}
+
+	SetActive();
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	}
+
+void CImPop3OfflineOperationFinder::AppendOfflineOpsL()
+// This private help function appends the offline operations associated with the
+// current message to the appropriate offline operations array.
+// The array to which it is added depends on the operation type. If there
+// is currently no array for this operation type then one is created.
+	{
+	// Get the offline operations for the current message.
+	CImOffLineOperationArray* localOfflineOpArray = CImOffLineOperationArray::NewL();
+	CleanupStack::PushL(localOfflineOpArray);
+	CImOffLineArrayStore* offlineArrayStore = new (ELeave) CImOffLineArrayStore(*localOfflineOpArray);
+	CleanupStack::PushL(offlineArrayStore);
+	CMsvStore* messageStore = iEntry.ReadStoreL();
+	CleanupStack::PushL(messageStore);
+	offlineArrayStore->RestoreL(*messageStore);
+	
+	// Append the local offline operations to the correct operations list.
+	TInt opCounter;
+	TInt offlineOps = localOfflineOpArray->CountOperations();
+	const CImOffLineOperation* newOperation;
+	for (opCounter = 0; opCounter < offlineOps; opCounter++)
+		{
+		newOperation = &localOfflineOpArray->Operation(opCounter);
+		// Append newOperation to the correct list as each list holds a different operation type.
+
+		// Find the appropriate offline operation list.
+		CImOffLineOperationArray* operationArray = 0;
+		if (iOfflineOperations->Count() == 0)
+			{
+			// If there are no arrays then create the first one.
+			operationArray = CImOffLineOperationArray::NewL();
+			CleanupStack::PushL(operationArray);
+			iOfflineOperations->AppendL(operationArray, sizeof(void*));
+			CleanupStack::Pop(operationArray);
+			}
+		else
+			{
+			TBool found = EFalse;
+			TInt index = 0;
+			// Search the different arrays for one that contains the correct operation types.
+			while ((!found) && (index < iOfflineOperations->Count()))
+				{
+				if ((*iOfflineOperations)[index]->CountOperations())
+					{
+					if (((*iOfflineOperations)[index]->Operation(0)).OpType() == newOperation->OpType())
+						{
+						found = ETrue;
+						operationArray = (*iOfflineOperations)[index];
+						}
+					}
+				index++;
+				}
+
+			if (!found)
+				{
+				// If there are no appropriate arrays then create a new one.
+				operationArray = CImOffLineOperationArray::NewL();
+				CleanupStack::PushL(operationArray);
+				iOfflineOperations->AppendL(operationArray, sizeof(void*));
+				CleanupStack::Pop(operationArray);
+				}
+			}
+
+		operationArray->AppendOperationL(*newOperation);
+		}
+
+	CleanupStack::PopAndDestroy(3); // offlineArrayStore, messageStore, localOfflineOperationArray
+	}
+
+
+TBool CImPop3OfflineOperationFinder::AppropriateOperation(const CImOffLineOperation& aOperation) const
+	{
+// Returns true if the operation should be run at this point.
+// Delete operations should be run at the end of the session, all others at the start.
+	return (aOperation.OpType()==CImOffLineOperation::EOffLineOpDelete) ? iQuitting : (!iQuitting);
+	}
+
+
+void CImPop3OfflineOperationFinder::FindNext()
+// Finds the next offline operations.
+// The results of this function call are valid as soon as the function returns.
+	{
+	TBool go = ETrue;
+
+	CImOffLineOperationArray* operationArray = 0;
+	iOperationIndex++;
+	while (go)
+		{
+		go = ETrue;
+
+		if (iOfflineOperations->Count())
+			{
+			operationArray = (*iOfflineOperations)[iOperationArrayIndex];
+			}
+		else
+			{
+			operationArray = 0;
+			}
+
+		if (operationArray == 0)
+			{
+			go = EFalse;
+			}
+		else if (iOperationIndex == operationArray->CountOperations())
+		// If there are no more operations in this array then move on to the next one.
+			{
+			iOperationArrayIndex++;
+			iOperationIndex = 0;
+			}
+		else if (!AppropriateOperation(operationArray->Operation(0)))
+		// If the operations in this array aren't of an appropriate type then move on to the next one.
+			{
+			iOperationArrayIndex++;
+			iOperationIndex = 0;
+			}
+		else
+		// An appropriate operation has been found.
+			{
+			go = EFalse;
+			}
+
+		if (iOperationArrayIndex == iOfflineOperations->Count())
+		// If there are no more operation arrays then don't carry on looking.
+			{
+			go = EFalse;
+			}
+		}
+
+	// Set the operaitons details for later.
+	if (OperationFound())
+		{
+		iOperationDetails.iOpType = OfflineOperation().OpType();
+		iOperationDetails.iOperationNumber = iOperationIndex;
+		iOperationDetails.iOperationsOfType = ((*iOfflineOperations)[iOperationArrayIndex])->CountOperations();
+		}
+	else
+		{
+		iOperationDetails.iOpType = CImOffLineOperation::EOffLineOpNone;
+		}
+	}
+
+TBool CImPop3OfflineOperationFinder::OperationFound() const
+// Returns true if an operation has been found.
+	{
+	return (iOperationArrayIndex < (iOfflineOperations->Count()));
+	}
+
+const CImOffLineOperation& CImPop3OfflineOperationFinder::OfflineOperation() const
+// Returns the current offline operations.
+	{
+	return ((*iOfflineOperations)[iOperationArrayIndex])->Operation(iOperationIndex);
+	}
+
+const CImPop3OfflineOperationFinder::TOperationDetails& CImPop3OfflineOperationFinder::OperationDetails() const
+// Returns the details for the current operation.
+	{
+	return iOperationDetails;
+	}
+
+CImPop3OfflineOperationFinder::~CImPop3OfflineOperationFinder()
+	{
+	delete iChildMessages;
+	TInt index = 0;
+	if (iOfflineOperations)
+		{
+		// Delete each of the CImOffLineOperationArrays
+		while (index < iOfflineOperations->Count())
+			{
+			delete (*iOfflineOperations)[index];
+			index++;
+			}
+		delete iOfflineOperations;
+		}
+	}
+
+void CImPop3OfflineOperationFinder::DoRunL()
+	{
+	++iMessageIndex;
+	if (iMessageIndex < iChildMessages->Count())
+		// If there are more messages then check them for offline operations.
+		{
+		CheckNextChildMessageL();
+		}
+	else
+		// If there are no more messages then go back and find all the offline operations.
+		{
+		delete iChildMessages;
+		iChildMessages = 0;
+
+		// Move the offline copy opertion array to the front of the list.
+		TInt index = 0;
+		TBool go = ETrue;
+		while ((index < iOfflineOperations->Count())
+				&& go)
+			{
+			CImOffLineOperation::TOffLineOpType opType = ((*iOfflineOperations)[index]->Operation(0)).OpType();
+			if (opType == CImOffLineOperation::EOffLineOpCopyToLocal)
+				{
+				// Move the offline copy operation array to the front of the list.
+				go = EFalse;
+				CImOffLineOperationArray* populateOperationList = (*iOfflineOperations)[index];
+				iOfflineOperations->Delete(index);
+				iOfflineOperations->InsertL(index, populateOperationList, sizeof(void*));
+				}
+			else
+				{
+				index++;
+				}
+			}
+
+		// Move the offline populate opertion array to the front of the list.
+		index = 0;
+		go = ETrue;
+		while ((index < iOfflineOperations->Count())
+				&& go)
+			{
+			CImOffLineOperation::TOffLineOpType opType = ((*iOfflineOperations)[index]->Operation(0)).OpType();
+			if (opType == CImOffLineOperation::EOffLineOpCopyWithinService)
+				{
+				// Move the offline populate operation array to the front of the list.
+				go = EFalse;
+				CImOffLineOperationArray* populateOperationList = (*iOfflineOperations)[index];
+				iOfflineOperations->Delete(index);
+				iOfflineOperations->InsertL(index, populateOperationList, sizeof(void*));
+				}
+			else
+				{
+				index++;
+				}
+			}
+
+		// Set the iOperationIndex to the first appropriate operation.
+		iOperationArrayIndex = 0;
+		iOperationIndex = -1;
+		FindNext();
+
+		Complete(KErrNone);
+		}
+	}
+
+void CImPop3OfflineOperationFinder::DoCancel()
+	{
+	CMsgActive::DoCancel();
+	}
+
+void CImPop3OfflineOperationFinder::DoComplete()
+	{
+
+	}
+
+void CImPop3OfflineOperationFinder::ConstructL()
+	{
+	CActiveScheduler::Add(this);
+	iOfflineOperations = new (ELeave) CArrayVarFlat<CImOffLineOperationArray*>(3);
+	}
+
+CImPop3OfflineOperationFinder::CImPop3OfflineOperationFinder(CMsvServerEntry& aEntry) : CMsgActive(EPriorityStandard), iEntry(aEntry)
+	{
+
+	}
+
+
+
+
+
+
+// API
+// ===
+// The CImPop3SetOfflineOps class provides a means of asynchronously adding
+// offline operations to the message stores of the appropriate messages.
+
+// Use the AddOfflineOperationL to add the offline operations.
+
+
+CImPop3SetOfflineOps* CImPop3SetOfflineOps::NewL(CMsvServerEntry& aEntry)
+	{
+	CImPop3SetOfflineOps* self = NewLC(aEntry);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+CImPop3SetOfflineOps* CImPop3SetOfflineOps::NewLC(CMsvServerEntry& aEntry)
+	{
+	CImPop3SetOfflineOps* self = new (ELeave) CImPop3SetOfflineOps(aEntry);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CActiveScheduler::Add(self);
+	//	ConstructL();
+	return self;
+	}
+
+void CImPop3SetOfflineOps::AddOfflineOperationL(const CMsvEntrySelection *aMessageSelection,
+							CImOffLineOperation::TOffLineOpType aOperationType,
+							TMsvId aTargetFolderId,
+							TRequestStatus& aStatus)
+// Adds the aOperationType operation to each message in aMessageSelection.
+// The aTargetFolderId is not used for some operation types, eg. deleting.
+	{
+	iState = EAddingOfflineOperations;
+
+	iMessageCounter = 0;
+	iOperationType = aOperationType;
+	iDestinationEntryId = aTargetFolderId;
+
+	delete iSourceMessages;
+	iSourceMessages = 0;
+
+	delete iMessagesToCopyLocally;
+	iMessagesToCopyLocally = new (ELeave) CMsvEntrySelection;
+
+	iSourceMessages = aMessageSelection->CopyL();
+	
+	TInt count = iSourceMessages->Count();
+	iOfflineOperationArrayFlag.Reset();
+	
+	for(TInt i=0; i< count; ++i )
+		{
+		iOfflineOperationArrayFlag.AppendL(EFalse);	
+		}
+	
+	
+	Queue(aStatus);
+
+	AddOfflineOperationL();
+	}
+
+
+void CImPop3SetOfflineOps::CancelOfflineOperationsL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
+// Cancel all pending operations on each message in aSelection.
+	{
+	iState = ECancellingOfflineOperations;
+	iMessageCounter = 0;
+	
+	delete iSourceMessages;
+	iSourceMessages = 0;
+
+	iSourceMessages = aSelection.CopyL();
+	Queue(aStatus);
+
+	CancelOfflineOperationL();
+	}
+
+// Resumes a cancelled "Cancel Offline Operations" operation.
+void CImPop3SetOfflineOps::ResumeCancelOfflineOperationsL(TRequestStatus& aStatus)
+	{
+	Queue(aStatus);
+	CancelOfflineOperationL();
+	}
+
+void CImPop3SetOfflineOps::AddOfflineOperationL()
+	{
+	// Set the current entry, ready for the operation to be attached to it.
+	User::LeaveIfError(iEntry.SetEntry((*iSourceMessages)[iMessageCounter]));
+
+	iModifiedOperationType = iOperationType;
+
+	if ((iEntry.Entry()).Complete())
+	// Any messages that have already been populated can be copied locally without
+	// the need to be online.
+	// This is done after any offline operations have been added but they are
+	// saved for later here:
+		{
+		if (iOperationType == CImOffLineOperation::EOffLineOpCopyToLocal)
+			{
+			iMessagesToCopyLocally->AppendL(iEntry.Entry().Id());
+			iModifiedOperationType = CImOffLineOperation::EOffLineOpNone;
+			}
+
+		if (iOperationType == CImOffLineOperation::EOffLineOpMoveToLocal)
+			{
+			iMessagesToCopyLocally->AppendL(iEntry.Entry().Id());
+			iModifiedOperationType = CImOffLineOperation::EOffLineOpDelete;
+			}
+		}
+
+	if (iModifiedOperationType == CImOffLineOperation::EOffLineOpNone)
+		{
+		iStatus = KRequestPending;
+		SetActive();
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		return;
+		}
+
+	CImOffLineOperationArray* operationArray = CImOffLineOperationArray::NewL();
+	CleanupStack::PushL(operationArray);
+	CImOffLineArrayStore* operationStore = new (ELeave) CImOffLineArrayStore(*operationArray);
+	CleanupStack::PushL(operationStore);
+	CMsvStore* messageStore = iEntry.EditStoreL();
+	CleanupStack::PushL(messageStore);
+
+	// Get the array of operations currently attached to the message.
+	operationStore->RestoreL(*messageStore);
+
+	// If the operation is a move or delete then ensure that it doesn't interfere with any existing operations.
+	if ((iModifiedOperationType == CImOffLineOperation::EOffLineOpMoveToLocal)
+		|| (iModifiedOperationType == CImOffLineOperation::EOffLineOpDelete))
+		{
+		// Find out if there is a move or a delete operation already pending on the message.
+		TBool moveOrDeleteAlreadyPending = EFalse;
+		CImOffLineOperation::TOffLineOpType opType;
+		TInt counter = operationArray->CountOperations();
+
+		while (counter > 0)
+			{
+			counter--;
+			opType = (operationArray->Operation(counter)).OpType();
+			if ((opType == CImOffLineOperation::EOffLineOpMoveToLocal)
+				|| (opType == CImOffLineOperation::EOffLineOpDelete))
+				{
+				moveOrDeleteAlreadyPending = ETrue;
+				counter = 0;
+				}
+			}
+
+		if (moveOrDeleteAlreadyPending)
+			{
+			if (iModifiedOperationType == CImOffLineOperation::EOffLineOpMoveToLocal)
+				{
+				// Change the move operation to a copy, it will be deleted by the existing move or delete.
+				iModifiedOperationType = CImOffLineOperation::EOffLineOpCopyToLocal;
+				}
+
+			if (iModifiedOperationType == CImOffLineOperation::EOffLineOpDelete)
+				{
+				// No need to append this delete operation as it will be delete be the existing move or delete.
+				iModifiedOperationType = CImOffLineOperation::EOffLineOpNone;
+				}
+			}
+		}
+
+	CImOffLineOperation* operation = new (ELeave) CImOffLineOperation();
+	CleanupStack::PushL(operation);
+
+	TMsvEmailEntry entry = iEntry.Entry();
+
+	switch (iModifiedOperationType)
+		{
+		case CImOffLineOperation::EOffLineOpCopyFromLocal:
+		case CImOffLineOperation::EOffLineOpMoveFromLocal:
+		case CImOffLineOperation::EOffLineOpMoveWithinService:
+		case CImOffLineOperation::EOffLineOpChange:
+		case CImOffLineOperation::EOffLineOpCreate:
+		case CImOffLineOperation::EOffLineOpMtmSpecific:
+			User::Leave(KErrNotSupported);
+
+		case CImOffLineOperation::EOffLineOpCopyToLocal:
+			operation->SetCopyToLocal((*iSourceMessages)[iMessageCounter], iDestinationEntryId);
+			break;
+
+		case CImOffLineOperation::EOffLineOpDelete:
+			operation->SetDelete((*iSourceMessages)[iMessageCounter]);
+			break;
+
+		case CImOffLineOperation::EOffLineOpCopyWithinService:
+			operation->SetCopyWithinService((*iSourceMessages)[iMessageCounter], iDestinationEntryId);
+			break;
+
+		case CImOffLineOperation::EOffLineOpMoveToLocal:
+			operation->SetMoveToLocal((*iSourceMessages)[iMessageCounter], iDestinationEntryId);
+			break;
+
+		case CImOffLineOperation::EOffLineOpNone:
+			break;
+
+		default:
+			User::Leave(KErrNotSupported);
+		}
+	
+
+	if (iModifiedOperationType != CImOffLineOperation::EOffLineOpNone)
+		{
+		operationArray->AppendOperationL(*operation);
+		entry.SetOperation(ETrue);
+		iOfflineOperationArrayFlag[iMessageCounter] = ETrue;
+		}
+
+	CImPop3OfflineUtilities::SetOfflineFlags(*operationArray, entry);
+
+	operationStore->StoreL(*messageStore);
+	iEntry.ChangeEntry(entry);
+	messageStore->CommitL();
+	CleanupStack::PopAndDestroy(4); // operation, messageStore, operationStore, operationArray
+	iStatus = KRequestPending;
+	SetActive();
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	}
+
+void CImPop3SetOfflineOps::CancelOfflineOperationL()
+	{
+	SetActive();
+
+	// Set the current entry, ready for the operation to be attached to it.
+	User::LeaveIfError(iEntry.SetEntry((*iSourceMessages)[iMessageCounter]));
+	TMsvEmailEntry entry = iEntry.Entry();
+	CImOffLineOperationArray* operationArray = CImOffLineOperationArray::NewL();
+	CleanupStack::PushL(operationArray);
+	CImOffLineArrayStore* operationStore = new (ELeave) CImOffLineArrayStore(*operationArray);
+	CleanupStack::PushL(operationStore);
+	CMsvStore* messageStore = iEntry.EditStoreL();
+	CleanupStack::PushL(messageStore);
+
+	// Get the array of operations currently attached to the message.
+	operationStore->RestoreL(*messageStore);
+
+	TInt counter = operationArray->CountOperations();
+	while (counter > 0)
+		{
+		counter--;
+		operationArray->Delete(counter);
+		}
+
+	operationStore->StoreL(*messageStore);
+	messageStore->CommitL();
+	entry.SetOperation(EFalse);
+	entry.SetDisconnectedOperation(ENoDisconnectedOperations);
+	iEntry.ChangeEntry(entry);
+	CleanupStack::PopAndDestroy(3); // messageStore, operationStore, operationArray
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete(status, KErrNone);
+	}
+
+void CImPop3SetOfflineOps::CopyLocalMessageL()
+	// Copy the current local message.
+	{
+	User::LeaveIfError(iEntry.SetEntry((*iSourceMessages)[iMessageCounter]));
+	User::LeaveIfError(iEntry.SetEntry(iEntry.Entry().Parent()));
+
+	iStatus = KRequestPending;
+	iTransfer->StartL((*iSourceMessages)[iMessageCounter], iDestinationEntryId, CImPop3TransferMessage::EImPop3CopyTransfer, iStatus);
+
+	SetActive();
+	}
+
+CImPop3SetOfflineOps::~CImPop3SetOfflineOps()
+	{
+	Cancel();
+	delete iSourceMessages;
+	delete iMessagesToCopyLocally;
+	delete iTransfer;
+	iOfflineOperationArrayFlag.Close();
+	}
+
+void CImPop3SetOfflineOps::Progress(TPop3Progress& rPop3Progress) const
+	{
+	// It is only necessary to report the progress when it is in the
+	// ECopyingLocalEntries state. All other states should be near-instantaneous
+	// and a 0 filled progress will do.
+
+	switch (iState)
+		{
+		case ECopyingLocalEntries:
+ 			if (iOperationType == CImOffLineOperation::EOffLineOpMoveToLocal)
+ 				{
+ 				rPop3Progress.iPop3Progress = TPop3Progress::EPopMoving;
+ 				rPop3Progress.iPop3SubStateProgress = TPop3Progress::EPopMoving;
+ 				}
+ 			else
+  				{
+ 				rPop3Progress.iPop3Progress = TPop3Progress::EPopCopying;
+ 				rPop3Progress.iPop3SubStateProgress = TPop3Progress::EPopCopying;
+ 				}
+			rPop3Progress.iTotalMsgs = iMessagesToCopyLocally->Count();
+			rPop3Progress.iMsgsToProcess = rPop3Progress.iTotalMsgs - iMessageCounter;
+			rPop3Progress.iBytesDone = 0;
+			rPop3Progress.iTotalBytes = 0;
+			rPop3Progress.iErrorCode = 0;
+			break;
+
+		case EAddingOfflineOperations:
+			if (iOperationType == CImOffLineOperation::EOffLineOpDelete)
+				{
+				rPop3Progress.iPop3Progress = TPop3Progress::EPopDeleting;
+				rPop3Progress.iTotalMsgs = iSourceMessages->Count();
+				rPop3Progress.iMsgsToProcess = rPop3Progress.iTotalMsgs - iMessageCounter;
+				rPop3Progress.iBytesDone = 0;
+				rPop3Progress.iTotalBytes = 0;
+				rPop3Progress.iErrorCode = 0;
+				rPop3Progress.iPop3SubStateProgress = TPop3Progress::EPopDeleting;
+				}
+  			else if (iOperationType == CImOffLineOperation::EOffLineOpMoveToLocal)
+ 				{
+ 				rPop3Progress.iPop3Progress = TPop3Progress::EPopMoving;
+ 				rPop3Progress.iTotalMsgs = iSourceMessages->Count();
+ 				rPop3Progress.iMsgsToProcess = rPop3Progress.iTotalMsgs - iMessageCounter;
+ 				rPop3Progress.iBytesDone = 0;
+				rPop3Progress.iTotalBytes = 0;
+ 				if(rPop3Progress.iErrorCode != KErrDisconnected)
+ 					{
+ 					rPop3Progress.iErrorCode = 0;
+ 					}
+ 				rPop3Progress.iPop3SubStateProgress = TPop3Progress::EPopMoving;
+ 				}
+  			else if ((iOperationType == CImOffLineOperation::EOffLineOpCopyToLocal) ||
+ 				(iOperationType == CImOffLineOperation::EOffLineOpCopyWithinService))
+ 				{
+ 				rPop3Progress.iPop3Progress = TPop3Progress::EPopCopying;
+ 				rPop3Progress.iTotalMsgs = iSourceMessages->Count();
+ 				rPop3Progress.iMsgsToProcess = rPop3Progress.iTotalMsgs - iMessageCounter;
+ 				rPop3Progress.iBytesDone = 0;
+ 				rPop3Progress.iTotalBytes = 0;
+ 				if(rPop3Progress.iErrorCode  != KErrDisconnected)
+ 					{
+ 					rPop3Progress.iErrorCode = 0; 					
+ 					}
+ 				rPop3Progress.iPop3SubStateProgress = TPop3Progress::EPopCopying;
+ 				}
+			break;
+
+		case ECancellingOfflineOperations:
+			rPop3Progress.iPop3Progress = TPop3Progress::EPopCancellingOfflineOps;
+			rPop3Progress.iTotalMsgs = iSourceMessages->Count();
+			rPop3Progress.iMsgsToProcess = rPop3Progress.iTotalMsgs - iMessageCounter;
+			rPop3Progress.iBytesDone = 0;
+			rPop3Progress.iTotalBytes = 0;
+			rPop3Progress.iErrorCode = 0;
+			rPop3Progress.iPop3SubStateProgress = TPop3Progress::EPopCancellingOfflineOps;
+			break;
+
+		default:
+            {
+            _LIT(KPop3Session,"Pop3 session");
+			__ASSERT_DEBUG(EFalse, 	User::Panic(KPop3Session, EPopInvalidState));
+            }
+			break;
+		}
+	}
+
+//private:
+void CImPop3SetOfflineOps::DoRunL()
+	{
+	// Create the ghost destination entry if one is required.
+	switch (iState)
+		{
+		case EAddingOfflineOperations:
+			if ((iModifiedOperationType == CImOffLineOperation::EOffLineOpCopyToLocal)
+				|| (iModifiedOperationType == CImOffLineOperation::EOffLineOpMoveToLocal))
+				{
+				User::LeaveIfError(iEntry.SetEntry((*iSourceMessages)[iMessageCounter]));
+				TMsvEntry ghostEntry = iEntry.Entry();
+				ghostEntry.iRelatedId = iEntry.Entry().Id();
+				ghostEntry.SetComplete(EFalse);
+				User::LeaveIfError(iEntry.SetEntry(iDestinationEntryId));
+				iEntry.CreateEntry(ghostEntry);
+				}
+
+			iMessageCounter++;
+			if (iMessageCounter < iSourceMessages->Count())
+				{
+				AddOfflineOperationL();
+				}
+			else
+				{
+				iMessageCounter = 0;
+				if (iMessageCounter < iMessagesToCopyLocally->Count())
+					{
+					iState = ECopyingLocalEntries;
+					CopyLocalMessageL();
+					}
+				}
+			break;
+
+		case ECancellingOfflineOperations:
+			iMessageCounter++;
+			if (iMessageCounter < iSourceMessages->Count())
+				{
+				CancelOfflineOperationL();
+				}
+			break;
+
+		case ECopyingLocalEntries:
+			iMessageCounter++;
+			if (iMessageCounter < iMessagesToCopyLocally->Count())
+				{
+				CopyLocalMessageL();
+				}
+			break;
+		}
+	}
+
+void CImPop3SetOfflineOps::DoCancel()
+	{
+	iTransfer->Cancel();
+	
+		switch(iState)
+			{
+			case ECopyingLocalEntries:
+				{
+				TInt messageCounter = iMessageCounter;
+				
+				// Loop to delete all the operations attached to the messages in iSourceMessages.
+				for(TInt i = iSourceMessages->Count() - messageCounter; i > 0; -- i)
+					{
+					DeleteOfflineOperationL(messageCounter);
+					messageCounter++;
+					}
+				break;
+				}
+			
+			case EAddingOfflineOperations:
+				{
+				// Loop to delete all the operations attached to the messages in iSourceMessages.
+				for(TInt count = 0; count <= iMessageCounter; ++count)
+					{
+					DeleteOfflineOperationL(count);
+					}
+				break;
+				}
+			
+			case ECancellingOfflineOperations:
+			default:
+				{
+				// Nothing to be done here.
+				break;
+				}
+			}
+		iOfflineOperationArrayFlag.Reset();
+		CMsgActive::DoCancel();
+	}
+
+
+void CImPop3SetOfflineOps::DeleteOfflineOperationL(TInt aMessageCounter)
+	{
+	// Set the current entry, ready for the operation to be attached to it.
+	User::LeaveIfError(iEntry.SetEntry((*iSourceMessages)[aMessageCounter]));
+	TMsvEmailEntry entry = iEntry.Entry();
+	
+	if(iOfflineOperationArrayFlag[aMessageCounter])
+		{
+		CImOffLineOperationArray* operationArray = CImOffLineOperationArray::NewL();
+		CleanupStack::PushL(operationArray);
+		CImOffLineArrayStore* operationStore = new (ELeave) CImOffLineArrayStore(*operationArray);
+		CleanupStack::PushL(operationStore);
+		CMsvStore* messageStore = iEntry.EditStoreL();
+		CleanupStack::PushL(messageStore);
+
+		// Get the array of operations currently attached to the message.
+		operationStore->RestoreL(*messageStore);
+
+		TInt counter = operationArray->CountOperations();
+		if (counter > 0)
+			{
+			counter--;
+			operationArray->Delete(counter);
+			}
+		
+		operationStore->StoreL(*messageStore);
+		messageStore->CommitL();
+		
+		if(counter == 0)
+			{
+			entry.SetOperation(EFalse);
+			entry.SetDisconnectedOperation(ENoDisconnectedOperations);	
+			}
+		else
+			{
+			CImPop3OfflineUtilities::SetOfflineFlags(*operationArray, entry);		
+			}
+		iEntry.ChangeEntry(entry);
+		CleanupStack::PopAndDestroy(3); // messageStore, operationStore, operationArray	
+		}
+	}
+	
+
+void CImPop3SetOfflineOps::DoComplete(TInt& /*status*/)
+	{
+
+	}
+
+CImPop3SetOfflineOps::CImPop3SetOfflineOps(CMsvServerEntry& aEntry) : CMsgActive(EPriorityStandard), iEntry(aEntry)
+	{
+
+	}
+
+void CImPop3SetOfflineOps::ConstructL()
+	{
+	iTransfer = CImPop3TransferMessage::NewL(iEntry);
+	}
+
+
+
+void CImPop3OfflineUtilities::DeleteL(const CImOffLineOperation& aOperation, CMsvServerEntry& aEntry)
+// Finds the specified operation in the message tree and then deletes it.
+	{
+	aEntry.SetEntry(aOperation.MessageId());
+	CImOffLineOperationArray* localOperationArray = CImOffLineOperationArray::NewL();
+	CleanupStack::PushL(localOperationArray);
+	CImOffLineArrayStore* operationStore = new (ELeave) CImOffLineArrayStore(*localOperationArray);
+	CleanupStack::PushL(operationStore);
+	CMsvStore* messageStore = aEntry.EditStoreL();
+	CleanupStack::PushL(messageStore);
+
+	// Get the array of operations currently attached to the message.
+	operationStore->RestoreL(*messageStore);
+
+	// Look for the offline operation
+	TBool found = EFalse;
+	TInt index = 0;
+	while ((!found) && (index < localOperationArray->CountOperations()))
+		{
+		if (aOperation == localOperationArray->Operation(index))
+			{
+			found = ETrue;
+			}
+		else
+			{
+			index++;
+			}
+		}
+
+	if (found)
+		{
+		// Remove the entry from the list
+		localOperationArray->Delete(index);
+
+		// Write out the list to the stream
+		operationStore->StoreL(*messageStore);
+		messageStore->CommitL();
+
+		// Set the TMsvEmailEntry details
+		TMsvEmailEntry entry = aEntry.Entry();
+		SetOfflineFlags(*localOperationArray, entry);
+		aEntry.ChangeEntry(entry);
+		}
+
+	CleanupStack::PopAndDestroy(3); // messageStore, operationStore, operationArray
+	}
+
+void CImPop3OfflineUtilities::SetOfflineFlags(const CImOffLineOperationArray& aOperationArray, TMsvEmailEntry& aEntry)
+	{
+	// Sets the DisconnectedOperation flag on the given TMsvEmailEntry.
+	// Note that it is NOT commited to the index.
+	TImDisconnectedOperationType operationType = ENoDisconnectedOperations;
+	TInt index = 0;
+	while (index < aOperationArray.CountOperations())
+		{
+		if (operationType != ENoDisconnectedOperations)
+			{
+			operationType = EDisconnectedMultipleOperation;
+			}
+		else
+			{
+			switch (aOperationArray.Operation(index).OpType())
+				{
+				case CImOffLineOperation::EOffLineOpCopyToLocal:
+					operationType = EDisconnectedCopyToOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpCopyFromLocal:
+					operationType = EDisconnectedCopyFromOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpCopyWithinService:
+					operationType = EDisconnectedCopyWithinServiceOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpMoveToLocal:
+					operationType = EDisconnectedMoveToOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpMoveFromLocal:
+					operationType = EDisconnectedMoveFromOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpMoveWithinService:
+					operationType = EDisconnectedMoveWithinServiceOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpDelete:
+					operationType = EDisconnectedDeleteOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpChange:
+					operationType = EDisconnectedChangeOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpCreate:
+					operationType = EDisconnectedCreateOperation;
+					break;
+				case CImOffLineOperation::EOffLineOpMtmSpecific:
+					operationType = EDisconnectedSpecialOperation;
+					break;
+				default:
+					operationType = EDisconnectedUnknownOperation;
+					break;
+				}
+			}
+		index++;
+		}
+	aEntry.SetDisconnectedOperation(operationType);
+	}
+
+