diff -r 000000000000 -r 72b543305e3a email/pop3andsmtpmtm/clientmtms/src/CACHEMAN.CPP --- /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 +#include +#include +#include +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#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; + }