// 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;
}