diff -r 000000000000 -r f979ecb2b13e pimappservices/calendar/server/src/agsfilemanager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pimappservices/calendar/server/src/agsfilemanager.cpp Tue Feb 02 10:12:19 2010 +0200 @@ -0,0 +1,2065 @@ +// 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 +#include + +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 (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 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(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 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 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(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; countCloseAgenda(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; countFileShutdownDelayTimer().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; countDelete(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; countFileName())) + { + // 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; countModel()->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; countCollectionId()) + { + // 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; countCollectionId()) + { + // Doesnt take ownership + return (*iFileList)[count]->Model()->GetFileIdL(); + } + } + return KNullFileId; + } + +void CAgnServFileMgr::BackupReStoreChanged(MCalChangeCallBack2::TChangeType aChange) + { + if (iFileList) + { + RPointerArray sessions; + TRAP_IGNORE(iAgnServer.FetchSessionsL(sessions)); + const TInt countSessions = sessions.Count(); + TInt ii =0; + if(aChange == MCalChangeCallBack2::EBackupStart + || aChange == MCalChangeCallBack2::ERestoreStart) + { + for (ii=0; iiBackupRestoreCancelTask(); + } + } + + const TInt countFiles= iFileList->Count(); + for (TInt jj=0; jjModel()->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; + } + + + +