// 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 <msvuids.h>#include <msvstd.h>#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<CMsvCacheVisibleFolder> 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; index<driveList->Count(); 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<CMsvCacheEntry> 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<CMsvCacheEntry> 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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; index<count; ++index) { dqIter.SetToFirst(); TBool found = EFalse; while((folderNode = dqIter++)!=NULL) { TRAPD(err, folderNode->DeleteEntryL(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; index<count; ++index) { iRecursionIndex = iOrigEntryPos; TRAP(leave, DoExpandSelectionRecursivelyL(children->At(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; index<count; ++index) { DoExpandSelectionRecursivelyL(children->At(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<CMsvCacheVisibleFolder> 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<TMsvId>* 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<TMsvId>* childId = new(ELeave) RArray<TMsvId>; 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<TMsvEntry&> (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(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<TMsvId>* 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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<const TMsvEntry>& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm) { TSecureId dummy = KMsvServerId.iId; GetChildrenL(aId, aSelection, aOrdering, aMtm, EFalse, dummy); }/** * GetChildrenL() */void CMsvIndexAdapter::GetChildrenL(TMsvId aId, CArrayPtr<const TMsvEntry>& 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; ii<count; ii++) { aSelection.AppendL(entries->At(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<TUid>* mtmList = new(ELeave) CArrayFixFlat<TUid>(KMsvMtmListGranularity); CleanupStack::PushL(mtmList); if (KUidMsvNullEntry != aFilter.SortMtm()) { mtmList->AppendL(aFilter.SortMtm()); } RPointerArray<CMsvCacheEntry> selection; CleanupClosePushL(selection); CMsvCacheVisibleFolder* folderNode = NULL; CMsvEntryArray* entries = CMsvEntryArray::NewLC(*mtmList); TDblQueIter<CMsvCacheVisibleFolder> 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; i<count; ++i) { TRAP(err, aSelection.AppendL(entries->At(i)->Id())); } delete entries; return err; }/** * FilterChildrenListL */ void CMsvIndexAdapter::FilterChildrenListL(TMsvId aId, RPointerArray<CMsvCacheEntry> aChacheEntry, CMsvEntryArray& aEntryArray, const CMsvEntryFilter& aFilter, TBool aFilterByOwnerId, TSecureId aOwnerId) { TBool getInvisibleEntries = aFilter.Order().ShowInvisibleEntries(); TInt count = aChacheEntry.Count(); for(TInt i=0; i<count; ++i) { TMsvEntry entry = aChacheEntry[i]->Entry(); 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<TMsvId>* 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<TMsvId>* 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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<TInt32>* newData = new(ELeave)CArrayFixSeg<TInt32>(KMsvChangeAttributesListGranularity); CleanupStack::PushL(newData); CArrayFixSeg<TInt32>* newPcSyncCount = new(ELeave)CArrayFixSeg<TInt32>(KMsvChangeAttributesListGranularity); CleanupStack::PushL(newPcSyncCount); newData->ResizeL(aSelection.Count(), 0); newPcSyncCount->ResizeL(aSelection.Count(), 0); TInt count=aSelection.Count(); TDblQueIter<CMsvCacheVisibleFolder> 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<TMsvId>* 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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<CMsvCacheVisibleFolder> 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; index<count; ++index) { // If the entry is a visible folder, then delete the node. dqIter.SetToFirst(); while((folderNode = dqIter++)!=NULL) { if(folderNode->GetFolderId() == 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<const TMsvEntry>& aSelection, const TMsvSelectionOrdering& aOrdering, TUid aMtm, TBool aFilterByOwnerId, TSecureId aOwnerId) { TMsvPreferredDrive driveEntry; CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList(); for(TInt index=0; index<driveList->Count(); ++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; index<driveList->Count(); ++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<CMsvCacheVisibleFolder> 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<CMsvCacheEntry> 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; index<driveList->Count(); ++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<CMsvCacheEntry> 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 _DEBUGvoid 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<CMsvCacheVisibleFolder> 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)