// Copyright (c) 1997-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:
//

#include "agsfilemanager.h"

#include "agsasyncdelete.h"
#include "agscategoryindex.h"
#include "agscategorylist.h"
#include "agsentrymanager.h"
#include "agsfileconverter.h"
#include "agsfilesearch.h"
#include "agsentrymodel.h"
#include "agsattachmentindex.h"
#include "agmutil.h"
#include "agsmain.h"
#include "agspermanentdata.h"
#include "agssess.h"
#include "calcommonimpl.h"
#include "agmdebug.h"
#include "agstzruleindex.h"
#include "agmcalendarinfo.h"

#include <apparc.h>
#include <s32file.h>

const TInt KAgnCompactionThresholdInBytes=10240;

//
// CAgnServFile
//
 
// Make sure the active object has the same priority as the server
// If it's higher, the client will be blocked when the server
// does an extended operation. If lower, the server won't ever
// get around to the extended tasks, unless it is left alone by
// all clients.
CAgnServFile::CAgnServFile(RFs& aFs, CAgnServer& aAgnServer) :
	CActive(CActive::EPriorityStandard),
	iAgnServer(aAgnServer),
	iFs(aFs),
	iActiveStep(CCalAsyncTaskManager::ENoAction),
#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
	iShutdownNotification(EFalse),
#endif
	iIsFileDisabled(EFalse)
	{
	CActiveScheduler::Add(this);
	iRefCount = 0;
	}


CAgnServFile* CAgnServFile::NewL(RFs& aFs, CAgnServer& aAgnServer)
	{
	CAgnServFile* self = new(ELeave)CAgnServFile(aFs, aAgnServer);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CAgnServFile::ConstructL()
	{
	const TInt KMessageVectorGranularity = 4; // unlikely to be 4 messages simultaneously
	iMessageVector = new (ELeave) CArrayFixFlat<TAgnMessageToComplete> (KMessageVectorGranularity);
	iFileShutdownDelayTimer = CAgnServFileShutdownDelayTimer::NewL(*this);
	}

CAgnServFile::~CAgnServFile()
	{
	CloseAgendaImmediately();
	
	delete iFileName;
	delete iMessageVector;
	delete iCategoryList;
	delete iAsyncDelete;
	delete iFileShutdownDelayTimer;
	}
	
CAgnTlsProxy* CAgnServFile::TimeZoneConverter() const
	{
	return iAgnServer.TimeZoneConverter();
	}


void CAgnServFile::Start()
//
// Set the object to the active state and make a request
//
	{
	if ( ! IsActive())
		{
		SetActiveAndMakeRequest();
		}
	}

void CAgnServFile::RunL()
	{
	if (iActiveStep == CCalAsyncTaskManager::ENoAction)
		{
		return;
		}

	TBool completed = EFalse;
	TRAPD(err, completed = DoStepL());
	if (err != KErrNone )
		{
		DoTaskCompleteL(err, iSession);
		}
	else if ( ! completed)
		{
		// The current step is not complete, reschedule the active object
		// do some more work.
		SetActiveAndMakeRequest();
		}
	}

TBool CAgnServFile::HasServerSession() const
	{
	return (iSession != NULL);
	}

const TDesC& CAgnServFile::PrivatePath() const
	{
	if (iAgnServer.FileMgr())
		{
		return iAgnServer.FileMgr()->PrivatePath();
		}
	return KNullDesC();
	}

void CAgnServFile::DoCancel()
	{
	// do nothing
	}


void CAgnServFile::SetActiveAndMakeRequest()
//
// Set the object to the active state and make a request
//
	{
	SetActive();
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status,KErrNone);
	}

const TDesC& CAgnServFile::FileName() const
	{
	if (iFileName)
		{
		return *iFileName;
		}
	return KNullDesC();
	}

void CAgnServFile::GetAttachmentFolderNameL(TDes& aFolderName)
	{
	GetAttachmentFolderNameL(FileName(), aFolderName);
	}
	
void CAgnServFile::GetAttachmentFolderNameL(const TDesC& aFileName, TDes& aFolderName)
	{
	// extension added to calender file name to give folder name for storing attachments
	_LIT(KCalAttachmentsExtension, "_a");
	_LIT(KCalDirectory, "\\");

	__ASSERT_ALWAYS(aFileName.Length() + KCalAttachmentsExtension().Length() + KCalDirectory().Length() <= aFolderName.MaxLength(), User::Leave(KErrArgument));

	aFolderName.Copy(aFileName);
	aFolderName.Append(KCalAttachmentsExtension);
	aFolderName.Append(KCalDirectory);
	}

void CAgnServFile::OpenAgendaL(const TDesC& aFilename, CalCommon::TCalFileVersionSupport& aStatus)
	{
	_DBGLOG_BASIC(AgmDebug::DebugLog("Opening Calendar File - '%S'",&aFilename);)

	if(aFilename.Length() > KMaxFileName)
		{
		_DBGLOG_BASIC(AgmDebug::DebugLog("KErrArgument:  Calendar filename length is greater than max filename length permitted");)
		User::Leave(KErrArgument);
		}

	// Check the file exists
	if (FileExistsL(aFilename) == EFalse)
		{
		_DBGLOG_BASIC(AgmDebug::DebugLog("KErrNotFound: Filename - %S not found", &aFilename);)
		User::Leave(KErrNotFound);
		}
	
	// If the file is in ROM, should leave
	if (aFilename.Length() > 2)
		{
		TDriveName driveName;
		driveName.Append(aFilename[0]);
		driveName.UpperCase();
		TDriveUnit drive(driveName);
		TDriveInfo driveInfo;
		User::LeaveIfError(iFs.Drive(driveInfo, drive));
		if (driveInfo.iDriveAtt == KDriveAttRom)
			{
			_DBGLOG_BASIC(AgmDebug::DebugLog("KErrNotSupported: The file is in ROM. This is not supported");)
			User::Leave(KErrNotSupported);
			}
		}
	
	iFileName = aFilename.AllocL();

	iReadOnly = FileIsReadOnlyL(FileName());
	CreateFileStoreL(aFilename);
	
	// Check that the file version is compatible
	// with the entry model's version
	TAgnVersion version;
	GetFileVersionSupportStatusL(version, aStatus);
	
	if(aStatus == CalCommon::EUnsupportedFileVersion)
		{
		User::Leave(KErrNotSupported);
		}
	
	// Create a model for this file
	CreateModelForFileL();
	}

void CAgnServFile::ReopenAgendaAfterRestoreL()
	{
	//We only build index if it was built before restore started.
	TBool toBuiildIndex = iIndexesBuilt;
	CreateFileStoreL(*iFileName);//iIndexesBuilt is set to EFalse in this function
	CreateModelForFileL();
	if (toBuiildIndex)
		{
		while(iIndexesBuilt ==0)
			{
			DoBuildIndexStepL();
			}
		TRAPD(err, iModel->BuildIndexCompleteL());
		if(err != KErrNone && err != KErrServerBusy)
			{
			User::Leave(err);
			}
		iModel->CommitL();
		}
	Model()->CheckTzDbModificationL();
	if(RefreshTzRules())
		{
		TPckgBuf<NTzUpdate::TTzRulesChange> pubSubBuf;
		if(KErrNone == RProperty::Get(NTzUpdate::KPropertyCategory, NTzUpdate::ETzRulesChange, pubSubBuf))
			{
			TTime tzChange = pubSubBuf().iUTCTimeOfRulesChange;
			Model()->HandleTzRulesChangeL(tzChange);
			}

		SetRefreshTzRules(EFalse);
		}
	}

void CAgnServFile::CreateFileStoreL(const TDesC& aFilename)
	{
	CFileStore* store = NULL;
	iDictionary = CApaProcess::ReadRootStreamLC(iFs, store, aFilename, EFileRead);
	CleanupStack::Pop(); // dictionary
	CleanupStack::PushL(store);
	iModelStreamId = iDictionary->At(KUidAgnModel);
	if (iModelStreamId == KNullStreamId)
		{
		_DBGLOG_BASIC(AgmDebug::DebugLog("KErrCorrupt: Corrupt model stream");)
		User::Leave(KErrCorrupt);
		}
	CleanupStack::PopAndDestroy(store);

	// Create the permanent file store
	//      
	if (iReadOnly)
		{
		iStore =  CPermanentFileStore::OpenL(iFs,aFilename,EFileShareAny|EFileRead);
		}
	else
		{
		iStore =  CPermanentFileStore::OpenL(iFs,aFilename,EFileShareAny|EFileRead|EFileWrite);
		}

	CAgnCalendarInfo* info(NULL);
	TStreamId calendarInfoStreamId = iDictionary->At(KUidAgnCalendarInfo);

	if (calendarInfoStreamId != KNullStreamId)
		{
		// Calendar info has been set on this file
		RStoreReadStream readStream;
		readStream.OpenLC(*iStore, calendarInfoStreamId);
		info = CAgnCalendarInfo::NewL();
		CleanupStack::PushL(info);
		info->FileInternalizeL(readStream);
		info->SetIsValid(ETrue);
		iIsFileDisabled = (!info->Enabled());
		CleanupStack::PopAndDestroy(info);
		CleanupStack::PopAndDestroy(&readStream);
		}
	}

void CAgnServFile::GetFileVersionSupportStatusL(TAgnVersion& aFileVersion, CalCommon::TCalFileVersionSupport& aStatus)
	{
	RStoreReadStream readStream;
	readStream.OpenLC(*(StoreL()),iModelStreamId);
	readStream >> aFileVersion;
	CleanupStack::PopAndDestroy(&readStream);
	CalFileVersionUtils::FileVersionSupportedL(aFileVersion, aStatus);
	}

void CAgnServFile::CreateModelForFileL()
	{
	// Create a model for this file
	iModel = CAgnEntryModel::NewL(this);
	
	// Set the model to run in server mode
	// Open the requested file
	iModel->OpenL(*iStore, iModelStreamId);
	iIndexesBuilt = EFalse;
	iProgressTotal = 0; 
	if (!iCategoryList)
		{
		iCategoryList = CAgnCategoryList::NewL(*this);
		}
	iModel->CategoryIndex().SetCategoryList(iCategoryList);
	
	// we've got here, so the file isn't on the z:\ drive
	SaveCategoryListL();
	SetFileNameHashL();
	}

void CAgnServFile::SetFileNameHashL()
	{
	// There is a file associated with the model
	TParsePtrC parse(FileName());
	TPtrC drive(parse.Drive());
	TPtrC name(parse.Name());
	HBufC* fileNameAndDriveOnly = HBufC::NewLC(drive.Length() + name.Length());
	TPtr ptr = fileNameAndDriveOnly->Des();
	ptr.Append(drive);
	ptr.Append(name);
	iFileNameHash = FoldAndGenerateHashL(*fileNameAndDriveOnly);
	CleanupStack::PopAndDestroy(fileNameAndDriveOnly);
	}

TUint32 CAgnServFile::FileNameHash() const
	{
	return iFileNameHash;
	}
	
void CAgnServFile::SaveCategoryListL()
	{
	TStreamId streamId = DictionaryLookup(KUidAgnCategoryList);

	if (streamId == KNullStreamId)
		{
		// If the category list stream does not exist, create it.
		CreateCategoryListL();
		}
	else
		{
		// category stream exists, we are re-opening the file
		InternalizeCategoryListL(streamId);
		}
	
	iModel->RestoreCategoriesL();
	}


void CAgnServFile::CreateCategoryListL() const
	{
	TStreamId streamId = WriteCategoryListL(*iStore);
	AddStreamToDictionaryL(KUidAgnCategoryList, streamId);
	}

TStreamId CAgnServFile::WriteCategoryListL(CStreamStore& aStore) const
	{
	RStoreWriteStream writeStream;
	TStreamId streamId = writeStream.CreateLC(aStore);
	iCategoryList->ExternalizeL(writeStream);
	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);
	return streamId;
	}

void CAgnServFile::InternalizeCategoryListL(const TStreamId& aStreamId)
	{
	RStoreReadStream readStream;
	readStream.OpenLC(*iStore,aStreamId);
	iCategoryList->InternalizeL(readStream);
	CleanupStack::PopAndDestroy(&readStream);
	}


void CAgnServFile::ExternalizeCategoryListL()
	{
	TStreamId streamId = DictionaryLookup(KUidAgnCategoryList);
	RStoreWriteStream writeStream;
	writeStream.ReplaceLC(*iStore,streamId);
	iCategoryList->ExternalizeL(writeStream);
	writeStream.CommitL();
	CleanupStack::PopAndDestroy(&writeStream);
	User::LeaveIfError(iStore->Commit());
	}


CAgnCategoryList& CAgnServFile::CategoryList() const
	{
	return *iCategoryList;
	}

void CAgnServFile::StartBuildIndex(TAgnMessageToComplete& aMessageToComplete)
		
	{
	if (iIndexesBuilt)
		{
		// The indexes have already been bulit.
		aMessageToComplete.Message().Complete(KErrNone);
		return;
		}
	
	// Add this request to the async. index build request queue.
	//
	TRAPD(err, AddAsyncRequesterL(aMessageToComplete));
	if (err != KErrNone)
		{
		aMessageToComplete.Message().Complete(err);
		}
	else if (iActiveStep != CCalAsyncTaskManager::EBuildIndex)
		{
		DoStartBuildIndex();
		}
	}

void CAgnServFile::RequestProgressL(TAgnMessageToComplete& aMessageToComplete)
		
	{
	if (iActiveStep == CCalAsyncTaskManager::ENoAction)
		{
		// there are no progress to report
		aMessageToComplete.Message().Complete(KErrNone);
		return;
		}

	// Add this request to the async. index build request queue.
	//
	TRAPD(err, AddAsyncRequesterL(aMessageToComplete));
	if (err != KErrNone)
		{
		aMessageToComplete.Message().Complete(err);
		// If are already building the indexes then don't broadcast the error.
		}
	}
	
void CAgnServFile::DoStartBuildIndex()
	{
	// If we are already building the index then there is nothing
	// more to do, this request will be completed when the index has been
	// built.
	//

	// Start building the index.
	iModel->ResetIndexes();
	if ( ! iModel->StreamsAreEmpty())
		{			
		// Report % complete and continue building index.
		iActiveStep = CCalAsyncTaskManager::EBuildIndex;
		iIndexesBuilt = EFalse;
		iProgressTotal = 0;
		Start();
		}
	else
		{
		// Agenda is empty, nothing to do for indexes. Complete immediately.
		iIndexesBuilt = ETrue;
		CompleteRequests(ETrue, KErrNone, iSession);
		}	
	}

TBool CAgnServFile::AreIndexesBuilt() const
	{
	return iIndexesBuilt;
	}

// static function which can be added to a cleanup item on cleanup stack
void CAgnServFile::CloseAgenda(TAny* aFile)
	{
	CAgnServFile* file = static_cast<CAgnServFile*>(aFile);
	file->CloseAgendaImmediately();
	delete file;
	}

void CAgnServFile::DoSaveIndexFile()
	{
	if (iModel == NULL)
		{
		return;
		}
	if (iIndexesBuilt && iModel->IsIndexFileDirty())
		{
		TInt trapErr = KErrNone;
		TRAP(trapErr, iModel->SaveIndexFileL());
		if (trapErr != KErrNone)
			{
			// if we can't save the index file, we need to
			// make sure that it is marked as dirty
			TRAP_IGNORE(iModel->MarkIndexFileAsDirtyL());
			}
		}
	}	

/** 
* Closes the calendar file in the current agenda server session immediately or after a delay.
* The delay is triggered using a timer owned by the calendar file. If a session then tries to access the same calendar file, the delay is cancelled and the file is not closed
* @param aCloseAgendaWithDelay ETrue if a delay is required before the closure of the calendar file, EFalse if the calendar file is to be closed immediately
*/
void CAgnServFile::CloseAgenda(TBool aCloseImmediately)
	{
	__ASSERT_DEBUG(iRefCount>=0,User::Invariant());
	
	if (iRefCount>0)
		{
		// Decrease the reference count	
		--iRefCount;
		
		if(iRefCount == 0)
			{
			
			if(!aCloseImmediately) // Decide whether we need to trigger the file close timer 
				{
				Cancel();
				// Kick off the timer delay before closing the file
				iFileShutdownDelayTimer->Start();
				}
			else 
				{
				// Close the file immediately
				iFileShutdownDelayTimer->DoCloseAgenda();
				}
			}
		}
	}
	
void CAgnServFile::CloseAgendaImmediately()
	{
	// We're now sure that we are closing the agenda file.
	// We need to bring the cached index file up to date.
	DoSaveIndexFile();	
	Cancel();
	
	//The alarms can also be queued by a shutdown notification from the system state manager.
	//If the alarms have been queued then we do not want to do so again, so checking here.
	if (iStore && iModel
#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
		&& !iShutdownNotification
#endif
		)
		{
		//queue next 10 alarms within 31 days since it is the last client
		TRAP_IGNORE(iModel->FindAndQueueNextFewAlarmsL());	
		}
	
	if (IsActive())
		{
		Deque();
		}

	// Make sure we're not still compacting
	iCompactor.Close();

	// No more references, so actually close the file
	delete iModel;
	iModel = NULL;

	// Make sure the compactor is shut down before deleting the store
	delete iStore;
	iStore = NULL;

	delete iDictionary;
	iDictionary = NULL;
	}



// Add to the file session reference count
void CAgnServFile::AddReference()
	{
	++iRefCount;
	iFileShutdownDelayTimer->Cancel();
	}

// Getter - file session reference count
TInt CAgnServFile::ReferenceCount() const
	{
	return iRefCount;
	}


CFileStore* CAgnServFile::StoreL() const
	{
	if(!iStore)
		{
		//It shouldn't happen unless the client go for server function directly
		User::Leave(KErrLocked);
		}
	return iStore;
	}

CStreamDictionary* CAgnServFile::Dictionary() const
	{
	return iDictionary;
	}

CAgnEntryModel* CAgnServFile::Model() const
	{
	return iModel;
	}


TStreamId CAgnServFile::DictionaryLookup(TUid aUid) const
	{
	return iDictionary->At(aUid);
	}

TBool CAgnServFile::IsLocked() const
	{
	TBool ret = EFalse;
	if(iActiveStep != CCalAsyncTaskManager::ENoAction)
	    {
	    ret = ETrue;
	    }
	else
	    {
	    ret = iLocked;
	    }
	
	return ret;
	}

void CAgnServFile::SetLock(TBool aLocked)
    {
    iLocked = aLocked;
    }

TBool CAgnServFile::IsReadOnly() const
	{
	return iReadOnly;
	}


TBool CAgnServFile::DoStepL()
	{
	TBool completed = EFalse;

	TInt progress = 0;
	switch (iActiveStep)
		{
		case CCalAsyncTaskManager::EBuildIndex:
			_DBGLOG_ASYNC(AgmDebug::DebugLogTimeStampL("DoStepL: Agenda Serv File: Async Build Index step");)
			progress = DoBuildIndexStepL();
			break;

		case CCalAsyncTaskManager::EDeleteEntry:
			_DBGLOG_ASYNC(AgmDebug::DebugLogTimeStampL("DoStepL: Agenda Serv File: Async Delete Entries");)
			progress = iAsyncDelete->DoDeleteStepL();
			break;

		case CCalAsyncTaskManager::EFilterCategory:
			_DBGLOG_ASYNC(AgmDebug::DebugLogTimeStampL("DoStepL: Agenda Serv File: Async Filter Category");)
		case CCalAsyncTaskManager::EDeleteCategory:
			_DBGLOG_ASYNC(AgmDebug::DebugLogTimeStampL("DoStepL: Agenda Serv File: Async Delete Category");)
			
			if (iIndexesBuilt == EFalse)
				{
				_DBGLOG_ASYNC(AgmDebug::DebugLogTimeStampL("DoStepL: Agenda Serv File: Indexes are not built");)
			
				// check the entry model's version
				TAgnVersion version;
				CalCommon::TCalFileVersionSupport status;
				GetFileVersionSupportStatusL(version, status);
			
				if (status != CalCommon::EFileIsCurrentVersion)
					{
					_DBGLOG_ASYNC(AgmDebug::DebugLogTimeStampL("KErrNotSupported: Indexes must be built before any category operations can be done on a non-current file");)
							
					// indexes must be built before any category operations can be done on a non-current file
					User::Leave(KErrNotSupported);
					}
				}

			progress = iModel->CategoryIndex().DoStepCategoryL(iActiveStep);
			break;
			
		default:	
			completed = EFalse;	// return Complete status
		}

	iProgressTotal = progress;

	if (iProgressTotal == KAgnPercentageComplete)
		{
		completed = DoTaskCompleteL(KErrNone, iSession);//complete
		}
	else
		{
		CompleteRequests(EFalse, iProgressTotal, iSession);
		}
			
	return completed;
	}


TInt CAgnServFile::DoBuildIndexStepL()
	{
	TInt indexProgress=1;//0 implies finished
	if (iIndexesBuilt)
		{
		// Index building is already completed.
		return KAgnPercentageComplete;
		}

	// Some some work on building the index, 0 means it is finished otherwise
	// the percentage progress from 1 to 100 is returned.
	indexProgress = iModel->DoIndexBuildStepL();

	// If the file has been completely loaded, then we can set that the category
	// list has had its counters incremented for all entries in this file
	if (indexProgress == KAgnPercentageComplete)
		{
		iIndexesBuilt = ETrue;
		}

	return indexProgress;
	}

TInt CAgnServFile::DoCompactL()
	{
	TInt ret = EFalse;
	TRAPD(err, ret = DoCompactorStepL());
	if (err != KErrNone)
		{
		// Compactor error, so stop it
		iCompactionStage = ENothing;
		iCompactor.Close();
		User::Leave(err);
		}

	if (ret)
		{
		// Finished compacting, so get rid of the CIdle
		iCompactionStage = ENothing;
 		return ETrue;
		}

	return EFalse;
	}


TBool CAgnServFile::DoCompactorStepL()
	{
	// Attempt to compact the file
	if (iCompactionStage==ENothing)
		{
		iCompactor.OpenL(*iStore, iNextCompactEffort);
		iCompactionStage=EReclaiming;
		return EFalse;
		}

	// Set the compaction stage to ENothing, so if the next step
	iCompactor.NextL(iNextCompactEffort);
	if (iNextCompactEffort == 0)
		{// Stage is finished
		CStreamStore& store=*iStore;
		if (iCompactionStage==EReclaiming)
			{
			// Decide if there is enough free space to compact - 
			// The threshold is the greater of KAgnCompactionThresholdInBytes (10240 bytes)
			// or 1/10th of the file size
			TInt totalSize=0;
			User::LeaveIfError(iStore->File().Size(totalSize));
			TInt threshold = KAgnCompactionThresholdInBytes;
			if (threshold < totalSize/10)
				{
				threshold = totalSize/10;	
				}
			if (iCompactor.Available() < threshold)
				// There is space to reclaim, but not enough to make it worthwhile, so stop the active object
				{
				iCompactor.Close();
				iCompactionStage = ENothing;
				return ETrue;
				}
			// There is enough space to reclaim to make it worthwhile, so kick it off
			store.CommitL();
			iCompactor.Close();
			iCompactor.CompactL(store, iNextCompactEffort);
			iCompactionStage=ECompacting;
			}
		else if (iCompactionStage==ECompacting)
			{// Compaction is all done, so stop
			store.CommitL();
			iCompactor.Close();
			iCompactionStage=ENothing;
			return ETrue;
			}
		}
	return EFalse;
	}

TBool CAgnServFile::IsInterestedSession(CAgnServerSession* aSession)
	{
	const TInt count = iMessageVector->Count();
	
	for (TInt ii=0; ii< count; ++ii)
		{
		if(&iMessageVector->At(ii).Session() == aSession)
			{
			return ETrue;
			}
		}
	
	return EFalse;
	}

void CAgnServFile::CancelTaskL(CAgnServerSession* aSession)
    {
    Cancel();
    CompleteRequests(ETrue, KErrCancel, aSession);
    }
// If an error occurs during a index build or deletetion request
// then complete it and broadcast the error as the progress count.
//
TBool CAgnServFile::DoTaskCompleteL(TInt aErr, CAgnServerSession* aSession)
	{
	if(aErr == KErrCancel && !IsInterestedSession(aSession))
		{
		return EFalse;
		}
	
	TBool complete = ETrue;
	switch(iActiveStep)
		{
		case CCalAsyncTaskManager::EDeleteEntry:
			{
			iActiveStep = CCalAsyncTaskManager::ENoAction;
			if(aErr != KErrNone)
				{
				iModel->Rollback();
				}
			else
				{
				iModel->CommitL();
				CompactFileL();
				// Indicate that there is more processing to do.
				}
			
			iModel->SetBufferedDeleting(EFalse);
			delete iAsyncDelete;
			iAsyncDelete = NULL;
			TInt64 fileid = Model()->GetFileIdL();
			TRAP_IGNORE(iSession->BulkChangeCompletedL(fileid, aErr)); // transmits error to client
			iSession = NULL;
			}
			break;
		case CCalAsyncTaskManager::EDeleteCategory:
			iActiveStep = CCalAsyncTaskManager::ENoAction;
			if(aErr != KErrNone)
				{
				if(!iModel->CategoryIndex().FirstStep())
					{
					TInt err=0;
					TRAP(err,CategoryList().RollBackL());
					TRAP(err,iModel->CategoryIndex().RollBackDeleteCategoryL());
					__ASSERT_DEBUG(err==KErrNone, Panic(EAgmErrRollbackFailed));
					}
				}
			else
				{
				TRAPD(err, iModel->CommitL());
				if (err != KErrNone)
					{
					iModel->Rollback();
					aErr = err;
					}
				}
			break;
		case CCalAsyncTaskManager::EBuildIndex:
			if (aErr == KErrNone)
				{
				iModel->BuildIndexCompleteL();
				iModel->CommitL();
				iActiveStep = CCalAsyncTaskManager::ENoAction;
				}
			else if (aErr == KErrOverflow || aErr == KErrEof)
				{
				// the file is corrupt
				aErr = KErrCorrupt;
				}
			break;
		}
	CompleteRequests(ETrue, aErr, aSession);
	return complete;	
	}

TInt CAgnServFile::AddAsyncRequester(TAgnMessageToComplete& aMessageToComplete)
	{
	// Try to add this async request to the queue
	// and complete the message if it fails
	
	TRAPD(err, AddAsyncRequesterL(aMessageToComplete));
	
	if (err != KErrNone)
		{
		aMessageToComplete.Message().Complete(err);
	    iSession = NULL;        	
		}

	return err;
	}

void CAgnServFile::AddStreamToDictionaryL(const TUid& aUid, const TStreamId& aStreamId) const
	{
	AddStreamToDictionaryL(aUid, aStreamId, *iDictionary, *iStore);
	}

void CAgnServFile::AddStreamToDictionaryL(const TUid& aUid, const TStreamId& aStreamId, 
			CStreamDictionary& aDictionary, CPersistentStore& aStore) const
	{
	aDictionary.AssignL(aUid, aStreamId);

	// Re-write the dictionary stream
	RStoreWriteStream stream;
	TStreamId streamId = aStore.Root();
	stream.ReplaceLC(aStore, streamId);
	stream<< aDictionary;
	stream.CommitL();
	CleanupStack::PopAndDestroy(); // dictionary stream
	iStore->CommitL();
	}

void CAgnServFile::CompactFileL()
	{
	_DBGLOG_BASIC(AgmDebug::DebugLog("Agenda Serv File: Synchronous Compact file");)
		
   	while (!DoCompactL())
		{
		}
	}

void CAgnServFile::TidyByDateSetup(CAgnServerSession& aSession,
								   const TAgnFilter& aFilter,
								   const TTime& aUndatedTodoDate,
								   const TTime& aStartDate,
								   const TTime& aEndDate)
	{
	iSession = &aSession;
	iTidyByDateFilter = aFilter;
	iTidyByDateUndatedTodoDate = aUndatedTodoDate;
	iTidyByDateStartDate = aStartDate;
	iTidyByDateEndDate = aEndDate;	
	}


void CAgnServFile::TidyByDateStartL(TAgnMessageToComplete& aMessageToComplete, TAgnChangeFilter& aChangeFilter)
	{
	if(iAsyncDelete)
		{
		iModel->SetBufferedDeleting(EFalse);
		delete iAsyncDelete;
		iAsyncDelete = NULL;
		}
		
	iAsyncDelete = iModel->CreateAsyncDeleteL(aChangeFilter);

	iModel->CommitL(); // commit changes before beginning delete
	
	// Ask the async delete object to set itself up.
	// this will create a list of all the entries to delete
	// and tell us if there is any work to be done.
	TBool entriesToDelete = iAsyncDelete->SetUpDeleteL(iTidyByDateFilter,
														iTidyByDateUndatedTodoDate,
														iTidyByDateStartDate,
														iTidyByDateEndDate);

	if (!entriesToDelete)
		{
		// There are no entries that match the filter
		// so delete the active delete object and
		// complete the request. 
		delete iAsyncDelete;
		iAsyncDelete = NULL;
		aMessageToComplete.Message().Complete(KErrNone);
		iSession = NULL;
		}
	else
		{
		// There are entries to delete so add the request message
		// to our list of messages so that we can complete
		// it later when the async delete is finished.
		TInt error = AddAsyncRequester(aMessageToComplete);

		if (error != KErrNone)
			{
			// We were unable to add the request message our list.
			// The message has been completed with the
			// correct error code so we just need to clean up.
			delete iAsyncDelete;
			iAsyncDelete = NULL;
			iSession = NULL;
			}
		else
			{
			// Start performing the tidy via the RunL function of this Active Object.
			iModel->SetBufferedDeleting(ETrue);
			iActiveStep = CCalAsyncTaskManager::EDeleteEntry;
			Start();
			}
		}
	}

void CAgnServFile::CategoryTaskStartL(TAgnMessageToComplete& aMessageToComplete, CCalAsyncTaskManager::TAsyncAction aTask)
	{
	if ( AddAsyncRequester(aMessageToComplete) == KErrNone )
		{
		iActiveStep = aTask;
		Start();
		}
	}

void CAgnServFile::CreateDirL(const TDesC& aFileName) const
	{
	CreateDirL(iFs, aFileName);
	}

TBool CAgnServFile::FileExistsL(const TDesC& aFileName) const
	{
	return FileExistsL(iFs, aFileName);
	}

void CAgnServFile::DeleteFileL(const TDesC& aFileName) const
	{
	DeleteFileL(iFs, aFileName);
	}

void CAgnServFile::MoveFileL(const TDesC& aSource, const TDesC& aDestination) const
	{
	CFileMan* fileMan = CFileMan::NewL(iFs);
	CleanupStack::PushL(fileMan);
	User::LeaveIfError(fileMan->Move(aSource, aDestination, CFileMan::ERecurse));
	CleanupStack::PopAndDestroy(fileMan);
	}

void CAgnServFile::CopyFileL(const TDesC& aSource, const TDesC& aDestination) const
	{
	CFileMan* fileMan = CFileMan::NewL(iFs);
	CleanupStack::PushL(fileMan);
	User::LeaveIfError(fileMan->Copy(aSource, aDestination, CFileMan::ERecurse));
	CleanupStack::PopAndDestroy(fileMan);
	}

// Return EFalse if file does not exist, or ETrue if it does. Leaves if file status cannot be determined.
TBool CAgnServFile::FileIsReadOnlyL(const TDesC& aFileName) const
	{
	TUint attrib;
	User::LeaveIfError(iFs.Att(aFileName, attrib));
	return attrib & KEntryAttReadOnly;
	}
	
// ensures a directory exists for this filename. If directory already exists, nothing happens
/*static*/ void CAgnServFile::CreateDirL(RFs& aFs, const TDesC& aDirectory)
	{
	TInt err = aFs.MkDirAll(aDirectory);
	if (err != KErrAlreadyExists)
		{
		User::LeaveIfError(err);
		}
	}

// Return EFalse if file does not exist, or ETrue if it does. Leaves if file status cannot be determined.
/*static*/ TBool CAgnServFile::FileExistsL(RFs& aFs, const TDesC& aFileName)
	{
	TUint dummy;
	TInt err = aFs.Att(aFileName, dummy);
	if (err == KErrNotFound || err == KErrPathNotFound)
		{
		return EFalse;
		}
	User::LeaveIfError(err);
	return ETrue;
	}
	
// Deletes file but does not leave if it cannot be found
/*static*/ void CAgnServFile::DeleteFileL(RFs& aFs, const TDesC& aFileName)
	{
	TInt err = aFs.Delete(aFileName);
	if (err != KErrNotFound && err != KErrPathNotFound)
		{
		User::LeaveIfError(err);
		}
	}

/**
Opens a file with the specified name. Used for creating attachment files.
*/
void CAgnServFile::OpenFileL(RFile& aFileHandle, const TDesC& aFileName)
	{
	User::LeaveIfError(aFileHandle.Open(iFs, aFileName, EFileRead | EFileShareReadersOnly));
	}

/**
Creates a new file with the specified name. Used for creating attachment files.
*/
void CAgnServFile::CreateNewFileL(RFile& aFileHandle, const TDesC& aFileName)
	{
	User::LeaveIfError(aFileHandle.Create(iFs, aFileName, EFileRead | EFileWrite | EFileShareExclusive));
	}

void CAgnServFile::ReplaceConvertedAgendaFileL(CAgnEntryManager& aEntryManager, CAgnTzRuleIndex& aTzRuleIndex)
	{
	// close the agenda file but don't delete the model since indexes are already built
	CAgnEntryModel* tmpModel = iModel;
	iModel = NULL;

	CloseAgendaImmediately();

	iModel = tmpModel;

	// delete the existing file and rename the new one in its place
	HBufC* convertedFileName = HBufC::NewLC(FileName().Length() + KUpdatedAgendaFileExtension().Length());
	TPtr convertedFileNamePtr(convertedFileName->Des());
	convertedFileNamePtr.Append(FileName());
	convertedFileNamePtr.Append(KUpdatedAgendaFileExtension);
	
	DeleteFileL(FileName());
	User::LeaveIfError(iFs.Rename(convertedFileNamePtr, FileName()));
	
	CleanupStack::PopAndDestroy(convertedFileName);
	
	// re-open the agenda stream store, but don't rebuild indexes as no need
	CFileStore* store = NULL;
	iDictionary = CApaProcess::ReadRootStreamLC(iFs, store, FileName(), EFileRead);
	CleanupStack::Pop(); // dictionary
    CleanupStack::PushL(store);
	iModelStreamId = iDictionary->At(KUidAgnModel);
	if (iModelStreamId == KNullStreamId)
		{
		User::Leave(KErrCorrupt);
		}
	CleanupStack::PopAndDestroy(store);
	
	iStore = CPermanentFileStore::OpenL(iFs,FileName(),EFileShareAny|EFileRead|EFileWrite);
	
	aTzRuleIndex.ReloadStore(*iDictionary, *iStore);
	iModel->LoadNewStreamStoreL(*iStore, iModelStreamId, aEntryManager, aTzRuleIndex);
	
	// Calculate the file name hash key
	// for publish and subscribe notifications
	SetFileNameHashL();
	}

HBufC8* CAgnServFile::GetPropertyValueLC(TStreamId aStreamId)
    {
    // Calendar info has been set on this file
    RStoreReadStream readStream;
    readStream.OpenLC(*iStore, aStreamId);
    HBufC8* value = HBufC8::NewL(readStream, KMaxTInt);
    CleanupStack::PopAndDestroy(&readStream);
    CleanupStack::PushL(value);
    return value;
    }

TBool CAgnServFile::SetCalendarInfoL(const CAgnCalendarInfo& aCalendarInfo)
    {
	TBool fileInfoExist = EFalse;
	
    // Fetch the existing calendar info
    TStreamId calendarInfoStreamId = iDictionary->At(KUidAgnCalendarInfo);
    TStreamId newStreamId;
    
    RStoreWriteStream writeStream;
    
    if (calendarInfoStreamId == KNullStreamId)
        {
        // Write all the properties to seperate streams
        aCalendarInfo.ExternalizePropertiesL(*iStore, NULL);
    
        // The calendar info stream does not exist so create it
        newStreamId = writeStream.CreateLC(*iStore);
        }
    else
        {
        // Read the current metadata
        RStoreReadStream readStream;
        readStream.OpenLC(*iStore, calendarInfoStreamId);
        CAgnCalendarInfo* info(CAgnCalendarInfo::NewL());
        CleanupStack::PushL(info);
        info->FileInternalizeL(readStream);
        CleanupStack::Pop(info);
        CleanupStack::PopAndDestroy(&readStream);
        CleanupStack::PushL(info);
        
        // Write all the properties to seperate streams
        aCalendarInfo.ExternalizePropertiesL(*iStore, info);
        
        CleanupStack::PopAndDestroy(info);
        
        writeStream.ReplaceLC(*iStore, calendarInfoStreamId);
		
		fileInfoExist = ETrue;
        }
    
    
    // Update the values etc 
    aCalendarInfo.FileExternalizeL(writeStream);
    
    writeStream.CommitL();
    CleanupStack::PopAndDestroy(&writeStream);
    
    if (calendarInfoStreamId == KNullStreamId)
        {
        AddStreamToDictionaryL(KUidAgnCalendarInfo, newStreamId);
        }
		
    iIsFileDisabled = (!aCalendarInfo.Enabled());
    TParsePtrC parser(FileName());
    TFileName fileName = parser.Drive();
    fileName.Append(parser.NameAndExt());
    if(iIsFileDisabled)
        {
        iAgnServer.AlarmServer().SetAlarmStatusForCalendarFile(fileName, EAlarmStatusDisabled); 
        }
    else
        {
        iAgnServer.AlarmServer().SetAlarmStatusForCalendarFile(fileName, EAlarmStatusEnabled); 
        }
    
    User::LeaveIfError(iStore->Commit());
    return fileInfoExist;
    }
	
TBool CAgnServFile::IsCalendarInfoExistL() const
    {
    TStreamId calendarInfoStreamId = iDictionary->At(KUidAgnCalendarInfo);
    return (calendarInfoStreamId != KNullStreamId)?ETrue:EFalse;
    }
	
CAgnCalendarInfo* CAgnServFile::GetCalendarInfoLC() const
    {
    CAgnCalendarInfo* info(CAgnCalendarInfo::NewL());
    CleanupStack::PushL(info);
    
    TStreamId calendarInfoStreamId = iDictionary->At(KUidAgnCalendarInfo);
    
    if (calendarInfoStreamId != KNullStreamId)
        {
        // Calendar info has been set on this file
        RStoreReadStream readStream;
        readStream.OpenLC(*iStore, calendarInfoStreamId);
        info->FileInternalizeL(readStream);
        CleanupStack::PopAndDestroy(&readStream);
        info->SetIsValid(ETrue);
        }
    else
        {
        // There is no calendar info for this file so return an invalid version
        info->SetIsValid(EFalse);
        }
    
    // make sure we set the file on the info that we are returning
    TParsePtrC parse(*iFileName);
    TFileName shortFileName(parse.Drive());
    shortFileName.Append(parse.NameAndExt());
    info->SetFileNameL(shortFileName);
    
    return info;
    }

void CAgnServFile::AddAsyncRequesterL(TAgnMessageToComplete& aMessageToComplete)
	{
	// It is only possible to have more than one client in the message queue
	// if the asych task is building index
	iMessageVector->AppendL(aMessageToComplete);
	if ( (iActiveStep != CCalAsyncTaskManager::ENoAction) &&
			(iActiveStep != CCalAsyncTaskManager::EBuildIndex) )
		{
		__ASSERT_DEBUG(iMessageVector->Count()==1, Panic(EAgmErrTwoAsyncTasksStarted));
		}
	iSession = &aMessageToComplete.Session();
 	}

void CAgnServFile::CompleteRequests(TBool aIsCompleted, TInt aCompleteCode, CAgnServerSession* aSession)
	{
	if(aIsCompleted)
		{
		iActiveStep = CCalAsyncTaskManager::ENoAction;
		}

	TInt count = iMessageVector->Count();
	for (TInt i = count -1 ; i >= 0 ; i--)
		{
		if (aCompleteCode == KErrCancel)
			{//One client should only cancel its own building index task
			if (aSession == &iMessageVector->At(i).Session())
				{
				iMessageVector->At(i).Message().Complete(KErrCancel);
				iMessageVector->Delete(i);
				if (iMessageVector->Count() == 0)
					{
					// if this operation has failed with a KErrCancel error, cancel the active object
					iActiveStep = CCalAsyncTaskManager::ENoAction;
					}
				break;
				}
			}
		else
			{
			if (aIsCompleted || (iMessageVector->At(i)).ReportProgress())
				{
				(iMessageVector->At(i)).Message().Complete(aCompleteCode);	
				iMessageVector->Delete(i);
				}
		
			}
		}
	}

CAgnServer& CAgnServFile::Server() const
	{
	return iAgnServer;
	}

CAgnServerSession* CAgnServFile::ServerSession()
    {
    return iSession;
    }

void CAgnServFile::DoCloseAgenda()
	{
	CloseAgendaImmediately();
	// Closing the file (itself) is the last thing the file should do. The file is owned and closed by the file manager.
	// Do NOT attempt to use the file or the delay close timer object after closing the file
	iAgnServer.FileMgr()->CloseAgendaFile(this);
	}

// Getter - File close timer 
CAgnServFileShutdownDelayTimer& CAgnServFile::FileShutdownDelayTimer() const
	{
	__ASSERT_DEBUG(iFileShutdownDelayTimer, User::Invariant());
	return *iFileShutdownDelayTimer;
	}

void CAgnServFile::SetRefreshTzRules(TBool aSetRefresjTzRule)
    {
    iRefreshTzRules = aSetRefresjTzRule;
    }

TBool CAgnServFile::RefreshTzRules() const
    {
    return iRefreshTzRules;
    }

#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
void CAgnServFile::QueueAlarmsImmediately()
	{
	if (iStore && iModel)
		{
		TRAP_IGNORE(iModel->FindAndQueueNextFewAlarmsL());	
		}
	}

void CAgnServFile::DeleteAlarmsAndRequeueSessionAlarm()
	{
	if (iStore && iModel)
		{
		TRAP_IGNORE(iModel->DeleteAlarmsAndRequeueSessionAlarmL());	
		}
	}

void CAgnServFile::SetShutdownFlag(TBool aNotificationFlag)
	{
	iShutdownNotification = aNotificationFlag;
	}
#endif


void CAgnServFile::TzRulesHaveChangedL()
	{
	RPointerArray<CAgnServerSession> sessions;//does not own
	CleanupClosePushL(sessions);
	iAgnServer.FetchSessionsL(sessions);
	const TInt count = sessions.Count();
	TInt64 fileId = iModel->GetFileIdL();
	TAgnChange change;
	change.iOperationType = MCalChangeCallBack2::EChangeTzRules;
	change.iFileId = fileId;
	for ( TInt i = 0; i < count; ++i )
		{
		sessions[i]->AddChangeL(change);
		}
	CleanupStack::PopAndDestroy(&sessions);
	}

void CAgnServFile::SetCollectionId(TCalCollectionId aCollectionId)
	{
	iCollectionId = aCollectionId;
	}

TUint8 CAgnServFile::CollectionId() const
	{
	return iCollectionId;
	}

TInt CAgnServFile::BackupReStoreChanged(MCalChangeCallBack2::TChangeType aChangeType)
	{
	TInt err = KErrNone;
	RFile file;         

	switch(aChangeType)
		{
		case MCalChangeCallBack2::EBackupStart:
			SetLock(ETrue);
			BackupRestoreLock(ETrue);
			(iStore->File()).Close();
			iStore->Detach();
			break;
		case MCalChangeCallBack2::EBackupEnd:
			if (iReadOnly)
				{
				err = file.Open(iFs,*iFileName,EFileShareAny|EFileRead);
				}
			else
				{
				err = file.Open(iFs,*iFileName,EFileShareAny|EFileRead|EFileWrite);
				}
			iStore->Reattach(file);
			SetLock(EFalse);
			BackupRestoreLock(EFalse);
			break;
		case MCalChangeCallBack2::ERestoreStart:
			SetLock(ETrue);
			Model()->MarkIndexFileAsDirtyL();
			BackupRestoreLock(ETrue);
			delete iStore;
			iStore = NULL;
			delete iDictionary;
			iDictionary = NULL;
			break;
		case MCalChangeCallBack2::ERestoreEnd:
			delete iModel;
			iModel = NULL;
			SetLock(EFalse);
			TRAP(err, ReopenAgendaAfterRestoreL());
			BackupRestoreLock(EFalse);
			break;
		default:
			User::Invariant();
			break;
		}
	return err;
	}

void CAgnServFile::BackupRestoreLock(TBool aLock)
	{
	iBackupRestoreLock = aLock;
	RPointerArray<CAgnServerSession> sessions;
	CleanupClosePushL(sessions);

	//Fetch all sessions
	TRAP_IGNORE (iAgnServer.FetchSessionsL(sessions));
	TInt countSessions = sessions.Count();
	for(TInt ii = 0; ii< countSessions; ++ii)
		{
		// lock the client
		sessions[ii]->LockClient(aLock);
		}
	CleanupStack::PopAndDestroy(&sessions); 
	}

TBool CAgnServFile::IsBackupRestoreLock() const
	{
	return iBackupRestoreLock;
	}


// aServeFile is NOT owned
CAgnServFileShutdownDelayTimer* CAgnServFileShutdownDelayTimer::NewL(CAgnServFile& aServFile)
	{
	CAgnServFileShutdownDelayTimer* self = new(ELeave) CAgnServFileShutdownDelayTimer(aServFile);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}
	
CAgnServFileShutdownDelayTimer::CAgnServFileShutdownDelayTimer(CAgnServFile& aServFile)
	: CTimer(EPriorityIdle), iServFile(aServFile)
	{
	}

/** 
Starts the timer to close the file. Usually the file will be closed after a delay of 5 seconds.
However if a backup or restore operation is in progress the file will be closed immediately as 
the backup engine needs access to the files.
*/
void CAgnServFileShutdownDelayTimer::Start()
	{
	if (iServFile.Server().BackupRestoreAgent().BackupInProgress() || 
		iServFile.Server().BackupRestoreAgent().RestoreInProgress())
		{
		Cancel();
		DoCloseAgenda();
		}
	// The timer shouldn't be active, in release mode we cope with it though to be 
	// more robust
	else if (!IsActive())
		{
		After(KServerShutdownDelay);
		}
	else
		{
		__ASSERT_DEBUG(!IsActive(), Panic(EAgmErrFileTimerAlreadyActive));
		}
	}

void CAgnServFileShutdownDelayTimer::ConstructL()
	{
	CTimer::ConstructL();
	CActiveScheduler::Add(this);
	}


void CAgnServFileShutdownDelayTimer::DoCloseAgenda()
	{
	iServFile.DoCloseAgenda();
	}

void CAgnServFileShutdownDelayTimer::CloseAgenda()
	{
	// Check if the timer is active. If the timer is active, it implies the last session of the file has closed and no other sessions are open
	// For example this is used to cancel the timer just before deleting the calendar file incase the timer is the only thing preventing the deletion
	if(IsActive())
		{
		Cancel();
		DoCloseAgenda();
		}
	}

void CAgnServFileShutdownDelayTimer::RunL()
	{
	if(iStatus == KErrNone)
		{
		DoCloseAgenda();
		}
	}

// CAgnServFileMgr //

CAgnServFileMgr::CAgnServFileMgr(RFs& aFs, CAgnServer& aAgnServer)
: iAgnServer(aAgnServer), iFs(aFs)
	{
	}


CAgnServFileMgr::~CAgnServFileMgr()
	{
	delete iPermanentData;
	
	// Make sure file list is empty
	if (iFileList)
		{
		for (TInt count = iFileList->Count() - 1; count >= 0; --count)
			{
			delete (*iFileList)[count];
			}
		delete iFileList;
		}
	}


CAgnServFileMgr* CAgnServFileMgr::NewL(RFs& aFs, CAgnServer& aAgnServer)
	{
	CAgnServFileMgr* self = new(ELeave) CAgnServFileMgr(aFs, aAgnServer);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CAgnServFileMgr::ConstructL()
	{
	const TInt KFileListGranularity = 1; // usually only one Calendar file opened at any one time
	iFileList = new (ELeave) CArrayFixFlat<CAgnServFile*>(KFileListGranularity);
	User::LeaveIfError(iFs.PrivatePath(iPrivatePath));
	}

void CAgnServFileMgr::CreatePermanentDataL()
	{
	// Attempt to pre-create the permanent data if the server is in
	// non-transient mode.
	if (iAgnServer.ServerMode() == ENonTransientServer)
		{
		iPermanentData = CAgnPermanentData::NewL(iAgnServer, *this);
		// If StartDataCreationL() leaves then iPermanentData will still 
		// be valid, therefore it is not sensible to let the Leave panic 
		// the entire server; hence TRAP_IGNORE is used.
		TRAP_IGNORE(iPermanentData->StartDataCreationL());
		}
	}

CAgnServFile& CAgnServFileMgr::OpenAgendaL(const TDesC& aFilename, CAgnServer& aAgnServer, CalCommon::TCalFileVersionSupport& aStatus)
	{
	//Parse the filename
	HBufC* realfilename = ParseFilenameLC(aFilename);
	realfilename->Des().Fold();
	
	CAgnServFile* agnServFile = NULL;
	
	// Ownership not passed
	agnServFile = GetFile(*realfilename);
	if(agnServFile == NULL)
		{
		// New file - File has not yet been opened, so open it here
		agnServFile = CAgnServFile::NewL(iFs, aAgnServer);
		CleanupStack::PushL(TCleanupItem(CAgnServFile::CloseAgenda, agnServFile));

		agnServFile->OpenAgendaL(*realfilename, aStatus);
		CAgnServFile* servFile = agnServFile;
		TUint8 lastShortFileId = 0;
		TInt count = iFileList->Count();
		if(count>0)
			{
			lastShortFileId = (*iFileList)[count-1]->CollectionId();
			if(KMaxTUint8 == lastShortFileId)
				{
				User::Leave(KErrOverflow);
				}
			}
		servFile->SetCollectionId(lastShortFileId + 1);
		iFileList->AppendL(servFile);
		CleanupStack::Pop(); // CAgnServFile::CloseAgenda
		}
	else
		{
		// The file already exists so populate aStatus with its version
		// support status because this will be sent back to the client
		TAgnVersion version;
		agnServFile->GetFileVersionSupportStatusL(version, aStatus);
		}
		
	// This will increment the file reference counter and cancel the file shutdown timer
	agnServFile->AddReference();
	CleanupStack::PopAndDestroy(realfilename);
	return *agnServFile;
	}

/** 
* Closes the calendar file in the current agenda server session immediately or after a delay.
* The delay is triggered using a timer owned by the calendar file. If a session then tries to access the same calendar file, the delay is cancelled and the file is not closed
* @param aCloseImmediately EFalse if a delay is required before the closure of the calendar file, ETrue if the calendar file is to be closed immediately
* @param aServFile Calendar file to be closed
*/
TInt CAgnServFileMgr::CloseAgenda(CAgnServFile& aServFile, TBool aCloseImmediately)
	{
	// Close an instance of this store
	TInt fileCount = iFileList->Count();

	for (TInt count=0; count<fileCount; count++)
		{
		CAgnServFile* servFile = (*iFileList)[count];
		if (&aServFile == servFile)
			{
			servFile->CloseAgenda(aCloseImmediately);
			return KErrNone;
			}
		}
	return KErrNotFound;
	}

CFileStore* CAgnServFileMgr::CreateAgendaFileLC(const TDesC& aFileName)
	{
	HBufC* resolvedFileName = ParseFilenameLC(aFileName); //Resolve the filename
	CAgnServFile::CreateDirL(iFs, *resolvedFileName);

	// If the permanent data object is holding this file open, 
	// free it so the client can (re)create it.
	if (iPermanentData && iPermanentData->IsOnlyClientOfFile(*resolvedFileName))
		{
		iPermanentData->ReleaseFileL(*resolvedFileName);
		}	

	CFileStore* fileStore = CPermanentFileStore::ReplaceL(iFs, *resolvedFileName, EFileWrite); //Create the file
	CleanupStack::PopAndDestroy(resolvedFileName);
	CleanupStack::PushL(fileStore);
	return fileStore;
	}

void CAgnServFileMgr::DeleteAgendaFileL(const TDesC& aClientFileName)
	{
	//Parse the filename
	HBufC* realfilename = ParseFilenameLC(aClientFileName);
	realfilename->Des().Fold();
	
	// Ownership not passed
	CAgnServFile* agnServFile = GetFile(*realfilename);
	// Check and cancel the file close timer, if active, to enable file deletion
	if (agnServFile)
		{
		agnServFile->FileShutdownDelayTimer().CloseAgenda();
		}

	// If the permanent data object is holding this file open,
	// free it so the client can delete it.
	if (iPermanentData && iPermanentData->IsOnlyClientOfFile(*realfilename))
		{
		iPermanentData->ReleaseFileL(*realfilename);
		}
	
	if ( ! CAgnServFile::FileExistsL(iFs, *realfilename))
		{
		User::Leave(KErrNotFound);
		}
	
	const TInt KFolderNameLength = realfilename->Length() + 8;
	HBufC* folderName = HBufC::NewLC(KFolderNameLength);
	TPtr folderNamePtr = folderName->Des();
	
	CAgnServFile::GetAttachmentFolderNameL(*realfilename, folderNamePtr);
	
	// delete all attachments in the same directory as the calendar file
	CFileMan* fileMan = CFileMan::NewL(iFs);	
	TInt err = fileMan->RmDir(folderNamePtr);
	delete fileMan;
	
	if (err != KErrNotFound && err != KErrPathNotFound)
		{
		User::LeaveIfError(err);
		}

	CAgnServFile::DeleteFileL(iFs, *realfilename);
	
	const TInt KIndexFileNameLength = realfilename->Length() + KIdxFilePostFixLength;
	HBufC* indexFileName = HBufC::NewLC(KIndexFileNameLength);
	TPtr indexfilenamePtr = indexFileName->Des();
	indexfilenamePtr.Append(*realfilename);
	indexfilenamePtr.Append(KIdxFilePostFix());
	TInt delErr = iFs.Delete(indexfilenamePtr);
	// it is valid for the index file not to be present so we only
	// leave on other errors
	if ((delErr != KErrNotFound) && (delErr != KErrPathNotFound))
		{
		User::LeaveIfError(delErr);
		}
	CleanupStack::PopAndDestroy(indexFileName);
	CleanupStack::PopAndDestroy(folderName);
	CleanupStack::PopAndDestroy(realfilename);
	}

const TDesC& CAgnServFileMgr::PrivatePath() const
	{
	return iPrivatePath;
	}

CDesCArray* CAgnServFileMgr::ListAgendaFilesL() const
	{
	return CAgnFileScanner::ListAgendaFilesL(iFs,TUidType(KPermanentFileStoreLayoutUid, KUidAppDllDoc, KUidAgnApp), iPrivatePath);
	}

/** Parse the Agenda file name

The file name combination in unsecured data is a file name with full path. 
In secured data, it includes a drive name and a file name but not a full path.,
i.e. DriveName:FileName 

@param aFileNameDes The file name combination.
@return the full path of the Agenda file.
*/
HBufC* CAgnServFileMgr::ParseFilenameLC(const TDesC& aClientFileName) const
	{
	if (aClientFileName.Locate('\\')>=0)//there is a path included explicitly
		{
		User::Leave(KErrNotSupported);
		}

	HBufC* filename = NULL;
	const TInt KClientFileNameLength = aClientFileName.Length();
	if (KClientFileNameLength > KCalMaxFilePath)
		{
		User::Leave(KErrBadName);
		}	
	if (KClientFileNameLength >= 2 && aClientFileName[1] == KDriveDelimiter)
		{
		filename = HBufC::NewLC(aClientFileName.Length() + iPrivatePath.Length());
		TPtr filenamePtr1(filename->Des());
		filenamePtr1.Copy(aClientFileName.Left(2));
		filenamePtr1.Append(iPrivatePath);
		filenamePtr1.Append(aClientFileName.Right(KClientFileNameLength - 2));
		}
	else
		{
		TDriveUnit driveUnit(EDriveC);
		const TDesC* calendarFileName = &aClientFileName;
		
		if (KClientFileNameLength == 0)
			{//use the default agenda filename
			calendarFileName = &KDefaultAgendaFileName;
			}
		
		filename = HBufC::NewLC(KMaxDriveName + iPrivatePath.Length() + calendarFileName->Length());
		TPtr filenamePtr2(filename->Des());
		
		filenamePtr2.Copy(driveUnit.Name());
		filenamePtr2.Append(iPrivatePath);
		filenamePtr2.Append(*calendarFileName);
		}
	return filename;
	}

TBool CAgnServFileMgr::AgendaFileExistsL(const TDesC& aClientFileName) const
	{
	HBufC* realfilename = ParseFilenameLC(aClientFileName);
	TBool fileExists = CAgnServFile::FileExistsL(iFs, *realfilename);
	CleanupStack::PopAndDestroy(realfilename);
	return fileExists;
	}

TBool CAgnServFileMgr::FileCloseTimersRunning() const
	{
	const TInt KFileCount = iFileList->Count();

	for (TInt count=0; count<KFileCount; ++count)
		{
		CAgnServFile* servFile = (*iFileList)[count];
		if(servFile->FileShutdownDelayTimer().IsActive())
			{
			return ETrue;
			}
		}
	return EFalse;
	}

/** Closes all files that are scheduled for close immediately.

This function is needed when files that are currently scheduled for close need to be closed
immediately. For instance this is used in the backup and restore case where the files need to
be closed immediately because the backup engine needs to access them.
*/
void CAgnServFileMgr::CloseScheduledFilesImmediately()
	{
	for (TInt count = iFileList->Count()-1; count >= 0; --count)
		{
		CAgnServFile* servFile = (*iFileList)[count];
		if (servFile->FileShutdownDelayTimer().IsActive())
			{
			servFile->FileShutdownDelayTimer().Cancel();
			servFile->CloseAgendaImmediately();
			iAgnServer.ShutdownServer();
			delete servFile;
			iFileList->Delete(count);
			}
		}
	}
#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
void CAgnServFileMgr::QueueAlarmsImmediatelyForShutdown()
	{
	for (TInt count = iFileList->Count()-1; count >= 0; --count)
		{
		CAgnServFile* servFile = (*iFileList)[count];
		if(servFile)
			{
			servFile->QueueAlarmsImmediately();
			servFile->SetShutdownFlag(ETrue);
			}
		}
	}

void CAgnServFileMgr::RequeueAlarmsForShutdownCancellation()
	{
	for (TInt count = iFileList->Count()-1; count >= 0; --count)
		{
		CAgnServFile* servFile = (*iFileList)[count];
		if(servFile)
			{
			servFile->DeleteAlarmsAndRequeueSessionAlarm();
			servFile->SetShutdownFlag(EFalse);
			}
		}
	}
#endif
// Remove the agenda file from the list and delete it
void CAgnServFileMgr::CloseAgendaFile(CAgnServFile* aServFile)
	{
	// Close an instance of this store
	const TInt KFileCount = iFileList->Count();
	
	#ifdef _DEBUG
	// Calendar file should exist in the list
	TBool fileFound = EFalse;
	#endif
	
	for (TInt count=0; count<KFileCount; ++count)
		{
		CAgnServFile* servFile = (*iFileList)[count];
		if (aServFile == servFile)
			{
			iFileList->Delete(count);
		#ifdef _DEBUG
			fileFound = ETrue;
		#endif
			break;
			}
		}

	// Calendar file should exist in the list whilst deletion and shouldnt have been removed earlier
	__ASSERT_DEBUG(fileFound, Panic(EAgmErrCalendarFileNotFound));
	
	// Close the calendar file
	delete aServFile;
	// Trigger server shutdown in case this was the last calendar file open by the server
	if(iFileList->Count() == 0)
		{
		iAgnServer.ShutdownServer();
		}
	}

// Iterate through the list of files owned by the file manager. Ownership is not passed
// @return Pointer to the matching file or NULL if it isnt found.
CAgnServFile* CAgnServFileMgr::GetFile(const TDesC& aFilename) const
	{
	// Check that the file is not already open
	const TInt KFileCount = iFileList->Count();
	
	for (TInt count=0; count<KFileCount; count++)
		{
		if (!aFilename.CompareF((*iFileList)[count]->FileName()))
			{
			// Doesnt take ownership
			CAgnServFile* agnServFile = (*iFileList)[count];
			return agnServFile;
			}
		}
	return NULL;
	}

CAgnServFile* CAgnServFileMgr::GetFileL(TInt64 aFileId) const
	{
	const TInt KFileCount = iFileList->Count();
	
	for (TInt count=0; count<KFileCount; count++)
		{
		if (aFileId == (*iFileList)[count]->Model()->GetFileIdL())
			{
			// Doesnt take ownership
			return  (*iFileList)[count];
			}
		}
	User::Leave(KErrNotFound);
	return NULL;
	}

CAgnServFile* CAgnServFileMgr::GetFileL(TCalCollectionId aCollectionId) const
	{
	const TInt KFileCount = iFileList->Count();
	
	for (TInt count=0; count<KFileCount; count++)
		{
		if (aCollectionId == (*iFileList)[count]->CollectionId())
			{
			// Doesnt take ownership
			return (*iFileList)[count];
			}
		}

	User::Leave(KErrNotFound);
	return NULL;
	}

TInt CAgnServFileMgr::Count()
    {
    return iFileList->Count();
    }

CAgnServFile* CAgnServFileMgr::File(TInt aIndex)
    {
    return (*iFileList)[aIndex];
    }

TInt64 CAgnServFileMgr::GetLongFileIdL(TCalCollectionId aCollectionId) const
	{
	const TInt KFileCount = iFileList->Count();
	
	for (TInt count=0; count<KFileCount; count++)
		{
		if (aCollectionId == (*iFileList)[count]->CollectionId())
			{
			// Doesnt take ownership
			return (*iFileList)[count]->Model()->GetFileIdL();
			}
		}
	return KNullFileId;
	}

void CAgnServFileMgr::BackupReStoreChanged(MCalChangeCallBack2::TChangeType aChange)
	{
	if (iFileList)
		{
		RPointerArray<CAgnServerSession> sessions;
		TRAP_IGNORE(iAgnServer.FetchSessionsL(sessions));
		const TInt countSessions = sessions.Count();
		TInt ii =0;
		if(aChange == MCalChangeCallBack2::EBackupStart
				|| aChange == MCalChangeCallBack2::ERestoreStart)
			{
			for (ii=0; ii<countSessions; ++ii)
				{
				sessions[ii]->BackupRestoreCancelTask();
				}
			}
		
		const TInt countFiles= iFileList->Count();
		for (TInt jj=0; jj<countFiles; ++jj)
			{
			CAgnServFile* servFile = (*iFileList)[jj];
			TInt64 oldFileId = KNullFileId;          
			TRAP_IGNORE(oldFileId = servFile->Model()->GetFileIdL());
			TInt err = servFile->BackupReStoreChanged(aChange);
			if(aChange == MCalChangeCallBack2::ERestoreEnd && err != KErrNone)
				{
				aChange = MCalChangeCallBack2::ERestoredFileCanNotBeOpened;
				}
			
			for(ii = 0; ii< countSessions; ++ii)
				{
				TRAP_IGNORE(sessions[ii]->BackupReStoreChangedL(oldFileId,
									*servFile, aChange));
				}
			
			if(aChange == MCalChangeCallBack2::ERestoredFileCanNotBeOpened)
				{
				servFile->CloseAgenda(ETrue);
				}
			}
		sessions.Close();
		}
	}

	TBool CAgnServFile::IsFileDisabled()
	{
	return iIsFileDisabled;
	}




