/*
* 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 <e32base.h>
#include <e32svr.h>
#include <f32file.h>
#include <shareddataclient.h> // RequestFreeDiskSpaceLC
#include <barsc.h> // Resource access
#include <barsread.h> // Resource access
#include <CbsServer.rsg>
#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 <centralrepository.h> // 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<TUint16>& 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