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