email/pop3andsmtpmtm/clientmtms/src/CACHEMAN.CPP
changeset 0 72b543305e3a
child 34 84197e66a4bd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/clientmtms/src/CACHEMAN.CPP	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,838 @@
+// Copyright (c) 1999-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:
+// CACHEMAN.CPP
+//
+
+#include "CACHEMAN.H"
+#include "MIUTHDR.H"
+#include <msvapi.h>
+#include <msvuids.h>
+#include <msvids.h>
+#include <mmsvattachmentmanager.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <cimprunemessage.h>
+#include "cimfinder.h"
+#include "timrfc822datefield.h"
+#include "msvconsts.h"
+#endif
+
+// The CImEntryStack implementation
+
+CImFinder::CImEntryStack* CImFinder::CImEntryStack::NewL()
+	{
+	CImFinder::CImEntryStack* self;
+	self = new (ELeave) CImFinder::CImEntryStack();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+CImFinder::CImEntryStack::~CImEntryStack()
+	{
+	delete iFolders;
+	}
+
+void CImFinder::CImEntryStack::PushL(TMsvId aId)
+	{
+	iFolders->AppendL(aId);
+	}
+
+void CImFinder::CImEntryStack::Reset()
+	{
+	iFolders->Reset();
+	}
+
+TMsvId CImFinder::CImEntryStack::PopL()
+	{
+	TInt32 count = iFolders->Count();
+	if (count == 0)
+		{
+		User::Leave(KErrOverflow);
+		}
+	
+	TMsvId folderId = (*iFolders)[count - 1];
+	iFolders->Delete(count-1);
+	return folderId;
+	}
+
+void CImFinder::CImEntryStack::ConstructL()
+	{
+	iFolders = new (ELeave) CMsvEntrySelection;
+	}
+
+TBool CImFinder::CImEntryStack::Empty() const
+	{
+	return (iFolders->Count() == 0);
+	}
+
+
+// Implementation of abstract CImFinder class
+
+EXPORT_C void CImFinder::FindFirstL(TMsvId aRootEntry, TRequestStatus &aStatus)
+	{
+	iEntryStack->Reset();
+	iEntryStack->PushL(aRootEntry);
+	ASSERT(iState != EImmfFindingEntry);
+	FindNextL(aStatus);
+	}
+
+void CImFinder::FindFirstL(const CMsvEntrySelection& aInitialSelection, TRequestStatus &aStatus)
+	{
+	iEntryStack->Reset();
+
+	TInt index = aInitialSelection.Count();
+	while (index--)
+		// Push each given entry on the stack so that we will visit it later.
+		{
+		iEntryStack->PushL(aInitialSelection[index]);
+		}
+
+	ASSERT(iState != EImmfFindingEntry);
+	FindNextL(aStatus);
+	}
+
+EXPORT_C CImFinder::~CImFinder()
+	{
+	delete iEntryStack;
+	}
+
+void CImFinder::ConstructL()
+	{
+	iEntryStack = CImEntryStack::NewL();
+	CActiveScheduler::Add(this);
+	}
+
+void CImFinder::DoRunL()
+	{	
+	if (iState == EImmfFindingEntry)
+		{
+		FindNextL(iStatus);
+		}
+	}
+
+CImFinder::CImFinder(CMsvEntry& aEntry) : CMsgActive(EPriorityStandard), 	iCurrentEntry(aEntry)
+	{
+	}
+
+EXPORT_C void CImFinder::FindNextL(TRequestStatus &aStatus)
+	{
+	TImmfState tempState = iState;
+	
+	if (!(iEntryStack->Empty()))
+		// If there are entries on the stack then check them.
+		{
+		iCurrentEntry.SetEntryL(iEntryStack->PopL());
+		TUid entryType = iCurrentEntry.Entry().iType;
+		
+		AddChildEntriesL();
+
+		iStatus=KRequestPending;
+		SetActive();
+		TRequestStatus* status=&iStatus;
+		User::RequestComplete(status, KErrNone);
+
+		if (IsRequiredEntryType(entryType))
+			// If the entry is of a required type then return it.
+			{
+			iState = EImmfEntryFound;
+			}
+		else
+			// Otherwise keep looking for one.
+			{
+			iState = EImmfFindingEntry;
+			}
+		}
+	else
+		// If there is nothing on the stack then there are no more entries.
+		{
+		iStatus=KRequestPending;
+		SetActive();
+		TRequestStatus* status=&iStatus;
+		User::RequestComplete(status, KErrNotFound);
+		iState = EImmfNothingFound;
+		}
+
+	if (tempState != EImmfFindingEntry)
+		{
+		Queue(aStatus);
+		}
+	}
+
+
+
+// Implementation of CImMessageFinder class
+
+EXPORT_C CImMessageFinder* CImMessageFinder::NewL(CMsvEntry& aEntry)
+	{
+	CImMessageFinder* self = CImMessageFinder::NewLC(aEntry);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+EXPORT_C CImMessageFinder* CImMessageFinder::NewLC(CMsvEntry& aEntry)
+	{
+	CImMessageFinder* self = new (ELeave) CImMessageFinder(aEntry);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+void CImMessageFinder::AddChildEntriesL()
+	{
+	if ((iCurrentEntry.Entry().iType == KUidMsvFolderEntry)
+		|| (iCurrentEntry.Entry().iType == KUidMsvServiceEntry))
+		{
+		// Push the folders on to the entry stack
+		TMsvSelectionOrdering ordering;
+		ordering.SetShowInvisibleEntries(EFalse);
+		iCurrentEntry.SetSortTypeL(ordering);
+		CMsvEntrySelection* entrySelection = iCurrentEntry.ChildrenWithTypeL(KUidMsvFolderEntry);
+		CleanupStack::PushL(entrySelection);
+		TInt counter;
+		TInt count = entrySelection->Count();
+		for (counter = 0; counter < count; counter++)
+			{
+			iEntryStack->PushL((*entrySelection)[counter]);
+			}
+		CleanupStack::PopAndDestroy(); // entrySelection
+
+		// Push the messages on to the entry stack
+		entrySelection = iCurrentEntry.ChildrenWithTypeL(KUidMsvMessageEntry);
+		CleanupStack::PushL(entrySelection);
+		count = entrySelection->Count();
+		for (counter = 0; counter < count; counter++)
+			{
+			iEntryStack->PushL((*entrySelection)[counter]);
+			}
+		CleanupStack::PopAndDestroy(); // entrySelection
+		}
+	}
+
+TBool CImMessageFinder::IsRequiredEntryType(TUid aEntryType) const
+	{
+	if (aEntryType == KUidMsvMessageEntry)
+		return ETrue;
+	else
+		return EFalse;
+	}
+
+CImMessageFinder::CImMessageFinder(CMsvEntry& aEntry) : CImFinder(aEntry)
+	{
+	}
+
+
+
+// Implementation of CImMessageFinder class
+
+EXPORT_C CImEntryFinder* CImEntryFinder::NewL(CMsvEntry& aEntry)
+	{
+	CImEntryFinder* self = CImEntryFinder::NewLC(aEntry);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+EXPORT_C CImEntryFinder* CImEntryFinder::NewLC(CMsvEntry& aEntry)
+	{
+	CImEntryFinder* self = new (ELeave) CImEntryFinder(aEntry);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+	
+void CImEntryFinder::AddChildEntriesL()
+	{
+	if (iCurrentEntry.Entry().Owner())
+		{
+		// Push the child entries on to the entry stack
+		TMsvSelectionOrdering ordering;
+		ordering.SetShowInvisibleEntries(ETrue);
+		iCurrentEntry.SetSortTypeL(ordering);
+		CMsvEntrySelection* entrySelection = iCurrentEntry.ChildrenL();
+		CleanupStack::PushL(entrySelection);
+		TInt counter;
+		TInt count = entrySelection->Count();
+		for (counter = 0; counter < count; counter++)
+			{
+			iEntryStack->PushL((*entrySelection)[counter]);
+			}
+		CleanupStack::PopAndDestroy(); // entrySelection		
+		}
+	}
+
+TBool CImEntryFinder::IsRequiredEntryType(TUid /*aEntryType*/) const
+// Always true because all entry types are needed.
+	{
+	return ETrue;
+	}
+
+CImEntryFinder::CImEntryFinder(CMsvEntry& aEntry) : CImFinder(aEntry)
+	{
+	}
+
+
+
+// Implementation of CImMessage Finder
+
+EXPORT_C CImMessageCounter* CImMessageCounter::NewL(CMsvEntry& aEntry)
+	{
+	CImMessageCounter* self = CImMessageCounter::NewLC(aEntry);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+EXPORT_C CImMessageCounter* CImMessageCounter::NewLC(CMsvEntry& aEntry)
+	{
+	CImMessageCounter* self = new (ELeave) CImMessageCounter(aEntry);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+EXPORT_C TInt CImMessageCounter::Count()
+	{
+	return iCount;
+	}
+
+void CImMessageCounter::AddChildEntriesL()
+	{
+	if ((iCurrentEntry.Entry().iType == KUidMsvFolderEntry)
+		|| (iCurrentEntry.Entry().iType == KUidMsvServiceEntry))
+		{
+		// Push the folders on to the entry stack
+		TMsvSelectionOrdering ordering;
+		ordering.SetShowInvisibleEntries(EFalse);
+		iCurrentEntry.SetSortTypeL(ordering);
+		CMsvEntrySelection* entrySelection = iCurrentEntry.ChildrenWithTypeL(KUidMsvFolderEntry);
+		CleanupStack::PushL(entrySelection);
+		TInt counter;
+		TInt count = entrySelection->Count();
+		for (counter = 0; counter < count; counter++)
+			{
+			iEntryStack->PushL((*entrySelection)[counter]);
+			}
+		CleanupStack::PopAndDestroy(); // entrySelection
+
+		// Count the messages in the folder
+		entrySelection = iCurrentEntry.ChildrenWithTypeL(KUidMsvMessageEntry);
+		CleanupStack::PushL(entrySelection);
+		iCount += entrySelection->Count();
+		CleanupStack::PopAndDestroy(); // entrySelection
+		}
+	else if (iCurrentEntry.Entry().iType == KUidMsvMessageEntry)
+		{ // count itself if it's a message entry
+		iCount++;
+		}
+	}
+
+TBool CImMessageCounter::IsRequiredEntryType(TUid) const
+	{
+	// The message counter doesn't require any entry types to be returned.
+	return EFalse;
+	}
+
+CImMessageCounter::CImMessageCounter(CMsvEntry& aEntry) : CImFinder(aEntry)
+	{
+
+	}
+
+
+// Implementation of CImPruneMessage
+
+EXPORT_C CImPruneMessage* CImPruneMessage::NewL(CMsvEntry& aEntry, RFs& aFs)
+/** Allocates and constructs a new CImPruneMessage object.
+
+@param aEntry CMsvEntry for use by the object in performing the delete operations. 
+It does not need to be set to any particular message context. The caller retains 
+ownership.
+@param aFs Open file server session handle
+@return New CImPruneMessage object */
+	{
+	CImPruneMessage* self = CImPruneMessage::NewLC(aEntry, aFs);
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+EXPORT_C CImPruneMessage* CImPruneMessage::NewLC(CMsvEntry& aEntry, RFs& aFs)
+/** Allocates and constructs a new CImPruneMessage object, leaving the object on 
+the cleanup stack.
+
+@param aEntry CMsvEntry for use by the object in performing the delete operations. 
+It does not need to be set to any particular message context. The caller retains 
+ownership.
+@param aFs Open file server session handle
+@return New CImPruneMessage object */
+	{
+	CImPruneMessage* self = new (ELeave) CImPruneMessage(aEntry, aFs);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+EXPORT_C void CImPruneMessage::StartL(TMsvId aMessageEntry, TRequestStatus &aStatus)
+/** Starts the asynchronous message clean operation.
+
+@param aMessageEntry The ID of the entry from which to delete the body text 
+and attachment data
+@param aStatus Asynchronous status word that completes with KErrNone when the 
+body text and all of the attachments have been deleted. */
+	{
+	iCurrentEntry.SetEntryL(aMessageEntry);
+	TMsvEmailEntry entry(iCurrentEntry.Entry());
+
+	if (entry.Complete() || entry.BodyTextComplete())
+		{
+		entry.SetComplete(EFalse);
+		entry.SetBodyTextComplete(EFalse);
+		iChangeOperation = iCurrentEntry.ChangeL(entry, iStatus);
+		SetActive();
+		}
+	else
+		{
+		iStatus=KRequestPending;
+		SetActive();
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		}
+
+	iState = EImPruneUpdatingFirstEntry;
+	Queue(aStatus);
+	iRootEntryId = aMessageEntry;
+	}
+
+CImPruneMessage::~CImPruneMessage()
+/** Destructor. */
+	{
+	Cancel();
+	delete iEntryFinder;
+	delete iFileManager;
+	delete iStore;
+	delete iChangeOperation;	
+	}
+
+void CImPruneMessage::ConstructL()
+	{
+	CActiveScheduler::Add(this);
+	iEntryFinder = CImEntryFinder::NewL(iCurrentEntry);
+	iFileManager = CFileMan::NewL(iFs);
+	}
+
+void CImPruneMessage::DoRunL()
+	{
+	TInt error = KErrNone;
+	switch (iState)
+		{
+		case EImPruneUpdatingFirstEntry:
+			delete iChangeOperation;
+			iChangeOperation = 0;
+			PruneEntryL();
+			iState = EImPruneFindFirstEntry;			
+			SetActive();
+			break;
+		case EImPruneFindFirstEntry:
+			ResetStoreL();
+			iEntryFinder->FindFirstL(iRootEntryId, iStatus);
+			iState = EImPruneFindingEntry;
+			SetActive();
+			break;
+		case EImPruneUpdatingEntry:
+			delete iChangeOperation;
+			iChangeOperation = 0;
+			PruneEntryL();
+			iState = EImPruneFindNextEntry;
+			SetActive();
+			break;
+		case EImPruneFindNextEntry:
+			ResetStoreL();
+			iEntryFinder->FindNextL(iStatus);
+			iState = EImPruneFindingEntry;
+			SetActive();
+			break;
+		case EImPruneFindingEntry:
+			delete iStore;
+			iStore = NULL;
+			if (iStatus == KErrNone)
+				// An entry has been found, so prune it.
+				{
+				// Ignore any error as it probably just means that the entry is locked.
+				// We can afford to skip locked entries.
+				if (iCurrentEntry.Entry().Complete())
+					{
+					TMsvEntry entry = iCurrentEntry.Entry();
+					entry.SetComplete(EFalse);
+					TRAPD(ignore, iChangeOperation = iCurrentEntry.ChangeL(entry, iStatus));
+					}
+
+				if (!iChangeOperation)
+					{
+					TRequestStatus* status = &iStatus;
+					iStatus=KRequestPending;
+					User::RequestComplete(status, KErrNone);	
+					}
+				iState = EImPruneUpdatingEntry;
+				SetActive();
+				}
+			break;
+		}
+	}
+
+void CImPruneMessage::DoComplete(TInt& status)
+	{
+	if ((iState == EImPruneFindingEntry) && (status == KErrNotFound))
+	// The not found status is expected, it just means that the entry finder has already found all the entries.
+		{
+		status = KErrNone;
+		}
+	}
+
+void CImPruneMessage::DoCancel()
+	{
+	iEntryFinder->Cancel();
+	if (iChangeOperation)
+		iChangeOperation->Cancel();
+	CMsgActive::DoCancel();
+	}
+
+CImPruneMessage::CImPruneMessage(CMsvEntry& aEntry, RFs& aFs) 
+	: CMsgActive(EPriorityStandard), 
+	iCurrentEntry(aEntry), 
+	iFs(aFs)
+	{
+	__DECLARE_NAME(_S("CImPruneMessage"));
+	}
+
+void CImPruneMessage::PruneEntryL()
+	{
+	// delete the rich text stream
+	iStore = iCurrentEntry.EditStoreL();
+	if (iStore->IsPresentL(KMsvEntryRichTextBody))
+		{	
+		iStore->DeleteBodyTextL();
+		iStore->CommitL();
+		}
+		
+	// If the entry has an attachment we need to remove it.
+	// In order to preserve the message structure, we won't delete the attachment
+	// completely, but instead we will set the file size to 0 and mark the attachment
+	// as not complete.
+	
+	TBool completeSelf = ETrue;
+		
+	MMsvAttachmentManager& attachmentMgr = iStore->AttachmentManagerL();
+	
+	// as this is email attachment entry , it has one attachment per entry
+	if(attachmentMgr.AttachmentCount() > 0)
+		{
+		CMsvAttachment* attachmentInfo = attachmentMgr.GetAttachmentInfoL(0);
+		CleanupStack::PushL(attachmentInfo);
+
+		// We only want to remove file attachments
+		if (attachmentInfo->Type() == CMsvAttachment::EMsvFile)
+			{
+			RFile file = attachmentMgr.GetAttachmentFileForWriteL(0);
+			TInt error = file.SetSize(0);
+			file.Close();
+
+			if (error == KErrNone)
+				{
+				attachmentInfo->SetComplete(EFalse);
+				attachmentMgr.ModifyAttachmentInfoL(attachmentInfo, iStatus);
+				// ModifyAttachmentInfoL takes ownership of the attachment info, so just pop
+				// it off the cleanup stack and set the pointer to NULL to show we don't
+				// own it
+				CleanupStack::Pop(attachmentInfo);
+				attachmentInfo = NULL;
+
+				// We have called an async routine, so set the flag to show that we don't
+				// need to complete ourself
+				completeSelf = EFalse;
+				}
+			}
+
+		if (attachmentInfo != NULL)
+			{
+			CleanupStack::PopAndDestroy(attachmentInfo);
+			}	
+		}
+	
+	if (completeSelf)
+		{
+		TRequestStatus* status = &iStatus;
+		iStatus=KRequestPending;
+		User::RequestComplete(status, KErrNone);
+		}
+	}
+
+void CImPruneMessage::ResetStoreL()
+	{
+	if (iStore != NULL)
+	   {
+	   iStore->CommitL();
+	   delete iStore;
+	   iStore = NULL;	
+	   }
+	}
+
+// Implementation of CImCacheManager
+
+EXPORT_C void CImCacheManager::StartL(TMsvId aRootEntry, TRequestStatus &aStatus)
+/** Performs a recursive cache cleanup starting from a specified root entry.
+
+Cached data for entries below the specified root are cleaned-up if they meet 
+the criteria specified by the implementation of Filter().
+
+@param aRootEntry Entry from which to start cache cleanup
+@param aStatus Asynchronous status word signalled when the operation is complete */
+	{
+	delete iMessagesToPrune;
+	iMessagesToPrune = 0;
+	iState = EImcmCountingMessages;
+	aStatus = KRequestPending;
+	iReport = &aStatus;
+	iMessageCounter->FindFirstL(aRootEntry, iStatus);
+	iRootEntry = aRootEntry;
+	SetActive();
+	}
+
+EXPORT_C void CImCacheManager::StartL(const CMsvEntrySelection& aSelection, TRequestStatus &aStatus)
+/** Performs a recursive cache cleanup for a specified selection of root entries.
+
+Cached data for entries below the specified roots are cleaned-up if they meet 
+the criteria specified by the implementation of Filter().
+
+@param aSelection List of entries from which to start cache cleanup
+@param aStatus Asynchronous status word signalled when the operation is complete */
+	{
+	delete iMessagesToPrune;
+	iMessagesToPrune = 0;
+	iState = EImcmCountingMessages;
+	aStatus = KRequestPending;
+	iReport = &aStatus;
+
+	// Save the selection for later as we need it when we actually prune the messages
+	iMessagesToPrune = new (ELeave) CMsvEntrySelection();
+	TInt index = aSelection.Count();
+	while (index--)
+		{
+		iMessagesToPrune->AppendL(aSelection[index]);
+		}
+
+	// Set off the operation to count all the messages
+	iMessageCounter->FindFirstL(aSelection, iStatus);
+
+	SetActive();
+	}
+
+
+EXPORT_C void CImCacheManager::ConstructL()
+/** Second phase constructor.
+
+All classes derived from CImCacheManager must call this function before StartL() 
+is called. */
+	{
+	TMsvSelectionOrdering ordering;
+	iCurrentEntry = CMsvEntry::NewL(iSession, KMsvRootIndexEntryId, ordering);
+	iMessageFinder = CImMessageFinder::NewL(*iCurrentEntry);
+	iMessageCounter = CImMessageCounter::NewL(*iCurrentEntry);
+	iProgress.iTotalMessages = 1;
+	iProgress.iMessagesProcessed = 0;
+	CActiveScheduler::Add(this);
+	}
+
+EXPORT_C const TDesC8& CImCacheManager::ProgressL()
+/** Gets information on the progress of the operation.
+
+@return Packaged TImCacheManagerProgress object holding operation progress 
+details */
+	{
+	if (iProgress.iTotalMessages == 0)
+		{
+		iProgress.iTotalMessages = 1;
+		}
+
+	if (iProgress.iMessagesProcessed > iProgress.iTotalMessages)
+		{
+		iProgress.iMessagesProcessed = iProgress.iTotalMessages;
+		}
+
+	iProgressBuf = iProgress;
+	return iProgressBuf;
+	}
+
+/** Implements the active object completion protocol for a cache cleanup operation.   
+
+@see CActive::RunL() */
+EXPORT_C void CImCacheManager::RunL()
+	{
+	TInt error = iStatus.Int();
+	
+	if ((iState == EImcmCountingMessages)
+		&& (error == KErrNotFound))
+		// A KErrNotFound is a normal condition from the message counter.
+		{
+		error = KErrNone;
+		}
+
+	if (error == KErrNone)
+		{
+		TRAP(error, DoRunL());
+		}
+
+	if(error != KErrNone)
+		{
+		if (error == KErrNotFound)
+			// A KErrNotFound is a normal condition from the message finder.
+			{
+			error = KErrNone;
+			}
+
+		User::RequestComplete(iReport, error);
+		}
+	}
+
+/** Implements the active object cancellation protocol for a cache cleanup operation.
+
+As the cache manager is an active object, a cache cleanup  
+operation started by Start() can be cancelled using CActive::Cancel().
+
+Cancellation halts any further deletion of message data, but does 
+not restore any data that has already been deleted by the cleanup operation.    
+
+@see CActive::Cancel() */
+EXPORT_C void CImCacheManager::DoCancel()
+	{
+	iMessageFinder->Cancel();
+	iMessageCounter->Cancel();
+
+	if (iDeleteOperation)
+		iDeleteOperation->Cancel();
+
+	User::RequestComplete(iReport,KErrCancel);
+	}
+
+void CImCacheManager::DoRunL()
+	{
+	switch (iState)
+		{
+		case EImcmCountingMessages:
+			iProgress.iTotalMessages = iMessageCounter->Count();
+			
+			if (iMessagesToPrune)
+				// If we are pruning a selection the prune from the selection
+				iMessageFinder->FindFirstL(*iMessagesToPrune, iStatus);
+			else
+				// otherwise start at the root entry
+				iMessageFinder->FindFirstL(iRootEntry, iStatus);
+
+			iState = EImcmLookingForMessage;
+			SetActive();
+			break;
+
+		case EImcmLookingForMessage:
+			if (iStatus == KErrNone)
+				{
+				if (Filter())
+					{
+					PruneMessageL();
+					}
+				else
+					{
+					iMessageFinder->FindNextL(iStatus);
+					iState = EImcmLookingForMessage;
+					SetActive();
+					}
+				}
+			iProgress.iMessagesProcessed++;
+			break;
+
+		case EImcmPruningMessages:
+		case EImcmSkippingPrune:
+			// The current message has been pruned, find another one.
+			if (iDeleteOperation)
+				{
+				delete iDeleteOperation;
+				iDeleteOperation = 0;
+				}
+			iMessageFinder->FindNextL(iStatus);
+			iState = EImcmLookingForMessage;
+			SetActive();
+			break;
+
+		default:
+			break;
+		}
+
+	}
+
+void CImCacheManager::PruneMessageL()
+	{
+	if (iCurrentEntry->Entry().Owner())
+		{
+		iState = EImcmPruningMessages;
+
+		// Remove any associated store 
+		if (iCurrentEntry->HasStoreL())
+			{
+			CMsvStore* store=iCurrentEntry->EditStoreL();
+			CleanupStack::PushL(store);
+			store->DeleteL();
+			CleanupStack::PopAndDestroy(store);
+			}
+
+		// Get a list of the child entries
+		CMsvEntrySelection* children = iCurrentEntry->ChildrenL();
+		CleanupStack::PushL(children);
+
+		// Delete the child entries
+		// The Complete and BodyTextComplete flags are cleared by the
+		// server MTM when the delete is processed.
+		iDeleteOperation = iCurrentEntry->DeleteL(*children, iStatus);
+		SetActive();
+		CleanupStack::PopAndDestroy(children);
+		}
+	else
+		{
+		// If this entry doesn't own any children,
+		// or we can't do the ChangeL then skip this entry and move on.
+		iState = EImcmSkippingPrune;
+		iStatus=KRequestPending;
+		SetActive();		
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		}
+	}
+
+EXPORT_C CImCacheManager::CImCacheManager(CMsvSession& aSession, TRequestStatus& aObserverRequestStatus) 
+	: CMsvOperation(aSession, EPriorityStandard, aObserverRequestStatus), 
+	iSession(aSession)
+/** Constructor.
+
+@param aSession Message server session
+@param aObserverRequestStatus Unused */
+	{
+	__DECLARE_NAME(_S("CImCacheManager"));
+	}
+
+EXPORT_C CImCacheManager::~CImCacheManager()
+/** Destructor.
+
+This cancels any operation in progress. */
+	{
+	Cancel();
+	delete iCurrentEntry;
+	delete iMessageFinder;
+	delete iMessageCounter;
+	delete iMessagesToPrune;
+	delete iDeleteOperation;
+	}