messagingfw/msgsrvnstore/server/src/indexcontext.cpp
author hgs
Wed, 03 Nov 2010 22:41:46 +0530
changeset 62 db3f5fa34ec7
parent 0 8e480a14352b
permissions -rw-r--r--
201044_02

// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#ifdef _DEBUG
#undef _NO_SERVER_LOGGING_
#endif



#include <s32file.h>
#include <bautils.h>
#include <barsc.h>

#include <msgs.rsg>

#include "indexcontext.h"
#include "MSVSERV.H"
#include "MSVUTILS.H"
#include "MSVRBLD.H"

// will this header be expoerted to epoc\include
#include <sqldb.h>
#include "msvindexadapter.h"

#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
	_LIT(KDBFileName,"\\messaging.db");
#else
	_LIT(KDBFileName,"[1000484B]messaging.db");
#endif

_LIT(KServerResourceFile,"\\resource\\messaging\\msgs.rsc");

#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
	_LIT(KStoreDeletedFile,"\\private\\1000484b\\storedeleted.tmp");
#else
	_LIT(KStoreDeletedFile,"storedeleted.tmp");
#endif

const TInt KMsvInitDelayTime = 0x00000001; // as soon as possible
const TUid KUidMsvIndexFile = {0x10003C6B};
extern const TInt KMsvIndexStoreVersionNumber = 1;   // See MsvIndex.cpp
//**********************************
// CMsvIndexContext
//**********************************

CMsvIndexContext* CMsvIndexContext::NewL(CMsvServer& aServer, MMsvContextObserver& aObserver)
	{
	CMsvIndexContext* self = CMsvIndexContext::NewLC(aServer, aObserver);
	CleanupStack::Pop();
	return self;
	}

CMsvIndexContext* CMsvIndexContext::NewLC(CMsvServer& aServer, MMsvContextObserver& aObserver)
	{
	CMsvIndexContext* self = new(ELeave)CMsvIndexContext(aServer, aObserver);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

void CMsvIndexContext::ConstructL()
	{
	iUpdateEntryStreams = new(ELeave)CMsvEntrySelection;
	iRemoveEntries = new(ELeave)CMsvEntrySelection;

	TFileName fileName(KServerResourceFile);
	MsvUtils::AddPathAndExtensionToResFileL(fileName);
 	BaflUtils::NearestLanguageFile(iServer.FileSession(), fileName);

	// Load the initial index structure from resource file
	RResourceFile resource;
	resource.OpenL(iServer.FileSession(), fileName);
	CleanupClosePushL(resource);

	// Get ready to read resource

	iBuf = resource.AllocReadL(R_SERVER_INDEX_STARTUP);

	CleanupStack::PopAndDestroy(); //resource

	}

CMsvIndexContext::~CMsvIndexContext()
	{
	Cancel();
	delete iBuf;
	delete iIndexAdapter;
	delete iUpdateEntryStreams;
	delete iRemoveEntries;
	}

CMsvIndexContext::CMsvIndexContext(CMsvServer& aServer, MMsvContextObserver& aObserver)
: CActive(EPriorityStandard), iServer(aServer), iObserver(aObserver)
	{
	CActiveScheduler::Add(this);
	}



// CODE USED AFTER PREQ 557.
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)

/**
 * CreateIndex()
 * 
 * Code added for PREQ 557.
 */
void CMsvIndexContext::CreateIndexL()
	{
	__ASSERT_DEBUG(iProgress.iState == TMsvIndexLoadProgress::EIndexNotLoaded, PanicServer(EMsvBadIndexState));
	
	DoCreateIndexL();
	IndexLoadingCompleted();	
	}
	
	

void CMsvIndexContext::IndexLoadingCompleted()
	{
	iProgress.iError = KErrNone;
	
	IndexLoaded(EMsvNullNotification);

	// Tell the observer that we've finshed
	iObserver.ContextComplete(KErrNone, iRunMailInit);
	}




/**
 * DoCreateIndexL()
 *
 * Create the index adapter object.
 * Code added in PREQ 557.
 */
void CMsvIndexContext::DoCreateIndexL()
	{	
	// Remember stuff
	iObserverStatus = NULL;
	iRunMailInit = EFalse;

	// Update drive status in preferred drive list.
	TDriveState driveStatus = EMsvInvalidDriveStatus;
	for(TUint index=0; index<CMsvPreferredDriveList::GetDriveList()->Count(); index++)
		{
		UpdateDriveStatusL(index, driveStatus);
		}

	// Look for the current drive.
	// The first drive in the preferred drive list, whose 
	// status is either EMsvMessageStoreAvailable or 
	// EMsvMessageStoreUnavailable will be the current drive.
	TMsvPreferredDrive driveEntry;
	CMsvPreferredDriveList *driveList = CMsvPreferredDriveList::GetDriveList();
	for(TInt currentDriveIndex=0; currentDriveIndex<driveList->Count(); ++currentDriveIndex)
		{
		driveEntry = (*driveList)[currentDriveIndex];
		if( (EMsvMessageStoreAvailableStatus   == driveEntry.status) ||
			(EMsvMessageStoreUnavailableStatus == driveEntry.status)
		  )
			{
			// Set the current drive index to server.
			CMsvPreferredDriveList::GetDriveList()->SetCurrentDriveIndex(currentDriveIndex);
			
			TParse parse;
			TPtrC drive(TDriveUnit(driveEntry.driveNum).Name());
			parse.Set(KMsvDefaultIndexFile2(), &drive, NULL);
			iMessageFolder = parse.DriveAndPath();
				
			// If the drive already has a message store...
			if(EMsvMessageStoreUnavailableStatus == driveEntry.status)
				{
				// Create the database and all standard table.
				CMsvDBAdapter::CreateDBL(driveEntry.driveNum);
		
				// Update the drive status.
				CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(currentDriveIndex, EMsvMessageStoreAvailableStatus);
				
				ResetAndCreateNewMailStoreL();
				iRunMailInit = ETrue;
			
				// Creating index adapter object.
				iIndexAdapter = CMsvIndexAdapter::NewL(iServer);
				
				// Get updated drive id.
				CreateStandardEntriesFromResourceFileL(KCurrentDriveId);		
				iIndexAdapter->SetLocalServiceComplete();
				}
			else
				{
				// The drive already has a message store.
				
				// Creating index adapter object.
				iIndexAdapter = CMsvIndexAdapter::NewL(iServer);
				
				ResetAndCreateNewMailStoreL(EFalse);						
				}
			break;
			}
		}	
	}	




/**
 * UpdateDriveStatusL()
 * @param TUint: The index of the said drive in preferred
 *               drive list.
 */	
void CMsvIndexContext::UpdateDriveStatusL(TUint aDriveIndex, TDriveState& aStoreStatus)
	{
	aStoreStatus = EMsvInvalidDriveStatus;
	TBool deleteStore = EFalse;
	TMsvPreferredDrive driveEntry;
	CMsvPreferredDriveList::GetDriveList()->DriveInfoL(aDriveIndex, driveEntry);

	// Check if the media is available in the drive.
	TVolumeInfo volume;
	if (iServer.FileSession().Volume(volume, driveEntry.driveNum) != KErrNone)
		{
		CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvDriveDiskNotAvailableStatus);
		aStoreStatus = EMsvDriveDiskNotAvailableStatus;
		return;
		}
	
	// Validate the database. The function opens the database
	// and check its version and returns appropriate error.
	TRAPD(err, CMsvDBAdapter::ValidateDatabaseL(driveEntry.driveNum));
	
	// If no error, database is available.
	if(KErrNone == err)
		{
		CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvMessageStoreAvailableStatus);
		return;
		}
	
	// If the database is not found in the drive.
	if(KErrNotFound == err)
		{
		// Check if index file exists.
		RFile file;
		TParse parse;
		TPtrC drive(TDriveUnit(driveEntry.driveNum).Name());
		parse.Set(KMsvDefaultIndexFile2, &drive, NULL);
		TFileName indexFileName = parse.FullName();
		err = file.Open(iServer.FileSession(), indexFileName, EFileShareAny|EFileWrite);
		//file.Close();
		
		// If index file exists, set drive status to NOT SUPPORTED.
		if(KErrNone == err)
			{
			CPermanentFileStore* fileStoreIndex = NULL;
			TRAPD(permError, fileStoreIndex = CPermanentFileStore::FromL(file));
			if(KErrNone == permError)
				{
				// Check if the store is corrupt. If so then delete it.
				if (fileStoreIndex->Type() != TUidType(KPermanentFileStoreLayoutUid, KUidMsvIndexFile))
					{
					deleteStore = ETrue;
					}
				}
			// There was an error in getting a permanent filestore object.
			// Mark the message store for deletion.
			else
				{
				deleteStore = ETrue;
				}
			
			delete fileStoreIndex;
			
			//If message store is corrupted, wipe it.
			if(deleteStore)
				{
				TFileName mail2Folder = parse.DriveAndPath();
				CFileMan* fileMan = CFileMan::NewL(iServer.FileSession());
				// Remove the readonly attribute..
				(void)fileMan->Attribs(mail2Folder, 0, KEntryAttReadOnly, TTime(0), CFileMan::ERecurse);
				CleanupStack::PushL(fileMan);
				//Check if the mailfolder exists..
				TBool mail2FolderExists = BaflUtils::FileExists(iServer.FileSession(), mail2Folder);
				if(mail2FolderExists)
					{
					// Remove old message store if exists..
					User::LeaveIfError(fileMan->RmDir(mail2Folder));
					}
				CleanupStack::PopAndDestroy(fileMan);
				CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvMessageStoreUnavailableStatus);
				aStoreStatus = EMsvMessageStoreCorruptStatus;
				
				// Create store delete file in the respective drive.
				CreateStoreDeleteFile(driveEntry.driveNum);
				}
			//Else only set status as EMsvMessageStoreNotSupportedStatus.
			else
				{
				CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvMessageStoreNotSupportedStatus);
				aStoreStatus = EMsvMessageStoreNotSupportedStatus;
				}
			
			file.Close();
			return;
			}
		
		// If index file does not exists, set drive status to STORE UNAVAILABLE.
		if((KErrNotFound == err) || (KErrPathNotFound == err))
			{
			CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvMessageStoreUnavailableStatus);
			aStoreStatus = EMsvMessageStoreUnavailableStatus;
			return;
			}
			
		User::Leave(err);
		}
	
	//If a database exists, but is corrupt.
	if( (KSqlErrCorrupt == err) ||
		(KSqlErrNotDb   == err)
	  )
		{
		// If the database is corrupt, delete
		// the database and old message store.
		
		// Delete the database.
		TParse parse;
		TPtrC drive(TDriveUnit(driveEntry.driveNum).Name());
		parse.Set(KDBFileName, &drive, NULL);
		TFileName dBFile = parse.FullName();
		RSqlDatabase::Delete(dBFile);
		
		// Delete the message store. //ISSUE Can we use ResetAndCreateNewMailStore()?
		parse.Set(KMsvDefaultIndexFile2, &drive, NULL);
		TFileName mail2Folder = parse.DriveAndPath();
		CFileMan* fileMan = CFileMan::NewL(iServer.FileSession());
		// Remove the readonly attribute..
		(void)fileMan->Attribs(mail2Folder, 0, KEntryAttReadOnly, TTime(0), CFileMan::ERecurse);
		CleanupStack::PushL(fileMan);
		//Check if the mailfolder exists..
		TBool mail2FolderExists = BaflUtils::FileExists(iServer.FileSession(), mail2Folder);
		if( mail2FolderExists)
			{
			// Remove old message store if exists..
			User::LeaveIfError(fileMan->RmDir(mail2Folder));
			}
		CleanupStack::PopAndDestroy(fileMan);
		
		// Update the drive status.
		CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvMessageStoreUnavailableStatus);
		aStoreStatus = EMsvMessageStoreCorruptStatus;

		// Create store delete file in the respective drive.
		CreateStoreDeleteFile(driveEntry.driveNum);
		return;
		}
	   
	//If the database version is not supported.
	if(KErrNotSupported == err)
		{
		// If the drive as old version of database.
		CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aDriveIndex, EMsvMessageStoreNotSupportedStatus);
		aStoreStatus = EMsvMessageStoreNotSupportedStatus;
		return;
		}

	// Do not return DB error.
	if(err <= KSqlErrGeneral)
		{
		// If DB Error, return KErrGeneral.
		User::LeaveIfError(KErrGeneral);
		}
	else
		{
		User::LeaveIfError(err);
		}
	}
	
	


TInt CMsvIndexContext::ChangeDrive(TUint aNewDriveIndex, TRequestStatus& aStatus)
	{
	TInt err = ChangeDrive(aNewDriveIndex, ETrue, ETrue);
	
	// Remember the status which we'll complete
	aStatus = KRequestPending;
	iObserverStatus = &aStatus;
	return err;
	}



void CMsvIndexContext::ChangeDriveCompleted(TInt aError)
	{
	// Remember error
	iProgress.iError = aError;	
	
	if (iObserverStatus)
		{
		User::RequestComplete(iObserverStatus, aError);
		}
	}
	
	
	
/**
 * ChangeDriveL()
 * @param TUint: Index of the new current drive in the preferred drive list.
 * @param TBool: True, if old drive is still present in the preferred drive list.
 *               False, otherwise.
 * 
 */
TInt CMsvIndexContext::ChangeDrive(TUint aNewDriveIndex, TBool aIsRemovePartial /*= ETrue*/, TBool aAsync /*=EFalse */)
	{
	TDriveNumber newDriveNumber;
	TRAPD(err, DoChangeDriveL(aNewDriveIndex, aIsRemovePartial, newDriveNumber));

	// Add a function ChangeDriveComplete() in server
	// which should be called here. The function should 
	// run mailInit and recreate backup object.
	iObserver.ChangeDriveComplete(err, iRunMailInit, newDriveNumber);
	
	if(aAsync)
		{
		iStatus = KRequestPending;
		SetActive();
		TRequestStatus* st = &iStatus;
		User::RequestComplete(st, err);
		}
	return err;
	}



void CMsvIndexContext::DoChangeDriveL(TUint aNewDriveIndex, TBool aIsRemovePartial, TDriveNumber& aNewDriveNumber)
	{
	iObserverStatus = NULL;
	TMsvPreferredDrive driveEntry;
	CMsvPreferredDriveList::GetDriveList()->DriveInfoL(aNewDriveIndex, driveEntry);

	aNewDriveNumber = driveEntry.driveNum;
	if(EMsvMessageStoreUnavailableStatus == driveEntry.status)
		{
		// Create the database and all standard table.
		CMsvDBAdapter::CreateDBL(driveEntry.driveNum);
		
		// Update the drive status.
		CMsvPreferredDriveList::GetDriveList()->UpdateDriveStatusL(aNewDriveIndex, EMsvMessageStoreAvailableStatus);
		
		TParse parse;
		TPtrC drive(TDriveUnit(driveEntry.driveNum).Name());
		parse.Set(KMsvDefaultIndexFile2(), &drive, NULL);
		iMessageFolder = parse.DriveAndPath();
		
		ResetAndCreateNewMailStoreL();
		iRunMailInit = ETrue;
			
		// Perform ChangeDrive operation.
		iIndexAdapter->ChangeDriveL(aNewDriveIndex, aIsRemovePartial);
		
		// Read initial entries from resources
		CreateStandardEntriesFromResourceFileL(KCurrentDriveId);
		iIndexAdapter->SetLocalServiceComplete();
		}
	else
		{
		// Update the message folder local variable.
		TParse parse;
		TPtrC drive(TDriveUnit(driveEntry.driveNum).Name());
		parse.Set(KMsvDefaultIndexFile2(), &drive, NULL);
		iMessageFolder = parse.DriveAndPath();
		
		ResetAndCreateNewMailStoreL(EFalse);
		// Perform ChangeDrive operation.
		iIndexAdapter->ChangeDriveL(aNewDriveIndex, aIsRemovePartial);			
		iRunMailInit = EFalse;
		}
	
	// Create service directories in new drive.
	TRAP_IGNORE(DoCreateServiceDirsL(KCurrentDriveId));
	
	TRAP_IGNORE(LocalizeStandardFoldersL());
	}




void CMsvIndexContext::GetInPreparationIds(TUint aDriveId)
	{
	delete iRemoveEntries;
	iRemoveEntries = new CMsvEntrySelection;
	
	iIndexAdapter->GetInPreparationIds(*iRemoveEntries, aDriveId);
	}
	



/**
 * CreateStandardEntriesFromResourceFileL()
 * @param None:
 *
 * Will read messaging resource file and create entries.
 */	
void CMsvIndexContext::CreateStandardEntriesFromResourceFileL(TUint aDriveId)
	{
	// Read initial entries from resources
	TResourceReader reader;
	reader.SetBuffer(iBuf);	
	const TInt numberOfEntries = reader.ReadInt16();
	
	for (TInt index=0; index < numberOfEntries; ++index)
		{
		TMsvEntry newEntry;
				
		// Values from resource file
		newEntry.iId = MaskTMsvId(aDriveId, reader.ReadInt32());
		newEntry.iParentId = reader.ReadInt32();
		newEntry.iServiceId = reader.ReadInt32();
		newEntry.iType.iUid = reader.ReadInt32();
		newEntry.iMtm.iUid = reader.ReadInt32();
		newEntry.iData = reader.ReadInt32();
		newEntry.iDescription.Set(reader.ReadTPtrC());
		newEntry.iDetails.Set(reader.ReadTPtrC());
		newEntry.iDate.UniversalTime();
		newEntry.iSize=0;

		// Create the new entry.
		// This is required to create associated service directory.
		User::LeaveIfError(iServer.AddEntry(this, newEntry, KMsvServerId, EFalse));
		}
	}

#else		// #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)



void CMsvIndexContext::CreateIndexL(TInt aDrive, TRequestStatus& aStatus)
	{
	// Kick off index loading
	CreateIndexL(aDrive, EFalse);
	// Remember the status which we'll complete
	aStatus = KRequestPending;
	iObserverStatus = &aStatus;
	}



void CMsvIndexContext::CreateIndexL(TInt aDrive, TBool aSync)
	{
	__ASSERT_DEBUG(iProgress.iState == TMsvIndexLoadProgress::EIndexNotLoaded, PanicServer(EMsvBadIndexState));
	__ASSERT_DEBUG(RFs::IsValidDrive(aDrive), PanicServer(EMsvInvalidDrive));

	// Remember stuff
	iConfig.iDrive = TDriveUnit(aDrive);
	iObserverStatus = NULL;
	iRunMailInit = EFalse;

	// Set the drive for this index file
	TParse parse;
	TPtrC drive(iConfig.iDrive.Name());
	parse.Set(KMsvDefaultIndexFile2(), &drive, NULL);

	// Construct the message folder name etc
	iMessageFolder = parse.DriveAndPath();
	TFileName indexFileName = parse.FullName();
	// Adding the new code
	// Now we are creating the new database adapter.
	// Find out if the db exists
	// Construct the db file name
	parse.Set(KDBFileName, &drive, NULL);
	TFileName dBFile = parse.FullName();	
	RSqlDatabase db;
	TRAPD(err,db.OpenL(dBFile));
	TBool create=EFalse;
	
	// Database may be corrupted, so delete the old database and create a new one.
	if(err == KSqlErrCorrupt || err == KSqlErrNotDb )
		{
		RSqlDatabase::Delete(dBFile);
		create = ETrue;
		}
	// Database is not present. We need to check if the store is an old one.
	// If it is, we check if it is corrupt. 
	// If it is corrupt we can safely delete the store and create a new one
	// in its place. If not, then we leave with KErrNotSupported stating that
	// the old store is incompatible with this implementation of the message
	// store which uses an SQL database for the index.
	else if( err == KErrNotFound)
		{
		// Check if any of the old index files exist in the Mail2 folder.
		TParse parse1;
		parse1.Set(KMsvDefaultIndexFile, &drive, NULL); //old version of index file
		if( BaflUtils::FileExists(iServer.FileSession(), parse1.FullName()) ||
		    BaflUtils::FileExists(iServer.FileSession(), indexFileName))
			{
			// Open index store.
			RFile file;
			TInt error = file.Open(iServer.FileSession(), indexFileName, EFileShareAny|EFileWrite);
			
			// Check if we have an old 'legacy' version of the index file.
			CPermanentFileStore* index = NULL;
			TRAPD(permError, index = CPermanentFileStore::FromL(file));
			CleanupStack::PushL(index);
			if (KErrNone == permError)
				{
				// Check if the store is corrupt. If so then delete it.
				if (index->Type() != TUidType(KPermanentFileStoreLayoutUid, KUidMsvIndexFile))
					{
					create = ETrue;
					}
				}
			// There was an error in getting a permanent filestore object.
			// Delete the message store.
			else
				{
				create = ETrue;
				}
			
			CleanupStack::PopAndDestroy(); //index
			file.Close();
			
			// If the old store is valid, then throw an error.
			if(!create)
				{
				User::Leave(KErrNotSupported);
				}
			}
		//Index file does not exist in Mail2 folder. Go ahead with database creation.
		else
			{
			create = ETrue;
			}
		}	
	else
		{
		db.Close();
		User::LeaveIfError(err);
		}
	
	db.Close();
	
	if(!create)
		{
		// Check for database version.
		TRAP(err, CMsvDBAdapter::ValidateDatabaseL(dBFile));
		User::LeaveIfError(err);
		
		iIndexAdapter = CMsvIndexAdapter::OpenL(iServer, dBFile);
		ResetAndCreateNewMailStoreL(EFalse);
		}
	else
		{
		iIndexAdapter = CMsvIndexAdapter::NewL(iServer, dBFile);
		ResetAndCreateNewMailStoreL();
		CreateStoreDeleteFile();
		iRunMailInit = ETrue;
		
		TResourceReader reader;
		reader.SetBuffer(iBuf);	
		// Read initial entries from resources
		const TInt numberOfEntries = reader.ReadInt16();
		for (TInt index=0; index<numberOfEntries; ++index)
			{
			TMsvEntry newEntry;
			// Values from resource file			
			newEntry.iId = reader.ReadInt32();
			newEntry.iParentId = reader.ReadInt32();
			newEntry.iServiceId = reader.ReadInt32();
			newEntry.iType.iUid = reader.ReadInt32();
			newEntry.iMtm.iUid = reader.ReadInt32();
			newEntry.iData = reader.ReadInt32();
			newEntry.iDescription.Set(reader.ReadTPtrC());
			newEntry.iDetails.Set(reader.ReadTPtrC());
			newEntry.iDate.UniversalTime();
			newEntry.iSize=0;

			// Create the new entry
			// This is required to create associated service directory.
			TInt error = iServer.AddEntry(this, newEntry, KMsvServerId, EFalse);
			User::LeaveIfError(error);
			}
		iIndexAdapter->SetLocalServiceComplete();	
			
		}
	if(aSync)
		{
		IndexLoadingCompleted(KErrNone);
		}
	else
		{
		iStatus = KRequestPending;
		SetActive();
		TRequestStatus* st = &iStatus;
		User::RequestComplete(st, KErrNone);// do we need to propogate the error from adapter to here
		}
	}



TInt CMsvIndexContext::LoadStoreConfig(TBool aLoad)
//
// In-between function to save the cost of a trap
//
	{
	TRAPD(error, aLoad ? CMsvServer::CurrentConfigL(iServer.FileSession(), iConfig) : DoStoreConfigL());
	return error;
	}




void CMsvIndexContext::DoStoreConfigL()
	{
	// we only want to store the config file if it has changed,
	// we also don't care about UniqueIDs for the internal drive
	if(iConfig.iDebug!=iConfig.iDebugAsLoaded ||
		iConfig.iDrive!=iConfig.iDriveAsLoaded ||
		(iConfig.iDrive!=iServer.FileSession().GetSystemDrive() && iConfig.iUniqueID!=iConfig.iUniqueIDAsLoaded))
		{
		TChar driveChar= iServer.FileSession().GetSystemDriveChar();
		TBuf<2> systemDrive;
		systemDrive.Append(driveChar);
		systemDrive.Append(KDriveDelimiter);
	    TPath pathName(systemDrive);
		pathName.Append(KServerINIFile);
		CDictionaryFileStore *store=CDictionaryFileStore::OpenLC(iServer.FileSession(),pathName,KNullUid);
		RDictionaryWriteStream stream;
		stream.AssignLC(*store, KUidMsvMessageDriveStream);

		stream.WriteUint8L(KMsvMessageDriveStreamVersionNumber); // version number
		stream << iConfig.iDrive.Name();
		stream.WriteUint32L(iConfig.iUniqueID);
		stream.WriteInt8L(iConfig.iDebug);

		stream.CommitL();
		store->CommitL();
		CleanupStack::PopAndDestroy(2,store); // stream, store
		}
	}



void CMsvIndexContext::IndexLoadingCompleted(TInt aError)
	{
	// Remember error
	iProgress.iError = aError;

	// If there is no index at this point it's because the index loading is synchronous
	if (!iIndexAdapter && !aError)
		return;
	
	// If there was an error, wipe the message folder and create a blank one
	if (aError == KErrCorrupt || aError == KErrOverflow || aError == KErrNotFound || aError == KErrEof || aError == KErrNotSupported)
		{
#ifndef _NO_SERVER_LOGGING_
		iServer.Log(_L("Index corrupted, starting rebuild (%d)"), aError);
#endif
		// Note: Following doesn't send any notifications
		IndexFailedToLoad(KErrNone);
		
		//Create a file to let the UI know that the store has been deleted.
 		CreateStoreDeleteFile();
		}

	// Get the drive Id
	if (!aError)
		{
		TVolumeInfo volume;
		aError = iServer.FileSession().Volume(volume, TInt(iConfig.iDrive));
		if (!aError)
			{
			iConfig.iUniqueID = volume.iUniqueID;
			}
		}		

	// If we are not rebuilding, then inform clients of outcome
	if (aError)
		IndexFailedToLoad(aError);
	else
		IndexLoaded(EMsvNullNotification);

	// Tell the observer that we've finshed
	iObserver.ContextComplete(aError, iRunMailInit);

	if (iObserverStatus)
		User::RequestComplete(iObserverStatus, aError);
	}



void CMsvIndexContext::IndexFailedToLoad(TInt aError)
//
// The index has failed to load. Remove partially created in-memory index
//
	{
	// Update progress
	iProgress.iError = aError;
	delete iIndexAdapter;
	iIndexAdapter = NULL;
	iProgress.iState = TMsvIndexLoadProgress::EIndexNotLoaded;
	if (aError)
		iServer.NotifyChanged(EMsvIndexFailedToLoad, KMsvNullIndexEntryId, aError);
	}


#endif 		// #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)

	
	

void CMsvIndexContext::IndexLoaded(TMsvServerChangeNotificationType aNotification)
//
// The index has been successfully loaded
//
	{
	iProgress.iState = TMsvIndexLoadProgress::EIndexComplete;
	iProgress.iError = KErrNone;

	// Remove any orphaned entries
	__ASSERT_DEBUG(iRemoveEntries->Count()==0, PanicServer(EMsvRemovingOrphanedEntries));
	iIndexAdapter->GetInPreparationIds(*iRemoveEntries);

	// kick off the delay timer, this will allow the message server to make
	// any changes it wants to the index on startup, currently it will
	// remove any entries in iRemoveEntries and try and localize the standard folders.
	if (!iServer.Delay().IsActive())
		iServer.Delay().After(KMsvInitDelayTime);
	
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
	TRAP_IGNORE(DoCreateServiceDirsL(KCurrentDriveId));
#else
	TRAP_IGNORE(DoCreateServiceDirsL());
#endif

	// Queue notification - it will be sent before EMsvIndexLoaded
	// when we can guarantee that index is fully loaded
	if (aNotification != EMsvNullNotification)
		iServer.QueueNotifyChanged(aNotification);
	}

void CMsvIndexContext::LocalizeStandardFoldersL()
	{
	// Get ready to read resource
	TResourceReader reader;
	reader.SetBuffer(iBuf);

	// Read initial entries from resources
	const TInt numberOfEntries = reader.ReadInt16();
	for (TInt ii=0; ii<numberOfEntries; ii++)
		{
		TMsvEntry resourceEntry;

		// Values from resource file
		resourceEntry.iId = reader.ReadInt32();
		resourceEntry.iParentId = reader.ReadInt32();
		resourceEntry.iServiceId = reader.ReadInt32();
		resourceEntry.iType.iUid = reader.ReadInt32();
		resourceEntry.iMtm.iUid = reader.ReadInt32();
		resourceEntry.iData = reader.ReadInt32();
		resourceEntry.iDescription.Set(reader.ReadTPtrC());
		resourceEntry.iDetails.Set(reader.ReadTPtrC());

		// Add Universal time and initialise size
		resourceEntry.iDate.UniversalTime();
		resourceEntry.iSize=0;
		TMsvEntry *entry;
		
		// 557. Will by default pick entries from current drive.
		TInt error = iIndexAdapter->GetEntry(resourceEntry.Id(),entry);
		if(error ==KErrNone)
			{
			TMsvEntry newEntry= *entry;
			TBool changed=EFalse;
			if(newEntry.iDescription.Compare(resourceEntry.iDescription)!=0)
				{
				newEntry.iDescription.Set(resourceEntry.iDescription);
				changed=ETrue;
				}
			if(newEntry.iDetails.Compare(resourceEntry.iDetails)!=0)
				{
				newEntry.iDetails.Set(resourceEntry.iDetails);
				changed=ETrue;
				}

			// ignore the error we don't want to fail just because the
			// inbox isn't in the right language, we will try again next
            // time the server starts.
			if(changed!=EFalse)
				{
				iIndexAdapter->LockEntry(newEntry.Id());
				iIndexAdapter->ChangeEntryInternal(newEntry, KMsvServerId);
				iIndexAdapter->ReleaseEntry(newEntry.Id());
				}
		   	}
		else if( error == KErrNotFound )
        	{
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
			resourceEntry.SetParent(MaskTMsvId(KCurrentDriveId, resourceEntry.iParentId));
			resourceEntry.SetId(MaskTMsvId(KCurrentDriveId, resourceEntry.iId));
#endif
			// Create the new entry
			iServer.AddEntry(this, resourceEntry, KMsvServerId, EFalse);
        	}
	    }
	}



#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
void CMsvIndexContext::DoCreateServiceDirsL(TUint aDriveId)
#else
void CMsvIndexContext::DoCreateServiceDirsL()
#endif
	{
	CMsvEntryFilter* filter = CMsvEntryFilter::NewLC();
	filter->SetOrder(TMsvSelectionOrdering(0, EMsvSortByNone, ETrue));

	CMsvEntrySelection* selection = new(ELeave)CMsvEntrySelection;
	CleanupStack::PushL(selection);
	TInt err = KErrNone;
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
	err = iIndexAdapter->GetChildrenId(MaskTMsvId(aDriveId, KMsvRootIndexEntryId), *filter, *selection);
#else
	err = iIndexAdapter->GetChildrenId(KMsvRootIndexEntryId, *filter, *selection);
#endif

	// make sure the service directories are there
	if (err == KErrNone)
		{
		TInt count = selection->Count();
		while (count--)
			{
			TFileName filename;
			filename.Copy(iMessageFolder);

			TMsvId id = selection->At(count);

#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
			MsvUtils::ConstructEntryName(UnmaskTMsvId(id), MaskTMsvId(aDriveId, id), filename, MsvUtils::EPath);
#else
			MsvUtils::ConstructEntryName(id, id, filename, MsvUtils::EPath);
#endif

			iServer.FileSession().MkDir(filename); // ignore any error
			}
		}

	CleanupStack::PopAndDestroy(2); // selection, filter
	}




void CMsvIndexContext::DoCancel()
	{
	if (iIndexAdapter)
		iIndexAdapter->Cancel();
	if (iObserverStatus)
		User::RequestComplete(iObserverStatus, KErrCancel);
	}

void CMsvIndexContext::RunL()
	{
#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)	
	// In 557, ChangeDriveL() can be asynchronous,
	// but CreateIndexL() will always be synchronous.
	ChangeDriveCompleted(iStatus.Int());
#else
	IndexLoadingCompleted(iStatus.Int());
#endif
	}

const TMsvIndexLoadProgress& CMsvIndexContext::Progress()
	{
	// Get the progress from the index first
	if (iIndexAdapter)
		iProgress.iIndex = iIndexAdapter->Progress();
	return iProgress;
	}



TBool CMsvIndexContext::GetAndClearIndexCorruptFlagL()
	{
	RFs& fs=iServer.FileSession();

#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
	TDriveNumber driveNum = CMsvPreferredDriveList::GetDriveList()->CurrentDriveNumber();
	User::LeaveIfError(fs.SetSessionToPrivate(driveNum));
#else
	User::LeaveIfError(fs.SetSessionToPrivate(Config().iDrive));
#endif		// #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)

	TInt error=fs.Delete(KStoreDeletedFile);
	if(error !=KErrNone && error != KErrNotFound)
		{
		User::Leave(error);
		}
	// at this point error is either KErrNone if the file indicating the index
	// was corrupt is present and has been deleted or KErrNotFound if the file
	// was not present. The function returns ETrue if the file was present and
	// therefore the index was corrupt and the user should be informed.
	return error == KErrNone ? ETrue : EFalse;
	}



#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
void CMsvIndexContext::CreateStoreDeleteFile(TDriveNumber aDriveNum)
#else
void CMsvIndexContext::CreateStoreDeleteFile()
#endif
	{
	
#if (!defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
	TDriveNumber aDriveNum = (TDriveNumber)(TInt) Config().iDrive;
#endif

	RFs& fs = iServer.FileSession();
	
	TInt err = fs.SetSessionToPrivate(aDriveNum);	
	if(err == KErrNone)
		{
		RFile storeDeletedFile;
		#if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
			TParse parse;
			TPtrC drive(TDriveUnit(aDriveNum).Name());
			parse.Set(KStoreDeletedFile, &drive, NULL);
			TFileName fileName = parse.FullName();
		#else
			TFileName fileName(KStoreDeletedFile);
		#endif		// #if (defined SYMBIAN_MESSAGESTORE_UNIT_TESTCODE)
		
		err = storeDeletedFile.Replace(fs, fileName, EFileShareExclusive);
		if(err == KErrNone)
			{
			_LIT8(KMsvIndexStoreDeleted,"StoreDeleted");
			storeDeletedFile.Write(KMsvIndexStoreDeleted);
			}
		storeDeletedFile.Close();
		}
	}




// Reset Mail Store(Mail2) so that old messages are deleted when new index file created..
void CMsvIndexContext::ResetAndCreateNewMailStoreL(TBool aDelete)
	{
	CFileMan* fileMan = CFileMan::NewL(iServer.FileSession());
	CleanupStack::PushL(fileMan);

	if(aDelete)
		{
		//Check if the mailfolder exists..
		if(BaflUtils::FileExists(iServer.FileSession(), iMessageFolder))
			{
			// Remove the readonly attribute..
			(void)fileMan->Attribs(iMessageFolder,0, KEntryAttReadOnly, TTime(0), CFileMan::ERecurse);
			// Remove old message store if exists..
			User::LeaveIfError(fileMan->RmDir(iMessageFolder));
			}
		}
		
	// Create the folder for the message store
	TInt err = iServer.FileSession().MkDirAll(iMessageFolder);
	if(err != KErrAlreadyExists)
		{
		User::LeaveIfError(err);
		}
	CleanupStack::PopAndDestroy(fileMan);
	}