diff -r 000000000000 -r ff3b6d0fd310 cbs/CbsServer/ServerSrc/CCbsDbImpTopicList.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cbs/CbsServer/ServerSrc/CCbsDbImpTopicList.cpp Tue Feb 02 01:11:09 2010 +0200 @@ -0,0 +1,2757 @@ +/* +* Copyright (c) 2003 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: This file contains the implementation of the CCbsDbImpTopicList class +* member functions. +* +* This class represents the topic list contained in the database. +* +*/ + + + +// INCLUDE FILES +#include +#include +#include + +#include // RequestFreeDiskSpaceLC + +#include // Resource access +#include // Resource access + +#include + +#include "CbsServerPanic.h" +#include "CbsStreamHelper.h" +#include "CbsDbConstants.h" +#include "CbsUtils.h" +#include "CCbsDbImpTopicList.h" +#include "MCbsDbTopicListObserver.H" +#include "CCbsDbImpTopicMessages.h" + +#include "CbsLogger.h" + +#include // for local variation +#include "cbsinternalcrkeys.h" // for local variation +#include "cbsvariant.hrh" // for local variation + +// CONSTANTS + +// Initial size for topic cache array +const TInt KDefaultTopicListSize = 10; + +// Size of the topic stream, used in checking against FFS free space limit +const TInt KTopicStreamSize = 92; + +// Size of the topic messages stream, FFS critical level check +const TInt KEmptyTopicMessagesStreamSize = 4; + +// Size of topic list stream, FFS critical level check +const TInt KTopicListStreamSize = 85; + +// Size of topic list stream (root), FFS critical level check +const TInt KTopicListRootStreamSize = 2; + +// Time in microseconds to wait after a critical store exception +// before a recovery attempt is made. +const TInt KWaitAfterCriticalStoreException = 2000000; + +// Used when unsaved message stream ids are deleted from topic messages +// stream +const TInt KTypicalNumberOfTopicMessages = 6; + +// Minimum interval between compacting the stores in minutes +const TInt KMinimumCompactInterval = 30; + +// Number of the index topic +const TInt KIndexTopicNumber = 0; + +// Space for reading messages +const TInt KReadMessageSize = 92; + +const TInt KTopicsGranularity = 1; + +const TInt KTopicListsGranularity = 1; + +const TInt KTopicIdsGranularity = 5; + +// ================= MEMBER FUNCTIONS ======================= + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CCbsDbImpTopicList +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CCbsDbImpTopicList::CCbsDbImpTopicList( + RFs& aFs, + CCbsDbImp& aDatabase ) + : iDatabase( aDatabase ), + iFs( aFs ), + iTopicCount( 0 ), + iInitializing( EFalse ), + iDeleteAllTopics( EFalse ) + { + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::ConstructL( + const TDesC& aTopicsFile, + const TDesC& aUnsavedMessagesFile ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::ConstructL()"); + + // Create observer array + iObservers = new ( ELeave ) CArrayFixFlat< MCbsDbTopicListObserver* >( + KCbsDbObserverArraySize ); + + // Create topic array, additional memory size in new alloc, 1*192 B = 192 B + iTopics = new ( ELeave ) CArrayFixFlat< TCbsDbImpTopic >( KTopicsGranularity ); + + // Create the root item table, additional memory size in new alloc, 1*184 B = 184 B + iTopicLists = new ( ELeave ) CArrayFixFlat< TCbsDbImpTopicList >( KTopicListsGranularity ); + + // Create topic ID array, additional memory size in new alloc, 5*4 B = 20 B + iTopicIds = new ( ELeave ) CArrayFixFlat< TStreamId >( KTopicIdsGranularity ); + + // Initialize iPreviousCompact to now + iPreviousCompact.UniversalTime(); + + // Copy datafile names from parameters + iTopicsFilename = aTopicsFile.AllocL(); + iUnsavedMessagesFilename = aUnsavedMessagesFile.AllocL(); + + // Fetch local variation bits from CenRep + CRepository* repository = CRepository::NewL( KCRUidCbsVariation ); + TInt err = repository->Get( KCbsVariationFlags, iLVBits ); + CBSLOGSTRING2("CBSSERVER: CCbsRecEtel::ConstructL(): CenRep error: %d", err ); + if ( err ) + { + iLVBits = 0; + } + delete repository; + + // If aLoadFactorySettings, then the files are recreated and initialized. + // If not, then the nonexisting files are created, files opened and + // data internalized. + + TBool unsavedMsgFileExists( CbsUtils::ExistsL( iFs, *iUnsavedMessagesFilename ) ); + + TBool loadFactorySettings( !CbsUtils::ExistsL( iFs, aTopicsFile ) ); + TRAPD( error, OpenFilesL( loadFactorySettings, EFalse ) ); + + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::ConstructL(): OpenFilesL() error: %d", error ); + + if ( error == KErrDiskFull ) + { + // Delete the store and open is again + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ConstructL(): About to delete iTopicStore..." ); + + delete iTopicStore; + iTopicStore = NULL; + + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ConstructL(): iTopicStore deletion finished." ); + + // Try to open the store again + TRAPD( error, iTopicStore = CFileStore::OpenL( iFs, *iTopicsFilename, EFileRead | EFileWrite ) ); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::ConstructL(); iTopicStore OpenL() error: %d", error ); + if ( error ) + { + InitializeListL( ETrue ); + } + } + else + { + if ( loadFactorySettings ) + { + // Create a topic list for standard topics + if ( iTopicLists->Count() == 0 ) + { + CreateStandardTopicListL(); + } + } + + // Only load the topics, if not already loaded when opening the files + if ( loadFactorySettings || unsavedMsgFileExists ) + { + // Load the topics + LoadDefaultTopicStreamL(); + } + + // Compact the topic store + TopicStoreL()->CompactL(); + TopicStoreL()->CommitL(); + } + + // Reset the unread message count to be sure that UI client gets the correct information, + // at this point count is always zero. + for ( TInt i( 0 ); i < iTopics->Count(); ++i ) + { + iTopics->At( i ).iTopicData.iUnreadMessages = 0; + } + + __TEST_INVARIANT; + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::ConstructL()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CCbsDbImpTopicList* CCbsDbImpTopicList::NewL( + RFs& aFs, + const TDesC& aTopicsFile, + const TDesC& aUnsavedMessagesFile, + CCbsDbImp& aDatabase ) + { + CCbsDbImpTopicList* self = + new ( ELeave ) CCbsDbImpTopicList( aFs, aDatabase ); + CleanupStack::PushL( self ); + self->ConstructL( aTopicsFile, aUnsavedMessagesFile ); + CleanupStack::Pop(); + return self; + } + +// Destructor +CCbsDbImpTopicList::~CCbsDbImpTopicList() + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::~CCbsDbImpTopicList()"); + delete iTopics; + delete iTopicStore; + delete iTopicLists; + delete iTopicIds; + delete iUnsavedMessageStore; + delete iObservers; + delete iUnsavedMessagesFilename; + if ( iTopicsFilename ) + { + delete iTopicsFilename; + } + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::~CCbsDbImpTopicList()"); + } + +#ifndef __SECURE_BACKUP__ +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::ChangeFileLockL +// Closes or reopens the settings file if requested by a backup. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::ChangeFileLockL( + const TDesC& aFileName, + TFileLockFlags aFlags ) + { + CBSLOGSTRING2("CBSSERVER: >>> CCbsDbImpTopicList::ChangeFileLockL() (1): flag: %d", aFlags ); + + if ( aFlags == ETakeLock && iTopicStore == NULL ) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (1): Try to open store..."); + + // Try to open the store. + iTopicStore = CPermanentFileStore::OpenL( iFs, + aFileName, EFileRead | EFileWrite ); + + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (1): Store opened."); + } + else if ( aFlags != ETakeLock ) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (1): Deleting iTopicStore..."); + + delete iTopicStore; + iTopicStore = NULL; + + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (1): iTopicStore deleted."); + } + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::ChangeFileLockL() (1)"); + } +#else + +// ----------------------------------------------------------------------------- +// CCbsDbImpSettings::ChangeFileLockL +// Closes or reopens the settings file if requested by backup. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::ChangeFileLockL( const TCbsBackupRequest& aRequest ) + { + CBSLOGSTRING2("CBSSERVER: >>> CCbsDbImpTopicList::ChangeFileLockL() (2): aRequest: %d", aRequest ); + + // If backing up or restoring, release locks + if ( ( aRequest == ECbsBackup || + aRequest == ECbsRestore ) ) + { + delete iTopicStore; + iTopicStore = NULL; + + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (2): iTopicStore deleted."); + } + // Else take files into use again + else if ( ( aRequest == ECbsNoBackup || + aRequest == ECbsBackupNotDefined ) && + iTopicStore == NULL) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (2): Calling CPermanentFileStore::OpenL()..."); + // Try to open the store. + iTopicStore = CPermanentFileStore::OpenL( iFs, + iTopicsFilename->Des(), EFileRead | EFileWrite ); + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::ChangeFileLockL() (2): CPermanentFileStore::OpenL() finished."); + } + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::ChangeFileLockL() (2)"); + } + +#endif + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CheckFileLockL +// Check if the server has a file lock. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::CheckFileLockL() const + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::CheckFileLockL()"); + if ( iTopicStore == NULL ) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::CheckFileLockL(): iTopicStore == NULL, leaving with KErrLocked..."); + User::Leave( KErrLocked ); + } + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::CheckFileLockL()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CreateNewTopicListL +// Creates a new topic list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::CreateNewTopicListL( const TDesC& aTopicListName ) + { + // FFS critical level check + CbsUtils::FFSCriticalLevelCheckL( KTopicListStreamSize + + KCbsDbTopicArraySize, iFs ); + + TInt topicListCount = iTopicLists->Count(); + + // Check if there already is 10 topic lists + if ( topicListCount < KCbsRootItemsSize ) + { + // Create the stream for this topic list entry + RStoreWriteStream outstream; + TStreamId topicListStreamId( + outstream.CreateLC( *TopicStoreL() ) ); // on CS + + // Set the values for this new topic list and set it as "current". + // Reset the topic count of this topic list. + iCurrentTopicList.iTopicCount = 0; + + // Topic list stream id for this list + iCurrentTopicList.iTopicListId = topicListStreamId; + + // List name + iCurrentTopicList.iTopicListName = aTopicListName; + + // Is this the default list + iCurrentTopicList.iIsDefaultTopicList = ETrue; + + // Number of this list, which is a int value between 0...9 + iCurrentTopicList.iNumber = iTopicLists->Count(); + + // Add this list to the list array + TKeyArrayFix key( _FOFF( TCbsDbImpTopicList, iNumber ), ECmpTUint16 ); + iTopicLists->InsertIsqL( iCurrentTopicList, key ); + + // Write the values to this topic list's stream + outstream << iCurrentTopicList.iTopicListName; + CbsStreamHelper::WriteBoolL( outstream, iCurrentTopicList.iIsDefaultTopicList ); + outstream.WriteInt16L( iCurrentTopicList.iNumber ); + outstream.WriteInt16L( iCurrentTopicList.iTopicCount ); + + // Write space for topic stream IDs + for ( TInt i( 0 ); i < KCbsDbTopicArraySize; i++ ) + { + outstream << TStreamId( 0 ); + } + + outstream.CommitL(); + CleanupStack::PopAndDestroy(); // outstream + + // Reset the topic array and add an index topic to this + // topic list + iTopics->Reset(); + AddIndexTopicL(); + + // Update the root stream + UpdateRootStreamL( EFalse ); + + // Commit file changes + CommitFilesL(); + + // Load the topics (count = 1, Index topic only), + // so that the array is up to date + LoadTopicsL( iCurrentTopicList.iTopicListId ); + } + else + { + User::Leave( KErrGeneral ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::SetTopicMessages +// Sets the topic messages db object for this topic list. +// Note that this function does not transfer ownership +// of aMessages. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::SetTopicMessages( + CCbsDbImpTopicMessages* aMessages ) + { + __ASSERT_ALWAYS( aMessages != NULL, + CbsServerPanic( ECbsTopicMessagesNull ) ); + iMessages = aMessages; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::TopicCount +// Returns the total amount of topics the list contains. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCbsDbImpTopicList::TopicCount() const + { + return iTopics->Count(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::TopicListCount +// Get topic list count. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCbsDbImpTopicList::TopicListCount() const + { + return iTopicLists->Count(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::TopicStoreL +// Returns pointer to the current store which contains topics +// of the server (i.e., Topics-file) and saved messages. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +CFileStore* CCbsDbImpTopicList::TopicStoreL() const + { + CheckFileLockL(); + return iTopicStore; + } + +// --------------------------------------------------------- +// TopicFilename() +// +// --------------------------------------------------------- +const TDesC& CCbsDbImpTopicList::TopicFilename() const + { + return *iTopicsFilename; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UnsavedMessagesStore +// Returns a pointer to the store, which contains unsaved +// messages of the server (Unsaved Messages-file, "cbs4.dat"). +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +CFileStore* CCbsDbImpTopicList::UnsavedMessagesStore() const + { + return iUnsavedMessageStore; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UnsavedMessagesFilename +// Returns a reference to the name of the file, which contains unsaved +// messages of the server (Unsaved Messages-file, "cbs4.dat"). +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +const TDesC& CCbsDbImpTopicList::UnsavedMessagesFilename() const + { + return *iUnsavedMessagesFilename; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::ExtractTopicNumber +// Extracts topic handle from message handle. +// Note that the method does not check that the message exists. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCbsDbTopicNumber CCbsDbImpTopicList::ExtractTopicNumber( + const TCbsDbMessageHandle& aHandle ) const + { + return TCbsDbTopicNumber( aHandle >> 16 ); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetTopicMessagesIdL +// Returns the topic messages stream id by topic handle. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::GetTopicMessagesIdL( + TCbsDbTopicNumber aNumber, TStreamId& aId ) const + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::GetTopicMessagesIdL()"); + + // Find the topic. + TInt index( TopicIndexInList( aNumber ) ); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::GetTopicMessagesIdL(): Leaving if index != 0. Index: %d.", index ); + User::LeaveIfError( index ); + + // Return the topic message stream id. + aId = iTopics->At( index ).iTopicMessagesId; + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::GetTopicMessagesIdL()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicMessagesIdL +// Updates the topic messages stream id by topic handle. +// The new identifier is expected not to be a null id. +// Note that the method will not commit changes to the store. +// It also changes the internal state and thus if the method leaves, +// it is good to reload the whole root stream. +// The method will delete the old topic messages stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicMessagesIdL( + TCbsDbTopicNumber aNumber, + const TStreamId& aNewId ) + { + __TEST_INVARIANT; + // Find position. + TInt index( TopicIndexInList( aNumber ) ); + User::LeaveIfError( index ); + + // Get the old id. + TStreamId oldId( iTopics->At( index ).iTopicMessagesId ); + + // Get the topic information. + TCbsDbTopic topic; + GetTopicL( index, topic ); + + // Write information to the stream. + RStoreWriteStream outstream; + outstream.OpenLC( *TopicStoreL(), iTopics->At( index ).iTopicId ); // on CS + WriteTopicInformationL( outstream, topic, aNewId ); + outstream.CommitL(); + CleanupStack::PopAndDestroy(); + + // Delete the old stream. + TopicStoreL()->DeleteL( oldId ); + + // Update the topic messages id. + iTopics->At( index ).iTopicMessagesId = aNewId; + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GenerateMessageHandle +// Generates a new message handle using the topic +// handle of the message and a given random value. +// Note that it must be checked that the message handle is unique +// in that topic. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TCbsDbMessageHandle CCbsDbImpTopicList::GenerateMessageHandle( + const TCbsDbTopicNumber aNumber, + TUint16 aRandom ) const + { + return ( aNumber << 16 ) + aRandom; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::ReloadRootStreamL +// Reloads the root stream to the memory. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::ReloadRootStreamL() + { + LoadRootStreamL(); + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::InformNewMessageReceivedL +// Informs that a new message has been received in a topic. +// This method is called by CCbsDbImpTopicMessages. After internal +// records are changed, the observers are informed of this event. +// Note: leaves changes in stores uncommited. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::InformNewMessageReceivedL( + const TCbsDbMessageHandle& aMessageHandle ) + { + __TEST_INVARIANT; + // Find topic by handle. + TCbsDbTopicNumber number( ExtractTopicNumber( aMessageHandle ) ); + + TCbsDbTopic topic; + FindTopicByNumberL( number, topic ); + + topic.iUnreadMessages++; + + // Increase the counter in cache + TInt position = TopicIndexInList( topic.iNumber ); + iTopics->At( position ).iTopicData.iUnreadMessages++; + + // Write topic information to topic stream but leave changes uncommited. + UpdateTopicCountersL( topic, EFalse ); + + if ( topic.iHotmarked ) + { + SetHotmarkedMessage( aMessageHandle ); + } + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::InformMessageSavedL +// Informs that a message has been set as saved. +// Updates the saved messages counters +// for the topic of the message and the whole system. +// Called by CCbsDbTopicMessages. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::InformMessageSavedL( + const TCbsDbMessageHandle& aMessageHandle ) + { + __TEST_INVARIANT; + // Find topic by handle. + TCbsDbTopicNumber number( ExtractTopicNumber( aMessageHandle ) ); + + TCbsDbTopic topic; + FindTopicByNumberL( number, topic ); + + topic.iSavedMessages++; + + // Increase the counter in cache + TInt position = TopicIndexInList( topic.iNumber ); + iTopics->At( position ).iTopicData.iSavedMessages++; + + // Write topic information to topic stream but leave changes uncommited. + UpdateTopicCountersL( topic, EFalse ); + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::InformUnreadMessageReadL +// Informs that an unread message has been read. +// Updates the counters when a message is read by the client. +// Note: leaves changes in stores uncommited. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::InformUnreadMessageReadL( + const TCbsDbMessageHandle& aMessageHandle ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::InformUnreadMessageReadL()"); + + __TEST_INVARIANT; + + // Check for file lock + CheckFileLockL(); + + // Check disk space + TRAPD( error, CbsUtils::FFSCriticalLevelCheckL( KReadMessageSize, iFs ) ); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::InformUnreadMessageReadL(): FFSCriticalLevelCheckL returned: %d", error ); + if ( error == KErrDiskFull ) + { + return; + } + + // Find topic by number. + TCbsDbTopicNumber number( ExtractTopicNumber( aMessageHandle ) ); + + TCbsDbTopic topic; + FindTopicByNumberL( number, topic ); + + // Decrease the counter + topic.iUnreadMessages--; + + // Decrease the counter in cache + TInt position = TopicIndexInList( topic.iNumber ); + iTopics->At( position ).iTopicData.iUnreadMessages--; + + // Write topic information to topic stream but leave changes uncommited. + UpdateTopicCountersL( topic, EFalse ); + + __TEST_INVARIANT; + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::InformUnreadMessageReadL()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::InformMessageDeletedL +// Informs that an unread message has been deleted. +// Updates the counters when a message is deleted. +// Note: leaves changes in stores uncommited. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::InformMessageDeletedL( + const TCbsDbMessageHandle& aMessageHandle, + TBool aPermanent, + TBool aRead ) + { + // Find topic by handle. + TCbsDbTopicNumber number( ExtractTopicNumber( aMessageHandle ) ); + + TCbsDbTopic topic; + FindTopicByNumberL( number, topic ); + + TInt position = TopicIndexInList( topic.iNumber ); + + if ( aPermanent ) + { + topic.iSavedMessages--; + iTopics->At( position ).iTopicData.iSavedMessages--; + } + + if ( !aRead ) + { + topic.iUnreadMessages--; + iTopics->At( position ).iTopicData.iUnreadMessages--; + } + + // Write topic information to topic stream but leave changes uncommited. + UpdateTopicCountersL( topic, ETrue ); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::TotalSavedMessages +// Returns the number of saved messages in the current topic list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCbsDbImpTopicList::TotalSavedMessages() const + { + // Return the total amount of saved messages. + TInt count( 0 ); + for ( TInt i( 0 ); i < iTopics->Count(); i++ ) + { + count += iTopics->At( i ).iTopicData.iSavedMessages; + } + + return count; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetTopicCount +// Returns the number of topic stored in this topic list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::GetTopicCount( + TInt& aCount ) const + { + // Return the total amount of topics. + aCount = TopicCount(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::InitializeListL +// Initializes the whole topic list. +// Creates and opens the topic list file, +// invalidates the cache and informs the observers. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::InitializeListL( const TBool aFileOpenFailed ) + { + __TEST_INVARIANT; + + if ( !aFileOpenFailed ) + { + // Check for file lock + CheckFileLockL(); + } + + // About to write to FFS: make critical level check + CbsUtils::FFSCriticalLevelCheckL( 0, iFs ); + + if ( iMessages != NULL && iMessages->IsLockedMessages() ) + { + User::Leave( KErrAccessDenied ); + } + + // If only one topic list exists, just delete and recreate the whole file + if ( iTopicLists->Count() == 1 || aFileOpenFailed == 1 ) + { + delete iTopicStore; + iTopicStore = NULL; + delete iUnsavedMessageStore; + iUnsavedMessageStore = NULL; + CbsUtils::DeleteFileL( iFs, *iTopicsFilename ); + CbsUtils::DeleteFileL( iFs, *iUnsavedMessagesFilename ); + + iTopicLists->Reset(); + + // Create new files. + OpenFilesL( EFalse, ETrue ); + } + + iIsHotmarkedMessage = EFalse; + iLastTopicNumber = 0; + iMessageHandle = 0; + + // Inform the message manager. + if ( iMessages ) + { + iMessages->InvalidateCache(); + } + + // Notify everyone. + NotifyTopicListInitializedL(); + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetTopicL +// Returns a topic matching the given index. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::GetTopicL( + TInt aIndex, + TCbsDbTopic& aTopic ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::GetTopicL()"); + + __TEST_INVARIANT; + + // Check that aIndex is in proper range. + if ( ( aIndex < 0 ) || ( aIndex >= iTopics->Count() ) ) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::GetTopicL(): Leaving with KErrArgument..."); + User::Leave( KErrArgument ); + } + + // And then get the information from the array. + aTopic = iTopics->At( aIndex ).iTopicData; + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::GetTopicL()"); + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::FindTopicByNumberL +// Returns a topic matching the given topic number +// (in GSM Specs this is called the Message Identifier) +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::FindTopicByNumberL( + TCbsDbTopicNumber aNumber, + TCbsDbTopic& aTopic ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::FindTopicByNumberL()"); + + __TEST_INVARIANT; + + TInt topicIndex( TopicIndexInList( aNumber ) ); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::FindTopicByNumberL(): Leaving if topicIndex < 0: topicIndex: %d.", topicIndex ); + User::LeaveIfError( topicIndex ); + + GetTopicL( topicIndex, aTopic ); + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::FindTopicByNumberL()"); + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::AddTopicL +// Adds a new topic to the list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::AddTopicL( + TCbsDbTopic& aTopic, const TBool aDetected ) + { + __TEST_INVARIANT; + + // Check for file lock + CheckFileLockL(); + + // Check that topic number is in proper range + if ( !CheckTopicNumber( aTopic.iNumber ) ) + { + User::Leave( KErrArgument ); + } + + // Check if there is a topic with the + // same topic number in current topic list. + if ( TopicIndexInList( aTopic.iNumber ) != KErrNotFound ) + { + User::Leave( KErrAlreadyExists ); + } + + // There should not be any saved or unread messages + aTopic.iSavedMessages = 0; + aTopic.iUnreadMessages = 0; + + // Variated feature. Also, only topics that were not detected automatically + // are subscribed by default. + if ( iLVBits & KCbsLVFlagTopicSubscription && !aDetected ) + { + aTopic.iSubscribed = ETrue; + } + else + { + aTopic.iSubscribed = EFalse; + } + + // Now we have the handle, so let's add the topic. + TRAPD( error, DoAddTopicL( aTopic ) ); + if ( error != KErrNone ) + { + RevertFilesL(); + __TEST_INVARIANT; + User::Leave( error ); + } + else + { + iLastTopicNumber = aTopic.iNumber; + NotifyTopicAddedL( aTopic.iNumber ); + } + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicNameAndNumberL +// Updates the name and the topic number of a topic +// matching the given handle to the database. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicNameAndNumberL( + TCbsDbTopicNumber aOldNumber, + TCbsDbTopicNumber aNewNumber, + const TCbsDbTopicName& aName ) + { + // Check for file lock + CheckFileLockL(); + + // First, check that the new number is ok. + if ( !CheckTopicNumber( aNewNumber ) ) + { + User::Leave( KErrArgument ); + } + + // First find and then update. + TCbsDbTopic topic; + FindTopicByNumberL( aOldNumber, topic ); + + // If no changes to topic, no need to update + if ( !( aOldNumber == aNewNumber && topic.iName == aName ) ) + { + if ( topic.iProtected ) + { + User::Leave( KErrAccessDenied ); + } + + topic.iName = aName; + topic.iNumber = aNewNumber; + + UpdateTopicL( aOldNumber, topic ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicSubscriptionStatusL +// Updates the new topic subscription status to the database. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicSubscriptionStatusL( + TCbsDbTopicNumber aNumber, + TBool aStatus ) + { + __TEST_INVARIANT; + + // Update topic subsciption status. + TCbsDbTopic topic; + FindTopicByNumberL( aNumber, topic ); + + topic.iSubscribed = aStatus; + UpdateTopicL( aNumber, topic ); + __TEST_INVARIANT; + + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicHotmarkStatusL +// Updates the new topic hotmarking status to the database. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicHotmarkStatusL( + TCbsDbTopicNumber aNumber, + TBool aStatus ) + { + __TEST_INVARIANT; + + // Check for file lock + CheckFileLockL(); + + // Update topic hotmark status. + TCbsDbTopic topic; + FindTopicByNumberL( aNumber, topic ); + + topic.iHotmarked = aStatus; + UpdateTopicL( aNumber, topic ); + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DeleteTopicL +// Deletes an existing topic and all its messages. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DeleteTopicL( + TCbsDbTopicNumber aNumber ) + { + __TEST_INVARIANT; + + // Try to find the position. If it is not found, leave. + TInt position( TopicIndexInList( aNumber ) ); + User::LeaveIfError( position ); + + // Check that there are no locked messages in the topic. + if ( iMessages->IsLockedMessagesInTopic( aNumber ) ) + { + User::Leave( KErrAccessDenied ); + } + + // Topic only in one topic list, not an index topic, so delete the topic. + if ( aNumber != 0 ) + { + // Just try to delete + TRAPD( error, DoDeleteTopicL( position ) ); + if ( error != KErrNone ) + { + // It failed, so we must revert. + RevertFilesL(); + + // Inform the topic messages. + iMessages->InvalidateCacheIfTopic( aNumber ); + + __TEST_INVARIANT; + User::Leave( error ); + } + else + { + // Notify the observers + NotifyTopicDeletedL( aNumber ); + } + } + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetUnreadMessageCount +// Gets the total amount of unread messages. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::GetUnreadMessageCount( + TInt& aCount ) const + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::GetUnreadMessageCount()"); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::GetUnreadMessageCount(): Topic count: %d", iTopics->Count() ); + + // Return the total amount of unread messages. + TInt count( 0 ); + for ( TInt i( 0 ); i < iTopics->Count(); i++ ) + { + count += iTopics->At( i ).iTopicData.iUnreadMessages; + } + + aCount = count; + + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::GetUnreadMessageCount(): Unread msgs found: %d", count ); + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::GetUnreadMessageCount()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetHotmarkedMessageHandleL +// Returns the handle to the latest hotmarked message. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::GetHotmarkedMessageHandleL( + TCbsDbMessageHandle& aMessage ) + { + __TEST_INVARIANT; + + // Check if there is a hotmarked message. + if ( iIsHotmarkedMessage ) + { + // If there is, then return it. + aMessage = iMessageHandle; + iIsHotmarkedMessage = EFalse; + } + else + { + // Otherwise leave. + __TEST_INVARIANT; + User::Leave( KErrNotFound ); + } + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UnreadHotmarkedMessageCount +// Returns the handle to the latest hotmarked message. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCbsDbImpTopicList::UnreadHotmarkedMessageCount() const + { + // Return the total amount of unread messages in hotmarked topics. + TInt count( 0 ); + for ( TInt i( 0 ); i < iTopics->Count(); i++ ) + { + TCbsDbTopic& topic = iTopics->At( i ).iTopicData; + if ( topic.iHotmarked ) + { + count += topic.iUnreadMessages; + } + } + return count; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::AddObserverL +// Adds a topic list observer. +// After an observer is added to the topic list, +// it will be notified whenever an event occurs. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::AddObserverL( + MCbsDbTopicListObserver* aObserver ) + { + __ASSERT_DEBUG( aObserver != 0, CbsServerPanic( ECbsObserverNull ) ); + iObservers->AppendL( aObserver ); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::RemoveObserver +// Removes a topic list observer. +// If aObserver is not in the list, the method will panic. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::RemoveObserver( + const MCbsDbTopicListObserver* aObserver ) + { + __TEST_INVARIANT; + __ASSERT_DEBUG( aObserver != 0, CbsServerPanic( ECbsObserverNull ) ); + + TBool observerFound( EFalse ); + TInt amountOfObservers( iObservers->Count() ); + + // Check if the observer exists and + // find its possible location index in the array. + for ( TInt index( 0 ); ( index < amountOfObservers ) && !observerFound; index++ ) + { + if ( aObserver == iObservers->At( index ) ) + { + iObservers->Delete( index ); + observerFound = ETrue; + } + } + + __TEST_INVARIANT; + +#ifdef _DEBUG + if ( !observerFound ) + { + CbsServerPanic( ECbsObserverNotFound ); + } +#endif + + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::TopicIndexInList +// Finds the index of the topic matching the given topic number +// in the topic list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCbsDbImpTopicList::TopicIndexInList( + TCbsDbTopicNumber aNumber ) const + { + // Create a TCbsDbImpTopic to compare against and use a binary search. + TKeyArrayFix key( _FOFF( TCbsDbImpTopic, iTopicData.iNumber ), ECmpTUint16 ); + TCbsDbImpTopic dummy; + TInt position; + + dummy.iTopicData.iNumber = aNumber; + TInt result( iTopics->FindIsq( dummy, key, position ) ); + + if ( result != KErrNone ) + { + position = KErrNotFound; + } + + return position; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetNextAndPrevTopicNumberL +// Retrieves numbers of topics that precede and succeed the topic +// with number aCurrentTopic. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::GetNextAndPrevTopicNumberL( + const TCbsTopicNumber& aCurrentTopic, + TCbsTopicNumber& aNextTopic, + TCbsTopicNumber& aPrevTopic, + TInt& aPosition ) + { + // Determine position of requested topic in topic list. + TInt index( TopicIndexInList( aCurrentTopic ) ); + User::LeaveIfError( index ); // if KErrNotFound + + // Determine position indications + aPosition = 0; + if ( index == 0 ) + { + aPosition |= ECbsHead; + } + else + { + aPrevTopic = iTopics->At( index-1 ).iTopicData.iNumber; + } + + if ( index == iTopics->Count()-1 ) + { + aPosition |= ECbsTail; + } + else + { + aNextTopic = iTopics->At( index+1 ).iTopicData.iNumber; + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::NotifyNewMessageArrivedL +// Notifies each observer that a new message has arrived to a topic. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::NotifyNewMessageArrivedL( + const TCbsDbMessageHandle& aHandle ) + { + // Notify each observer. + TInt count( iObservers->Count() ); + for ( TInt index( 0 ); index < count; index++ ) + { + iObservers->At( index )->TopicNewMessageReceivedIndL( aHandle ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::AppendSubscribedTopicsL +// Adds numbers of subscribed topics to the given array. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::AppendSubscribedTopicsL( + CArrayFixFlat& aSubscriptions ) const + { + TInt count( iTopics->Count() ); + for ( TInt i( 0 ); i < count; i++ ) + { + TCbsDbImpTopic& topic = iTopics->At( i ); + if ( topic.iTopicData.iSubscribed ) + { + aSubscriptions.AppendL( topic.iTopicData.iNumber ); + } + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::LoadRootStreamL +// Loads the root stream to the memory. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::LoadRootStreamL() + { + __ASSERT_DEBUG( iTopicLists != NULL, + CbsServerPanic( ECbsTopicListArrayNull ) ); + + // Get the root stream and open it. + TStreamId id( TopicStoreL()->Root() ); + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), id ); // on CS + + // Load the topic list count + TInt topicListCount( instream.ReadInt16L() ); + + // Sanity check + if ( topicListCount < 0 ) + { + User::Leave( KErrCorrupt ); + } + + // Reset the topic list array + iTopicLists->Reset(); + + // Load the topic list information + TCbsDbImpTopicList item; + instream >> item.iTopicListId; + ReadTopicListInformationL( item.iTopicListId, item ); + iTopicLists->AppendL( item ); + + // Destroy the stream. + CleanupStack::PopAndDestroy(); // instream + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::LoadDefaultTopicStreamL +// Loads the default topic list to the memory. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::LoadDefaultTopicStreamL() + { + __ASSERT_DEBUG( iTopics != NULL, + CbsServerPanic( ECbsTopicListArrayNull ) ); + + // Read root item count + TInt topicListCount = iTopicLists->Count(); + + // If there isn't any, create a topic list for standard topics here + if ( topicListCount == 0 ) + { + CreateStandardTopicListL(); + topicListCount = iTopicLists->Count(); + } + + TStreamId defaultTopicListId( 0 ); + TBool quitSearch( EFalse ); + + TInt i; + // Find the default topic list + for ( i = 0; ( i < topicListCount ) && !quitSearch; ++i ) + { + if ( iTopicLists->At( i ).iIsDefaultTopicList ) + { + defaultTopicListId = iTopicLists->At( i ).iTopicListId; + quitSearch = ETrue; + } + } + + CArrayFixFlat< TStreamId >* topicIds = + new ( ELeave ) CArrayFixFlat< TStreamId >( KTopicIdsGranularity ); + + // Open the default topic list stream + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), defaultTopicListId ); // on CS + + // Set the ID + iCurrentTopicList.iTopicListId = defaultTopicListId; + + // Set the name + HBufC* topicListName = HBufC::NewL( instream, KCbsDbTopicNameLength ); + iCurrentTopicList.iTopicListName.Copy( topicListName->Des() ); + delete topicListName; + topicListName = NULL; + + // Skip default list status, since it is always true in default topic list + CbsStreamHelper::ReadBoolL( instream ); + iCurrentTopicList.iIsDefaultTopicList = ETrue; + + // Set the topic list number + iCurrentTopicList.iNumber = instream.ReadInt16L(); + + // Read the amount of topics + TInt topicCount = instream.ReadInt16L(); + iCurrentTopicList.iTopicCount = topicCount; + + // Clear the topic array. + iTopics->ResizeL( 0 ); + + TStreamId id( 0 ); + + // Load the topic IDs + for ( i = 0; i < topicCount; i++ ) + { + instream >> id; + topicIds->AppendL( id ); + } + + // Destroy the stream. + CleanupStack::PopAndDestroy(); + + // Load necessary information for each topic + TInt count = topicIds->Count(); + TCbsDbImpTopic topic; + for ( i = 0; i < count; i++ ) + { + ReadTopicInformationL( topicIds->At( i ), topic ); + + if ( topic.iTopicData.iNumber == KIndexTopicNumber ) + { + HBufC* indexName = ReadIndexTopicNameLC(); // on CS + topic.iTopicData.iName.Copy( *indexName ); + CleanupStack::PopAndDestroy(); // indexName + } + + iTopics->AppendL( topic ); + } + delete topicIds; + topicIds = NULL; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::ReadTopicListInformationL +// Reads topic list information from stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::ReadTopicListInformationL( + const TStreamId& aId, + TCbsDbImpTopicList& aTopicList ) const + { + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), aId ); // on CS + + // Topic List name + HBufC* topicListName = HBufC::NewL( instream, KCbsDbTopicNameLength ); + aTopicList.iTopicListName.Copy( topicListName->Des() ); + delete topicListName; + topicListName = NULL; + + // Default list status + aTopicList.iIsDefaultTopicList = CbsStreamHelper::ReadBoolL( instream ); + + // Topic List number + aTopicList.iNumber = instream.ReadInt16L(); + + // Topic count of this topic list + aTopicList.iTopicCount = instream.ReadInt16L(); + + CleanupStack::PopAndDestroy(); // instream + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::ReadTopicInformationL +// Reads all information on topic found in stream aId +// into aTopic. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::ReadTopicInformationL( + const TStreamId& aId, + TCbsDbImpTopic& aTopic ) const + { + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), aId ); // on CS + + // Topic ID + aTopic.iTopicId = aId; + + // Read saved messages. + aTopic.iTopicData.iSavedMessages = instream.ReadInt16L(); + + // Read name. + instream >> aTopic.iTopicData.iName; + + // Read topic number (message identifier) + aTopic.iTopicData.iNumber = instream.ReadInt16L(); + + // Read statuses + aTopic.iTopicData.iProtected = CbsStreamHelper::ReadBoolL( instream ); // Protected + aTopic.iTopicData.iSubscribed = CbsStreamHelper::ReadBoolL( instream );// Subscribed + aTopic.iTopicData.iHotmarked = CbsStreamHelper::ReadBoolL( instream ); // Hotmarked + + // Read unread messages count + aTopic.iTopicData.iUnreadMessages = instream.ReadInt16L(); + + // Topic messages' stream ID + instream >> aTopic.iTopicMessagesId; + + // Sanity check + if ( aTopic.iTopicData.iSavedMessages > KCbsDbMaxSavedMessages + || aTopic.iTopicData.iNumber > KCbsMaxValidTopicNumber + || aTopic.iTopicData.iUnreadMessages > KCbsDbMaxReceivedMessages ) + { + User::Leave( KErrCorrupt ); + } + + CleanupStack::PopAndDestroy(); // instream + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DoAddTopicL +// Adds a topic into the database. +// Assumes aTopic is not a duplicate. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DoAddTopicL( + const TCbsDbTopic& aTopic ) + { + // About to write to FFS: make critical level check + CbsUtils::FFSCriticalLevelCheckL( KTopicStreamSize + + KEmptyTopicMessagesStreamSize, iFs ); + + // Generate general information about topic. + TCbsDbImpTopic topic; + topic.iTopicData = aTopic; + + // Write stream for messages. + topic.iTopicMessagesId = + CCbsDbImpTopicMessages::CreateDefaultTopicMessagesStreamL( + *TopicStoreL() ); + + // Create stream for topic information. + RStoreWriteStream outstream; + topic.iTopicId = outstream.CreateLC( *TopicStoreL() ); // on CS + + WriteTopicInformationL( outstream, aTopic, topic.iTopicMessagesId ); + + outstream.CommitL(); + CleanupStack::PopAndDestroy(); // outstream + + // Now, insert the new topic information to the array + TKeyArrayFix key( _FOFF( TCbsDbImpTopic, iTopicData.iNumber ), ECmpTUint16 ); + iTopics->InsertIsqL( topic, key ); + + // Update the topic list stream + UpdateTopicListStreamL( iCurrentTopicList, EFalse ); + + // We have modified only iTopicStore, so if CommitFilesL() leaves, + // we either get all committed or nothing committed. + CommitFilesL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DoUpdateTopicL +// Updates the data for a topic into the database. +// The position of the topic in the list is +// changed if necessary. +// If the position has to be changed, the handles of messages +// contained in the topic have to be changed as well because +// the high word of the handle identifies the topic by it's +// number. +// Please note that standard EPOC DoSomethingL protocol +// is not followed here as this function does NOT commit +// changes made to the store. Instead, the caller must ensure +// use of proper commit/revert operations. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DoUpdateTopicL( + const TCbsDbTopic& aTopic, + TBool aNeedToChange, + TInt aOldPosition, + TBool aDeleting ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::DoUpdateTopicL()"); + + RSharedDataClient sharedData; + // About to write to FFS: make critical level check + if ( aDeleting ) + { + User::LeaveIfError( sharedData.Connect() ); + sharedData.RequestFreeDiskSpaceLC( KTopicStreamSize ); // on CS + } + else + { + CbsUtils::FFSCriticalLevelCheckL( KTopicStreamSize, iFs ); + } + + TInt newPosition( aOldPosition ); + + // If there is need to change position, change it. + if ( aNeedToChange ) + { + TCbsDbImpTopic topic( iTopics->At( aOldPosition ) ); + + // Adjust handles of topic messages to match the changed topic number. + iMessages->UpdateHandlesOfTopicMessagesL( + topic.iTopicData.iNumber, aTopic.iNumber ); + + // Delete topic from the array. + iTopics->Delete( aOldPosition ); + + // Set the number and reinsert into array + topic.iTopicData.iNumber = aTopic.iNumber; + TKeyArrayFix key( _FOFF( TCbsDbImpTopic, iTopicData.iNumber ), ECmpTUint16 ); + newPosition = iTopics->InsertIsqL( topic, key ); + } + + iTopics->At( newPosition ).iTopicData.iSubscribed = aTopic.iSubscribed; + iTopics->At( newPosition ).iTopicData.iHotmarked = aTopic.iHotmarked; + iTopics->At( newPosition ).iTopicData.iName = aTopic.iName; + iTopics->At( newPosition ).iTopicData.iSavedMessages = aTopic.iSavedMessages; + iTopics->At( newPosition ).iTopicData.iUnreadMessages = aTopic.iUnreadMessages; + + // Replace existing stream. + RStoreWriteStream outstream; + outstream.ReplaceLC( *TopicStoreL(), + iTopics->At( newPosition ).iTopicId ); // on CS + WriteTopicInformationL( outstream, aTopic, + iTopics->At( newPosition ).iTopicMessagesId ); + + outstream.CommitL(); + CleanupStack::PopAndDestroy(); // outstream + + // Free the reserved space + if ( aDeleting ) + { + CleanupStack::PopAndDestroy(); // disk space + sharedData.Close(); + } + + // Update topic list stream, if necessary. + if ( aNeedToChange ) + { + UpdateTopicListStreamL( iCurrentTopicList, EFalse ); + } + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::DoUpdateTopicL()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DoDeleteTopicL +// Deletes a topic from the given position in the database. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DoDeleteTopicL( + TInt aPosition ) + { + // Check that the topic is not protected. + TCbsDbTopic topic; + GetTopicL( aPosition, topic ); + + // It is not allowed to delete a topic that is protected. It must + // first be updated to be not protected. + if ( topic.iProtected ) + { + User::Leave( KErrAccessDenied ); + } + + // First delete all messages the topic contains. + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), + iTopics->At( aPosition ).iTopicMessagesId ); // on CS + CCbsDbImpTopicMessages::DeleteAllTopicMessagesL( *TopicStoreL(), + *iUnsavedMessageStore, instream ); + iMessages->InvalidateCache(); + CleanupStack::PopAndDestroy(); // instream + + // Delete topic and topic messages streams. + TopicStoreL()->DeleteL( iTopics->At( aPosition ).iTopicMessagesId ); + TopicStoreL()->DeleteL( iTopics->At( aPosition ).iTopicId ); + + // Remove from the internal cache. + iTopics->Delete( aPosition ); + + iCurrentTopicList.iTopicCount--; + UpdateTopicListStreamL( iCurrentTopicList, ETrue ); + CommitFilesL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateRootStreamL +// Updates the root stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateRootStreamL( + TBool aDeleting ) + { + // Check the free space + TInt neededSpace( KTopicListRootStreamSize + iTopicLists->Count() * 2 ); + + RSharedDataClient sharedData; + // About to write to FFS: make critical level check + if ( aDeleting ) + { + User::LeaveIfError( sharedData.Connect() ); + sharedData.RequestFreeDiskSpaceLC( neededSpace ); // on CS + } + else + { + CbsUtils::FFSCriticalLevelCheckL( neededSpace, iFs ); + } + + // Now there is room for all topics. So we can just replace. + RStoreWriteStream outstream; + outstream.ReplaceLC( *TopicStoreL(), TopicStoreL()->Root() ); // on CS + + // Write root stream + WriteRootStreamL( outstream ); + CleanupStack::PopAndDestroy(); // outstream + + // Free the reserved space + if ( aDeleting ) + { + CleanupStack::PopAndDestroy(); // disk space + sharedData.Close(); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicListStreamL +// Updates topic list stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicListStreamL( + TCbsDbImpTopicList& aTopicList, + TBool aDeleting ) + { + TInt neededSpace( KTopicListStreamSize + iTopics->Count() * 2 ); + + RSharedDataClient sharedData; + // About to write to FFS: make critical level check + if ( aDeleting ) + { + User::LeaveIfError( sharedData.Connect() ); + sharedData.RequestFreeDiskSpaceLC( neededSpace ); // on CS + } + else + { + CbsUtils::FFSCriticalLevelCheckL( neededSpace, iFs ); + } + + // Replace the stream + RStoreWriteStream outstream; + outstream.ReplaceLC( *TopicStoreL(), aTopicList.iTopicListId ); // on CS + + // Write root stream + WriteTopicListStreamL( outstream, aTopicList ); + CleanupStack::PopAndDestroy(); // outstream + + // Free the reserved space + if ( aDeleting ) + { + CleanupStack::PopAndDestroy(); // disk space + sharedData.Close(); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicL +// Updates the information for a topic already existing +// in the database. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicL( + TCbsDbTopicNumber aTopicNumber, + TCbsDbTopic& aTopic ) + { + // Check that the new topic number is unique. + TBool needToChangePosition( ETrue ); + + // Try to find the topic and leave if it was not found. + TInt oldPosition( TopicIndexInList( aTopicNumber ) ); + User::LeaveIfError( oldPosition ); + + // Check if we have to change the position of the topic in the + // internal array + if ( aTopic.iNumber == aTopicNumber ) + { + needToChangePosition = EFalse; + } + else + { + TInt topicWithTheNewNumber( TopicIndexInList( aTopic.iNumber ) ); + if ( topicWithTheNewNumber != KErrNotFound && + topicWithTheNewNumber != oldPosition ) + { + User::Leave( KErrAlreadyExists ); + } + } + + // Write data to store. + // Deviation from EPOC standards: DoUpdateTopicL does NOT commit + // the store. + TRAPD( result, DoUpdateTopicL( aTopic, needToChangePosition, + oldPosition, EFalse ) ); + + // Commit both topic and unsaved msgs store + if ( result == KErrNone ) + { + TRAP( result, CommitFilesL() ); + } + + // If either DoUpdateTopicL or CommitFilesL fails => revert. + if ( result != KErrNone ) + { + TopicStoreL()->Revert(); + ReloadRootStreamL(); + User::Leave( result ); + } + else + { + // Notify the observers. + NotifyTopicModifiedL( aTopicNumber ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CheckTopicNumber +// Checks if a topic number is valid. +// The valid topic number range in this implementation is +// 000..999, with 000 indicating an index message. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCbsDbImpTopicList::CheckTopicNumber( + TCbsDbTopicNumber aNumber ) const + { + // Check that the number is in proper range + return aNumber <= KCbsMaxValidTopicNumber; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::WriteRootStreamL +// Write the root stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::WriteRootStreamL( + RWriteStream& aOut ) const + { + // Write the total amount of topic lists + TInt topicListCount( iTopicLists->Count() ); + aOut.WriteInt16L( topicListCount ); + + // Write space for topic list stream ids + TInt index; + for ( index = 0; index < topicListCount; index++ ) + { + aOut << iTopicLists->At( index ).iTopicListId; + } + + // Write null stream ids + for ( ; index < KCbsRootItemsSize; index++ ) + { + aOut << TStreamId( 0 ); + } + + aOut.CommitL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::WriteTopicListStreamL +// Writes topic list information to specified stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::WriteTopicListStreamL( + RWriteStream& aOut, + TCbsDbImpTopicList& aTopicList ) const + { + // Write the values to this topic list's stream + + // Topic List name + aOut << aTopicList.iTopicListName; + + // Is this the default list + CbsStreamHelper::WriteBoolL( aOut, aTopicList.iIsDefaultTopicList ); + + // Topic List number + aOut.WriteInt16L( aTopicList.iNumber ); + + // NUmber of topics in this list + aOut.WriteInt16L( iTopics->Count() ); + + // Write the stream IDs of the topics belonging to this list + TInt i; + for ( i = 0; i < iTopics->Count(); i++ ) + { + aOut << iTopics->At( i ).iTopicId; + } + + // Write space for the rest topic stream IDs + for ( ; i < KCbsDbTopicArraySize; i++ ) + { + aOut << TStreamId( 0 ); + } + + aOut.CommitL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::NotifyTopicListInitializedL +// Notifies each observer that the topic list has been initialized. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::NotifyTopicListInitializedL() + { + // Notify each observer. + TInt count( iObservers->Count() ); + for( TInt index( 0 ); index < count; index++ ) + { + iObservers->At( index )->TopicListInitializedIndL(); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::NotifyTopicAddedL +// Notifies each observer that a topic has been added. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::NotifyTopicAddedL( + TCbsDbTopicNumber aNumber ) + { + // Notify each observer. + TInt count( iObservers->Count() ); + for ( TInt index( 0 ); index < count; index++ ) + { + iObservers->At( index )->TopicAddedIndL( aNumber ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::NotifyTopicModifiedL +// Notifies each observer that a topic has been modified. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::NotifyTopicModifiedL( + TCbsDbTopicNumber aNumber ) + { + // Notify each observer. + TInt count( iObservers->Count() ); + for( TInt index( 0 ); index < count; index++ ) + { + iObservers->At( index )->TopicModifiedIndL( aNumber ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::NotifyTopicDeletedL +// Notifies each observer that a topic has been deleted. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::NotifyTopicDeletedL( + TCbsDbTopicNumber aNumber ) + { + // Notify each observer. + TInt count( iObservers->Count() ); + for( TInt index( 0 ); index < count; index++ ) + { + iObservers->At( index )->TopicDeletedIndL( aNumber ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::SetHotmarkedMessage +// Sets the hotmarked message handle. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::SetHotmarkedMessage( + const TCbsDbMessageHandle& aMessageHandle ) + { + // Set the hotmarked message. + iIsHotmarkedMessage = ETrue; + iMessageHandle = aMessageHandle; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CreateDefaultRootStreamL +// Creates a root stream and writes default values into it. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TStreamId CCbsDbImpTopicList::CreateDefaultRootStreamL( + CStreamStore& aStore ) const + { + // Create the stream + RStoreWriteStream outstream; + TStreamId id( outstream.CreateLC( aStore ) ); // on CS + + // Write the amount of topic lists + outstream.WriteInt16L( 0 ); + + // Write space for topic list stream ids + for ( TInt index( 0 ); index < KCbsRootItemsSize; index++ ) + { + outstream << TStreamId( 0 ); + } + + outstream.CommitL(); + CleanupStack::PopAndDestroy(); // aStore + + // Return the stream id. + return id; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::WriteTopicInformationL +// Writes topic data into a stream. +// This includes number of saved messages, +// number of unread messages, topic handle, +// topic name, topic number, protection status, +// subscription status and hotmark status. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::WriteTopicInformationL( + RWriteStream& aOut, + const TCbsDbTopic& aTopic, + const TStreamId& aTopicMessagesId ) const + { + // Write saved messages. + aOut.WriteInt16L( aTopic.iSavedMessages ); + + // Write name. + aOut << aTopic.iName; + + // Write topic number (message identifier) + aOut.WriteInt16L( aTopic.iNumber ); + + // Write statuses + CbsStreamHelper::WriteBoolL( aOut, aTopic.iProtected ); // Protected + CbsStreamHelper::WriteBoolL( aOut, aTopic.iSubscribed ); // Subscribed + CbsStreamHelper::WriteBoolL( aOut, aTopic.iHotmarked ); // Hotmarked + + // Write unread messages count + aOut.WriteInt16L( aTopic.iUnreadMessages ); + + // And then write the topic messages stream id. + aOut << aTopicMessagesId; + } + + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::RevertFilesL +// Reverts all changes made to three datafiles handled by this class. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::RevertFilesL() + { + + // Check for file lock + CheckFileLockL(); + + iTopicStore->Revert(); + iUnsavedMessageStore->Revert(); + ReloadRootStreamL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CommitFilesL +// Commits all changes made to two datafiles handled by this class. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::CommitFilesL() + { + TopicStoreL()->CommitL(); + TInt errorCode( iUnsavedMessageStore->Commit() ); + + // If committing of the unsaved msg store fails, remove all + // messages in the store and rebuild the topic list + if ( errorCode != KErrNone ) + { + TRAPD( errorCode2, RebuildUnsavedMessageStoreL() ); + if ( errorCode2 != KErrNone ) + { + CActiveScheduler::Stop(); + User::Leave( KErrServerTerminated ); + } + + // Tell the caller that something went wrong + User::Leave( errorCode ); + } + + // Check if we should compact + TTime now; + TTimeIntervalMinutes interval; + now.UniversalTime(); + now.MinutesFrom( iPreviousCompact, interval ); + if ( interval.Int() >= KMinimumCompactInterval ) + { + TopicStoreL()->CompactL(); + iUnsavedMessageStore->CompactL(); + iPreviousCompact = now; + } + + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::RebuildUnsavedMessageStoreL +// Deletes the unsaved messages' store and rebuilds it. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::RebuildUnsavedMessageStoreL() + { + // Close the stores and delete the unsaved store + delete iUnsavedMessageStore; iUnsavedMessageStore = NULL; + delete iTopicStore; iTopicStore = NULL; + CbsUtils::DeleteFileL( iFs, *iUnsavedMessagesFilename ); + + // Re-create the store + DoCreateStoreL( *iUnsavedMessagesFilename ); + TryToOpenFilesL( ETrue, EFalse ); + + // Remove the stream ids to unsaved messages, because + // they were all just deleted. + HandleDeletionOfUnsavedMessagesFileL(); + + // Compact the topic list + TopicStoreL()->CommitL(); + TopicStoreL()->CompactL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::RebuildTopicAndUnsavedStoresL +// Deletes and rebuilds topic/topic list and unsaved message stores. +// Loads Standard Topic List into memory. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::RebuildTopicAndUnsavedStoresL() + { + __TEST_INVARIANT; + + // Check for file lock + CheckFileLockL(); + + // About to write to FFS: make critical level check + CbsUtils::FFSCriticalLevelCheckL( KTopicListRootStreamSize + + KDefaultTopicListSize * 2, iFs ); + + if ( iMessages != NULL && iMessages->IsLockedMessages() ) + { + User::Leave( KErrAccessDenied ); + } + + delete iTopicStore; + iTopicStore = NULL; + delete iUnsavedMessageStore; + iUnsavedMessageStore = NULL; + CbsUtils::DeleteFileL( iFs, *iTopicsFilename ); + CbsUtils::DeleteFileL( iFs, *iUnsavedMessagesFilename ); + + // Create new files. + OpenFilesL( ETrue, EFalse ); + + // Add standard index topic. + AddIndexTopicL(); + + // Load the Standard Topic list + LoadDefaultTopicStreamL(); + + iIsHotmarkedMessage = EFalse; + iLastTopicNumber = 0; + iMessageHandle = 0; + + // Inform the message manager. + if ( iMessages ) + { + iMessages->InvalidateCache(); + } + + // Notify everyone. + NotifyTopicListInitializedL(); + + __TEST_INVARIANT; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::GetLatestTopicNumber +// Returns the number of the topic that was added last +// to the database by topic detection feature. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCbsDbImpTopicList::GetLatestTopicNumber( + TCbsTopicNumber& aNumber ) const + { + TInt result( KErrNone ); + if ( iLastTopicNumber == 0 ) + { + result = KErrNotFound; + } + aNumber = iLastTopicNumber; + return result; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::OpenFilesL +// After a call to this function, the file stores can be assumed +// to be open and initialized. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::OpenFilesL( + TBool aDeleteExistingFiles, + TBool aCreateNewTopicList ) + { + __ASSERT_DEBUG( iTopicsFilename->Length() > 0, + CbsServerPanic( ECbsInvalidFilenameDescriptor ) ); + __ASSERT_DEBUG( iUnsavedMessagesFilename->Length() > 0, + CbsServerPanic( ECbsInvalidFilenameDescriptor ) ); + + // if LFS, delete files, create files, open files, write root stream + // if not LFS, create file if it doesn't exist, open files + + // Close file stores. + delete iTopicStore; + iTopicStore = NULL; + delete iUnsavedMessageStore; + iUnsavedMessageStore = NULL; + + // If any of files doesn't exist, create the file. Also writes the root + // stream of the topic file, if necessary. + // It is possible that this operation fails because FFS is full. + // In this case the server will take a break and retry. If the second + // attempt fails, the server is shut down. + TBool unsavedMsgFileExists( EFalse ); + TRAPD( result, CreateFilesIfNecessaryL( unsavedMsgFileExists ) ); + if ( result != KErrNone ) + { + // Critical exception: wait for a while and retry. + User::After( KWaitAfterCriticalStoreException ); + TBool ignoreThis( EFalse ); // value of unsavedMsgFileExists preserved + TRAP( result, CreateFilesIfNecessaryL( ignoreThis ) ); + if ( result != KErrNone ) + { + __DEBUGGER(); + // Recovery is not possible: shut the server down. + CActiveScheduler::Stop(); + User::Leave( KErrServerTerminated ); + } + } + + // Open the files for use. Also reads the topic file root stream. + TryToOpenFilesL( unsavedMsgFileExists == EFalse && + aDeleteExistingFiles == EFalse, + aCreateNewTopicList ); + + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CreateFilesIfNecessaryL +// Creates CBS files, if appropriate. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::CreateFilesIfNecessaryL( + TBool& aUnsavedMsgFileExisted ) + { + if ( CbsUtils::ExistsL( iFs, *iTopicsFilename ) == EFalse ) + { + CbsUtils::FFSCriticalLevelCheckL( KTopicListRootStreamSize + + KDefaultTopicListSize * 2, iFs ); + + CPermanentFileStore* store = CPermanentFileStore::CreateLC( iFs, + *iTopicsFilename, EFileWrite ); // on CS + + store->SetTypeL( store->Layout() ); + TStreamId id( CreateDefaultRootStreamL( *store ) ); + store->SetRootL( id ); + store->CommitL(); + CleanupStack::PopAndDestroy(); // store + } + + aUnsavedMsgFileExisted = CbsUtils::ExistsL( iFs, + *iUnsavedMessagesFilename ); + if ( aUnsavedMsgFileExisted == EFalse ) + { + // No critical level check, because unsaved msgs reside on ramdisk + DoCreateStoreL( *iUnsavedMessagesFilename ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DoCreateStoreL +// Creates an empty file store with the given filename. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DoCreateStoreL( + const TDesC& aFilename ) + { + CFileStore* store = CPermanentFileStore::CreateLC( iFs, + aFilename, EFileWrite ); // on CS + store->SetTypeL( store->Layout() ); + store->CommitL(); + CleanupStack::PopAndDestroy(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::TryToOpenFilesL +// Tries to open topic and unsaved messages files. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::TryToOpenFilesL( + TBool aDeleteUnsavedMsgStreamIds, + TBool aCreateNewTopicList ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::TryToOpenFilesL()" ); + + if ( !iTopicStore ) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::TryToOpenFilesL(): *** NO iTopicStore (1) ***" ); + } + + // Try to open the store + TRAPD( error, iTopicStore = CFileStore::OpenL( iFs, *iTopicsFilename, EFileRead | EFileWrite ) ); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::TryToOpenFilesL(); iTopicStore OpenL() error: %d", error ); + + TRAPD( error2, iUnsavedMessageStore = CFileStore::OpenL( iFs, *iUnsavedMessagesFilename, EFileRead | EFileWrite ) ); + CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicList::TryToOpenFilesL(); iUnsavedMessageStore OpenL() error: %d", error2 ); + + if ( error || error2 ) + { + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::TryToOpenFilesL(): Calling InitializeListL( ETrue )..." ); + + InitializeListL( ETrue ); + + CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicList::TryToOpenFilesL(): Calling InitializeListL( ETrue ) finished." ); + } + else + { + if ( iTopicLists->Count() == 0 && + aDeleteUnsavedMsgStreamIds && + aCreateNewTopicList ) + { + // Create first topic list, since it was deleted with the file + CreateStandardTopicListL(); + } + + // Load the root stream for topic store. + LoadRootStreamL(); + + if ( aDeleteUnsavedMsgStreamIds ) + { + // Load the topics and repair the topic file + // since unsaved msg file has been deleted + LoadDefaultTopicStreamL(); + HandleDeletionOfUnsavedMessagesFileL(); + } + } + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::TryToOpenFilesL()" ); + } + +// --------------------------------------------------------- +// CCbsDbImpTopicList::ReadIndexTopicNameLC() +// Reads the localized index topic name +// (other items were commented in a header). +// --------------------------------------------------------- +HBufC* CCbsDbImpTopicList::ReadIndexTopicNameLC() + { + // Open localized resource file. + RResourceFile resourceFile; + + CbsUtils::FindAndOpenDefaultResourceFileLC( iFs, resourceFile ); // on CS + // Read "Index"-string. + TResourceReader reader; + reader.SetBuffer( resourceFile.AllocReadLC( R_TEXT_INDEX_TOPIC ) );//on CS + HBufC* text = reader.ReadHBufCL(); + CleanupStack::PopAndDestroy(2); // readerBuf, resourceFile + CleanupStack::PushL( text ); + + return text; + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::AddIndexTopicL +// Adds index topic to topic list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::AddIndexTopicL() + { + // Open localized resource file. + HBufC* text = ReadIndexTopicNameLC(); + + TCbsDbTopic topic; + topic.iName.Copy( *text ); + topic.iNumber = 0; + topic.iHotmarked = EFalse; + topic.iProtected = ETrue; + topic.iSubscribed = ETrue; + topic.iSavedMessages = 0; + topic.iUnreadMessages = 0; + + // Add the topic without notifying anybody. + TRAPD( error, DoAddTopicL( topic ) ); + if ( error != KErrNone ) + { + RevertFilesL(); + __TEST_INVARIANT; + User::Leave( error ); + } + + CleanupStack::PopAndDestroy(); // text + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::HandleDeletionOfUnsavedMessagesFileL +// Called to repair the database when Unsaved messages -file +// has been deleted, but Topics-file still contains information +// on unsaved messages. +// +// Things to do here: +// - Reset unread message counters, because a saved message +// is always also read. +// - Remove stream ids to unsaved messages from +// Topic messages -streams. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::HandleDeletionOfUnsavedMessagesFileL() + { + TRAPD( result, DoHandleDeletionOfUnsavedMessagesFileL() ); + if ( result != KErrNone ) + { + // Recovery impossible -> reset the database. + // Note that this function is not called when the database + // is initialized. + RebuildTopicAndUnsavedStoresL(); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DoHandleDeletionOfUnsavedMessagesFileL +// Does the work for HandleDeletionOfUnsavedMessagesFileL(). +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DoHandleDeletionOfUnsavedMessagesFileL() + { + // Reset the unread counter of each topic and delete stream ids of + // unsaved messages. + TInt numberOfTopics( iTopics->Count() ); + for ( TInt i( 0 ); i < numberOfTopics; i++ ) + { + TCbsDbTopic topic; + FindTopicByNumberL( iTopics->At( i ).iTopicData.iNumber, topic ); + topic.iUnreadMessages = 0; + DoUpdateTopicL( topic, EFalse, i, EFalse ); + DeleteUnsavedMessageStreamIdsL( iTopics->At( i ).iTopicMessagesId ); + } + + UpdateTopicListStreamL( iCurrentTopicList, ETrue ); + TopicStoreL()->CommitL(); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::DeleteUnsavedMessageStreamIdsL +// This function is called when the unsaved messages file has +// been deleted but the Topic messages -stream still contains +// stream ids to deleted message streams. +// All stream ids found in the given topic messages -stream +// pointing to the unsaved messages file are removed. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::DeleteUnsavedMessageStreamIdsL( + const TStreamId& aMsgStreamId ) const + { + CArrayFixFlat< TStreamId >* streamIds = + new ( ELeave ) CArrayFixFlat< TStreamId >( KTypicalNumberOfTopicMessages ); + CleanupStack::PushL( streamIds ); + + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), aMsgStreamId ); // on CS + + // Read msg count + TInt numberOfMsgs( instream.ReadInt16L() ); + + // Read max msg count + TInt maxNumberOfMsgs( instream.ReadInt16L() ); + + TInt index( 0 ); + + for ( ; index < numberOfMsgs; index++ ) + { + TBool saved( CbsStreamHelper::ReadBoolL( instream ) ); + TStreamId id( 0 ); + instream >> id; + if ( saved ) + { + streamIds->AppendL( id ); + } + } + + CleanupStack::PopAndDestroy(); // instream + + // Write stream ids of saved messages into the new topic message stream + // which replaces the stream identified with aMsgStreamId. + RStoreWriteStream outstream; + outstream.ReplaceLC( *TopicStoreL(), aMsgStreamId ); // on CS + + TInt numberOfSavedmessages( streamIds->Count() ); + + // Number of messages = number of saved messages + outstream.WriteInt16L( numberOfSavedmessages ); + + // Number of saved messages <= number of messages <= maxNumberOfMsgs + outstream.WriteInt16L( maxNumberOfMsgs ); + + for ( index = 0; index < numberOfSavedmessages; index++ ) + { + // All messages are saved (i.e., permanent) + CbsStreamHelper::WriteBoolL( outstream, ETrue ); + outstream << streamIds->At( index ); + } + + for ( ; index < maxNumberOfMsgs; index ++ ) + { + CbsStreamHelper::WriteBoolL( outstream, EFalse ); + outstream << TStreamId( 0 ); + } + + outstream.CommitL(); + CleanupStack::PopAndDestroy(2); // outstream, streamIds + + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicCountersL +// Resolves the topic position in topic list and uses this information +// to call DoUpdateTopicL. +// Changes to stores are NOT commited. +// Call to DoUpdateTopicL is not trapped. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicCountersL( + const TCbsDbTopic& aTopic, + const TBool aDeleting ) + { + CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicList::UpdateTopicCountersL()"); + + // Find out the position of the topic in topic list. + TInt index( TopicIndexInList( aTopic.iNumber ) ); + if ( index == KErrNotFound ) + { + User::Leave( KErrNotFound ); + } + + // DoUpdateTopicL leaves changes uncommited. EFalse for not having to + // change topic position in topic list. + DoUpdateTopicL( aTopic, EFalse, index, aDeleting ); + + CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicList::UpdateTopicCountersL()"); + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::CreateStandardTopicListL +// Creates the Standard topic list (topic list no. 0) +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::CreateStandardTopicListL() + { + // Read standard topic list name from the resource file + RResourceFile resourceFile; + CbsUtils::FindAndOpenDefaultResourceFileLC( iFs, resourceFile ); // on CS + + TResourceReader reader; + reader.SetBuffer( resourceFile.AllocReadLC( R_TEXT_STANDARD_TOPIC_LIST ) ); // on CS + HBufC* standardTopicListName = reader.ReadHBufCL(); + CleanupStack::PushL( standardTopicListName ); + + // Create a new topic list + CreateNewTopicListL( standardTopicListName->Des() ); + + CleanupStack::PopAndDestroy( 3 ); // resourceFile, reader, standardTopicListName + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::LoadTopicsIdsL +// Reads topic IDs of a topic list from the stream. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::LoadTopicsIdsL( + const TStreamId& aTopicListStreamId ) + { + // Open the topic list stream + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), aTopicListStreamId ); // on CS + + // Skip topic list name + delete HBufC::NewL( instream, KCbsDbTopicNameLength ); + + // Skip default list status + CbsStreamHelper::ReadBoolL( instream ); + + // Skip Topic List number + instream.ReadInt16L(); + + // Skip the amount of topics + TInt topicCount = instream.ReadInt16L(); + + // Clear the array + iTopicIds->ResizeL( 0 ); + + TStreamId id( 0 ); + + // Load the topic IDs + for ( TInt i = 0; i < topicCount; i++ ) + { + instream >> id; + iTopicIds->AppendL( id ); + } + + CleanupStack::PopAndDestroy(); // instream + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::LoadTopicsL +// Loads topics of a specified topic list. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::LoadTopicsL( + const TStreamId& aTopicListStreamId ) + { + // Open the topic list stream + RStoreReadStream instream; + instream.OpenLC( *TopicStoreL(), aTopicListStreamId ); // on CS + + // Skip topic list name + delete HBufC::NewL( instream, KCbsDbTopicNameLength ); + + // Skip default list status + CbsStreamHelper::ReadBoolL( instream ); + + // Skip Topic List number + instream.ReadInt16L(); + + // Skip the amount of topics + TInt topicCount = instream.ReadInt16L(); + + // Clear the arrays + iTopics->ResizeL( 0 ); + iTopicIds->ResizeL( 0 ); + + TStreamId id( 0 ); + + // Load the topic IDs + TInt i; + for ( i = 0; i < topicCount; i++ ) + { + instream >> id; + iTopicIds->AppendL( id ); + } + + CleanupStack::PopAndDestroy(); // instream + + // Load necessary information for each topic + TInt count = iTopicIds->Count(); + TCbsDbImpTopic topic; + for ( i = 0; i < count; i++ ) + { + ReadTopicInformationL( iTopicIds->At( i ), topic ); + + if ( topic.iTopicData.iNumber == KIndexTopicNumber ) + { + HBufC* indexName = ReadIndexTopicNameLC(); // on CS + topic.iTopicData.iName.Copy( *indexName ); + CleanupStack::PopAndDestroy(); // indexName + } + iTopics->AppendL( topic ); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::UpdateTopicStreamIdsL +// Updates the topic stream IDs. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::UpdateTopicStreamIdsL( + const TCbsDbTopicNumber aTopicNumber ) + { + // Try to find the topic from current topic list + TInt position( TopicIndexInList( aTopicNumber ) ); + if ( position >= 0 ) + { + // Remove from the internal cache of this topic list + iTopics->Delete( position ); + + iCurrentTopicList.iTopicCount--; + + // Update the stream + UpdateTopicListStreamL( iCurrentTopicList, EFalse ); + CommitFilesL(); + } + } + +// ----------------------------------------------------------------------------- +// CCbsDbImpTopicList::__DbgTestInvariant +// Checks that the object is in a valid state, and panics if it is not. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCbsDbImpTopicList::__DbgTestInvariant() const + { + /* +#if defined(_DEBUG) +#endif + */ + } + +// ========================== OTHER EXPORTED FUNCTIONS ========================= + +// End of File