cbs/CbsServer/ServerSrc/CCbsDbImpTopicMessages.cpp
changeset 0 ff3b6d0fd310
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cbs/CbsServer/ServerSrc/CCbsDbImpTopicMessages.cpp	Tue Feb 02 01:11:09 2010 +0200
@@ -0,0 +1,1720 @@
+/*
+* 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 module contains the implementation of CCbsDbImpTopicMessages class 
+*                member functions. 
+*
+*/
+
+
+
+// INCLUDE FILES
+
+#include <e32svr.h>
+
+#include <shareddataclient.h>
+
+#include "CbsServerPanic.h"
+#include "CbsStreamHelper.h"
+#include "CbsUtils.h"
+#include "CbsDbConstants.h"
+#include "CCbsDbImpTopicList.h"
+#include "CCbsDbImpTopicMessages.h"
+#include "CCbsRecEtel.h"
+#include "CCbsReceiverHelper.h"
+
+#include "CbsLogger.h"
+
+// CONSTANTS
+
+// Expected maximum number of locked messages at once. 
+const TInt KDefaultSpaceForLockedMessages = 1;
+
+// CB message header size in bytes.
+const TInt KCbMessageHeaderSize = 16;
+
+// The space used for the header of messages stream
+const TInt KMessageRootStreamSize = 6;
+
+/// The space used by one message in the messages stream
+const TInt KMessageEntrySize = 3;
+
+// Default space reserved for topic messages in message stream
+const TInt KDefaultSpaceForMessages = 6;
+
+// Space for reading messages
+const TInt KReadMessageSize = 92;
+
+
+// ================= MEMBER FUNCTIONS =======================
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::CCbsDbImpTopicMessages
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CCbsDbImpTopicMessages::CCbsDbImpTopicMessages( 
+    CCbsDbImpTopicList& aTopicList, 
+    RFs& aFs )
+    :iTopicList( aTopicList ), 
+    iCacheValid( EFalse ), 
+    iCachedTopicNumber( 0 ), 
+    iFs( aFs )
+    {
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::ConstructL()
+    {
+    // Allocate the default size for the message list.
+    iMessageList = 
+        new ( ELeave ) CArrayFixFlat< TCbsDbImpTopicMessagesCacheItem > 
+            ( KCbsDbMaxReceivedMessages + KCbsDbMaxSavedMessages );
+
+    // Allocate the array for locked messages.
+    iLockedMessages = new ( ELeave ) CArrayFixFlat< TCbsDbMessageHandle >
+        ( KDefaultSpaceForLockedMessages );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CCbsDbImpTopicMessages* CCbsDbImpTopicMessages::NewL( 
+    CCbsDbImpTopicList& aTopicList, RFs& aFs )
+    {
+    CCbsDbImpTopicMessages* self = 
+        new ( ELeave ) CCbsDbImpTopicMessages( aTopicList, aFs );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop();
+    return self;
+    }
+
+    
+// Destructor
+CCbsDbImpTopicMessages::~CCbsDbImpTopicMessages()
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::~CCbsDbImpTopicMessages()");
+    // Invalidate cache.
+    InvalidateCache();
+
+    // And then delete the message list.
+    delete iMessageList;
+    
+    // Delete locked messages.
+    delete iLockedMessages; 
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::~CCbsDbImpTopicMessages()");   
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::InvalidateCache
+// Resets the cache to default values.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::InvalidateCache()
+    {    
+    // Initialize members back to default.
+    iCacheValid = EFalse;
+    iCachedTopicNumber = 0;
+        
+    // Resize the message list. After the call is made, the
+    // array is empty.
+    if ( iMessageList )
+        {
+        iMessageList->Reset();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::InvalidateCacheIfTopic
+// Resets the cache to default values.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::InvalidateCacheIfTopic( 
+    const TCbsDbTopicNumber& aNumber )
+    {
+    // Check the handle and then invalidate
+    if ( iCachedTopicNumber == aNumber )
+        {
+        InvalidateCache();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::CreateDefaultTopicMessagesStreamL
+// Creates a default topic messages stream.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TStreamId CCbsDbImpTopicMessages::CreateDefaultTopicMessagesStreamL(
+    CStreamStore& aStore )
+    {
+    // Create the stream.
+    RStoreWriteStream outstream;
+    TStreamId id = outstream.CreateLC( aStore ); // on CS
+
+    // Write total amount of messages.
+    outstream.WriteInt16L( 0 );
+
+    // Write total amount of space for messages.
+    outstream.WriteInt16L( KDefaultSpaceForMessages );
+
+    for ( TInt index( 0 ); index < KDefaultSpaceForMessages; index++ )
+        {
+        // "Saved"-flag
+        CbsStreamHelper::WriteBoolL( outstream, EFalse );
+        // Write message stream id.
+        outstream << TStreamId( 0 );
+        }
+
+    outstream.CommitL();
+    CleanupStack::PopAndDestroy();  // outstream
+
+    return id;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::CreateDefaultTopicMessagesStreamL
+// Delete all topics messages.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DeleteAllTopicMessagesL( 
+    CStreamStore& aSavedStore,
+    CStreamStore& aUnsavedStore,
+    RReadStream& aIn )
+    {
+    // Read total amount of messages.
+    TInt total( aIn.ReadInt16L() );
+    
+    // Skip space for messages.
+    aIn.ReadInt16L();
+
+    // Now, delete all streams..
+    for ( TInt index( 0 ); index < total; index++ )
+        {
+
+        TBool isSaved( CbsStreamHelper::ReadBoolL( aIn ) );
+        TStreamId id;
+
+        // Read stream id.
+        aIn >> id;
+        CStreamStore& store = isSaved ? aSavedStore : aUnsavedStore;
+        store.DeleteL( id );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::IsLockedMessagesInTopic
+// Determines whether or not there are any locked messages in 
+// the topic.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCbsDbImpTopicMessages::IsLockedMessagesInTopic( 
+    const TCbsDbTopicNumber& aNumber ) const
+    {
+    TBool result( EFalse );
+
+    // Checks whether or not there is a locked message in the topic.
+    TInt count( iLockedMessages->Count() );
+    for ( TInt index( 0 ); index < count; index++ )
+        {
+        if ( iTopicList.ExtractTopicNumber( 
+            iLockedMessages->At( index ) ) == aNumber )
+            {
+            result = ETrue;
+            }
+        }    
+    return result;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::IsLockedMessages
+// Determines whether there is at least one locked message.
+// Returns ETrue, if there are any locked messages in the 
+// database.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCbsDbImpTopicMessages::IsLockedMessages() const
+    {
+    return ( iLockedMessages->Count() > 0 );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::GetMessageCountL
+// Returns the total amount of messages the topic contains.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::GetMessageCountL( 
+    const TCbsDbTopicNumber& aNumber, 
+    TInt& aCount )
+    {
+    // Load cache.
+    LoadCacheL( aNumber );
+
+    // and then get the message count.
+    aCount = iMessageList->Count();
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::GetMessageL
+// Returns message information.
+// Returns the message matching the given
+// index value. Leaves if the index is not valid.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::GetMessageL( 
+    const TCbsDbTopicNumber& aNumber, 
+    TInt aIndex, 
+    TCbsDbMessage& aMessage )
+    {
+    // Load the cache.
+    LoadCacheL( aNumber );
+
+    // Check if the index is not valid.
+    if ( ( aIndex < 0 ) || ( aIndex >= iMessageList->Count() ) )
+        {
+        User::Leave( KErrNotFound );
+        }
+
+    // Load the message.
+    LoadMessageL( aIndex, aMessage );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::FindMessageByHandleL
+// Finds the message for given handle and returns it.
+// Returns the message matching the given
+// index value.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::FindMessageByHandleL( 
+    const TCbsDbMessageHandle& aHandle, 
+    TCbsDbMessage& aMessage )
+    {
+    // Load cache
+    LoadCacheL( iTopicList.ExtractTopicNumber( aHandle ) );
+
+    // Load message
+    LoadMessageL( FindMessagePositionByHandleL( aHandle ), aMessage );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::GetMessageContentsL
+// Returns the contents of a message.
+// Stores the contents of the message to the buffer aContents. 
+// If aContents is too small to contain the whole message
+// body, the message body is truncated.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::GetMessageContentsL( 
+    const TCbsDbMessageHandle& aHandle, 
+    TPtr& aContents,
+    TUint aSize )
+    {
+    LoadCacheL( iTopicList.ExtractTopicNumber( aHandle ) );
+
+    TInt position( FindMessagePositionByHandleL( aHandle ) );
+
+    TCbsDbMessage message;
+    LoadMessageL( position, message );
+
+    LoadMessageContentsL( iMessageList->At( position ).iContentsId, 
+                          aContents, message.iPermanent, aSize );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DeleteMessageL
+// Deletes a message. Leaves if the message is protected
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DeleteMessageL( 
+    const TCbsDbMessageHandle& aHandle,
+    const CCbsRecEtel& aReceiver )
+    {
+    // Get the topic handle.
+    LoadCacheL( iTopicList.ExtractTopicNumber( aHandle ) );
+
+    // Check if the message is locked.
+    if ( IsMessageLocked( aHandle ) )
+        {
+        // Yes, leave.
+        User::Leave( KErrAccessDenied );
+        }
+    else
+        {
+        // Otherwise, delete.
+        TRAPD( error, DoDeleteMessageL( 
+            FindMessagePositionByHandleL( aHandle ) ) );
+
+        if ( error != KErrNone )
+            {
+            // Failed.
+            RevertFileOperationL( error );
+            }
+        else
+            {
+            // Update the soft notification dialog, if topic is hotmarked
+            UpdateSoftNotificationL( aHandle, aReceiver );
+            }
+        }
+
+    iTopicList.NotifyTopicModifiedL( 
+        iTopicList.ExtractTopicNumber( aHandle ) );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::AddMessageL
+// Creates a new message to the topic.
+// Stores a handle to the message in aHandle.
+// FFS critical level check is made prior to operation.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::AddMessageL( 
+    const TCbsDbTopicNumber& aNumber, 
+    TCbsDbMessage& aMessage, 
+    const TPtrC& aContents )
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::AddMessageL()");
+    
+    LoadCacheL( aNumber );
+
+    aMessage.iLength = aContents.Length();
+    CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::AddMessageL(): Content length: %d.", aMessage.iLength );
+
+    // Generate a new message handle.
+    aMessage.iHandle = GenerateNewMessageHandle( aNumber );    
+    
+    // Make sure that the message is not inserted as permanent
+    aMessage.iPermanent = EFalse;
+
+    TRAPD( error, DoAddMessageL( aMessage, aContents ) );
+    if ( error != KErrNone )
+        {
+        CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::AddMessageL(): DoAddMessageL() failed, error: %d.", error );
+        
+        // Failed.
+        RevertFileOperationL( error );
+        
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::AddMessageL(): RevertFileOperationL() finished OK." );
+        }
+        
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::AddMessageL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::SaveMessageL
+// Saves a message. 
+// This operation is called "keeping" in the UI. 
+// The CBS server has a global limit for saved 
+// messages. The function leaves, if this is exceeded. 
+// A saved message has it's permanent-flag raised.
+// FFS critical level check is made prior to the operation.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::SaveMessageL( 
+    const TCbsDbMessageHandle& aHandle,
+    const CCbsRecEtel& aReceiver )
+    {
+    // Load cache.
+    TCbsTopicNumber topicNumber( iTopicList.ExtractTopicNumber( aHandle ) );
+    LoadCacheL( topicNumber );
+
+    // Now find message by handle.
+    TInt position( FindMessagePositionByHandleL( aHandle ) );
+
+    TCbsDbMessage message;
+    LoadMessageL( position, message );
+    TStreamId contentsId( iMessageList->At( position ).iContentsId );
+
+    // This function should not be called if the message is already permanent.
+    if ( message.iPermanent )
+        {
+        User::Leave( KErrAlreadyExists );
+        }
+
+    // Check that the limit for maximum saved messages/topic is not exceeded.
+    TCbsDbTopic topic;
+    iTopicList.FindTopicByNumberL( topicNumber, topic );
+    if ( iTopicList.TotalSavedMessages() >= KCbsDbMaxSavedMessages )
+        {
+        User::Leave( KErrOverflow );
+        }
+
+    // FFS critical level check (header is 16 bytes, message coded in Unicode)
+    CbsUtils::FFSCriticalLevelCheckL( KCbMessageHeaderSize + 
+        2 * message.iLength, iFs );
+
+    // Create a temporary buffer for message body and load the contents into
+    // the buffer.
+    HBufC* buffer = HBufC::NewLC( message.iLength ); // on CS
+    TPtr contentPtr( buffer->Des() );
+    LoadMessageContentsL( contentsId, contentPtr, EFalse, message.iLength );
+
+    // Delete message's previous header and body streams.
+    TCbsDbImpTopicMessagesCacheItem item( iMessageList->At( position ) );    
+
+    TRAPD( result, DoSaveMessageL( message, item, contentPtr, position ) );
+    CleanupStack::PopAndDestroy( buffer );
+    if ( result != KErrNone )
+        {
+        RevertFileOperationL( result );
+        }
+    else
+        {
+        iTopicList.NotifyTopicModifiedL( 
+            iTopicList.ExtractTopicNumber( aHandle ) );
+            
+        // Update the soft notification dialog, if topic is hotmarked
+        UpdateSoftNotificationL( aHandle, aReceiver );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DoSaveMessageL
+// Saves a message.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DoSaveMessageL( 
+    TCbsDbMessage& aMessage,
+    const TCbsDbImpTopicMessagesCacheItem& aItem,
+    const TDesC& aContentPtr,
+    TInt aPosition )
+    {
+    CFileStore* store = iTopicList.UnsavedMessagesStore();
+    store->Delete( aItem.iId );
+    store->Delete( aItem.iContentsId );
+
+    TBool needToUpdateReadCounter( EFalse );
+    aMessage.iPermanent = ETrue;
+    if ( !aMessage.iRead ) 
+        {
+        needToUpdateReadCounter = ETrue;
+        aMessage.iRead = ETrue;
+        }
+
+    // Create new streams for message header and body.
+    TStreamId contentsStream( CreateMessageContentsStreamL( 
+        *iTopicList.TopicStoreL(), aContentPtr ) );
+    TStreamId messageStream( CreateMessageStreamL( 
+        *iTopicList.TopicStoreL(), aMessage, contentsStream ) );
+
+    // Update message's cache item information.
+	TCbsDbImpTopicMessagesCacheItem& item = iMessageList->At( aPosition );
+    item.iContentsId = contentsStream;
+    item.iId = messageStream;
+    item.iIsSaved = ETrue;
+    item.iMessage = aMessage;
+
+    // Update topic messages stream; the cache item is saved into the stream.
+    UpdateTopicMessagesStreamL( EFalse );
+
+    iTopicList.InformMessageSavedL( aMessage.iHandle );
+    if ( needToUpdateReadCounter )
+        {
+        iTopicList.InformUnreadMessageReadL( aMessage.iHandle );
+        }
+
+    // Commit changes to both files.
+    iTopicList.CommitFilesL();
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::ReadMessageL
+// Marks the message read
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::ReadMessageL(
+    const TCbsDbMessageHandle& aHandle,
+    const CCbsRecEtel& aReceiver )
+    {
+    //Checked that there is space for writing data on FFS.
+    CbsUtils::FFSCriticalLevelCheckL( KReadMessageSize, iFs );
+
+    // Load cache.
+    TCbsDbTopicNumber topicNum( iTopicList.ExtractTopicNumber( aHandle ) );
+    LoadCacheL( topicNum );
+
+    // Now find message by handle.
+    TInt position( FindMessagePositionByHandleL( aHandle ) );
+
+    TCbsDbMessage message;
+    LoadMessageL( position, message );
+    TStreamId contentsId( iMessageList->At( position ).iContentsId );
+    TStreamId id( iMessageList->At( position ).iId );
+
+    // Check if the message is already read, then there is nothing more to do.
+    if ( !message.iRead )
+        {
+        message.iRead = ETrue;
+
+        TRAPD( error, DoReadMessageL( id, message, contentsId ) );
+        if ( error == KErrNone )
+            {
+            iMessageList->At( position ).iMessage = message;
+            
+            iTopicList.NotifyTopicModifiedL( 
+                iTopicList.ExtractTopicNumber( aHandle ) );
+        
+            // Update the soft notification dialog, if topic is hotmarked
+            UpdateSoftNotificationL( aHandle, aReceiver );
+            }
+        else
+            {
+            RevertFileOperationL( error );
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::UpdateHandlesOfTopicMessagesL
+// Updates handles of messages in the topic with the number given in
+// aOldTopicNumber to match new topic number. 
+// Traverses through the given topic processing
+// each message stream encountered. Since the topic number is
+// the primary key and previous message handles can be assumed
+// to be unique, updating the high word of the handle to
+// match the new topic number is adequate.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::UpdateHandlesOfTopicMessagesL(
+    const TCbsDbTopicNumber& aOldTopicNumber, 
+    const TCbsDbTopicNumber& aNewTopicNumber )
+    {
+    LoadCacheL( aOldTopicNumber );
+
+    TInt msgs( iMessageList->Count() );
+
+    for ( TInt i( 0 ); i < msgs; i++ )
+        {
+        // Fetch message at this position
+        TCbsDbMessage message;
+        LoadMessageL( i, message );
+        TStreamId contentsStreamId( iMessageList->At( i ).iContentsId );
+        TStreamId messageStreamId( iMessageList->At( i ).iId );
+
+        // Insert new topic number as the high word of the handle
+        message.iHandle = ( aNewTopicNumber << 16 ) |
+            ( message.iHandle & 0xFFFF );
+        DoUpdateMessageL( messageStreamId, message, contentsStreamId );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::LockMessageL
+// Unlocks one message and then locks another message.
+// Message that is locked can not be removed. Topic that 
+// contains a locked message can not be removed.
+// Note that locking status is not persistent in the sense that 
+// when the power is switched off and turned on, no messages 
+// are automatically locked.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::LockMessageL( 
+    const TCbsDbMessageHandle& aUnlock, 
+    const TCbsDbMessageHandle& aLock )
+    {
+    // Check if the message exists: leave if not found, ignore return value
+    if ( aLock != 0 )
+        {
+        TCbsDbMessage message;
+        TInt position( FindMessagePositionByHandleL( aLock ) );
+        LoadMessageL( position, message );
+        }
+
+    TBool unlockNull( ETrue );
+
+    if ( aUnlock != 0 )
+        {
+        DeleteFromLocked( aUnlock );
+        unlockNull = EFalse;
+        }
+
+    if ( aLock != 0 )
+        {
+        InsertToLockedL( aLock ) ;
+        }
+
+    if ( unlockNull == EFalse )
+        {
+        LoadCacheL( iTopicList.ExtractTopicNumber( aUnlock ) );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::GetNextAndPrevMsgHandleL
+// Returns the handles of the next and previous message
+// when given a current message. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::GetNextAndPrevMsgHandleL(
+    const TCbsDbMessageHandle& aCurrentMsg,
+    TCbsDbMessageHandle& aNextMsg, 
+    TCbsDbMessageHandle& aPrevMsg, 
+    TInt& aPosition)
+    {
+    LoadCacheL( iTopicList.ExtractTopicNumber( aCurrentMsg ) );
+
+    // Load the current message
+    TInt index( FindMessagePositionByHandleL( aCurrentMsg ) );
+
+    // Reset position indications
+    aPosition = 0;
+
+    // Retrieve position indications and message handles
+    if ( index == 0 )
+        {
+        aPosition |= ECbsHead;
+        }
+    else
+        {
+        TCbsDbMessage prevMessage;
+        LoadMessageL( index - 1, prevMessage );
+        aPrevMsg = prevMessage.iHandle;
+        }
+
+    // If index points to the last element of iMessageList,
+    // raise flag ECbsTail.
+    if ( index == iMessageList->Count()-1 )
+        {
+        aPosition |= ECbsTail;
+        }
+    else
+        {
+        TCbsDbMessage nextMessage;
+        LoadMessageL( index+1, nextMessage );
+        aNextMsg = nextMessage.iHandle;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DoAddMessageL
+// Inserts a message into the database.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DoAddMessageL(
+    TCbsDbMessage& aMessage, 
+    const TDesC& aContents )
+    {
+    // Cache is assumed to be loaded. Message is assumed not to
+    // already exist in the DB.
+    CFileStore* store = aMessage.iPermanent ? 
+        iTopicList.TopicStoreL() : iTopicList.UnsavedMessagesStore();
+
+    if ( aMessage.iPermanent )
+        {
+        // About to write to FFS: make critical level check.
+        // Message is in Unicode characters, so its size
+        // is double the length.
+        CbsUtils::FFSCriticalLevelCheckL( KCbMessageHeaderSize + 
+            2 * aMessage.iLength, iFs );
+        }
+    else
+        {
+        CbsUtils::VolumeCriticalLevelCheckL(
+            iTopicList.UnsavedMessagesFilename(),
+            KCbMessageHeaderSize + 2 * aMessage.iLength, iFs );
+        }
+
+    // Create stream for contents.
+    TStreamId contentsStream( CreateMessageContentsStreamL( 
+        *store, aContents ) );
+
+    // Now we have to find the position to which the message should be
+    // inserted.         
+    TInt positionToBeInserted( -1 );
+
+    TTime messageTime( aMessage.iDateTime );
+    TInt index( 0 );
+
+    // Go through all messages in topic and find the position.
+    TInt count( iMessageList->Count() );
+    for ( ; index < count && positionToBeInserted == -1; index++ )
+        {
+        TCbsDbMessage message;
+        LoadMessageL( index, message );
+
+        TTime tmp( message.iDateTime );
+
+        if ( tmp <= messageTime )
+            {
+            positionToBeInserted = index;
+            }
+        }
+
+    // If we looped through, append
+    if ( positionToBeInserted == -1 )
+        {
+        positionToBeInserted = index;
+        }
+
+    // Create message stream.
+    TStreamId messageStream( CreateMessageStreamL( *store, 
+        aMessage, contentsStream ) );
+
+    // Add to internal cache.
+    TCbsDbImpTopicMessagesCacheItem item;
+    
+    item.iId = messageStream;
+    item.iIsMessage = EFalse;
+    item.iIsSaved = aMessage.iPermanent;
+
+    iMessageList->InsertL( positionToBeInserted, item );
+
+    // Check if message should be deleted.
+    DeleteReceivedIfNecessaryL();
+
+    // Update topic messages stream.
+    UpdateTopicMessagesStreamL( EFalse );
+
+    // Update counters.
+    iTopicList.InformNewMessageReceivedL( aMessage.iHandle );
+
+    // Commit changes to both stores.
+    iTopicList.CommitFilesL();
+
+    // Inform observers. Done after commit to make sure there's
+    // a message to inform of.
+    iTopicList.NotifyNewMessageArrivedL( aMessage.iHandle );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DoDeleteMessageL
+// Deletes a message from the database.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DoDeleteMessageL( 
+    TInt aPosition )
+    {    
+    TCbsDbMessage message;
+    LoadMessageL( aPosition, message );
+
+    // Delete the message, if it is possible.
+    DeleteMessageByPositionL( aPosition );
+    DeleteReceivedIfNecessaryL();
+    UpdateTopicMessagesStreamL( ETrue );
+
+    // Inform the topic list about deletion.
+    iTopicList.InformMessageDeletedL( message.iHandle, 
+        message.iPermanent, message.iRead );
+
+    iTopicList.CommitFilesL();
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DoUpdateMessageL
+// Updates the information for a message.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DoUpdateMessageL( 
+    const TStreamId& aId, 
+    const TCbsDbMessage& aMessage, 
+    const TStreamId& aContentsId ) const
+    {
+    // Update the stream contents.
+    CFileStore* store = aMessage.iPermanent ? 
+        iTopicList.TopicStoreL() : iTopicList.UnsavedMessagesStore();
+
+    RStoreWriteStream outstream;
+    outstream.ReplaceLC( *store, aId ); // on CS
+
+    WriteMessageInformationL( outstream, aMessage, aContentsId );
+
+    outstream.CommitL();
+    CleanupStack::PopAndDestroy(); // outstream
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DoReadMessageL
+// Marks a message read by updating it in the database.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DoReadMessageL( 
+    const TStreamId& aId, 
+    const TCbsDbMessage& aMessage, 
+    const TStreamId& aContentsId )
+    {
+    DoUpdateMessageL( aId, aMessage, aContentsId );
+    iTopicList.InformUnreadMessageReadL( aMessage.iHandle );
+    iTopicList.CommitFilesL();
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DoDeleteL
+// Deletes messages that are not supposed to be in the topic.
+// Because of message locking it is possible that there are more 
+// received messages than there should be. This method tries to 
+// delete this kind of message.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DoDeleteL()
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::DoDeleteL()");
+
+    // Delete temporary messages and update topic messages stream.
+    if ( DeleteReceivedIfNecessaryL() )
+        {
+        UpdateTopicMessagesStreamL( ETrue );    
+        }
+    iTopicList.CommitFilesL();
+    
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::DoDeleteL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::FindMessageByKeyL
+// Returns a handle to a message with the given key.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt CCbsDbImpTopicMessages::FindMessageByKeyL( 
+    TCbsDbTopicNumber aNumber, 
+    TCbsDbMessageKey aKey,
+    TCbsDbMessage& aMessage )
+    {
+    TCbsDbMessage message;
+    TInt count;
+    TBool result( KErrNotFound );
+
+    // Find a message even if the update numbers don't match
+    aKey &= 0xfff0;
+
+    // Try to find a message with the same key in the topic.
+    LoadCacheL( aNumber );
+    GetMessageCountL( aNumber, count );
+
+    for ( TInt index( 0 ); ( index < count ) && ( result != KErrNone ); index++ )
+        {
+        LoadMessageL( index, message );
+
+        if ( (message.iKey & 0xfff0) == aKey )
+            {
+            aMessage = message;
+            result = KErrNone; // message was found
+            }
+        }
+    return result;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::FindMessagePositionByHandleL
+// Returns the position of a message with the given handle.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TInt CCbsDbImpTopicMessages::FindMessagePositionByHandleL( 
+    const TCbsDbMessageHandle& aHandle )
+    {
+    // Load the cache.
+    LoadCacheL( iTopicList.ExtractTopicNumber( aHandle ) );
+
+    // Now, find the message
+    TInt count( iMessageList->Count() );
+
+    TCbsDbMessage message;
+    TBool found( EFalse );
+
+    // Go through messages.
+    TInt index( 0 );
+    for ( ; ( index < count ) && !found; )
+        {
+        // Load the message.
+        LoadMessageL( index, message );
+        
+        // Check if it is this one.
+        if ( message.iHandle == aHandle )
+            {
+            found = ETrue;
+            }
+        else
+            {
+            index++;
+            }        
+        }
+
+    if ( !found )
+        {
+        User::Leave( KErrNotFound );
+        }
+     
+    return index;        
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::LoadCacheL
+// Loads the cache, if it is not already loaded with this topic information.
+// The method does not load any message information to the cache 
+// - only what is really necessary.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::LoadCacheL( 
+    const TCbsDbTopicNumber& aNumber )
+    {
+    CBSLOGSTRING2("CBSSERVER: >>> CCbsDbImpTopicMessages::LoadCacheL(), topic number: %d.", aNumber );
+    
+    // Check if the cache is valid. If not, load it.
+    if ( !( iCacheValid && ( iCachedTopicNumber == aNumber ) ) ) 
+        {        
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::LoadCacheL(): Calling InvalidateCache()...");        
+        // Invalidate the old cache, if it is necessary.
+        InvalidateCache();        
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::LoadCacheL(): InvalidateCache() called OK.");
+
+        // Load message queue ids.
+        TStreamId id;
+        iTopicList.GetTopicMessagesIdL( aNumber, id );
+
+        ReadTopicMessagesCacheItemsL( *iTopicList.TopicStoreL(), 
+            id, *iMessageList, iSpaceForMessages );
+    
+        // Cache is valid.
+        iCachedTopicNumber = aNumber;
+        iCacheValid = ETrue;
+
+        // Check if it there are more topics than there should be.
+        TCbsDbTopic topic;
+        iTopicList.FindTopicByNumberL( aNumber, topic );
+        if ( ( iMessageList->Count() - topic.iSavedMessages ) > 
+             KCbsDbMaxReceivedMessages ) 
+            {
+            TRAPD( error, DoDeleteL() );
+            CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::LoadCacheL(): DoDeleteL() finished with %d.", error );        
+            
+            if ( error != KErrNone )
+                {
+                RevertFileOperationL( error );
+                }
+            }
+        }
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::LoadCacheL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::LoadMessageL
+// Loads the message header to the cache.
+// Note that it is assumed that the index is in proper range and 
+// the cache is already loaded.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::LoadMessageL( 
+    TInt aIndex, 
+    TCbsDbMessage& aMessage )
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::LoadMessageL()");
+
+    // Check that the aIndex is in proper range and.
+    // that the cache is valid (just for sure).
+    if ( aIndex < 0 || aIndex >= iMessageList->Count() ||
+         !iCacheValid )
+        {
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::LoadMessageL(): Leaving with KErrNotFound...");
+        User::Leave( KErrNotFound );
+        }
+
+    // Check if the message information is already loaded.
+    if ( iMessageList->At( aIndex ).iIsMessage )
+        {       
+        // Yes, just make a fast copy.
+        aMessage = iMessageList->At( aIndex ).iMessage;
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::LoadMessageL(): Msg already loaded, fast copy made.");
+        }
+    else
+        {
+        TStreamId id( TStreamId( 0 ) );
+        // Choose the file store: saved messages are stored elsewhere
+        // than other messages.
+        CFileStore* store = iMessageList->At( aIndex ).iIsSaved ?
+            iTopicList.TopicStoreL() : 
+            iTopicList.UnsavedMessagesStore();
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::LoadMessageL(): Store created.");
+
+        // Then read the message. If the message stream cannot be
+        // found, delete the reference to the message
+        TRAPD( error, ReadMessageInformationL( *store, 
+            iMessageList->At( aIndex ).iId, aMessage, id ) );
+            
+        CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::LoadMessageL(): ReadMessageInformationL() finished with %d.", error );
+        
+        if( error == KErrNotFound )
+            {
+            DeleteMessageByPositionL( aIndex );
+            UpdateTopicMessagesStreamL( ETrue );
+            iTopicList.CommitFilesL();    
+            }
+        
+        CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::LoadMessageL(): Leaving if error code < 0: error %d...", error );
+        User::LeaveIfError( error );
+
+        // And copy to the internal cache.
+        iMessageList->At( aIndex ).iIsMessage = ETrue;
+        iMessageList->At( aIndex ).iContentsId = id;
+        iMessageList->At( aIndex ).iMessage = aMessage;
+        }
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::LoadMessageL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::LoadMessageContentsL
+// Loads message contents (as much as it fits).
+// Restores the contents of a message
+// by reading a stream specified in parameter aContentsId.
+// Save status of message is required because saved and unsaved
+// messages are stored on separate files.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::LoadMessageContentsL( 
+    const TStreamId& aContentsId, 
+    TPtr& aContents,
+    TBool aIsSaved,
+    TUint aSize ) const
+    {
+    CFileStore* store = aIsSaved ? 
+        iTopicList.TopicStoreL() :
+        iTopicList.UnsavedMessagesStore();
+
+    // Read data to the buffer.
+    RStoreReadStream instream;
+    instream.OpenLC( *store, aContentsId ); // on CS
+
+    // Read the data into a temporary buffer first.
+	// Then copy to the aContents descriptor only
+	// as much as it can take.
+    TUint length( instream.ReadInt16L() );
+    if ( length > KCbsMaxCharsInMessage )
+        {
+        User::Leave( KErrCorrupt );
+        }
+
+	HBufC* buf = HBufC::NewL( instream, length );    
+	if ( length > aSize )
+		{
+		length = aSize;
+		}
+
+	aContents.Copy( buf->Ptr(), length );
+	delete buf;
+
+    CleanupStack::PopAndDestroy(); // instream
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::GenerateNewMessageHandle
+// Generates a new, unique handle for a message.
+// The generation uses topic number as 
+// basis and then increments the handle value until an unique
+// handle is found.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TCbsDbMessageHandle CCbsDbImpTopicMessages::GenerateNewMessageHandle( 
+    const TCbsDbTopicNumber& aNumber )
+    {
+    // Start with a reasonable seed. We'll try the number
+    // of messages in topic, plus one.
+    TUint16 handleLow( TUint16( iMessageList->Count() + 1 ) );
+    TCbsDbMessageHandle handle( 1 );
+    TInt error( KErrNone );
+
+    while ( error == KErrNone )
+        {
+        handleLow++;
+        if ( handleLow == 0 ) 
+            {
+            handleLow++;
+            }
+
+        // Generate a new message handle.
+        handle = iTopicList.GenerateMessageHandle( aNumber, 
+            handleLow );
+
+        TRAP( error, ( FindMessagePositionByHandleL( handle ) ) );
+        }
+    return handle;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DeleteReceivedIfNecessaryL
+// Checks that if there are too many received messages in the internal cache, 
+// then they will be deleted if they are not locked.  
+// Note: Count() is changed in for-loop.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCbsDbImpTopicMessages::DeleteReceivedIfNecessaryL()
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::DeleteReceivedIfNecessaryL()");
+    
+    // So check if there are too many received messages in the topic. 
+    // Thus delete if message is not locked.
+
+    __ASSERT_DEBUG( iCacheValid, CbsServerPanic( ECbsCacheNotValid ) );
+
+    TInt totalReceived( 0 );
+    TCbsDbMessage message;
+    TBool result( EFalse );
+    TInt index( 0 );
+
+    for ( index = 0; index < iMessageList->Count(); index++ ) 
+        {
+        LoadMessageL( index, message );
+
+        // Count the amount of received messages
+        if ( !message.iPermanent )
+            {
+            totalReceived++;
+            
+            // If there are too many, then check that it can be deleted.
+            if ( totalReceived > KCbsDbMaxReceivedMessages && 
+                !IsMessageLocked( message.iHandle ) )
+                {
+                // Delete the message.
+                DeleteMessageByPositionL( index );
+                iTopicList.InformMessageDeletedL
+                                    ( message.iHandle, 
+                                      message.iPermanent, 
+                                      message.iRead );
+                result = ETrue;
+                
+                // They are always deleted from internal cache
+                index--;
+                totalReceived--;
+                }
+            }
+        }
+
+    CBSLOGSTRING2("CBSSERVER: <<< CCbsDbImpTopicMessages::DeleteReceivedIfNecessaryL(), returning %d.", result );
+    return result;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DeleteMessageByPositionL
+// Deletes message by its position in the internal cache.
+// Note that after the call the internal cache won't contain 
+// the item.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DeleteMessageByPositionL( 
+    TInt aIndex )
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::DeleteMessageByPositionL()");
+    
+    // Delete from cache and from file.
+    __ASSERT_DEBUG( iCacheValid, CbsServerPanic( ECbsCacheNotValid ) );
+
+    TCbsDbImpTopicMessagesCacheItem item( iMessageList->At( aIndex ) );
+
+    // Determine the file where the message is stored.
+    CFileStore* store = item.iIsSaved ? iTopicList.TopicStoreL() :
+        iTopicList.UnsavedMessagesStore();
+
+    iMessageList->Delete( aIndex );
+
+    store->Delete( item.iId );
+    store->Delete( item.iContentsId );
+    
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::DeleteMessageByPositionL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::IsMessageLocked
+// Determines whether or not the message is locked.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TBool CCbsDbImpTopicMessages::IsMessageLocked( 
+    const TCbsDbMessageHandle& aHandle ) const
+    {
+    // Find out if the message is locked.
+    TBool found( EFalse );
+    TInt count( iLockedMessages->Count() );
+    for ( TInt index( 0 ); ( index < count ) && !found; index++ )
+        {
+        if ( iLockedMessages->At( index ) == aHandle )
+            {
+            found = ETrue;            
+            }
+        }
+
+    return found;    
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::DeleteFromLocked
+// Deletes from the locked messages.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::DeleteFromLocked( 
+    const TCbsDbMessageHandle& aHandle )
+    {
+    TBool deleted( EFalse );
+    TInt count( iLockedMessages->Count() );
+
+    for ( TInt index( 0 ); ( index < count ) && !deleted; index++ )
+        {
+        if ( iLockedMessages->At( index ) == aHandle )
+            {
+            iLockedMessages->Delete( index );
+            deleted = ETrue;            
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::InsertToLockedL
+// Adds a handle to the locked messages.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::InsertToLockedL( 
+    const TCbsDbMessageHandle& aHandle )
+    {
+    // Just append to the end. 
+    iLockedMessages->AppendL( aHandle );
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL
+// Updates topic messages stream from the internal cache.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(
+    TBool aDeleting )
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL()");
+    
+    __ASSERT_DEBUG( iCacheValid, CbsServerPanic( ECbsCacheNotValid ) );
+
+    TInt neededSpace( KMessageRootStreamSize + 
+        iMessageList->Count() * KMessageEntrySize );
+
+    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, first check if the information fits well to the current stream.
+    if ( iSpaceForMessages >= iMessageList->Count() )
+        {
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (3)");
+        
+        // Write data to the old stream.
+        TStreamId id;
+        iTopicList.GetTopicMessagesIdL( iCachedTopicNumber, id );
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (4)");
+        
+        // Replace the existing stream.
+        RStoreWriteStream outstream;
+        outstream.ReplaceLC( *iTopicList.TopicStoreL(), id ); // on CS
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (5)");
+    
+        WriteTopicMessagesStreamL( outstream );
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (6)");
+
+        outstream.CommitL();
+        CleanupStack::PopAndDestroy(); // outstream
+        }
+    else
+        {
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (7)");
+        // Calculate the amount of space for messages.
+        if ( iSpaceForMessages == 0 )
+            {
+            // Zero is not very good, lets put it greater.
+            iSpaceForMessages = KDefaultSpaceForMessages;
+            }
+
+        // But no need for extra messages.
+        iSpaceForMessages = 2 * iSpaceForMessages;
+        if ( iSpaceForMessages > 
+             ( KCbsDbMaxReceivedMessages + KCbsDbMaxSavedMessages ) )
+            {
+            iSpaceForMessages = 
+                KCbsDbMaxReceivedMessages + KCbsDbMaxSavedMessages;
+            }
+        if ( iMessageList->Count() > iSpaceForMessages )
+            {
+            iSpaceForMessages = iMessageList->Count();
+            }
+
+        // And finally write the data to stream.
+        RStoreWriteStream outstream;
+        TStreamId id = outstream.CreateLC( *iTopicList.TopicStoreL() );// on CS
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (8)");
+
+        WriteTopicMessagesStreamL( outstream );
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (9)");
+
+        outstream.CommitL();
+        CleanupStack::PopAndDestroy(); // outstream
+
+        // And then update topic messages stream. It will also delete 
+        // the old stream.
+        iTopicList.UpdateTopicMessagesIdL( iCachedTopicNumber, id );
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (10)");
+        }
+
+    // Free the reserved space
+    if ( aDeleting )
+        {
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL(): (11)");
+        CleanupStack::PopAndDestroy(); // disk space
+        sharedData.Close();
+        }
+
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::UpdateTopicMessagesStreamL()"); 
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::WriteTopicMessagesStreamL
+// Writes topic messages stream from the internal cache.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::WriteTopicMessagesStreamL( 
+    RWriteStream& aOut )
+    {
+    // Write total amount of messages.
+    TInt count( iMessageList->Count() );
+    aOut.WriteInt16L( count );
+
+    // Write total amount of space for messages.
+    aOut.WriteInt16L( iSpaceForMessages );
+
+    TInt index( 0 );
+
+    for ( ; index < count; index++ )
+        {
+        CbsStreamHelper::WriteBoolL( aOut,
+            iMessageList->At( index ).iIsSaved );
+
+        // Write message stream id.
+        aOut << iMessageList->At( index ).iId;
+        }
+
+    for ( ; index < iSpaceForMessages; index++ )
+        {
+        CbsStreamHelper::WriteBoolL( aOut, EFalse );
+
+        // Write null message stream id.
+        aOut << TStreamId( 0 );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::CreateMessageContentsStreamL
+// Creates a new message content stream and writes 
+// the given contents into this stream.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TStreamId CCbsDbImpTopicMessages::CreateMessageContentsStreamL(
+    CStreamStore&  aStore, 
+    const TDesC& aContents )
+    {
+    // Create stream and write data to it.
+    RStoreWriteStream outstream;
+    TStreamId id( outstream.CreateLC( aStore ) ); // on CS
+
+    // Just write the contents..
+    TInt count( aContents.Length() );
+    outstream.WriteInt16L( count );
+	outstream << aContents;
+
+    outstream.CommitL();
+    CleanupStack::PopAndDestroy(); // outstream
+
+    return id;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::CreateMessageStreamL
+// Creates a message stream and
+// writes the message into that stream. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TStreamId CCbsDbImpTopicMessages::CreateMessageStreamL( 
+    CStreamStore& aStore, 
+    TCbsDbMessage& aMessage, 
+    const TStreamId& aContents )
+    {
+    // Create stream and write data to it.
+    RStoreWriteStream outstream;
+    TStreamId id = outstream.CreateLC( aStore ); // on CS
+
+    // Write the handle.
+    outstream.WriteInt32L( aMessage.iHandle );
+
+    // Write the message key.
+    outstream.WriteInt16L( aMessage.iKey );
+    
+    // Write the language.
+    outstream.WriteInt16L( aMessage.iLanguage );
+
+    // Write the date and time.
+    outstream << aMessage.iDateTime;
+    
+    // Write the permanent status.
+    CbsStreamHelper::WriteBoolL( outstream, aMessage.iPermanent );
+    
+    // Write the read status.
+    CbsStreamHelper::WriteBoolL( outstream, aMessage.iRead );
+    
+    // Write the stream identifier to the contents.
+    outstream << aContents;
+
+    outstream.CommitL();
+    CleanupStack::PopAndDestroy(); // outstream
+
+    return id;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL
+// Reads all cache items for a topic.
+// Restores topic message cache from a stream.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL( 
+    const CStreamStore& aStore, 
+    const TStreamId& aId, 
+    TCbsDbImpTopicMessagesCache& aCache, 
+    TInt& aSpace )
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL()");
+    
+    RStoreReadStream instream;
+    instream.OpenLC( aStore, aId ); // on CS
+    CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL(): instream.OpenLC() OK.");
+
+    // Read total amount of messages.
+    TInt totalMessages( instream.ReadInt16L() );
+    CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL(): totalMessages: %d.", totalMessages );
+
+    // Read total amount of space for messages.
+    aSpace = instream.ReadInt16L();
+    CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL(): aSpace: %d.", aSpace );
+
+    // Sanity check: if the number of messages exceeds
+    // the worst case, leave. If the space for messages
+    // is smaller than number of messages, leave.
+    if ( totalMessages > KCbsDbMaxSavedMessages + KCbsDbMaxReceivedMessages 
+        || aSpace < totalMessages )
+        {
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL(): Stream corrupt, leaving with KErrCorrupt...");
+        User::Leave( KErrCorrupt );
+        }
+
+    TCbsDbImpTopicMessagesCacheItem item;
+
+    aCache.Reset();
+
+    for ( TInt index( 0 ); index < totalMessages; index++ )
+        {
+        CBSLOGSTRING2("CBSSERVER: CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL(): Looping msgs, i: %d.", index );
+        item.iIsSaved = CbsStreamHelper::ReadBoolL( instream );
+
+        // Read topic message stream id.
+        instream >> item.iId;
+
+        // Initialize other fields.
+        item.iIsMessage = EFalse;
+
+        // And finally, append the item to the array
+        aCache.AppendL( item );
+        
+        CBSLOGSTRING("CBSSERVER: CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL(): Msg appended to array.");
+        }
+
+    CleanupStack::PopAndDestroy(); // instream
+    
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::ReadTopicMessagesCacheItemsL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::ReadMessageInformationL
+// Restore a message from a stream.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::ReadMessageInformationL( 
+    const CStreamStore& aStore, 
+    const TStreamId& aId, 
+    TCbsDbMessage& aMessage, 
+    TStreamId& aContentsId )
+    {
+    CBSLOGSTRING("CBSSERVER: >>> CCbsDbImpTopicMessages::ReadMessageInformationL()");
+    
+    // Open the message information stream.
+    RStoreReadStream instream;
+    instream.OpenLC( aStore, aId ); // on CS
+
+    // Read the handle.
+    aMessage.iHandle = instream.ReadInt32L();
+
+    // Read the message key.
+    aMessage.iKey = instream.ReadInt16L();
+    
+    // Read the language.
+    aMessage.iLanguage = instream.ReadInt16L();    
+    
+    // Read the date and time.
+    TInt64 time;
+    instream >> time;
+    aMessage.iDateTime = time;
+    
+    // Read the permanent status.
+    aMessage.iPermanent = CbsStreamHelper::ReadBoolL( instream );
+
+    // Read the read status.
+    aMessage.iRead = CbsStreamHelper::ReadBoolL( instream );
+    
+    // Read the stream identifier to the contents.
+    instream >> aContentsId;
+
+    CleanupStack::PopAndDestroy(); // instream
+
+    if ( aContentsId == TStreamId( 0 ) )
+        {
+        aMessage.iLength = 0;
+        }
+    else
+        {
+        // Open message contents stream.
+        RStoreReadStream instreamContents;
+
+        instreamContents.OpenLC( aStore, aContentsId ); // on CS
+
+        // Read the length of the message.
+        aMessage.iLength = instreamContents.ReadInt16L();
+
+        CleanupStack::PopAndDestroy(); // instreamContents
+        }
+    CBSLOGSTRING("CBSSERVER: <<< CCbsDbImpTopicMessages::ReadMessageInformationL()");
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::WriteMessageInformationL
+// Writes message information into a stream. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::WriteMessageInformationL( 
+    RWriteStream& aOut, 
+    const TCbsDbMessage& aMessage, 
+    const TStreamId& aContentsId )
+    {    
+    // Write the handle.
+    aOut.WriteInt32L( aMessage.iHandle );
+    
+    // Write the message key.
+    aOut.WriteInt16L( aMessage.iKey );
+    
+    // Write the language.
+    aOut.WriteInt16L( aMessage.iLanguage );
+        
+    // Write the date and time.
+    aOut << aMessage.iDateTime;
+    
+    // Write the permanent status.
+    CbsStreamHelper::WriteBoolL( aOut, aMessage.iPermanent );
+    
+    // Write the read status.
+    CbsStreamHelper::WriteBoolL( aOut, aMessage.iRead );
+    
+    // Write the stream identifier to the contents.
+    aOut << aContentsId;
+    }
+
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::UpdateSoftNotificationL
+// Updates the soft notification (dialog) when unread message count changes.           
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::UpdateSoftNotificationL( 
+    const TCbsDbMessageHandle& aHandle,
+    const CCbsRecEtel& aReceiver )
+    {
+    TCbsDbTopic topic;
+    TCbsDbTopicNumber topicNum( iTopicList.ExtractTopicNumber( aHandle ) );
+    iTopicList.FindTopicByNumberL( topicNum, topic ); 
+    
+    // If topic is hotmarked, update the soft notification (dialog) ,
+    // since number of read messages was changed
+    if ( topic.iHotmarked )
+        {            
+        aReceiver.Interface().LaunchMessageSoftNotificationL( EFalse );
+        } 
+    }
+    
+// -----------------------------------------------------------------------------
+// CCbsDbImpTopicMessages::RevertFileOperationL
+// Reverts any not commited file operation in data files,
+// reloads cache and then leaves with aReason.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CCbsDbImpTopicMessages::RevertFileOperationL( 
+    TInt aReason )
+    {
+    iTopicList.RevertFilesL();
+    InvalidateCache();
+    User::Leave( aReason );
+    }
+
+// ========================== OTHER EXPORTED FUNCTIONS =========================
+
+//  End of File