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