diff -r 000000000000 -r 8e480a14352b messagingfw/msgsrvnstore/server/src/msvindexadapter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingfw/msgsrvnstore/server/src/msvindexadapter.cpp Mon Jan 18 20:36:02 2010 +0200 @@ -0,0 +1,4344 @@ +// 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: +// + +#ifdef _DEBUG +#undef _NO_SERVER_LOGGING_ +#endif + +/** + * User Includes + */ +#include "MSVSERV.H" +#include "MSVARRAY.H" +#include "msvdbadapter.h" +#include "msvcacheentry.h" +#include "msvindexadapter.h" +#include "msventryfreepool.h" +#include "msvcachevisiblefolder.h" +#include "msvsearchsortdeltacache.h" +#include "msvsearchsortcachemanager.h" + +/** + *System Includes + */ +#include +#include + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include "msvconsts.h" +#endif + +/** + * MACRO DEFINITIONS + */ +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + #define INITIAL_NUM_ENTRIES 10 +#else + #define INITIAL_NUM_ENTRIES 13 +#endif + +/** + * CONSTANT DEFINITIONS + */ +const TInt KMsvMtmListGranularity=8; +const TInt KMsvChangeAttributesListGranularity=32; + + + +/** + * FUNCTION DEFINITIONS + */ + + + +/** + * CMsvIndexAdapter() + */ +CMsvIndexAdapter::CMsvIndexAdapter(CMsvServer& aServer) + :CActive(EPriorityStandard), iServer(aServer), iFolderListHeader(CMsvCacheVisibleFolder::iOffset) + { + CActiveScheduler::Add(this); + } + + + + +/** + * ~CMsvIndexAdapter() + */ +CMsvIndexAdapter::~CMsvIndexAdapter() + { + +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Destroying CMsvIndexAdapter object.")); +#endif + + Cancel(); + + // 1. Close the database. +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Closing the database.")); +#endif + if(iDbAdapter != NULL) + delete iDbAdapter; + + // 2. Delete Cache. +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Destroying Cache.")); +#endif + + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + + dqIter.SetToFirst(); + while ((folderNode = dqIter++) != NULL) + { + folderNode->iDlink.Deque(); + delete folderNode; + } + + iFreePoolInstance->ReleaseEntry(iRootEntry); + iRootEntry = NULL; + + // 3. Delete internal data structure. + iNonCommittedAddedEntryList.ResetAndDestroy(); + iNonCommittedChangedEntryList.Reset(); + iNonCommittedAddedEntryList.Close(); + iNonCommittedChangedEntryList.Close(); + + // 4. Flush extra memory in entry freepool. + if(iFreePoolInstance != NULL) + { + iFreePoolInstance->FlushExcessMemory(); + } + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + // Added for 557. + iMaxMsvIdList.Close(); +#endif + + delete iIdle; + } + + + + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) +/** + * NewL() + * @return The newly created index adapter object. + * + * Function added as part of PREQ 557. + * It returns an instance of CMsvIndexAdapter class. + */ +CMsvIndexAdapter* CMsvIndexAdapter::NewL(CMsvServer& aServer) + { + CMsvIndexAdapter *self = new(ELeave) CMsvIndexAdapter(aServer); + CleanupStack::PushL(self); + + self->ConstructL(); + + CleanupStack::Pop(); + return self; + } + + + + +/** + * ConstructL() + * + * Function added as part of PREQ 557. + * The function is called from NewL(). + * 1. Create the DB adapter object. + * 2. Initialize freepool object. + * 3. Create the root entry in cache. + * 4. Browse through the preferred drive list. + * i. For each drive in the preferred drive list the function + * assigns a unique drive-id (0< Id <8). + * ii. Attach the DB to the primary database. + * iii. Update the maxId of the database to MaxId table. + * iv. If the database is not compatible or corrupt, update the + * appropriate drive status in CMsvServer data structure. + * v. Fetch children of root entry and Local-Service entry from the DB to the cache. + */ +void CMsvIndexAdapter::ConstructL() + { +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Inside ConstructL()...")); +#endif + + // Create database adapter object. + iDbAdapter = CMsvDBAdapter::NewL(); + +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("The database object is created succesfully.")); +#endif + + iProgress.iCompleted = 0; + iProgress.iTotal = iProgress.iRemaining = INITIAL_NUM_ENTRIES; + + // Initialize freepool object. + iFreePoolInstance = CMsvEntryFreePool::Instance(); + iFreePoolInstance->SetEssentialParam(&iFolderListHeader, this); + + // Drive id zero will be synonym of KCurrentDriveId. Drive + // id of a drive (including current) drive will never be zero. + // Drive of the current drive will always by KCurrentDriveId. + TUint driveId = KCurrentDriveId; + + // Since driveId starts from 1, the first + // entry in iMaxMsvIdList is not used. + // Enter a dummy entry in location zero. + iMaxMsvIdList.InsertL(0, 0); + + iProgress.iCompleted++; + iProgress.iRemaining--; + iProgress.iId = KMsvRootIndexEntryId; + + // For each drive in the preferred drive list, perform followings... + // 1. Check if a valid message store is available. + // 2. If not, skip the drive. + // 3. Assign a new drive Id to the drive and + // update the entry in preferred drive list. + // 4. Attach the drive database to the primary DB. + // 5. Update the max Id list. + // 6. For current drive, fetch the children of root entry into cache. + // 7. Fetch children of localService into cache. + CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList(); + TUint curDriveIndex = driveList->CurrentDriveIndex(); + for(TInt index=curDriveIndex; indexCount(); index++) + { + TMsvPreferredDrive driveEntry; + driveList->DriveInfoL(index, driveEntry); + + if(EMsvMessageStoreAvailableStatus == driveEntry.status) + { + // Assign a unique drive-id. + driveList->UpdateDriveIdL(index, driveId); + + // Attach the database. + TMsvId maxId; + iDbAdapter->AttachDBL(driveEntry.driveNum, driveId, maxId); + + // Insert the max Id in the list. + iMaxMsvIdList.InsertL( ((maxId >= MaskTMsvId(driveId, KFirstFreeEntryId))? (maxId+1): MaskTMsvId(driveId, KFirstFreeEntryId)), driveId); + driveId++; + } + iProgress.iCompleted++; + iProgress.iRemaining--; + } + + iFirstFreeDriveId = (driveId%8)? (driveId%8): 2; + + // Initializing remaining entries in MaxMsvId list. + for(TInt index=driveId; index<8; index++) + { + iMaxMsvIdList.InsertL(NULL, index); + } + + // For current drive fetch children of root entries also. + RPointerArray childEntryList; + CleanupClosePushL(childEntryList); + // Create root entry node in the cache. A root folder node will never + // be swapped out of cache. The children of root entry will be the + // children of root entry in the current drive. + // Root id is masked to set the driveId in the folder node. + iRootNode = CMsvCacheVisibleFolder::NewL(MaskTMsvId(KCurrentDriveId, KMsvRootIndexEntryId)); + iFolderListHeader.AddFirst(*iRootNode); + iRootNode->SetComplete(ETrue); + iDbAdapter->GetChildrenL(KMsvRootIndexEntryId, childEntryList); + iRootNode->AddEntryListL(childEntryList, ETrue); + childEntryList.Reset(); + + // Fetch child of LocalServices. + CMsvCacheVisibleFolder* localServiceFolder = CMsvCacheVisibleFolder::NewL(MaskTMsvId(KCurrentDriveId, KMsvLocalServiceIndexEntryId)); + iFolderListHeader.AddFirst(*localServiceFolder); + iDbAdapter->GetChildrenL(KMsvLocalServiceIndexEntryId, childEntryList); + localServiceFolder->AddEntryListL(childEntryList, ETrue); + CleanupStack::PopAndDestroy(1); //childEntryList + + // Create root entry. + iRootEntry = iFreePoolInstance->EntryL(); //taking it from the freepool. + iRootEntry->Entry().iType = KUidMsvRootEntry; + iRootEntry->Entry().iDate.UniversalTime(); + iRootEntry->Entry().SetId(KMsvRootIndexEntryId); + iRootEntry->Entry().SetParent(KErrNotFound); + iRootEntry->Entry().SetOwner(ETrue); + iRootEntry->Entry().iSize = 0; + + // Update progress + iProgress.iCompleted = iProgress.iTotal; + iProgress.iRemaining = 0; + iProgress.iId = KMsvRootIndexEntryId; + +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Cache initialized succesfully.")); +#endif + + iIdle = CIdle::NewL(CActive::EPriorityIdle); + CompleteSelf(); + } + + + +/** +GetInPreparationIds +*/ +void CMsvIndexAdapter::GetInPreparationIds(CMsvEntrySelection& aSelection, TUint aDriveId /*DEFAULT = 0 */) + { + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->GetInPreparationIdL(aSelection, aDriveId)); + } + } + + +#else // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + +/** + * NewL() + * @param TFileName: DB File Name. + * @return The newly created index adapter. + * + * It returns an instance of CMsvIndexAdapter class. + */ +CMsvIndexAdapter* CMsvIndexAdapter::NewL(CMsvServer& aServer, const TFileName& aDbFileName) + { + CMsvIndexAdapter *self = new(ELeave) CMsvIndexAdapter(aServer); + CleanupStack::PushL(self); + + self->ConstructNewL(aDbFileName); + + CleanupStack::Pop(); + return self; + } + + + + +/** + * OpenL() + * @param TFileName: DB File Name. + * @return The newly created index adapter. + * + * It returns an instance of CMsvIndexAdapter class. + */ +CMsvIndexAdapter* CMsvIndexAdapter::OpenL(CMsvServer& aServer, const TFileName& aDbFileName) + { + CMsvIndexAdapter *self = new(ELeave) CMsvIndexAdapter(aServer); + CleanupStack::PushL(self); + + self->ConstructOpenL(aDbFileName); + + CleanupStack::Pop(); + return self; + } + + + + +/** + * ConstructNewL() + * + * The function is called from NewL(). + * 1. Check if a database with the passed name already exists. + * 1.1. If yes, open the database. + * 1.2. If no, create a new database with the same name. + * 2. Creates entry cache. + * 3. If a new database is created, create root entry also. + */ +void CMsvIndexAdapter::ConstructNewL(const TFileName& aDbFileName) + { +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Creating CMsvIndexAdapter instance...")); +#endif + + // Delete the database first, if it already exists. + CMsvDBAdapter::DeleteDB(aDbFileName); + + // Get the instance of free pool. + iFreePoolInstance = CMsvEntryFreePool::Instance(); + + // Create the new database with the same name. + // If this leaves, nothing can be done and let NewL() leave. + iDbAdapter = CMsvDBAdapter::NewL(aDbFileName); +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("A new database %s is created..."), &aDbFileName); +#endif + + iProgress.iTotal = iProgress.iRemaining = 1; //root + // Create root entry. + CreateInitialSetOfEntriesL(); + + +#ifndef _NO_SERVER_LOGGING_ +// iServer.Log(_L("Cache initialized succesfully.")); +#endif + + iNextCreateId = KFirstFreeEntryId; + iIdle = CIdle::NewL(CActive::EPriorityIdle); + CompleteSelf(); + } + + + + + +/** + * ConstructOpenL() + * + * The function is called from NewL(). + * 1. Check if a database with the passed name already exists. + * 1.1. If yes, open the database. + * 1.2. If no, create a new database with the same name. + * 2. Creates entry cache. + * 3. If a new database is created, it then read message resource file + * and creates entry as mentioned in cache and database. + */ +void CMsvIndexAdapter::ConstructOpenL(const TFileName& aDbFileName) + { +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Creating CMsvIndexAdapter instance...")); +#endif + + // Get the instance of free pool + iFreePoolInstance = CMsvEntryFreePool::Instance(); + + // Open the database. + iDbAdapter = CMsvDBAdapter::OpenL(aDbFileName); + +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Opening database %s is succesful."), &aDbFileName); +#endif + + iProgress.iTotal = iProgress.iRemaining = INITIAL_NUM_ENTRIES; + CreateInitialCacheL(); + +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Cache initialized succesfully.")); +#endif + + TMsvId tmpNextId = NULL; + iDbAdapter->GetMaxTMsvIdL(tmpNextId); + iNextCreateId = (tmpNextId >= KFirstFreeEntryId)? (tmpNextId+1) : KFirstFreeEntryId; + + iIdle = CIdle::NewL(CActive::EPriorityIdle); + CompleteSelf(); + } + + + + +/** + * CreateInitialSetOfEntries() + * + * The function is called from ConstructL(), whenever a new DB is created. + * The function creates root entry into database and cache. + */ +void CMsvIndexAdapter::CreateInitialSetOfEntriesL() + { + +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Inside CreateInitialSetOfEntriesL(): Creating standard entries for new DB.")); +#endif + + // 1. Initialize the instance of free pool. + iFreePoolInstance->SetEssentialParam(&iFolderListHeader, this); + + // 2. Creating root entry into database. + iRootEntry = iFreePoolInstance->EntryL(); //taking it from the freepool. + iRootEntry->Entry().iType = KUidMsvRootEntry; + iRootEntry->Entry().iDate.UniversalTime(); + iRootEntry->Entry().SetId(KMsvRootIndexEntryId); + iRootEntry->Entry().SetParent(KErrNotFound); + iRootEntry->Entry().iSize = 0; + + TMsvId visibleFolderId = KMsvRootIndexEntryId; + iDbAdapter->CreateEntryL(iRootEntry->Entry(), visibleFolderId); + + // 3. Add root entry to cache as well + CMsvCacheVisibleFolder* rootFolder = CMsvCacheVisibleFolder::NewL(KMsvRootIndexEntryId); + iFolderListHeader.AddFirst(*rootFolder); + rootFolder->SetComplete(ETrue); + + // Update progress + iProgress.iCompleted++; + iProgress.iRemaining--; + iProgress.iId = KMsvRootIndexEntryId; + } + + + + + +/** + * CreateInitialCache() + * + * The function is called from NewL(), whenever an existing DB is opened. + * It creates the intial cache. It creates root entry in cache and fetches + * child of root from DB and push them into cache. It then fetches child + * entries of LocalServices and push them too into cache. + * + * >>>> The function can be enhanced to load children of additional folders + * to cache along with the above standard folders. These additional folders + * can be mentioned by licensees in msgs.ini file. + */ +void CMsvIndexAdapter::CreateInitialCacheL() + { +#ifndef _NO_SERVER_LOGGING_ + iServer.Log(_L("Inside CreateInitialCache(): Initializing Cache.")); +#endif + + //1. Initialize the instance of free pool. + iFreePoolInstance->SetEssentialParam(&iFolderListHeader, this); + + // 2. Add root entry to cache. + CMsvCacheVisibleFolder* rootFolder = CMsvCacheVisibleFolder::NewL(KMsvRootIndexEntryId); + iFolderListHeader.AddFirst(*rootFolder); + // 2.1 Update progress + iProgress.iCompleted++; + iProgress.iRemaining--; + iProgress.iId = KMsvRootIndexEntryId; + + // 3. Fetch child of root entries from DB. + RPointerArray childrenOfRoot; + iDbAdapter->GetChildrenL(KMsvRootIndexEntryId, childrenOfRoot); + + // 3.1 Get the root entry from DB. + CMsvCacheEntry *rootEntry = NULL; + TMsvId visibleFolder = NULL; + iDbAdapter->GetEntryL(KMsvRootIndexEntryId, rootEntry, visibleFolder); + // 3.2 Copy the TMsvEntry of root from CMsvCacheEntry. + //Mem::Copy(&iRootEntry->Entry(), &rootEntry->Entry(), sizeof(TMsvEntry)); + iRootEntry = rootEntry; + // 3.3 Release the cache entry back to the freepool. + //iFreePoolInstance->ReleaseEntry(rootEntry); + + // 4. Add children to cache. + rootFolder->AddEntryListL(childrenOfRoot, ETrue); + // 4.1 Update progress + iProgress.iCompleted += 4; + iProgress.iRemaining -= 4; + iProgress.iId = KMsvLocalServiceIndexEntryId; + + // 5. Set the visible folder as complete + rootFolder->SetComplete(ETrue); + + + // 6. Fetch child of LocalServices + childrenOfRoot.Reset(); + iDbAdapter->GetChildrenL(KMsvLocalServiceIndexEntryId, childrenOfRoot); + + CMsvCacheVisibleFolder* localFolder = CMsvCacheVisibleFolder::NewL(KMsvLocalServiceIndexEntryId); + iFolderListHeader.AddFirst(*localFolder); + + // 7. Add children to cache. + localFolder->AddEntryListL(childrenOfRoot); + // 7.1 Update progress + iProgress.iCompleted = iProgress.iTotal; + iProgress.iRemaining = 0; + iProgress.iId = KMsvGlobalInBoxIndexEntryId; + + // 8. Set the visible folder as complete + localFolder->SetComplete(ETrue); + childrenOfRoot.Close(); + } + + + +/** +GetInPreparationIds +*/ +void CMsvIndexAdapter::GetInPreparationIds(CMsvEntrySelection& aSelection) + { + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->GetInPreparationIdL(aSelection)); + } + } + + +#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + +/** + *CompleteSelf + *This will make sure that Runl is called. + */ +void CMsvIndexAdapter::CompleteSelf() + { + iStatus=KRequestPending; + SetActive(); + TRequestStatus* st=&iStatus; + User::RequestComplete(st, KErrNone); + } + + + +/** + * RunL() + */ +void CMsvIndexAdapter::RunL() + { + // Set the state for the background state machine. + iBackgroundOperationState = ERemoveDeletedEntries; + iBackgroundOperationPerformed = 0; + } + + + + +/** + * DoCancel() + */ +void CMsvIndexAdapter::DoCancel() + { + iIdle->Cancel(); + } + + + + +/** + * AddEntry() + */ +TInt CMsvIndexAdapter::AddEntry(TMsvEntry& aEntry, TSecureId aOwnerId, TBool aAutoAssignId) + { + TRAPD(err, DoAddEntryL(aEntry, aOwnerId, aAutoAssignId)); + return err; + } + + +/** + * AddEntryNoCommit() + */ +TInt CMsvIndexAdapter::AddEntryNoCommit(TMsvEntry& aEntry, TSecureId aOwnerId, TBool aAutoAssignId) + { + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->BeginTransactionL()); + } + else + { + return ErrorState(); + } + TRAPD(err, DoAddEntryL(aEntry, aOwnerId, aAutoAssignId, EFalse)); + if(err) + { + RollbackAdditions(); + } + return err; + } + + + +/** + * DoAddEntryL() + * + * Add the entry to the index and then the file. + * If either fails the function leaves. Creates + * its own copy of TMsvEntry (SetEntryL() code). + * This can be optimized further. + */ +void CMsvIndexAdapter::DoAddEntryL(TMsvEntry& aEntry, TSecureId aOwnerId, TBool aAutoAssignId, TBool aCommitToDb) + { + // Leave if the message store is not currently available. + User::LeaveIfError(iErrorState); + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + // get and set the unique id + if (aAutoAssignId) + { + aEntry.SetId(NextId(GetDriveId(aEntry.Parent()))); + } + + if (aEntry.iType == KUidMsvServiceEntry) + { + if(KMsvLocalServiceIndexEntryId == UnmaskTMsvId(aEntry.iId)) + { + aEntry.iServiceId = KMsvLocalServiceIndexEntryId; + } + else + { + aEntry.iServiceId = aEntry.Id(); + } + } +#else // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + if (aAutoAssignId) + { + aEntry.SetId(NextId()); + } + + if (aEntry.iType == KUidMsvServiceEntry) + { + aEntry.iServiceId = aEntry.Id(); + } +#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + CMsvCacheEntry* parentEntry = NULL; + FindEntryL(aEntry.Parent(), parentEntry); + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + if(UnmaskTMsvId(aEntry.iId) == KMsvDeletedEntryFolderEntryId) + { + aEntry.SetVisibleFolderFlag(ETrue); + } + else +#endif + { + // If entry is set to visible while creation, then set the visible folder + // flag to ETrue, this will not change for the lifetime of an TMsvEntry. + if(parentEntry->Entry().VisibleFolderFlag()) + { + if( (aEntry.Visible()) && + (aEntry.iType == KUidMsvFolderEntry || aEntry.iType == KUidMsvServiceEntry) + ) + { + aEntry.SetVisibleFolderFlag(ETrue); + } + else + { + aEntry.SetVisibleFolderFlag(EFalse); + } + } + else + { + aEntry.SetVisibleFolderFlag(EFalse); + } + } + + aEntry.SetOwner(EFalse); + TMsvId visibleFolderId; + if(!GetVisibleFolderId(aEntry.Parent(), visibleFolderId)) + { + User::Leave(KErrNotFound); + } + + // Add entry to cache. + CMsvCacheEntry* newCacheEntry = NULL; + newCacheEntry = iFreePoolInstance->EntryL(); + // Set the owner ID of the entry - the SID of the owning process. + newCacheEntry->SetEntryOwnerId(aOwnerId); + + CMsvCacheVisibleFolder* visibleFolder = NULL; + TRAPD(err, newCacheEntry->SetEntryL(aEntry); + visibleFolder = AddEntryToCacheL(visibleFolderId, newCacheEntry); + ); + if(err) + { + iFreePoolInstance->ReleaseEntryWithoutTransaction(newCacheEntry); + User::Leave(err); + } + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + TRAP(err, + if(aCommitToDb) + iDbAdapter->BeginTransactionL(); + iDbAdapter->CreateEntryL(aEntry, visibleFolderId); + // Set the owner flag of parent entry if not already done. + // For standard entry created from indexContext, parent Id will not be masked. + iDbAdapter->UpdateOwnerStatusL(MaskTMsvId(GetDriveId(aEntry.iId), aEntry.Parent()), parentEntry->Entry(), ETrue); + if(aCommitToDb) + iDbAdapter->CommitTransactionL(); + ); +#else + TRAP(err, + if(aCommitToDb) + iDbAdapter->BeginTransactionL(); + iDbAdapter->CreateEntryL(aEntry, visibleFolderId); + // Set the owner flag of parent entry if not already done. + // For standard entry created from indexContext, parent Id will not be masked. + iDbAdapter->UpdateOwnerStatusL(aEntry.Parent(), parentEntry->Entry(), ETrue); + if(aCommitToDb) + iDbAdapter->CommitTransactionL(); + ); +#endif + if(err & KSqlErrConstraint != err) + { + if(aCommitToDb) + iDbAdapter->RollbackTransactionL(); + // This will not leave... + TRAP_IGNORE(visibleFolder->DeleteEntryL(newCacheEntry->GetId())); + User::Leave(err); + } + parentEntry->Entry().SetOwner(ETrue); + + if(!aCommitToDb) + { + // Add entry to the list of non-committed entries. + CNonCommittedAddedEntries* nonCommittedAddedEntries = new(ELeave)CNonCommittedAddedEntries(visibleFolderId, newCacheEntry); + CleanupStack::PushL(nonCommittedAddedEntries); + iNonCommittedAddedEntryList.AppendL(nonCommittedAddedEntries); + CleanupStack::Pop(nonCommittedAddedEntries); + } + + if(aEntry.iType.iUid == KUidMsvMessageEntryValue && CMSvSearchSortCacheManager::Instance()->iManagerEntry != NULL) + { + TMsgType aType(ENewMsg); + if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0) + { + CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aEntry.Id(),aType); + } + } + } + + + + +/** + * GetVisibleFolderId() + * + * Returns ETrue, if the parent entry exists. Otherwise returns EFalse. + * IN: Parent Id of the entry, whose immediate visible folder has to be found. + * OUT: Visible folder Id. + */ +TBool CMsvIndexAdapter::GetVisibleFolderId(TMsvId aParentId, TMsvId& aVisibleFolderId) + { + // If the entry is either a root entry (parent=KErrNotFound) or + // child of root entry its visibleFolderId will be root entry itself. + if((KErrNotFound == aParentId) || (KMsvRootIndexEntryId == aParentId)) + { + aVisibleFolderId = KMsvRootIndexEntryId; + return ETrue; + } + + // Get the visible flag of parent entry. + + // If parent entry is visible, then immediateVisibleFolder of child + // should be parent Id. And if it is invisible then child's immediateVisibleFolder + // should be same as parent's immediateVisibleFolder. + // First check in cache. + if(!iFolderListHeader.IsEmpty()) + { + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + // Check if the current folder node is parent. + if( aParentId == folderNode->GetFolderId() ) + { + aVisibleFolderId = aParentId; + return ETrue; + } + } + dqIter.SetToFirst(); + CMsvCacheEntry* entry; + while((folderNode = dqIter++)!=NULL) + { + // Check if the current folder node is parent. + if(folderNode->GetEntry(aParentId, entry)) + { + if(entry->Entry().VisibleFolderFlag()) + { + aVisibleFolderId = aParentId; + } + else + { + //If the visible folder is not the parent, then + //the current folder must be the visible folder, + //as otherwise the parent would have been under another + //folder node. + aVisibleFolderId = folderNode->GetFolderId(); + } + return ETrue; + } + } + } + + // Entry cannot be found in cache, + // Now search in DB. + TInt err = KErrNone; + if(iDbAdapter) + { + TRAP(err, iDbAdapter->GetVisibleFolderIdL(aParentId, aVisibleFolderId)); + } + else + { + return EFalse; + } + if(KErrNone == err) + { + return ETrue; + } + return EFalse; + } + + + + + +/** + * DeleteEntry() + */ +TInt CMsvIndexAdapter::DeleteEntry(TMsvId aId) + { + CMsvEntrySelection* selection = NULL; + + TRAPD(error, selection = new(ELeave)CMsvEntrySelection; selection->AppendL(aId);); + if(KErrNone == error) + { + error = DeleteSelection(*selection); + } + + TMsgType aType(EDeletedMsg); + if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0 && error == KErrNone) + { + CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aId,aType); + } + + delete selection; + return error; + } + + + + + +/** + * DeleteSelection + * Deletes a selection of entries. + * + * @param aSelection Selection of TMsvIds of the entries to be deleted + * @return TInt System-wide error code. + */ +TInt CMsvIndexAdapter::DeleteSelection(const CMsvEntrySelection& aSelection) + { + TInt count = aSelection.Count(); + + if(!iDbAdapter) + { + return ErrorState(); + } + // Only start a transaction if there are more than 1 entries to be deleted. + if(count > 1) + { + TRAP_IGNORE(iDbAdapter->BeginTransactionL()); + } + // If the transaction is opened, deleted + // entry will be released temporarily. + iFreePoolInstance->BeginTransaction(); + TInt error = KErrNone; + TRAP(error, DoDeleteSelectionL(aSelection)); + if (error) + { + if(count > 1) + { + TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); + } + iFreePoolInstance->RollbackTransaction(); + } + else + { + if(count > 1) + { + TRAP_IGNORE(iDbAdapter->CommitTransactionL()); + } + iFreePoolInstance->CommitTransaction(); + } + return error; + } + + + + + +/** + * DoDeleteSelectionL + */ +void CMsvIndexAdapter::DoDeleteSelectionL(const CMsvEntrySelection& aSelection) + { + User::LeaveIfError(iErrorState); + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + const TInt count = aSelection.Count(); + if(count <= 0) + { + return; + } + + CMsvCacheEntry* entry = NULL; + TBool releaseEntry = FindEntryL(aSelection.At(0), entry, EFalse); + TMsvId parentId = entry->Entry().Parent(); + if(releaseEntry) + { + iFreePoolInstance->ReleaseEntry(entry, ETrue); + } + CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection; + CleanupStack::PushL(children); + + for (TInt index=0; indexDeleteEntryL(aSelection.At(index))); + if(err != KErrNotFound && err != KErrNone) + { + User::Leave(err); + } + else if(err == KErrNone) + { + found = ETrue; + break; + } + } + if(!found) + { + if(iDbAdapter) + { + if(iDbAdapter->EntryExistsL(aSelection.At(index))) + { + User::Leave(KErrAccessDenied); + } + } + else + { + User::Leave(ErrorState()); + } + User::Leave(KErrNotFound); + } + + if(iDbAdapter) + { + iDbAdapter->DeleteEntryL(aSelection.At(index)); + } + else + { + User::Leave(ErrorState()); + } + } + //Find the parent entry in cache and check for any children. + //If there are no more children under the parent, then + //reset the owner flag in cache and database. + TRAPD(err, releaseEntry = FindEntryL(parentId, entry, EFalse)); + //If parent was also deleted then nothing left to do. + if(err == KErrNotFound) + { + CleanupStack::PopAndDestroy(); //children + return; + } + TRAP(err, + GetChildrenIdL(parentId, *children); + if(0 == children->Count() && entry->Entry().Owner()) + { + if(iDbAdapter) + { + iDbAdapter->UpdateOwnerStatusL(parentId, entry->Entry(), EFalse); + } + else + { + User::Leave(ErrorState()); + } + entry->Entry().SetOwner(EFalse); + } + ); + // Release the entry, since it is not added to cache. + if(releaseEntry) + { + iFreePoolInstance->ReleaseEntry(entry, ETrue); + } + User::LeaveIfError(err); + CleanupStack::PopAndDestroy(); //children + } + + + + + +/** + * ExpandSelectionRecursively() + * Expands a selection to include all the descendents of the entries. They are order + * in such a way that they can be deleted in order, children first + * + * @param aSelection CMsvEntrySelection, which contains the TMsvIds of the entry passed. + * @return TInt System wide error code. + */ +TInt CMsvIndexAdapter::ExpandSelectionRecursively(CMsvEntrySelection& aSelection) + { + TInt error = KErrNone; + iOrigEntryPos=0; + iRecursionSelection=&aSelection; + TInt count = aSelection.Count(); + while (iOrigEntryPos < count) + { + TMsvId entryId = aSelection.At(iOrigEntryPos); + if( !EntryExists(entryId) ) + { + error = KErrNotFound; + aSelection.Delete(iOrigEntryPos); + } + else + { + // if the entry has children + CMsvEntrySelection* children = NULL; + TRAPD(err, children = new (ELeave)CMsvEntrySelection); + if(KErrNone != err) + { + return err; + } + TInt leave = KErrNone; + TRAP(leave, GetChildrenIdL(entryId, *children)); + if(KErrNone != leave) + { + delete children; + return leave; + } + TInt count = children->Count(); + for(TInt index=0; indexAt(index))); + if(KErrNone != leave) + { + delete children; + return leave; + } + } + delete children; + ++iOrigEntryPos; + } + } + return error; + } + + + + + +/** + * DoExpandSelectionRecursivelyL + * Expands a selection to include all the descendents of the entry with TMsvId aId. + * + * @param aId TMsvId of the entire + * @return void + */ +void CMsvIndexAdapter::DoExpandSelectionRecursivelyL(TMsvId aId) + { + // include this entry + iRecursionSelection->InsertL(iRecursionIndex,aId); + iOrigEntryPos++; + + // if aEntry has children + CMsvEntrySelection* children = new (ELeave)CMsvEntrySelection; + CleanupStack::PushL(children); + + GetChildrenIdL(aId, *children); + + TInt count = children->Count(); + for(TInt index=0; indexAt(index)); + } + + CleanupStack::PopAndDestroy(1, children); //children + } + + + +/** + * GetChildrenId() + * @param TMsvId: Id os the parent. + * @param CMsvEntrySelection: List of child Ids. + * + * This function should be used only by ExpandSelectionRecursively() + * and DoExpandSelectionRecursivelyL(). + * --- 557 --- + * If aId is an standard id ensure that it is masked. If the aId is + * not masked the function will return children from current drive. + */ +void CMsvIndexAdapter::GetChildrenIdL(TMsvId aId, CMsvEntrySelection& aSelection) + { + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + if(IsStandardId(aId)) + { + TUint driveId = GetDriveId(aId); + TMsvId unmaskedId = UnmaskTMsvId(aId); + + dqIter.SetToFirst(); + // Look for the entry in cache. + if(!iFolderListHeader.IsEmpty()) + { + while(NULL != (folderNode = dqIter++)) + { + if((unmaskedId == folderNode->GetFolderId()) && (driveId == folderNode->GetDrive())) + { + if(!folderNode->GetChildrenIdL(aSelection)) + { + if(iDbAdapter) + { + iDbAdapter->GetChildrenIdL(aId, aSelection); + } + else + { + User::Leave(ErrorState()); + } + } + return; + } + } + } + if(iDbAdapter) + { + iDbAdapter->GetChildrenIdL(aId, aSelection); + } + else + { + User::Leave(ErrorState()); + } + return; + } +#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + dqIter.SetToFirst(); + // Look for the entry in cache. + if(!iFolderListHeader.IsEmpty()) + { + // Check if aId is one of the visible folder, + // already present in the cache. + while(NULL != (folderNode = dqIter++)) + { + if(aId == folderNode->GetFolderId()) + { + // Fetch children. Returns EFalse if unable + // to get them. + if(!folderNode->GetChildrenIdL(aSelection)) + { + if(iDbAdapter) + { + // Childrens not fetched. Get them from DB. + iDbAdapter->GetChildrenIdL(aId, aSelection); + } + else + { + User::Leave(ErrorState()); + } + } + return; + } + } + + // Entry is not present in the visibleFolder list. + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + if(NULL != entry->ChildIdArray()) + { + RArray* childId = entry->ChildIdArray(); + TInt size = childId->Count(); + TInt index = 0; + while(index < size) + { + aSelection.AppendL((*childId)[index++]); + } + return; + } + else + { + if(iDbAdapter) + { + iDbAdapter->GetChildrenIdL(aId, aSelection); + } + else + { + User::Leave(ErrorState()); + } + RArray* childId = new(ELeave) RArray; + TInt size = aSelection.Count(); + TInt index = 0; + while(index < size) + { + TInt err = childId->Append(aSelection[index++]); + if(KErrNone != err) + { + childId->Reset(); + childId->Close(); + delete childId; + User::Leave(err); + } + } + entry->SetChildIdArray(childId); + return; + } + } + } + + // Could not find parent entry in cache. + // Check in DB now. + if(iDbAdapter) + { + iDbAdapter->GetChildrenIdL(aId, aSelection); + } + else + { + User::Leave(ErrorState()); + } + } + + + + +/** + * ChangeEntry + * Change the contents of a TMsvEntry. + * + */ +TInt CMsvIndexAdapter::ChangeEntry(const TMsvEntry& aEntry, TSecureId aOwnerId, TBool aForcedUpdate) + { + TRAPD(err, DoChangeEntryL(aEntry, aOwnerId, EFalse, aForcedUpdate, ETrue)); + return err; + } + + +/** + * ChangeEntryNoCommit + */ +TInt CMsvIndexAdapter::ChangeEntryNoCommit(const TMsvEntry& aEntry, TSecureId aOwnerId, TBool aForcedUpdate) + { + if(!iDbAdapter) + { + return ErrorState(); + } + + TRAP_IGNORE(iDbAdapter->BeginTransactionL()); + TRAPD(err, DoChangeEntryL(aEntry, aOwnerId, EFalse, aForcedUpdate, EFalse)); + if(err) + { + RollbackChanges(); + } + return err; + } + + +/** + * ChangeEntryInternal() + */ +TInt CMsvIndexAdapter::ChangeEntryInternal(const TMsvEntry& aEntry, TSecureId aOwnerId) + { + TRAPD(err, DoChangeEntryL(aEntry, aOwnerId, ETrue, EFalse, ETrue)); + return err; + } + + + +/** + * DoChangeEntryL + */ +void CMsvIndexAdapter::DoChangeEntryL(const TMsvEntry& aNewEntryContents, TSecureId aOwnerId, TBool aChangeStandardFolder, TBool aForcedUpdate, TBool aCommitToFile) + { + // Leave if the message store is not currently available + User::LeaveIfError(iErrorState); + + // 1. Handle root entry differently. + if(aNewEntryContents.Id() == KMsvRootIndexEntryId) + { + TMsvEntry& entry = const_cast (aNewEntryContents); + iDbAdapter->UpdateEntryL(entry, KMsvRootIndexEntryId, EFalse); + iRootEntry->Entry() = entry; + return; + } + + // 2. Find entry in cache. + CMsvCacheEntry* oldEntry = NULL; + CMsvCacheVisibleFolder* oldFolderNode = NULL; + GetVisibleFolderDetailsL(aNewEntryContents.Id(), oldEntry, oldFolderNode); + + // 3. Validate entry. + // If the entry is not present in cache, it means it is not locked. + // Can only change a locked entry and not one marked as a standard + // folder unless aChangeStandardFolder is true. + if( (!oldEntry->IsEntryLocked()) || (aChangeStandardFolder==EFalse && oldEntry->Entry().StandardFolder()) ) + { + User::Leave(KErrAccessDenied); + } + + // Reserving space in changed entry list array. + if(!aCommitToFile) + { + iNonCommittedChangedEntryList.ReserveL(iNonCommittedChangedEntryList.Count()+1); + } +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + // We do not support movement of entry to a folder present in a different drive. + if( oldEntry->Entry().Parent() != KErrNotFound && //-1 will appear to be masked. disregard this. + GetDriveId(aNewEntryContents.Id()) != GetDriveId(oldEntry->Entry().Parent())) + { + User::Leave(KErrNotSupported); + } +#endif + + TMsvId oldParentId = oldEntry->Entry().Parent(); + TMsvId oldVisibleFolderId = oldFolderNode->GetFolderId(); + + // Check if the parent id is also changed. + CMsvCacheEntry* oldParentEntry = NULL; + CMsvCacheEntry* newParentEntry = NULL; + TBool resetOldParentOwnerFlag = EFalse; + CMsvEntrySelection* descendentList = NULL; + TMsvId newParentId = aNewEntryContents.Parent(); + CMsvCacheVisibleFolder* newVisibleFolderNode = NULL; + if(oldParentId != newParentId) + { + // YES, PARENT ID IS CHANGED. + // These steps are similar to MoveEntry() + newVisibleFolderNode = DoChangeEntryPreambleL(oldEntry, + newParentId, + oldParentEntry, + newParentEntry, + oldVisibleFolderId, + resetOldParentOwnerFlag, + descendentList + ); + } // if(oldParentId != newParentId) + TMsvId newVisibleFolderId = newVisibleFolderNode ? newVisibleFolderNode->GetFolderId() : oldVisibleFolderId; + // Update entry in cache. + TBool changedPrivateInfo; + CMsvCacheEntry* bkpEntry = NULL; + TRAPD(err, bkpEntry = iFreePoolInstance->EntryL(); + bkpEntry->SetEntryL(oldEntry->Entry()); + bkpEntry->SetEntryOwnerId(oldEntry->EntryOwnerId()); + oldEntry->CopyEntryL(aNewEntryContents, changedPrivateInfo); + ); + if(err) + { + iFreePoolInstance->ReleaseEntryWithoutTransaction(bkpEntry); + if(newVisibleFolderNode) + newVisibleFolderNode->DeleteEntryL(oldEntry->GetId()); + User::Leave(err); + } + UpdateDates(*oldEntry, EFalse); + if(aNewEntryContents.Connected()) + { + oldEntry->Entry().SetConnected(EFalse); + } + if(aForcedUpdate || changedPrivateInfo && aOwnerId != KMsvServerId ) + { + oldEntry->SetEntryOwnerId(aOwnerId); + } + + TRAP(err, + if(aCommitToFile) + iDbAdapter->BeginTransactionL(); + // Update the actual entry + if(oldParentId != newParentId) + { + iDbAdapter->UpdateEntryL(oldEntry->Entry(), newVisibleFolderId); + } + else + { + iDbAdapter->UpdateEntryL(oldEntry->Entry(), newVisibleFolderId, EFalse); + } + // Reset old parent owner flag. + if(resetOldParentOwnerFlag) + iDbAdapter->UpdateOwnerStatusL(oldParentId, oldParentEntry->Entry(), EFalse); + // Set new parent owner flag. + if(newParentEntry && (!newParentEntry->Entry().Owner())) + iDbAdapter->UpdateOwnerStatusL(newParentId, newParentEntry->Entry(), ETrue); + // Update the child entries visibleFolderId in DB. + if(descendentList) + iDbAdapter->UpdateVisibleFolderL(descendentList, newVisibleFolderId); + if(aCommitToFile) + iDbAdapter->CommitTransactionL(); + ); + if(err) + { + if(aCommitToFile) + TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); + oldEntry->SetEntryOwnerId(bkpEntry->EntryOwnerId()); + oldEntry->CopyEntryL(bkpEntry->Entry(), changedPrivateInfo); + iFreePoolInstance->ReleaseEntryWithoutTransaction(bkpEntry); + if(newVisibleFolderNode) + newVisibleFolderNode->DeleteEntryL(oldEntry->GetId()); + User::Leave(err); + } + // Nothing should fail after this. Ensure that + // all leaving function after this does not leave. + if(aCommitToFile) + { + iFreePoolInstance->ReleaseEntryWithoutTransaction(bkpEntry); + DoChangeEntryPostamble(oldFolderNode, newVisibleFolderId, oldEntry->GetId(), descendentList, newParentEntry, oldParentEntry, resetOldParentOwnerFlag); + if(descendentList) + { + CleanupStack::PopAndDestroy(descendentList); + } + } // if(aCommitToFile) + else + { + // Store relevant data for later commit. + TNonCommittedChangedEntries entryDetails(oldFolderNode, + newVisibleFolderNode, + oldEntry, + bkpEntry, + oldParentEntry, + newParentEntry, + descendentList, + resetOldParentOwnerFlag); + iNonCommittedChangedEntryList.Append(entryDetails); + if(descendentList) + CleanupStack::Pop(descendentList); + } + + // Update entry in SearchSort delta cache. + if(aNewEntryContents.iType.iUid == KUidMsvMessageEntryValue && CMSvSearchSortCacheManager::Instance()->iManagerEntry != NULL) + { + TMsgType aType(EUpdatedMsg); + if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0) + { + CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aNewEntryContents.Id(), aType); + } + } + } + + + +// Used only by DoChangeEntryL() +CMsvCacheVisibleFolder* CMsvIndexAdapter::DoChangeEntryPreambleL(CMsvCacheEntry*& aOldEntry, TMsvId aNewParentId, CMsvCacheEntry*& aOldParentEntry, CMsvCacheEntry*& aNewParentEntry, TMsvId aOldVisibleFolderId, TBool& aResetOldParentOwnerFlag, CMsvEntrySelection*& aDescendentList) + { + // YES, PARENT ID IS CHANGED. + // These steps are similar to MoveEntry() + TBool des; + IsADescendent(aOldEntry->Entry().Id(), aNewParentId, des); + if (des) + { + User::Leave(KErrArgument); + } + + // Fetch the parent entries. + FindEntryL(aOldEntry->Entry().Parent(), aOldParentEntry); + FindEntryL(aNewParentId, aNewParentEntry); + + // Check if the owner flag of the old parent needs to be reset. + CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection; + CleanupStack::PushL(children); + GetChildrenIdL(aOldEntry->Entry().Parent(), *children); + if(1 == children->Count() && aOldParentEntry->Entry().Owner()) + { + aResetOldParentOwnerFlag = ETrue; + } + CleanupStack::PopAndDestroy(); //children + + // If required, pre-allocate memory for child array of newParentEntry. + if(aNewParentEntry->ChildIdArray()) + { + // This to ensure that future append does not fail. + aNewParentEntry->ChildIdArray()->ReserveL(aNewParentEntry->ChildIdArray()->Count() + 1); + } + + // Find the visibleFolder of new parent. + TMsvId newVisibleFolderId = NULL; + if(!GetVisibleFolderId(aNewParentId, newVisibleFolderId)) + { + User::Leave(KErrNotFound); + } + + // Check if the visible folders are different. + CMsvCacheVisibleFolder* newVisibleFolderNode = NULL; + if(aOldVisibleFolderId != newVisibleFolderId) + { + // If the visible folders are different, check if + // child entries also needs to be moved. + if(!aOldEntry->Entry().VisibleFolderFlag()) + { + // If yes, create a descendent list of the entry. + aDescendentList = new(ELeave) CMsvEntrySelection; + CleanupStack::PushL(aDescendentList); + aDescendentList->AppendL(aOldEntry->GetId()); + User::LeaveIfError(ExpandSelectionRecursively(*aDescendentList)); + } + + // Add duplicate entry under new visible folder. Removing + // entry from old visible folder is done later. + CMsvCacheEntry* newCacheEntry = iFreePoolInstance->EntryL(); + TRAPD(err, newCacheEntry->DupNDestroyL(aOldEntry); + newVisibleFolderNode = AddEntryToCacheL(newVisibleFolderId, newCacheEntry); + ); + if(err) + { + iFreePoolInstance->ReleaseEntryWithoutTransaction(newCacheEntry); + User::Leave(err); + } + aOldEntry = newCacheEntry; + } // if(oldVisibleFolderId != newVisibleFolderId) } + return newVisibleFolderNode; + } + + + +// Used only by DoChangeEntryL() +void CMsvIndexAdapter::DoChangeEntryPostamble(CMsvCacheVisibleFolder* aOldFolderNode, TMsvId aNewVisibleFolderId, TMsvId aEntryId, CMsvEntrySelection* aDescendentList, CMsvCacheEntry* aNewParentEntry, CMsvCacheEntry* aOldParentEntry, TBool aResetOldParentOwnerFlag) + { + // Removing entry from old visible folder. + if(aOldFolderNode->GetFolderId() != aNewVisibleFolderId) + { + // Will never leave. + //aOldFolderNode->DeleteEntryL(oldEntry->GetId()); + TRAP_IGNORE(aOldFolderNode->DeleteEntryL(aEntryId)); + } + + // Removing child entries from old visible folder. + if(aDescendentList) + { + for(TInt index=(aDescendentList->Count()-2); index>=0; index--) + { + // Can leave with KErrNotFound. + TRAP_IGNORE(aOldFolderNode->DeleteEntryL(aDescendentList->At(index))); + } + } + + // Add child id to new parent child array. + if(aNewParentEntry && aNewParentEntry->ChildIdArray()) + { + // This will not leave, as memory is already reserved. + TRAP_IGNORE(aNewParentEntry->ChildIdArray()->AppendL(aEntryId)); + } + + // Remove child from old parent's child array. + if(aOldParentEntry) + { + RArray* oldParentChildArr = aOldParentEntry->ChildIdArray(); + if(oldParentChildArr) + { + TInt pos = oldParentChildArr->Find(aEntryId); + if(pos != KErrNotFound) + { + oldParentChildArr->Remove(pos); + } + } + } + + // Update owner flag of parent entries. + if(aResetOldParentOwnerFlag) + { + aOldParentEntry->Entry().SetOwner(EFalse); + } + if(aNewParentEntry) + { + aNewParentEntry->Entry().SetOwner(ETrue); + } + } + + + + +/** + * GetEntry() + */ +TInt CMsvIndexAdapter::GetEntry(TMsvId aId, TMsvEntry*& aEntry) + { + TSecureId dummy; + return GetEntry(aId, aEntry, dummy); + } + + + +/** + * GetEntryNoCache() + * If aAddToCache is EFalse, entry will not be added to cache, + * if the entry is fetched from DB. The user of this function + * should ensure that it releses the memory occupied by entry + * to the freepool. + */ +TInt CMsvIndexAdapter::GetEntryNoCache(TMsvId aId, TMsvEntry* aEntry) + { + if(KMsvRootIndexEntryId == aId) + { + *aEntry = iRootEntry->Entry(); + return KErrNone; + } + + TBool aIsDanglingEntry = EFalse; + CMsvCacheEntry* serverEntry=NULL; + TRAPD(err, aIsDanglingEntry = FindEntryL(aId, serverEntry, EFalse)); + + if (err == KErrNone) + { + *aEntry = serverEntry->Entry(); + // If entry is not present in cache, + // it should be handled carefully. + if(aIsDanglingEntry) + { + // Release CMsvCacheEntry to freepool. + iFreePoolInstance->ReleaseEntry(serverEntry, ETrue); + } + return KErrNone; + } + else + { + return KErrNotFound; + } + } + + + + +/** + * GetEntry() + */ +TInt CMsvIndexAdapter::GetEntry(TMsvId aId, TMsvEntry*& aEntry, TSecureId& aOwnerId) + { + if(KMsvRootIndexEntryId == aId) + { + aEntry = &iRootEntry->Entry(); + aOwnerId = 0; + return KErrNone; + } + + CMsvCacheEntry* serverEntry=NULL; + TRAPD(err, FindEntryL(aId, serverEntry)); + + if (err == KErrNone) + { + aEntry = &serverEntry->Entry(); + aOwnerId = serverEntry->EntryOwnerId(); + return KErrNone; + } + else + { + return KErrNotFound; + } + } + + + +/** + * FindEntryInCache() + * @param aId: Entry Id. + * @return aEntry: CMsvCacheEntry + * @return bool: ETrue if entry found, otherwise EFalse. + * + * Find an entry only in cache and return the entry. + */ +TBool CMsvIndexAdapter::FindEntryInCache(TMsvId aId, CMsvCacheEntry*& aEntry) + { + if(KMsvRootIndexEntryId == aId) + { + aEntry = iRootEntry; + return ETrue; + } + + // If cache is not empty. + if(!iFolderListHeader.IsEmpty()) + { + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + + while((folderNode = dqIter++)!=NULL) + { + if(folderNode->GetEntry(aId, aEntry)) + { + return ETrue; + } + } + } + aEntry = NULL; + return EFalse; + } + + + +/** + * FindEntryL() + * + * WARNING: + * Returns CMsvCacheEntry which is still owned by Cache. so aEntry should + * never be deleted by the caller. + * + * If the entry is fetched from DB and not added to cache, the function + * returns ETrue, indicating that aEntry should be release to the freepool + * by the caller. + */ +TBool CMsvIndexAdapter::FindEntryL(TMsvId aId, CMsvCacheEntry*& aEntry, TBool aAddToCache /*DEFAULT=ETrue */) + { + // First search the entry in cache, + if(FindEntryInCache(aId, aEntry)) + { + return EFalse; + } + + // Entry cannot be found in cache, + // Now search in DB. + TMsvId visibleEntryId; + if(iDbAdapter) + { + iDbAdapter->GetEntryL(aId, aEntry, visibleEntryId); + } + else + { + User::Leave(ErrorState()); + } + + if(aAddToCache) + { + // Add entry to cache. + AddEntryToCacheL(visibleEntryId, aEntry); + } + else + { + // If the entry is not added to cache, we need not start + // the background operation. We can return ETrue saying + // entry is dangling and needs to be freed. + return ETrue; + } + if (!iIdle->IsActive()) + { + iIdle->Start(TCallBack(BackGroundOperations, this)); + } + return EFalse; + } + + + + +/** + * AddEntryNoVisibleL() + */ +void CMsvIndexAdapter::AddEntryNoVisibleL(CMsvCacheEntry* aEntry) + { + TMsvId visibleFolder; + if(GetVisibleFolderId(aEntry->Entry().Parent(), visibleFolder)) + { + AddEntryToCacheL(visibleFolder, aEntry); + } + } + + + +/** + * AddEntryToCacheL() + */ +CMsvCacheVisibleFolder* CMsvIndexAdapter::AddEntryToCacheL(TMsvId aVisibleEntryId, CMsvCacheEntry* aEntry) + { +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + TUint driveId = GetDriveId(aEntry->GetId()); + if(IsStandardId(aEntry->GetId())) + { + aEntry->Entry().SetId(UnmaskTMsvId(aEntry->GetId())); + } +#endif + // First search the visibleEntry in cache, + // if cache is not empty... + if(!iFolderListHeader.IsEmpty()) + { + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + + while((folderNode = dqIter++)!=NULL) + { + TMsvId folderId = folderNode->GetFolderId(); + if(aVisibleEntryId == folderId) + { + // Visible folder exists. +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + // For standard entries also check for driveId. + if(IsStandardId(aVisibleEntryId)) + { + if(driveId == folderNode->GetDrive()) + { + folderNode->AddEntryL(aEntry); + return folderNode; + } + } + else +#endif + { + // Add entry to this folder. + folderNode->AddEntryL(aEntry); + return folderNode; + } + } + } + } + + // Visible folder not found in the folder list. + // Create the visible folder and add it to the + // beginning of the list. +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(MaskTMsvId(GetDriveId(aEntry->GetId()), aVisibleEntryId)); +#else + CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(aVisibleEntryId); +#endif + iFolderListHeader.AddFirst(*newFolder); + + // Now add entry to the visible folder + newFolder->AddEntryL(aEntry); + return newFolder; + } + + + +/** + * GetChildrenL() + */ +void CMsvIndexAdapter::GetChildrenL(TMsvId aId, CArrayPtr& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm) + { + TSecureId dummy = KMsvServerId.iId; + GetChildrenL(aId, aSelection, aOrdering, aMtm, EFalse, dummy); + } + + + +/** + * GetChildrenL() + */ +void CMsvIndexAdapter::GetChildrenL(TMsvId aId, CArrayPtr& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm, TBool aFilterByOwnerId, TSecureId aOwnerId) + { + CMsvEntryFilter* filter = CMsvEntryFilter::NewLC(); + filter->SetOrder(aOrdering); + filter->SetSortMtm(aMtm); + CMsvEntryArray* entries = DoGetChildrenL(aId, *filter, aFilterByOwnerId, aOwnerId); + CleanupStack::PushL(entries); + + TInt count = entries->Count(); + for (TInt ii=0; iiAt(ii)); + } + CleanupStack::PopAndDestroy(2, filter); // filter and entries + } + + + + +/** + * DoGetChildrenL + */ +CMsvEntryArray* CMsvIndexAdapter::DoGetChildrenL(TMsvId aId, const CMsvEntryFilter& aFilter, TBool aFilterByOwnerId, TSecureId aOwnerId) + { + // Check if entry for aId exists... + CMsvCacheEntry* entry=NULL; +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + TUint driveId = GetDriveId(aId); + TMsvId unmaskedId = UnmaskTMsvId(aId); + if(IsStandardId(aId)) + { + FindEntryL(unmaskedId, entry); + } + else +#endif + { + FindEntryL(aId, entry); + } + + TBool isParentAVisibleFolder = EFalse; + if(entry->Entry().VisibleFolderFlag()) + { + isParentAVisibleFolder = ETrue; + } + + CArrayFixFlat* mtmList = new(ELeave) CArrayFixFlat(KMsvMtmListGranularity); + CleanupStack::PushL(mtmList); + if (KUidMsvNullEntry != aFilter.SortMtm()) + { + mtmList->AppendL(aFilter.SortMtm()); + } + + RPointerArray selection; + CleanupClosePushL(selection); + + CMsvCacheVisibleFolder* folderNode = NULL; + CMsvEntryArray* entries = CMsvEntryArray::NewLC(*mtmList); + TDblQueIter dqIter(iFolderListHeader); + + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + if(IsStandardId(aId)) + { + // Look for the entry in cache. + if(!iFolderListHeader.IsEmpty()) + { + dqIter.SetToFirst(); + // Check if aId is one of the visible folder, + // already present in the cache. + while(NULL != (folderNode = dqIter++)) + { + if((folderNode->GetFolderId() == unmaskedId) && (folderNode->GetDrive() == driveId)) + { + folderNode->GetChildrenL(aId, iDbAdapter, selection); + FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId); + CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller. + CleanupStack::PopAndDestroy(2); + return entries; + } + } + } + } + else + { +#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + // Look for the entry in cache. + if(!iFolderListHeader.IsEmpty()) + { + dqIter.SetToFirst(); + // Check if aId is one of the visible folder, + // already present in the cache. + while(NULL != (folderNode = dqIter++)) + { + if(aId == folderNode->GetFolderId()) + { + if(iDbAdapter == NULL) + { + User::Leave(ErrorState()); + } + folderNode->GetChildrenL(aId, iDbAdapter, selection); + FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId); + CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller. + CleanupStack::PopAndDestroy(2); + return entries; + } + } + + // If the parent is not a visible folder + // check if it is child of one of the visible folder. + if(EFalse == isParentAVisibleFolder) + { + dqIter.SetToFirst(); + while(NULL != (folderNode = dqIter++)) + { + TBool retVal=EFalse; + TRAPD(err, retVal = folderNode->GetChildrenL(aId, iDbAdapter, selection)) + + // aId not found under current visibleFolder. + if(KErrNotFound == err) + { + continue; + } + + // aId found under current visibleFolder. + if(retVal) + { + // Children is fetched succesfully. + FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId); + CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller. + CleanupStack::PopAndDestroy(2); + return entries; + } + else + { + // aId itself is a visibleFolder. + isParentAVisibleFolder = ETrue; + break; + } + } + } + } + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + } +#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + // Could not find parent entry in cache. + // Check in DB now. + TInt err = KErrNone; + if(iDbAdapter) + { + TRAP(err, iDbAdapter->GetChildrenL(aId, selection)); + } + else + { + User::Leave(ErrorState()); + } + if(err) + { + User::Leave(KErrNotFound); + } + FilterChildrenListL(aId, selection, *entries, aFilter, aFilterByOwnerId, aOwnerId); + + // Check if the entire entry list can be added to cache. + // This is important in a situation when all childs of a + // folder cannot be accomodated in cache. In such scenario + // only entries with higher TMsvId will be added to cache. + TInt excessEntries = iFreePoolInstance->ExcessMemoryAllocated(); + TBool isCompleteChildren = ETrue; + if(excessEntries) + { + isCompleteChildren = EFalse; + } + while(excessEntries) + { + if(selection.Count()) + { + iFreePoolInstance->RecordExcessMemoryL(selection[0]); + selection.Remove(0); + } + --excessEntries; + } + + // Add children to cache. + TMsvId visibleFolderId; + if(!isParentAVisibleFolder) + { + if(!GetVisibleFolderId(aId, visibleFolderId)) + { + User::Leave(KErrNotFound); + } + } + else + { + visibleFolderId = aId; + } + + TBool isEntryAdded = EFalse; + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + if(visibleFolderId == folderNode->GetFolderId()) + { +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + if(IsStandardId(visibleFolderId)) + { + if(GetDriveId(aId) == folderNode->GetDrive()) + { + isEntryAdded=ETrue; + folderNode->AddEntryListL(selection, (isCompleteChildren && isParentAVisibleFolder)); + } + } + else +#endif + { + isEntryAdded=ETrue; + folderNode->AddEntryListL(selection, (isCompleteChildren && isParentAVisibleFolder)); + } + } + } + + if(!isEntryAdded) + { + // If aId is the visibleFolder itself then pass + // aIsCompleteChildOfFolder as ETrue. +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + // Standard Id can be unmasked. + CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(MaskTMsvId(GetDriveId(aId), visibleFolderId)); +#else + CMsvCacheVisibleFolder* newFolder = CMsvCacheVisibleFolder::NewL(visibleFolderId); +#endif + iFolderListHeader.AddFirst(*newFolder); + newFolder->AddEntryListL(selection, (isCompleteChildren && isParentAVisibleFolder)); + } + + CleanupStack::Pop(entries); // Fetch entries from stack. It will be destroyed by caller. + CleanupStack::PopAndDestroy(2); + + //Start the background operations, if not running. + if (!iIdle->IsActive()) + { + iIdle->Start(TCallBack(BackGroundOperations, this)); + } + + return entries; + } + + + + +/** + * GetChildrenId() + */ +TInt CMsvIndexAdapter::GetChildrenId(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection) + { + TSecureId dummy = KMsvServerId.iId; + return GetChildrenId(aId, aFilter, aSelection, EFalse, dummy); + } + +/** + * GetChildrenId() + */ +TInt CMsvIndexAdapter::GetChildrenId(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection, TBool aFilterByOwnerId, TSecureId aOwnerId) + { + CMsvEntryArray* entries = NULL; + TInt err = KErrNone; + TRAP(err, entries=DoGetChildrenL(aId, aFilter, aFilterByOwnerId, aOwnerId)); + if(err) + { + delete entries; + return err; + } + + TInt count = entries->Count(); + for(TInt i=0; iAt(i)->Id())); + } + delete entries; + return err; + } + + + +/** + * FilterChildrenListL + */ +void CMsvIndexAdapter::FilterChildrenListL(TMsvId aId, RPointerArray aChacheEntry, CMsvEntryArray& aEntryArray, const CMsvEntryFilter& aFilter, TBool aFilterByOwnerId, TSecureId aOwnerId) + { + TBool getInvisibleEntries = aFilter.Order().ShowInvisibleEntries(); + TInt count = aChacheEntry.Count(); + for(TInt i=0; iEntry(); + TBool vis = aChacheEntry[i]->Entry().Visible(); + if ((aChacheEntry[i]->Entry().Visible() || getInvisibleEntries) && + (aFilter.Service() == KMsvNullIndexEntryId || aChacheEntry[i]->Entry().iServiceId == aFilter.Service()) && + (aFilter.Mtm() == KNullUid || aChacheEntry[i]->Entry().iMtm == aFilter.Mtm()) && + (aFilter.Type() == KNullUid || aChacheEntry[i]->Entry().iType == aFilter.Type()) && + (aFilter.LastChangeDate().Int64() == 0 || aChacheEntry[i]->LastChangeDate() >= aFilter.LastChangeDate())) + { + // Add the entry if - + // 1. Not filtering by owner ID OR + // 2. Filtering by owner ID, but entry owner ID matches OR + // 3. Entry is a standard folder OR + // 4. Entrt is a service entry. + if( !aFilterByOwnerId || + aChacheEntry[i]->EntryOwnerId() == aOwnerId || + aChacheEntry[i]->Entry().StandardFolder() || + aChacheEntry[i]->Entry().iType == KUidMsvServiceEntry ) + { + aEntryArray.AppendL(&aChacheEntry[i]->Entry()); + } + } + } + + if (aEntryArray.Count()) + { + if (aId==KMsvRootIndexEntryId) + { + aEntryArray.SortL(TMsvSelectionOrdering(KMsvNoGrouping, aFilter.Order().Sorting())); + } + else + { + aEntryArray.SortL(aFilter.Order()); + } + } + } + + + + +/** + * LockEntry() + */ +TInt CMsvIndexAdapter::LockEntry(TMsvId aId) + { + //__DEBUG_INVARIANT_ONEXIT; + + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aId, entry)) + if (err) + { + return KErrNotFound; + } + + return entry->LockEntry(); + } + + + +/** + * ReleaseEntry() + */ +TInt CMsvIndexAdapter::ReleaseEntry(TMsvId aId) + { + //__DEBUG_INVARIANT_ONEXIT; + + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + entry->ReleaseEntry(); + return KErrNone; + } + else + { + return KErrNotFound; + } + } + + + + +/** + * IsEntryLocked() + */ +TInt CMsvIndexAdapter::IsEntryLocked(TMsvId aId, TBool& aLocked) + { + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aId, entry)); + if(KErrNone != err) + { + aLocked = EFalse; + return err; + } + + aLocked = entry->IsEntryLocked(); + return KErrNone; + } + + + + + +/** + * LockStore() + */ +TInt CMsvIndexAdapter::LockStore(TMsvId aId) + { + //__DEBUG_INVARIANT_ONEXIT; + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aId, entry)); + if (err) + { + return KErrNotFound; + } + + return entry->LockStore(); + } + + + + +/** + * ReleaseStore() + */ +TInt CMsvIndexAdapter::ReleaseStore(TMsvId aId) + { + //__DEBUG_INVARIANT_ONEXIT; + + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + entry->ReleaseStore(); + return KErrNone; + } + else + { + return KErrNotFound; + } + } + + + + +/** + * IsStoreLocked() + */ +TInt CMsvIndexAdapter::IsStoreLocked(TMsvId aId, TBool& aLocked) + { + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aId, entry)); + if(KErrNone != err) + { + aLocked = EFalse; + return err; + } + aLocked = entry->IsStoreLocked(); + return KErrNone; + } + + + + +/** + * IsEntryAndStoreLocked() + */ +TInt CMsvIndexAdapter::IsEntryOrStoreLocked(TMsvId aId) + { + CMsvCacheEntry* entry = NULL; + TBool locked = EFalse; + if(FindEntryInCache(aId, entry)) + { + locked = entry->IsEntryOrStoreLocked(); + } + if(locked) + { + return KErrLocked; + } + else + { + return KErrNone; + } + } + + + +/** + * LockEntryAndStore() + */ +TInt CMsvIndexAdapter::LockEntryAndStore(TMsvId aId) + { + //__DEBUG_INVARIANT_ONEXIT; + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aId, entry)); + if (err) + { + return KErrNotFound; + } + + return entry->LockEntryAndStore(); + } + + + + +/** + * ReleaseEntryAndStore() + */ +TInt CMsvIndexAdapter::ReleaseEntryAndStore(TMsvId aId) + { + //__DEBUG_INVARIANT_ONEXIT; + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + entry->ReleaseEntryAndStore(); + return KErrNone; + } + else + { + return KErrNotFound; + } + } + + + + +/** + * IsEntryOrStoreLocked() + */ +TInt CMsvIndexAdapter::IsEntryOrStoreLocked(TMsvId aId, TBool& aLocked) + { + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + aLocked = entry->IsEntryOrStoreLocked(); + } + else + { + aLocked = EFalse; + } + return KErrNone; + } + + + + +/** + * IsStoreReadingLocked() + */ +TInt CMsvIndexAdapter::IsStoreReadingLocked(TMsvId aId, TBool& aLocked) + { + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + aLocked = entry->IsStoreReadingLocked(); + } + else + { + aLocked = EFalse; + } + return KErrNone; + } + + + + +/** + * IncStoreReaderCount() + */ +TInt CMsvIndexAdapter::IncStoreReaderCount(TMsvId aId) + { + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aId, entry)); + if (err) + { + return KErrNotFound; + } + + entry->IncStoreReaderCount(); + return KErrNone; + } + + + + +/** + * DecStoreReaderCount() + */ +TInt CMsvIndexAdapter::DecStoreReaderCount(TMsvId aId) + { + CMsvCacheEntry* entry = NULL; + if(FindEntryInCache(aId, entry)) + { + entry->DecStoreReaderCount(); + } + return KErrNone; + } + + + + +/** + * OwningService() + */ +TInt CMsvIndexAdapter::OwningService(TMsvId aId, TMsvId& aService) + { + aService = aId; + if (aId!=KMsvRootIndexEntryId) + { + FOREVER + { + CMsvCacheEntry* entry = NULL; + TRAPD(err, FindEntryL(aService, entry)); + if (err) + { + return KErrNotFound; + } + + if (KUidMsvServiceEntry==entry->Entry().iType) + { + break; + } + aService = entry->Entry().Parent(); + } + } + + return KErrNone; + } + + + + +/** + * IsLocal() + */ +TInt CMsvIndexAdapter::IsLocal(TMsvId aId, TBool& aLocal) + { + TInt errVal = KErrNone; + aLocal = ETrue; + + if (aId!=KMsvRootIndexEntryId) + { + TMsvId service; + errVal = OwningService(aId, service); + aLocal = (KMsvLocalServiceIndexEntryId==service); + } + + return errVal; + } + + + + +/** + * MoveEntry + * Moves a entry with TMsvId as aId to a folder with target id as aTarget + * + * @param aId TMsvId of the entry to be moved. + * @param aTarget TMsvId of the parent folder. + * @param aDescendents List of descendents id, if the caller also wants to move descendents + * in cache and DB. The last id in the descendent list must be the entry id itself. + * @return TInt System-wide error code. + */ +TInt CMsvIndexAdapter::MoveEntry(TMsvId aId, TMsvId aTarget, CMsvEntrySelection* aDescendents /* DEFAULT=NULL */) + { + TRAPD(err, DoMoveEntryL(aId, aTarget, aDescendents)); + return err; + } + + + +/** + * DoMoveEntryL() + * Called from MoveEntry() + * */ +void CMsvIndexAdapter::DoMoveEntryL(TMsvId aId, TMsvId aTarget, CMsvEntrySelection* aDescendents /* DEFAULT=NULL */) + { + // Leave if the message store is not currently available. + User::LeaveIfError(iErrorState); + + CMsvCacheEntry* entry = NULL; + CMsvCacheVisibleFolder* oldFolderNode = NULL; + TMsvId oldVisibleFolderId = KMsvNullIndexEntryIdValue; + + // 1. Find the entry in cache. + GetVisibleFolderDetailsL(aId, entry, oldFolderNode); + + // 2. Validate entry. + TBool des; + __ASSERT_DEBUG(entry->IsEntryAndStoreLocked(), PanicServer(EMsvMovingUnlockedEntry)); + __ASSERT_DEBUG(entry->Entry().Parent() != aTarget, PanicServer(EMsvMovingWithinSameEntry)); + IsADescendent(entry->Entry().Id(), aTarget, des); + if (des) + {// we need to put changes in another method. + User::Leave(KErrArgument); + } + + // 3. Fetch the parent entries. + CMsvCacheEntry* oldParentEntry = NULL; + CMsvCacheEntry* newParentEntry = NULL; + TMsvId oldParentId = entry->Entry().Parent(); + FindEntryL(oldParentId, oldParentEntry); + FindEntryL(aTarget, newParentEntry); + + // 4. Check if the owner flag of the old parent needs to be reset. + TBool resetOldParentOwnerFlag = EFalse; + CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection; + CleanupStack::PushL(children); + GetChildrenIdL(oldParentId, *children); + if(1 == children->Count() && oldParentEntry->Entry().Owner()) + { + // Just set this flag, this is updated in step 14. + resetOldParentOwnerFlag = ETrue; + } + CleanupStack::PopAndDestroy(); //children + + // 5. If required, pre-allocate memory for child array of newParentEntry. + // This is later updated in step 12. + RArray* newParentChildArr = newParentEntry->ChildIdArray(); + if(newParentChildArr) + { + // This to ensure that future append does not fail. + newParentChildArr->ReserveL(newParentChildArr->Count() + 1); + } + + // Step 6 & 7 is performed in this function. + TBool isChildEntriesNeedsUpdation = EFalse; + CMsvCacheVisibleFolder* newVisibleFolderNode = UpdateCacheForMoveEntryL(aTarget, + entry, + oldFolderNode, + aDescendents, + isChildEntriesNeedsUpdation); + + TMsvId newVisibleFolderId = newVisibleFolderNode? newVisibleFolderNode->GetFolderId(): oldFolderNode->GetFolderId(); + + // 8. Update the entry. + UpdateDates(*entry, EFalse); + entry->Entry().SetParent(aTarget); + + // 9. Update the DB. + TRAPD(err, + iDbAdapter->BeginTransactionL(); + // Update the actual entry + iDbAdapter->UpdateEntryL(entry->Entry(), newVisibleFolderId); + // Reset old parent owner flag. + if(resetOldParentOwnerFlag) + iDbAdapter->UpdateOwnerStatusL(oldParentId, oldParentEntry->Entry(), EFalse); + // Set new parent owner flag. + if(!newParentEntry->Entry().Owner()) + iDbAdapter->UpdateOwnerStatusL(aTarget, newParentEntry->Entry(), ETrue); + // Update the child entries visibleFolderId in DB. + if(isChildEntriesNeedsUpdation) + iDbAdapter->UpdateVisibleFolderL(aDescendents, newVisibleFolderId); + iDbAdapter->CommitTransactionL(); + ); + + if(err) + { + TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); + // Undo step 8. + entry->Entry().SetParent(oldParentId); + // Undo step 7. + if(newVisibleFolderNode) + newVisibleFolderNode->DeleteEntryL(aId); + if(isChildEntriesNeedsUpdation) + { + for(TInt index=(aDescendents->Count()-2); index>=0; index--) + { + newVisibleFolderNode->DeleteEntryL(aDescendents->At(index)); + } + } + User::Leave(err); + } + + // Nothing should fail after this. Ensure that + // all leaving function after this does not leave. + + // 10. Continuing step 7.1. + // Removing entry from old visible folder. + if(oldVisibleFolderId != newVisibleFolderId) + { + oldFolderNode->DeleteEntryL(aId); + } + + // 11. Continuing step 7.2. + // Removing child entries from old visible folder. + if(isChildEntriesNeedsUpdation) + { + for(TInt index=(aDescendents->Count()-2); index>=0; index--) + { + oldFolderNode->DeleteEntryL(aDescendents->At(index)); + } + } + + // 12. Add child id to new parent child array. + if(newParentChildArr) + { + // This will not leave, as memory is already reserved. + newParentChildArr->AppendL(aId); + } + + // 13. Remove child from old parent's child array. + RArray* oldParentChildArr = oldParentEntry->ChildIdArray(); + if(oldParentChildArr) + { + TInt pos = oldParentChildArr->Find(aId); + if(pos != KErrNotFound) + { + oldParentChildArr->Remove(pos); + } + } + + // 14. Update owner flag of parent entries. + if(resetOldParentOwnerFlag) + { + oldParentEntry->Entry().SetOwner(EFalse); + } + newParentEntry->Entry().SetOwner(ETrue); + } + + + + +void CMsvIndexAdapter::GetVisibleFolderDetailsL(TMsvId aEntryId, CMsvCacheEntry*& aEntry, CMsvCacheVisibleFolder*& aVisibleFolder) + { + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + while((aVisibleFolder = dqIter++)!=NULL) + { + if(aVisibleFolder->GetEntry(aEntryId, aEntry)) + { + break; + } + } + if(aEntry==NULL) + { + User::Leave(KErrNotFound); + } + } + + + + +CMsvCacheVisibleFolder* CMsvIndexAdapter::UpdateCacheForMoveEntryL(TMsvId aNewParentId, + CMsvCacheEntry*& aOrigEntry, + CMsvCacheVisibleFolder* aOldVisibleFolderNode, + CMsvEntrySelection* aDescendents, + TBool& aIsChildEntriesNeedsUpdation) + { + // 6. Find the visible folder of new parent. + TMsvId newVisibleFolderId = aOldVisibleFolderNode->GetFolderId(); + if(!GetVisibleFolderId(aNewParentId, newVisibleFolderId)) + { + User::Leave(KErrNotFound); + } + + // 7. Check if the visible folders are different. + CMsvCacheVisibleFolder* newVisibleFolderNode = NULL; + if(aOldVisibleFolderNode->GetFolderId() != newVisibleFolderId) + { + // 7.1. If the visible folders are different, add duplicate + // entry under new visible folder. Removing entry from old + // visible folder is done in step 10 of DoMoveEntryL(). + CMsvCacheEntry* newCacheEntry = iFreePoolInstance->EntryL(); + TRAPD(err, newCacheEntry->DupNDestroyL(aOrigEntry); + newVisibleFolderNode = AddEntryToCacheL(newVisibleFolderId, newCacheEntry); + ); + if(err) + { + iFreePoolInstance->ReleaseEntryWithoutTransaction(newCacheEntry); + User::Leave(err); + } + + // 7.2. Check if child entries also needs to be moved. + aOrigEntry = newCacheEntry; + if(aDescendents && !aOrigEntry->Entry().VisibleFolderFlag()) + { + aIsChildEntriesNeedsUpdation = ETrue; + + // Add duplicate child entry under new visible folder. + // Removing child entries from old visible folder is done in step 11. + CMsvCacheEntry* childEntry = NULL; + CMsvCacheEntry* dupChildEntry = NULL; + for(TInt index=(aDescendents->Count()-2); index>=0; index--) + { + if(aOldVisibleFolderNode->GetEntry(aDescendents->At(index), childEntry)) + { + dupChildEntry = NULL; + // Create the duplicate of original entry. + TRAP(err, dupChildEntry = iFreePoolInstance->EntryL(); + dupChildEntry->DupNDestroyL(childEntry); + // Add under new visible folder node. + newVisibleFolderNode->AddEntryL(dupChildEntry); + ); + if(err) + { + // Delete should not be leaving... + newVisibleFolderNode->DeleteEntryL(aOrigEntry->GetId()); // Undo step 7.1 + iFreePoolInstance->ReleaseEntryWithoutTransaction(dupChildEntry); // Undo current entry + // Undo remaining entries. + for(TInt numEntries=index+1; numEntries<=(aDescendents->Count()-2); numEntries++) + { + newVisibleFolderNode->DeleteEntryL(aDescendents->At(numEntries)); + } + User::Leave(err); + } + } // if(oldFolderNode->GetEntry(aDescendents->At(index), childEntry)) + } // for() + } // if(aDescendents && !entry->Entry().VisibleFolderFlag()) + } + return newVisibleFolderNode; + } + + + + +/** + * IsADescendent() + */ +TInt CMsvIndexAdapter::IsADescendent(TMsvId aAscendentId, TMsvId aDescendentId, TBool& aDescendent) + { + __ASSERT_DEBUG((KMsvRootIndexEntryId != aAscendentId) && (KMsvRootIndexEntryId != aDescendentId), PanicServer(EMsvDescendentArgumentsRoot)); + __ASSERT_DEBUG(aAscendentId!=aDescendentId, PanicServer(EMsvDescendentArgumentsEqual)); + + aDescendent=EFalse; + while (KMsvRootIndexEntryId != aDescendentId) + { + CMsvCacheEntry* entry; + TRAPD(err, FindEntryL(aDescendentId, entry)); + if (err) + { + return KErrNotFound; + } + if (aDescendentId==aAscendentId) + { + aDescendent=ETrue; + break; + } + aDescendentId=entry->Entry().Parent(); + } + + return KErrNone; + } + + + + +/** + * EntryExists() + */ +TBool CMsvIndexAdapter::EntryExists(TMsvId aId) + { + // First search the entry in cache, + // if cache is not empty... +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + if(KMsvRootIndexEntryId == aId) + { + return true; + } +#endif + if(!iFolderListHeader.IsEmpty()) + { + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + + while((folderNode = dqIter++)!=NULL) + { + if(aId == folderNode->GetFolderId()) + { + return ETrue; + } + if(folderNode->EntryExists(aId)) + { + return ETrue; + } + } + } + + TBool entryExists = EFalse; + if(iDbAdapter) + { + TRAP_IGNORE(entryExists = iDbAdapter->EntryExistsL(aId)); + } + else + { + return EFalse; + } + if(!entryExists) + { + return EFalse; + } + return ETrue; + } + + + + +/** + * ChangeTemporaryData() + */ +TInt CMsvIndexAdapter::ChangeTemporaryData(const TMsvEntry& aNewEntryContents) + { + //__DEBUG_INVARIANT_ONEXIT; + + CMsvCacheEntry* entry; + + // find the entry in the index + TRAPD(err, FindEntryL(aNewEntryContents.Id(), entry)); + if (err) + { + return KErrNotFound; + } + + // Can only change a locked entry and not one marked as read only + if (!entry->IsEntryLocked() || entry->Entry().StandardFolder()) + { + return KErrAccessDenied; + } + + // Check if just changing temporary flags (ie those not stored on file) + entry->Entry().iData=aNewEntryContents.iData; + + return KErrNone; + } + + + + +/** + * ChangeAttributes() + */ +TInt CMsvIndexAdapter::ChangeAttributes(CMsvEntrySelection& aSelection, TUint aSetAttributes, TUint aClearAttributes) + { + TRAPD(leave, DoChangeAttributesL(aSelection, aSetAttributes, aClearAttributes)); + return leave; + } + + + +void CMsvIndexAdapter::DoChangeAttributesL(CMsvEntrySelection& aSelection, TUint aSetAttributes, TUint aClearAttributes) + { + // Leave if the message store is not currently available + User::LeaveIfError(iErrorState); + + CArrayFixSeg* newData = new(ELeave)CArrayFixSeg(KMsvChangeAttributesListGranularity); + CleanupStack::PushL(newData); + CArrayFixSeg* newPcSyncCount = new(ELeave)CArrayFixSeg(KMsvChangeAttributesListGranularity); + CleanupStack::PushL(newPcSyncCount); + + newData->ResizeL(aSelection.Count(), 0); + newPcSyncCount->ResizeL(aSelection.Count(), 0); + + TInt count=aSelection.Count(); + TDblQueIter dqIter(iFolderListHeader); + CMsvCacheEntry* entry = NULL; + CMsvCacheVisibleFolder* folderNode = NULL; + TMsvId newVisibleFolderId = NULL; + if(iDbAdapter) + { + iDbAdapter->BeginTransactionL(); + } + else + { + User::Leave(ErrorState()); + } + while(count--) + { + // find the entry in the index + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + if(folderNode->GetEntry(aSelection.At(count), entry)) + { + newVisibleFolderId = folderNode->GetFolderId(); + break; + } + } + if (entry == NULL) + { + User::Leave(KErrNotFound); + } + // can only change a locked entry and not one marked as read only + if (!entry->IsEntryLocked() || entry->Entry().StandardFolder()) + { + User::Leave(KErrAccessDenied); + } + + // work out the new attributes + TBool persistedFlagsChanged; + if (!NewAttributes(entry->Entry(), newData->At(count), newPcSyncCount->At(count), aSetAttributes, aClearAttributes, persistedFlagsChanged)) + { + // attributes don't need changing + aSelection.Delete(count); + newData->Delete(count); + newPcSyncCount->Delete(count); + continue; + } + if(persistedFlagsChanged) + { + TMsvEntry tEntry = entry->Entry(); + tEntry.iData = newData->At(count); + tEntry.iPcSyncCount = newPcSyncCount->At(count); + TInt err = KErrNone; + if(iDbAdapter) + { + TRAP(err, iDbAdapter->UpdateEntryL(tEntry, newVisibleFolderId, EFalse)); + } + else + { + User::Leave(ErrorState()); + } + if(err) + { + iDbAdapter->RollbackTransactionL(); + User::Leave(err); + } + } + } + if(iDbAdapter) + { + iDbAdapter->CommitTransactionL(); + } + else + { + User::Leave(ErrorState()); + } + + count=aSelection.Count(); + while(count--) + { + // find the entry in the index + CMsvCacheEntry* entryPtr = NULL; + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + if(folderNode->GetEntry(aSelection.At(count), entryPtr)) + { + break; + } + } +#if defined(_DEBUG) + TBool found = (NULL != entryPtr); + __ASSERT_DEBUG(found, PanicServer(EMsvChangeAttEmtryNotFound)); +#endif + entryPtr->Entry().iData=newData->At(count); + entryPtr->Entry().iPcSyncCount=newPcSyncCount->At(count); + } + CleanupStack::PopAndDestroy(2, newData); + if (!iIdle->IsActive()) + { + iIdle->Start(TCallBack(BackGroundOperations, this)); + } + + } + +/** + * NewAttributes +*/ +TBool CMsvIndexAdapter::NewAttributes(const TMsvEntry& aEntry, TInt32& aNewData, TInt32& aNewPcSyncCount, TUint aSetAttributes, TUint aClearAttributes, TBool& aPersistedFlagsChanged) + { + aNewData = aEntry.iData; + aNewPcSyncCount = aEntry.iPcSyncCount; + + // KMsvPcSyncCountAttribute is a dummy flag that is used to increment/decrement the PC Sync Count + // The visibility attribute is actually recorded as an 'invisible' flag in the index + if (aSetAttributes) + { + if (aSetAttributes & KMsvPcSyncCountAttribute) + { + aNewPcSyncCount++; + aSetAttributes &= ~KMsvPcSyncCountAttribute; + } + + const TInt32 mask = KMsvSendStateMax << KMsvSendingStateShift; + if (aSetAttributes & mask) + { + aNewData &= ~mask; + } + + + aNewData |= aSetAttributes; + + if (aSetAttributes & KMsvVisibilityAttribute) + { + aNewData &= ~TMsvEntry::KMsvEntryInvisibleFlag; + } + if (aSetAttributes & KMsvPendingDeleteAttribute) + { + aNewData |= TMsvEntry::KMsvEntryPendingDeleteFlag; + } + } + + if (aClearAttributes) + { + if (aClearAttributes & KMsvPcSyncCountAttribute) + { + aNewPcSyncCount--; + aClearAttributes &= ~KMsvPcSyncCountAttribute; + } + + aNewData &= ~aClearAttributes; + + if (aClearAttributes&KMsvVisibilityAttribute) + { + aNewData |= TMsvEntry::KMsvEntryInvisibleFlag; + } + + if (aClearAttributes&KMsvPendingDeleteAttribute) + { + aNewData &= ~TMsvEntry::KMsvEntryPendingDeleteFlag; + } + + } + + aPersistedFlagsChanged = (aNewData&TMsvEntry::KMsvEntryPersistedFlags)!=(aEntry.iData&TMsvEntry::KMsvEntryPersistedFlags) || + aNewPcSyncCount != aEntry.iPcSyncCount; + return aNewData != aEntry.iData || aNewPcSyncCount != aEntry.iPcSyncCount; + } + +/** + * UpdateDates +*/ +void CMsvIndexAdapter::UpdateDates(CMsvCacheEntry& aEntry, TBool aSetCreatedDate) + { + TTime now; + now.UniversalTime(); + TMsvTime time; + time.SetTime(now); + aEntry.SetLastChangeDate(time); + + if (aSetCreatedDate) + { + aEntry.SetCreatedDate(time); + } + } + + + + +/** + * GetInternalEntry() + */ +TInt CMsvIndexAdapter::GetInternalEntry(TMsvId aId, CMsvCacheEntry*& aEntry) + { + CMsvCacheEntry* entry=NULL; + TRAPD(err, FindEntryL(aId, entry)); + if(err!=KErrNone) + { + return err; + } + if(entry==NULL) + { + return KErrNotFound; + } + aEntry=entry; + return KErrNone; + } + + +/** + * Progress() +*/ +TMsvIndexProgress& CMsvIndexAdapter::Progress() + { + return iProgress; + } + + +/** + * EntryTreeInfo() + */ +TInt CMsvIndexAdapter::EntryTreeInfo(TMsvId aId, TMsvServerEntryInfo& aEntryInfo) + { + aEntryInfo.Reset(); + aEntryInfo.iId = aId; + + if( aId != KMsvRootIndexEntryId ) + { + // Search up the tree to find which service the specified entry is under. + // Also, record the highest owning folder for the entry. + TMsvId id = aId; + CMsvCacheEntry* entry =NULL; + TRAPD(err, FindEntryL(id, entry)); + if(err) + return KErrNotFound; + + id = entry->Entry().iId; //Id may be masked in cache + aEntryInfo.iId = id; + + // Get info for the entry. + aEntryInfo.iEntryOwnerId = entry->EntryOwnerId(); + aEntryInfo.iMtm = entry->Entry().iMtm; + aEntryInfo.iType = entry->Entry().iType; + + if( aEntryInfo.iType == KUidMsvFolderEntry ) + { + // This is a folder - record as this may be needed. + aEntryInfo.iTopFolder = id; + } + + TBool found = (aEntryInfo.iType == KUidMsvServiceEntry); + while( !found ) + { + // Move up the tree and get the next entry... + id = entry->Entry().Parent(); + TRAPD(err, FindEntryL(id, entry)); + if(err) + return KErrNotFound; + + TUid type = entry->Entry().iType; + if( type == KUidMsvServiceEntry ) + { + // Ok, we've found the owning service - stop looking. + found = ETrue; + } + else if( type == KUidMsvFolderEntry ) + { + // This is a folder - record as this may be needed. + aEntryInfo.iTopFolder = id; + } + else + { + // This is a message or attachment entry - this implies that the + // original entry was part of an existing message. Set the owner + // ID to the highest parent. + aEntryInfo.iPartOfMessage = ETrue; + aEntryInfo.iParentOwnerId = entry->EntryOwnerId(); + } + } + // Record the service and MTM... + aEntryInfo.iService = id; + + aEntryInfo.iServiceMtm = entry->Entry().iMtm; + } + else + { + // This is the root entry! Mark the service as being the local service. + aEntryInfo.iType = KUidMsvRootEntry; + aEntryInfo.iMtm = KUidMsvLocalServiceMtm; + aEntryInfo.iService = KMsvLocalServiceIndexEntryId; + aEntryInfo.iServiceMtm = KUidMsvLocalServiceMtm; + aEntryInfo.iTopFolder = KMsvRootIndexEntryId; + } + return KErrNone; + } + + + + +/** + * CommitNonCommitedEntries() + */ +void CMsvIndexAdapter::CommitNonCommitedEntries() + { + // 1. Commit transaction in DB. + if(iDbAdapter) + { + TRAPD(err, iDbAdapter->CommitTransactionL()); + if(err) + { + RollbackAdditions(); + RollbackChanges(); + return; + } + } + + // 2. Update changed entries completely. + TInt count = iNonCommittedChangedEntryList.Count(); + while(count) + { + TNonCommittedChangedEntries changedEntry = iNonCommittedChangedEntryList[--count]; + CMsvCacheVisibleFolder* oldVisibleFolderNode = changedEntry.iOldVisibleFolderNode; + // Removing entry from old visible folder. + if(changedEntry.iNewVisibleFolderNode && + oldVisibleFolderNode->GetFolderId() != changedEntry.iNewVisibleFolderNode->GetFolderId()) + { + // Will never leave. + TRAP_IGNORE(changedEntry.iOldVisibleFolderNode->DeleteEntryL(changedEntry.iEntry->GetId())); + } + + // Removing child entries from old visible folder. + if(changedEntry.iDescendentList) + { + for(TInt index=(changedEntry.iDescendentList->Count()-2); index>=0; index--) + { + // Can leave with KErrNotFound. + TRAP_IGNORE(oldVisibleFolderNode->DeleteEntryL(changedEntry.iDescendentList->At(index))); + } + delete changedEntry.iDescendentList; + } + + // Add child id to new parent child array. + if(changedEntry.iNewParentEntry && (changedEntry.iNewParentEntry->ChildIdArray())) + { + // This will not leave, as memory is already reserved. + TRAP_IGNORE(changedEntry.iNewParentEntry->ChildIdArray()->AppendL(changedEntry.iEntry->GetId())); + } + + // Remove child from old parent's child array. + if(changedEntry.iOldParentEntry) + { + RArray* oldParentChildArr = changedEntry.iOldParentEntry->ChildIdArray(); + if(oldParentChildArr) + { + TInt pos = oldParentChildArr->Find(changedEntry.iEntry->GetId()); + if(pos != KErrNotFound) + { + oldParentChildArr->Remove(pos); + } + } + } + + // Update owner flag of parent entries. + if(changedEntry.iResetOldParentOwnerFlag) + { + changedEntry.iOldParentEntry->Entry().SetOwner(EFalse); + } + if(changedEntry.iNewParentEntry) + { + changedEntry.iNewParentEntry->Entry().SetOwner(ETrue); + } + } + + // 3. Reset non-committed entry list. + iNonCommittedAddedEntryList.ResetAndDestroy(); + iNonCommittedChangedEntryList.Reset(); + } + + + +/** + * RollbackAdditions() + */ +void CMsvIndexAdapter::RollbackAdditions() + { + // 1. Rollback transaction in DB. + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); + } + + // 2. Delete added entry from cache. + CMsvCacheVisibleFolder* folderNode = NULL; + TInt count = iNonCommittedAddedEntryList.Count(); + TDblQueIter dqIter(iFolderListHeader); + while(count) + { + --count; + dqIter.SetToFirst(); + while ((folderNode = dqIter++) != NULL) + { + if(iNonCommittedAddedEntryList[count]->iVisibleFolder == folderNode->GetFolderId()) + { + iNonCommittedAddedEntryList[count]->entry->LockEntry(); + // Error can cause if the entry does not exist. Ignore. + TRAP_IGNORE(folderNode->DeleteEntryL(iNonCommittedAddedEntryList[count]->entry->GetId())); + break; + } + }; + } + + // 3. Reset non-committed entry list. + iNonCommittedAddedEntryList.ResetAndDestroy(); + } + + + + + +/** + * RollbackChanges() + */ +void CMsvIndexAdapter::RollbackChanges() + { + // 1. Rollback transaction in DB. + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); + } + + // 2. Undo changed entry in cache. + TInt count = iNonCommittedChangedEntryList.Count(); + while(count) + { + TNonCommittedChangedEntries changedEntry = iNonCommittedChangedEntryList[--count]; + CMsvCacheEntry* oldEntry = changedEntry.iEntry; + + // If the visible folder Id of the entry is changed, + // oldEntry represents the entry under new visible folder node. + // Get the actual old entry from old visible folder node. + if(changedEntry.iNewVisibleFolderNode) + { + // This will be non leaving as entry is already present. + if(changedEntry.iOldVisibleFolderNode->GetEntry(oldEntry->GetId(), oldEntry)) + { + // Remove added entry from new visible node. + // This function will never leave in the current + // scenario, hence ignoring the error. + TRAP_IGNORE(changedEntry.iNewVisibleFolderNode->DeleteEntryL(oldEntry->GetId())); + } + } + oldEntry->SetEntryOwnerId(changedEntry.iBkpEntry->EntryOwnerId()); + oldEntry->RollBackCopyEntry(changedEntry.iBkpEntry->Entry()); + iFreePoolInstance->ReleaseEntryWithoutTransaction(changedEntry.iBkpEntry); + + if(changedEntry.iDescendentList) + { + delete changedEntry.iDescendentList; + } + } + + // 3. Reset non-committed entry list. + iNonCommittedChangedEntryList.Reset(); + } + + + + +CMsvIndexAdapter::TMsvServerEntryInfo::TMsvServerEntryInfo() + { + iId = KMsvNullIndexEntryIdValue; + iTopFolder = KMsvNullIndexEntryIdValue; + iService = KMsvNullIndexEntryIdValue; + iMtm = KUidMsvNullEntry; + iType = KUidMsvNullEntry; + iServiceMtm = KUidMsvNullEntry; + iEntryOwnerId = KMsvServerId.iId; + iParentOwnerId = KMsvServerId.iId; + iPartOfMessage = EFalse; + } + + + + +void CMsvIndexAdapter::TMsvServerEntryInfo::Reset() + { + iId = KMsvNullIndexEntryIdValue; + iTopFolder = KMsvNullIndexEntryIdValue; + iService = KMsvNullIndexEntryIdValue; + iMtm = KUidMsvNullEntry; + iType = KUidMsvNullEntry; + iServiceMtm = KUidMsvNullEntry; + iEntryOwnerId = KMsvServerId.iId; + iParentOwnerId = KMsvServerId.iId; + iPartOfMessage = EFalse; + } + + +/** +Need to see the posibilty of getting this first from cache +*/ +TBool CMsvIndexAdapter::GetNextSiblingL(TMsvId aId,TMsvId aParentId,TMsvId& aNextSiblingId) + { + TBool flag = EFalse; + if(iDbAdapter) + { + flag = iDbAdapter->GetNextSiblingL(aId, aParentId, aNextSiblingId); + } + else + { + User::Leave(ErrorState()); + } + return flag; + } + + +/** +Need to see the posibilty of getting this first from cache +*/ +TBool CMsvIndexAdapter::GetFirstChildIdL(TMsvId aParentId,TMsvId& aFirstChild) + { + TBool flag = EFalse; + if(iDbAdapter) + { + flag = iDbAdapter->GetFirstChildIdL(aParentId, aFirstChild); + } + else + { + User::Leave(ErrorState()); + } + return flag; + } + + +/** + * SetLocalServiceComplete + * @param TUint: Drive Id of the local service folder node. + * + * Code changes for PREQ 557. + * Sets the localservice visiblefolder. + */ +void CMsvIndexAdapter::SetLocalServiceComplete() + { + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + CMsvCacheVisibleFolder* folderNode = NULL; + + while((folderNode = dqIter++)!=NULL) + { + if(KMsvLocalServiceIndexEntryIdValue == folderNode->GetFolderId()) + { + folderNode->SetComplete(ETrue); + break; + } + } + } + + +/** + *BackGroundOperations() + *@param aPtr A TAny* + *@return TBool ETrue if the background operation needs to be rescheduled. + * EFalse if the background operation need not be rescheduled. + */ +TBool CMsvIndexAdapter::BackGroundOperations(TAny* aPtr) + { + return ((CMsvIndexAdapter*)aPtr)->DoBackGroundOperations(); + } + + + + + +/** + *DoBackGroundOperations() + *@param None + *@return TBool ETrue if the background operation needs to be rescheduled. + * EFalse if the background operation need not be rescheduled. + */ +TBool CMsvIndexAdapter::DoBackGroundOperations() + { + TInt stopBackgroundOperation = EFalse; + TRAPD(err, DoBackGroundOperationsL(stopBackgroundOperation)); + if(KErrNone == err && !stopBackgroundOperation) + { + return ETrue; + } + return EFalse; + } + + + + + +/** + *DoBackGroundOperations() + *This is the background operation state machine, this will perform a set + *of operations in each of the iteration. + * + *@param None + *@return void + */ +void CMsvIndexAdapter::DoBackGroundOperationsL(TBool& aStopBackgroundOperation) + { + iFreePoolInstance->RoutineFreePoolCleanUpL(); + switch(iBackgroundOperationState) + { + case ERemoveDeletedEntries: + { + iBackgroundOperationState = EAllocateMemoryOperation; + DoRemoveDeletedEntriesL(); + break; + } + case EAllocateMemoryOperation: + { + iBackgroundOperationState = ECheckBlockSize; + if(CheckAndAllocateMemoryL()) + { + iBackgroundOperationPerformed = 0; + break; + } + iBackgroundOperationPerformed++; + } + case ECheckBlockSize: + { + iBackgroundOperationState = ENoOperation; + SplitBlockL(); + break; + } + case ENoOperation: + default: + { + iBackgroundOperationState = ERemoveDeletedEntries; + // The background operation has run multiple times, without doing + // much work, so it can be stopped temporarily. + if(iBackgroundOperationPerformed >= 5) + { + iBackgroundOperationPerformed = 0; + aStopBackgroundOperation = ETrue; + } + + break; + } + } + + } + + + +/** + *CheckAndAllocateMemory + *Will check the state of the free pool and allocate memory if required. + *Allocation will be done if 70% of the CMsvCacheEntries created are used. + * + *@return TBool ETrue, if allocation has been done. + * EFalse, if no allocation is done. + */ +TBool CMsvIndexAdapter::CheckAndAllocateMemoryL() + { + if(iFreePoolInstance->IsAllocationRequiredL()) + { + iFreePoolInstance->AllocateMemoryL(); + return ETrue; + } + return EFalse; + } + + + + +/** + *SplitBlockL + *Will sort and split the blocks of CMsvCacheEntries. + * + *@return void + */ +void CMsvIndexAdapter::SplitBlockL() + { + TDblQueIter dqIter(iFolderListHeader); + dqIter.SetToFirst(); + CMsvCacheVisibleFolder* folderNode = NULL; + while((folderNode = dqIter++)!=NULL) + { + folderNode->SplitBlockL(); + } + } + + + +/** + *ForceDeleteEntry + *Deletes the TMsvEntry even if it is not in the cache.This does not require + *the entry to be locked. + * + *@param aId TMsvId, id ofthe entry to be deleted. + *@return TInt Any of the system wide error codes. + */ +TInt CMsvIndexAdapter::ForceDeleteEntry(TMsvId aId) + { + TRAPD(leave, DoForceDeleteEntryL(aId)); + return leave; + } + + + +/** + *DoForceDeleteEntryL + *Called from ForceDeleteEntry + * + *@param aId TMsvId, id ofthe entry to be deleted. + *@return void + */ +void CMsvIndexAdapter::DoForceDeleteEntryL(TMsvId aId) + { + User::LeaveIfError(iErrorState); + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + + if(!iDbAdapter) + { + User::Leave(ErrorState()); + } + CMsvCacheEntry* entry = NULL; + TBool releaseEntry = FindEntryL(aId, entry, EFalse); + TMsvId parentId = entry->Entry().Parent(); + TRAPD(err, iDbAdapter->DeleteEntryL(aId)); + // If the entry does not exists in cache, + // we need to release the entry explicitly. + if(releaseEntry) + { + iFreePoolInstance->ReleaseEntry(entry, ETrue); + User::LeaveIfError(err); + } + else + { + User::LeaveIfError(err); + // Otherwise remove the entry from cache. + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + TRAPD(err, folderNode->DeleteEntryL(aId, ETrue)); + if(KErrNone == err) + { + break; + } + } + } + //Find the parent entry in cache and check for any children. + //If there are no more children under the parent, then + //reset the owner flag in cache and database. + CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection; + CleanupStack::PushL(children); + releaseEntry = FindEntryL(parentId, entry, EFalse); + TRAP(err, + GetChildrenIdL(parentId, *children); + if(0 == children->Count() && entry->Entry().Owner()) + { + iDbAdapter->UpdateOwnerStatusL(parentId, entry->Entry(), EFalse); + entry->Entry().SetOwner(EFalse); + } + ); + if(releaseEntry) + { + iFreePoolInstance->ReleaseEntry(entry, ETrue); + } + User::LeaveIfError(err); + CleanupStack::PopAndDestroy(); //children + } + + + +/** + *DeleteSelectionUsingTransaction + *Deletes a selection of TMsvEntries.This will also delete enteries that are not locked. + * + *@param aSelection CMsvEntrySelection&, selection of the TMsvEntries. + @param TInt Any of the system wide error codes. + */ + +TInt CMsvIndexAdapter::DeleteSelectionUsingTransaction(const CMsvEntrySelection& aSelection) + { + TRAPD(leave, DoDeleteSelectionUsingTransactionL(aSelection)); + return leave; + } + + +/** + *DoDeleteSelectionUsingTransactionL + *Called from DeleteSelectionUsingTransaction. + * + *@param aSelection CMsvEntrySelection&, selection of the TMsvEntries. + @param void + */ +void CMsvIndexAdapter::DoDeleteSelectionUsingTransactionL(const CMsvEntrySelection& aSelection) + { + User::LeaveIfError(iErrorState); + if(!iDbAdapter) + { + User::Leave(iErrorState); + } + + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + const TInt count = aSelection.Count(); + + if(!count) + { + return; + } + + CMsvEntrySelection* children = new(ELeave)CMsvEntrySelection; + CleanupStack::PushL(children); + CMsvCacheEntry* entry = NULL; + TBool releaseEntry = FindEntryL(aSelection.At(0), entry, EFalse); + TMsvId parentId = entry->Entry().Parent(); + if(releaseEntry) + { + iFreePoolInstance->ReleaseEntry(entry, ETrue); + } + + // Find the parent in the cache - we may need to update its owner status flag both in cache and DB. + TBool updateTheParent = ETrue; + TRAPD(err, releaseEntry = FindEntryL(parentId, entry, EFalse)); // can't let it leave - need to delete the child in any case. + if(err == KErrNotFound) + { + updateTheParent = EFalse; + if(releaseEntry) + { + iFreePoolInstance->ReleaseEntry(entry, ETrue); + } + } + else + { + // Get parent's children. + GetChildrenIdL(parentId, *children); // can leave - we'll let it leave as there's nothing to be rolled back now. + if(children->Count() > 1) + updateTheParent = EFalse; + } + + // Perform DB operations: + // - Delete the entry. + // - Update owner status flag of the parent. + iDbAdapter->DeleteEntryL(aSelection); // can leave - we'll let it leave as there's nothing to be rolled back now. + if(updateTheParent) + { + TRAP(err, iDbAdapter->UpdateOwnerStatusL(parentId, entry->Entry(), EFalse)); + } + // Leave if DB operations fail. Cache is not touched. + if(err) + { + TRAP_IGNORE(iDbAdapter->RollbackTransactionL()); // + User::Leave(err); + } + else + { + if(updateTheParent) + { + entry->Entry().SetOwner(EFalse); + } + // Do the cache operations. These don't require any memory allocation and cannot fail. + for (TInt index=0; indexGetFolderId() == aSelection.At(index)) + { + folderNode->iDlink.Deque(); + delete folderNode; + break; + } + } + + // Delete the entry from under its parent visible folder. + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + TRAPD(err, folderNode->DeleteEntryL(aSelection.At(index), ETrue)); + if(KErrNone == err) + { + break; + } + } + } + } + + // Update SearchSort delta cache. + TMsgType aType(EDeletedMsg); + if(CMSvSearchSortCacheManager::Instance()->iManagerEntry != NULL) + { + if(CMSvSearchSortCacheManager::Instance()->iManagerEntry->Count()>0) + { + for(TInt ii =0; ii < aSelection.Count() ; ii ++) + { + CMsvSearchSortDeltaCache::Instance()->EntryInDeltaCache(aSelection[ii],aType); + } + } + } + + CleanupStack::PopAndDestroy(); //children + } + + + +/** + * BeginTransaction() + * + * Starts a new DB transaction. + */ +void CMsvIndexAdapter::BeginTransaction() + { + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->BeginTransactionL()); + } + } + + + +/** + * CommitTransaction() + * + * Commits an already opened transaction. + */ +void CMsvIndexAdapter::CommitTransaction() + { + if(iDbAdapter) + { + TRAP_IGNORE(iDbAdapter->CommitTransactionL()); + } + } + + + +/** + * DoRemoveDeletedEntriesL() + * + * Deletes all the entries whose PcSyncCount is 0 and delete flag is set. + */ +TBool CMsvIndexAdapter::DoRemoveDeletedEntriesL() + { + CMsvCacheEntry* parent = NULL; + CMsvCacheEntry* entry = NULL; + CMsvCacheEntry* siblingEntry = NULL; + TMsvId siblingId = KMsvNullIndexEntryId; + TMsvId visibleFolder = KMsvNullIndexEntryId; + TMsvId firstchildId = KMsvNullIndexEntryId; + TInt commitCount = 1; + + // Find deleted folder + FindEntryL(KMsvDeletedEntryFolderEntryId, parent, ETrue); + + CMsvEntrySelection* selection = new(ELeave)CMsvEntrySelection; + CleanupStack::PushL(selection); + + // All deleted entries are a child of deleted folder + if(iDbAdapter->GetFirstChildIdL(parent->Entry().Id(),firstchildId)) + { + iDbAdapter->GetEntryL(firstchildId, entry, visibleFolder); + } + + while(entry && commitCount > 0) + { + if (entry->Entry().PcSyncCount() <= 0) + { + // Add entry to list of things to be deleted + __ASSERT_DEBUG(entry->Entry().Deleted(), PanicServer(EMsvDeletedFlagNotSet)); + + User::LeaveIfError(LockEntryAndStore(entry->Entry().Id())); + selection->AppendL(entry->Entry().Id()); + // Will the next entry be deleted? + if(iDbAdapter->GetNextSiblingL(entry->Entry().Id(), parent->Entry().Id(), siblingId)) + { + iDbAdapter->GetEntryL(siblingId, siblingEntry, visibleFolder); + if(siblingEntry->Entry().PcSyncCount() > 0) + { + commitCount--; + } + } + else + break; + } + iFreePoolInstance->ReleaseEntryWithoutTransaction(entry); + entry = siblingEntry; + } + + if (selection->Count() > 0) + { + User::LeaveIfError(DeleteSelection(*selection)); + } + CleanupStack::PopAndDestroy(); // selection + + // Possibly more to delete if we did not get to the end of the child list + TBool retVal = entry != NULL; + iFreePoolInstance->ReleaseEntryWithoutTransaction(entry); + return retVal; + } + + + + +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + +/** + * GetChildrenAllL() + * + * Code added for PREQ 557. + * + * The function is similar to function GetChildrenL(), but + * additionally it fetches entries from all the drive in the + * preferred drive list. + */ +void CMsvIndexAdapter::GetChildrenAllL(TMsvId aId, CArrayPtr& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm, TBool aFilterByOwnerId, TSecureId aOwnerId) + { + TMsvPreferredDrive driveEntry; + CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList(); + for(TInt index=0; indexCount(); ++index) + { + driveEntry = (*driveList)[index]; + if(EMsvMessageStoreAvailableStatus == driveEntry.status) + { + GetChildrenL(MaskTMsvId(driveEntry.driveId, aId), aSelection, aOrdering, aMtm, aFilterByOwnerId, aOwnerId); + } + } + } + + + + +/** + * GetChildrenIdAll() + * + * Code added for PREQ 557. + * + * The function is similar to function GetChildrenId(), but + * additionally it fetches entries from all the drive in the + * preferred drive list. + */ +TInt CMsvIndexAdapter::GetChildrenIdAll(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection) + { + TSecureId dummy = KMsvServerId.iId; + return GetChildrenIdAll(aId, aFilter, aSelection, EFalse, dummy); + } + + + +// Overloaded. +TInt CMsvIndexAdapter::GetChildrenIdAll(TMsvId aId, const CMsvEntryFilter& aFilter, CMsvEntrySelection& aSelection, TBool aFilterByOwnerId, TSecureId aOwnerId) + { + TInt err = KErrNone; + TMsvPreferredDrive driveEntry; + CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList(); + for(TInt index=0; indexCount(); ++index) + { + driveEntry = (*driveList)[index]; + if(EMsvMessageStoreAvailableStatus == driveEntry.status) + { + err = GetChildrenId(MaskTMsvId(driveEntry.driveId, aId), aFilter, aSelection, aFilterByOwnerId, aOwnerId); + if(KErrNone != err) + { + return err; + } + } + } + return KErrNone; + } + + + +/** + * RemoveDriveL() + * + * This function is added in PREQ 557. + * @param TUint: The drive priority, which is also the index of drive + * entry in message server data structure. + * @param TBool: If the drive still remains in the preferred drive list. + * @return None. + * + */ +void CMsvIndexAdapter::RemoveDriveL(TUint aDriveId, TUint aDriveIndex, TBool aIsStdFolderVisible /*=ETrue */) + { + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + + // Browse through the folder list. + dqIter.SetToFirst(); + while ((folderNode = dqIter++) != NULL) + { + // If the folder is from the same drive. + // Caution: If the current drive is removed, + // this will also include root and localService. + if(folderNode->GetDrive() == aDriveId) + { + folderNode->iDlink.Deque(); + delete folderNode; + } + } // while ((folderNode = dqIter++) != NULL) + + // If current drive is removed, + // Remove localService entry as well. + + // Detach the database and remove maxId entry. + if(!aIsStdFolderVisible) + { + iDbAdapter->DetachDBL(aDriveId); + iMaxMsvIdList[aDriveId] = NULL; + + CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aDriveIndex, KMsvInvalidDriveId); + CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvDriveDiskNotAvailableStatus); + } + } + + + + + +/** + * AddDriveL() + * + * This function is added in PREQ 557. + * @param TUint: The drive priority, which is also the index of drive + * entry in message server data structure. + * + */ +void CMsvIndexAdapter::AddDriveL(TUint aDrivePriority) + { + // Assign a new drive id. + TUint driveId = GetNextAvailableDriveId(); + CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aDrivePriority, driveId); + + // Attach the database. + TMsvId maxId; + TMsvPreferredDrive driveEntry; + CMsvPreferredDriveList::GetDriveList()->DriveInfoL(aDrivePriority, driveEntry); +#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB) + TRAPD(err, iDbAdapter->AttachDBL(driveEntry.driveNum, driveId, maxId, &iServer.MessageDBAdapter())); +#else + TRAPD(err, iDbAdapter->AttachDBL(driveEntry.driveNum, driveId, maxId)); +#endif + WrapDBErrorL(err); + + // Insert the max Id in the list. + iMaxMsvIdList[driveId] = ((maxId >= MaskTMsvId(driveId, KFirstFreeEntryId))? (maxId+1) : MaskTMsvId(driveId, KFirstFreeEntryId)); + } + + + + +/** + * ChangeDriveL() + * + * This function is added in PREQ 557. + * @param TUint: The drive priority, which is also the index of drive + * entry in message server data structure. + * @param TBool: If the previous drive still remains in the preferred drive list. + * @return None. + * + */ +void CMsvIndexAdapter::ChangeDriveL(TUint aNewDriveIndex, TBool aIsStdFolderVisible /*= ETrue*/) + { + // Rule1: Remove all non-standard folder node of current drive. + TInt oldCurrentDriveIndex = CMsvPreferredDriveList::GetDriveList()->CurrentDriveIndex(); + RemoveDriveL(KCurrentDriveId, oldCurrentDriveIndex, EFalse); + + // If old drive should be visible, re-attach the drive. + // This assigns a new drive id to the old current drive. + if(aIsStdFolderVisible) + { + CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(oldCurrentDriveIndex, EMsvMessageStoreAvailableStatus); + AddDriveL(oldCurrentDriveIndex); + } + + + // Clear search sort cache db table + TRAPD(err, GetDbAdapter()->ClearDBContentsL()); + WrapDBErrorL(err); + + + // 3. Flush up entries from new current drive, + // if it is already attached. + + TMsvPreferredDrive driveEntry; + CMsvPreferredDriveList::GetDriveList()->DriveInfoL(aNewDriveIndex, driveEntry); + if(KMsvInvalidDriveId != driveEntry.driveId) + { + RemoveDriveL(driveEntry.driveId, aNewDriveIndex, EFalse); + } + + + // Assign a new drive Id to the drive. + CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aNewDriveIndex, KCurrentDriveId); + CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aNewDriveIndex, EMsvMessageStoreAvailableStatus); + + + // Assign a new drive Id to the drive. + CMsvPreferredDriveList::GetDriveList()->UpdateDriveIdL(aNewDriveIndex, KCurrentDriveId); + CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aNewDriveIndex, EMsvMessageStoreAvailableStatus); + // --------- Attach the database and update MaxId. + TMsvId maxId; +#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB) + TRAP(err, iDbAdapter->AttachDBL(driveEntry.driveNum, KCurrentDriveId, maxId, &iServer.MessageDBAdapter())); +#else + TRAP(err, iDbAdapter->AttachDBL(driveEntry.driveNum, KCurrentDriveId, maxId)); +#endif + WrapDBErrorL(err); + + // Insert the max Id in the list. + iMaxMsvIdList[KCurrentDriveId] = ((maxId >= MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId))? (maxId+1) : MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId)); + + + // ------- Create a root node and its children. + TMsvId folderId = MaskTMsvId(KCurrentDriveId, KMsvRootIndexEntryId); + + // Add local service folder node in the folder list. + iRootNode = CMsvCacheVisibleFolder::NewL(folderId); + iFolderListHeader.AddFirst(*iRootNode); + + // Fetch children of root folder to cache. + RPointerArray childEntryList; + CleanupClosePushL(childEntryList); + + TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList)); + WrapDBErrorL(err); + iRootNode->AddEntryListL(childEntryList, ETrue); + childEntryList.Reset(); + + + // ------- Create a local service node and its children. + folderId = MaskTMsvId(KCurrentDriveId, KMsvLocalServiceIndexEntryId); + + // Add local service folder node in the folder list. + CMsvCacheVisibleFolder* localServiceFolder = CMsvCacheVisibleFolder::NewL(folderId); + iFolderListHeader.AddFirst(*localServiceFolder); + + TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList)); + WrapDBErrorL(err); + localServiceFolder->AddEntryListL(childEntryList, ETrue); + + CleanupStack::PopAndDestroy(); // childEntryList. + } + + + +/** + * GetNextAvailableDriveId() + * + * This function is added in PREQ 557. + * @param None: + * @return TUint: The next available drive Id. + * + */ +TUint CMsvIndexAdapter::GetNextAvailableDriveId() + { + TMsvPreferredDrive driveEntry; + TUint driveId = iFirstFreeDriveId; + for(TUint index=0; index<8; index++) + { + TBool driveIdFound = EFalse; + CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList(); + for(TInt index=0; indexCount(); ++index) + { + driveEntry = (*driveList)[index]; + if(driveId == driveEntry.driveId) + { + driveIdFound = ETrue; + break; + } + } + if(EFalse == driveIdFound) + { + iFirstFreeDriveId = ((driveId)%7 ? (driveId+1) : 2); + return driveId; + } + driveId = ((driveId)%7 ? (driveId+1) : 2); + } + return KErrNone; + } + + + +/** + * ReloadCacheL() + * + * This function is used by backup and restore functionality + * when it finds that DB being restored is changed. The function + * destroys the cache of the current drive and recreates it. + */ +void CMsvIndexAdapter::ReloadCacheL() + { + TUint currDriveIndex = CMsvPreferredDriveList::GetDriveList()->CurrentDriveIndex(); + TDriveNumber currDriveNum = CMsvPreferredDriveList::GetDriveList()->CurrentDriveNumber(); + + // Attach the database. + TMsvId maxId; +#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB) + TRAPD(err, iDbAdapter->AttachDBL(currDriveNum, KCurrentDriveId, maxId, &iServer.MessageDBAdapter())); +#else + TRAPD(err, iDbAdapter->AttachDBL(currDriveNum, KCurrentDriveId, maxId)); +#endif + WrapDBErrorL(err); + + // Update the entry in drive list. + CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(currDriveIndex, EMsvMessageStoreAvailableStatus); + + // Insert the max Id in the list. + iMaxMsvIdList[KCurrentDriveId] = ((maxId >= MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId))? (maxId+1) : MaskTMsvId(KCurrentDriveId, KFirstFreeEntryId)); + + TMsvId folderId = MaskTMsvId(KCurrentDriveId, KMsvRootIndexEntryId); + + // Add local service folder node in the folder list. + iRootNode = CMsvCacheVisibleFolder::NewL(folderId); + iFolderListHeader.AddFirst(*iRootNode); + + // Fetch children of root folder to cache. + RPointerArray childEntryList; + CleanupClosePushL(childEntryList); + + // Updating children under root node. + TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList)); + WrapDBErrorL(err); + iRootNode->AddEntryListL(childEntryList, ETrue); + childEntryList.Reset(); + + // Create a local service node and its children. + folderId = MaskTMsvId(KCurrentDriveId, KMsvLocalServiceIndexEntryId); + + // Add local service folder node in the folder list. + CMsvCacheVisibleFolder* localServiceFolder = CMsvCacheVisibleFolder::NewL(folderId); + iFolderListHeader.AddFirst(*localServiceFolder); + + // Fetch children of localService folder to cache. + TRAP(err, iDbAdapter->GetChildrenL(folderId, childEntryList)); + WrapDBErrorL(err); + localServiceFolder->AddEntryListL(childEntryList, ETrue); + + CleanupStack::PopAndDestroy(); // childEntryList. + } +#endif // #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + + + + +#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE) +#ifdef _DEBUG +void CMsvIndexAdapter::PrintL() + { + _LIT8(KFolderId, "FOLDER ID: "); + _LIT8(KDriveId, " DRIVE ID: "); + _LIT8(KComplete, " COMPLETE FLAG: "); + _LIT8(KFalse, "FALSE."); + _LIT8(KTrue, "TRUE."); + + RFileLogger logger; + CleanupClosePushL(logger); + if (logger.Connect() == KErrNone) + { + logger.CreateLog(_L("msgs"), _L("Cache.txt"), EFileLoggingModeAppend); + logger.SetDateAndTime(EFalse, EFalse); + logger.Write(_L(" Message Index Cache Structure.")); + logger.Write(_L("--------------------------------")); + logger.Write(_L("")); + } + + CMsvCacheVisibleFolder* folderNode = NULL; + TDblQueIter dqIter(iFolderListHeader); + + dqIter.SetToFirst(); + while((folderNode = dqIter++)!=NULL) + { + RBuf8 text; + CleanupClosePushL(text); + text.CreateL(100); + text.Append(KFolderId); + text.AppendNum(folderNode->GetFolderId()); +#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT) + text.Append(KDriveId); + text.AppendNum(folderNode->GetDrive()); +#endif + text.Append(KComplete); + if(folderNode->IsComplete()) + { + text.Append(KTrue); + } + else + { + text.Append(KFalse); + } + logger.Write(text); + CleanupStack::PopAndDestroy(); // text + folderNode->Print(logger); + logger.Write(_L("")); + } + logger.CloseLog(); + CleanupStack::PopAndDestroy(); // logger + } +#endif // #ifdef _DEBUG +#endif // #if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE) +