omads/omadsextensions/adapters/mms/src/mmsdatastore.cpp
changeset 0 dab8a81a92de
child 51 8e7494275d3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omads/omadsextensions/adapters/mms/src/mmsdatastore.cpp	Mon Nov 23 14:46:41 2009 +0200
@@ -0,0 +1,1783 @@
+/*
+* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). 
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Part of SyncML Data Synchronization Plug In Adapter
+*
+*/
+
+
+#include <e32base.h>      
+#include <msvstd.h>       
+#include <msvapi.h>       
+#include <msvids.h>
+#include <mtclreg.h> 
+#include <mmsconst.h>
+#include <centralrepository.h>
+#include <mmscodecclient.h>
+#include <sysutil.h>
+#include <MmsEngineInternalCRKeys.h>
+
+#include "mmsdatastore.h"
+#include "omadsfolderobject.h"
+#include "mmsadaptermsvapi.h"
+#include "mmsdataproviderdefs.h"
+#include "logger.h"
+
+_LIT8( KMmsMimeType, "application/vnd.wap.mms-message" );
+_LIT8( KMmsMimeVersion, "1.2" );
+_LIT8( KFolderMimeType, "application/vnd.omads-folder+xml" );
+_LIT8( KFolderMimeVersion, "1.2" );
+
+const TInt KDataBufferSize = 1024;
+const TUint KMMS_Flag_Read = 0x01;
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::CMmsDataStore
+// C++ default constructor can NOT contain any code, that might leave.
+// -----------------------------------------------------------------------------
+CMmsDataStore::CMmsDataStore():
+    iHasHistory(EFalse),
+    iDataBaseOpened(EFalse),
+    iKey(TKeyArrayFix(_FOFF(TSnapshotItem, ItemId()), ECmpTInt))
+    { 
+    }
+  
+// -----------------------------------------------------------------------------
+// CMmsDataStore::ConstructL
+// Symbian 2nd phase constructor, can leave.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::ConstructL(CMsvSession &aMsvSession)
+    {
+    LOGGER_ENTERFN("CMmsDataStore::ConstructL");
+    
+    iMsvSession = &aMsvSession;
+    
+    // Waiter object to be used with CodecClient
+    iMsvWait = CMsvOperationActiveSchedulerWait::NewLC();
+    CleanupStack::Pop( iMsvWait );
+    iCodecClient = CMmsCodecClient::NewL( *iMsvSession );
+    iMsvApi = CMmsAdapterMsvApi::NewL( *iMsvSession );
+    
+    // Item UID sets, used to transfer change info
+    iNewItems = new (ELeave) CNSmlDataItemUidSet;
+    iDeletedItems = new (ELeave) CNSmlDataItemUidSet;
+    iUpdatedItems = new (ELeave) CNSmlDataItemUidSet;
+    iMovedItems = new (ELeave) CNSmlDataItemUidSet;
+    iSoftDeletedItems = new (ELeave) CNSmlDataItemUidSet;
+    
+    iFolderObjectParser = COMADSFolderObject::NewL();
+    
+    LOGGER_LEAVEFN("CMmsDataStore::ConstructL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CMmsDataStore* CMmsDataStore::NewL( CMsvSession &aMsvSession)
+    {
+    CMmsDataStore* self = new (ELeave) CMmsDataStore;
+    
+    CleanupStack::PushL( self );
+    self->ConstructL( aMsvSession );
+    CleanupStack::Pop( self );
+
+    return self;    
+    }
+
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::~CMmsDataStore
+// Destructor
+// -----------------------------------------------------------------------------
+CMmsDataStore::~CMmsDataStore()
+    {
+    LOGGER_ENTERFN("CMmsDataStore::~CMmsDataStore()");
+    
+    delete iDataBuffer;
+    
+    delete iChangeFinder;
+    delete iFolderObjectParser;
+    
+    delete iNewItems;
+    delete iDeletedItems;
+    delete iUpdatedItems;
+    delete iMovedItems;
+    delete iSoftDeletedItems;
+ 
+    delete iMsvApi;
+    delete iCodecClient;
+    delete iMsvWait;
+            
+    LOGGER_LEAVEFN("CMmsDataStore::~CMmsDataStore()");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoOpenL
+// Opens database. This operation is performed SYNCHRONOUSLY
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoOpenL( const TDesC& /*aStoreName*/,
+    MSmlSyncRelationship& aContext, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoOpenL");
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    if ( iDataBaseOpened )
+        {
+        User::RequestComplete( iCallerStatus, KErrInUse );
+        LOGGER_WRITE("CMmsDataStore::DoOpenL failed with KErrInUse.");
+        return;
+        }
+    
+    *iContext = aContext;
+
+    // Create ChangeFinder object
+    if ( iChangeFinder )
+        {
+        delete iChangeFinder;
+        iChangeFinder = NULL;
+        }
+    iChangeFinder = CChangeFinder::NewL( aContext, iKey, iHasHistory, KMmsDataProviderImplUid );
+    
+    // Set current snapshot, this will be compared against the old one   
+    RegisterSnapshotL();
+    
+    iDataBaseOpened = ETrue;
+    iCurrentState = EMmsOpenAndWaiting;
+    User::RequestComplete( iCallerStatus, KErrNone );   
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoOpenL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCancelRequest
+// Not supported, does nothing.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCancelRequest()
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoCancelRequestL");
+    LOGGER_LEAVEFN("CMmsDataStore::DoCancelRequestL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoStoreName
+// Returns the name of the DataStore
+// -----------------------------------------------------------------------------
+const TDesC& CMmsDataStore::DoStoreName() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoStoreName");
+    
+    if ( iDataBaseOpened )
+        {
+        LOGGER_LEAVEFN( "CMmsDataStore::DoStoreName" );
+        LOGGER_MSG_EC( "Database name: %S", &KNSmlDefaultLocalDbName );
+        return KNSmlDefaultLocalDbName;
+        }
+
+    LOGGER_LEAVEFN( "CMmsDataStore::DoStoreName" );
+    return KNullDesC;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoBeginTransactionL
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoBeginTransactionL()
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoBeginTransactionL");
+    LOGGER_WRITE( "CMmsDataStore::DoBeginTransactionL leaved with KErrNotSupported." );
+    User::Leave( KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCommitTransactionL
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCommitTransactionL( TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoCommitTransactionL" ); 
+    LOGGER_WRITE( "CMmsDataStore::DoCommitTransactionL failed with KErrNotSupported." );
+    
+    iCallerStatus = &aStatus;
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoRevertTransaction
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoRevertTransaction( TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoRevertTransaction" ); 
+    iCallerStatus = &aStatus;
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    LOGGER_LEAVEFN( "CMmsDataStore::DoRevertTransaction" );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoBeginBatchL
+// Batching is not supported.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoBeginBatchL()
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoBeginBatchL" );
+    LOGGER_WRITE( "CMmsDataStore::DoBeginBatchL leaved with KErrNotSupported." );
+    User::Leave( KErrNotSupported );    
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCommitBatchL
+// Batching is not supported
+// -----------------------------------------------------------------------------
+//
+void CMmsDataStore::DoCommitBatchL( RArray<TInt>& /*aResultArray*/, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoCommitBatchL" );  
+    LOGGER_WRITE( "CMmsDataStore::DoCommitBatchL failed with KErrNotSupported" );
+    iCallerStatus = &aStatus;
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCancelBatch
+// Batching is not supported
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCancelBatch()
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoCancelBatch" );
+    LOGGER_LEAVEFN( "CMmsDataStore::DoCancelBatch" );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoSetRemoteStoreFormatL
+// Not supported
+// -----------------------------------------------------------------------------
+//
+void CMmsDataStore::DoSetRemoteStoreFormatL( const CSmlDataStoreFormat& /*aServerDataStoreFormat*/ )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoSetRemoteStoreFormatL");
+    LOGGER_LEAVEFN("CMmsDataStore::DoSetRemoteStoreFormatL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoSetRemoteMaxObjectSize
+// Not supported
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoSetRemoteMaxObjectSize( TInt /*aServerMaxObjectSize*/ )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoSetRemoteMaxObjectSize");
+    LOGGER_LEAVEFN("CMmsDataStore::DoSetRemoteMaxObjectSize");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoMaxObjectSize
+// Reads the maximum MMS Message size from the central repository
+// -----------------------------------------------------------------------------
+TInt CMmsDataStore::DoMaxObjectSize() const
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoMaxObjectSize" );
+    
+    CRepository* repository( NULL );
+    TInt error( KErrNone );
+    TInt maxSendSize( 0 );
+    
+    // Create central repository instance
+    TRAP( error, repository = CRepository::NewL( KCRUidMmsEngine ) );  
+    if ( error == KErrNone )
+        {
+        // Obtain the size from the central repository.
+        // In the case of error we'll set the value to zero ("anything goes").
+        error = repository->Get( KMmsEngineMaximumSendSize, maxSendSize );
+        if ( error != KErrNone )
+            {
+            maxSendSize = 0;
+            }
+        
+        delete repository;
+        }
+    else
+        {
+        LOGGER_MSG_EC( "CRepository::NewL leaved with %d", error );
+        }       
+        
+    LOGGER_LEAVEFN( "CMmsDataStore::DoMaxObjectSize" );
+    return maxSendSize;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoOpenItemL
+// Opens item in the DataStore, reads it (either completely or partially) 
+// to the temporary buffer where it can be later read to the remote database.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoOpenItemL( TSmlDbItemUid aUid, TBool& aFieldChange, 
+    TInt& aSize, TSmlDbItemUid& aParent, TDes8& aMimeType, 
+    TDes8& aMimeVer, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoOpenItemL" );
+
+    LOGGER_MSG_EC( "Opening item %d.", aUid );
+    
+    // Store these for later use
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Check that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC( "CMmsDataStore::DoOpenItemL, invalid state %d.", iCurrentState );
+        User::RequestComplete( iCallerStatus, KErrNotReady );
+        return;
+        }
+        
+    TBool userFolderFound( EFalse );
+    TTime timeStamp;
+    TPtrC folderName;
+    userFolderFound = iMsvApi->FindUserFolderL( aUid, folderName, timeStamp );
+    
+    if ( userFolderFound )
+        {
+        // Allocate new buffer
+        SAFEDELETE( iDataBuffer );
+        iDataBuffer = CBufFlat::NewL( KDataBufferSize );
+        
+        iFolderObjectParser->SetName( folderName );
+        iFolderObjectParser->SetCreatedDate( timeStamp.DateTime() );
+        iFolderObjectParser->SetModifiedDate( timeStamp.DateTime() );
+        iFolderObjectParser->ExportFolderXmlL( *iDataBuffer ); 
+       
+        iParentId = KMsvMyFoldersEntryIdValue;
+        
+        iCurrentState = EFolderOpen;
+        iReadPosition = 0;
+        
+        aSize = iDataBuffer->Size();
+        }
+    else // Open MMS message
+        {
+        TInt error( KErrNone );
+        
+        CMsvEntry* entry( NULL );
+        TRAP( error, entry = iMsvSession->GetEntryL(aUid) );
+        if ( error != KErrNone )
+            {
+            User::RequestComplete( iCallerStatus, KErrNotFound ); 
+            LOGGER_MSG_EC("iMsvSession->GetEntryL failed with %d.", error);
+            return;
+            }
+        
+        TMsvEntry messageEntry = entry->Entry();
+        SAFEDELETE( entry );
+    
+        iCurrentId = aUid;
+        iParentId = messageEntry.Parent();
+        iReadCounter = 0;
+    
+        // Check whether we need to send the whole item
+        if ( iChangeFinder->UpdatePartialL( aUid ) )
+            {
+            LOGGER_WRITE("EMmsItemOpenFieldUpdate");
+            aSize = 1;
+            iCurrentState = EMmsItemOpenFieldUpdate;
+            }
+        else    
+            {
+            // Read the whole item from the message store to the buffer
+            TUint32 flags( 0 );
+            TRAP( error, iCodecClient->InitializeChunkedRetrievingL(
+                iCurrentId,
+                iParentId,
+                flags,
+                iUnread,
+                aSize,
+                iMsvWait->iStatus) );
+            
+            if ( error != KErrNone ) 
+                {
+                User::RequestComplete( iCallerStatus, error );
+                LOGGER_MSG_EC("iCodecClient->InitializeChunkedRetrievingL failed with %d.", error); 
+                return;         
+                }
+            
+            // Wait until the message has been processed
+            iMsvWait->Start();
+            
+            if ( iMsvWait->iStatus != KErrNone )
+                {
+                User::RequestComplete( iCallerStatus, iMsvWait->iStatus.Int() );
+                LOGGER_MSG_EC( "iCodecClient->InitializeChunkedRetrievingL failed with %d",
+                    iMsvWait->iStatus.Int() ); 
+                return;
+                }   
+            aSize++; // Status byte will be added also, reserve one additional byte for that.
+            iCurrentState = EMmsItemOpen;
+            }
+        } // Open MMS message
+    
+    aParent = iParentId;
+    
+    aFieldChange = iCurrentState == EMmsItemOpenFieldUpdate ? ETrue : EFalse;
+    
+    if ( iCurrentState == EFolderOpen ) // Message folder
+        {
+        TInt targetLength = KFolderMimeType().Length();
+        if ( aMimeType.MaxLength() < targetLength )
+            {
+            targetLength = aMimeType.MaxLength();
+            }
+        aMimeType.Copy( KFolderMimeType().Ptr(), targetLength );
+
+        // Set mime version (do not exceed the allocated buffer)
+        targetLength = KFolderMimeVersion().Length();
+        if ( aMimeVer.MaxLength() < targetLength )
+            {
+            targetLength = aMimeVer.MaxLength();
+            }
+        aMimeVer.Copy( KFolderMimeVersion().Ptr(), targetLength );
+        }
+    else // EMmsMessage
+        {   
+        TInt targetLength = KMmsMimeType().Length();
+        if ( aMimeType.MaxLength() < targetLength )
+            {
+            targetLength = aMimeType.MaxLength();
+            }
+        aMimeType.Copy( KMmsMimeType().Ptr(), targetLength );
+
+        // Set mime version (do not exceed the allocated buffer)
+        targetLength = KMmsMimeVersion().Length();
+        if ( aMimeVer.MaxLength() < targetLength )
+            {
+            targetLength = aMimeVer.MaxLength();
+            }
+        aMimeVer.Copy( KMmsMimeVersion().Ptr(), targetLength );
+        }
+    
+    LOGGER_WRITE_1("aSize: %d", aSize);
+    
+    // Signal we're complete
+    User::RequestComplete( iCallerStatus, KErrNone ); 
+
+    LOGGER_LEAVEFN("CMmsDataStore::DoOpenItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCreateItemL
+// Create new item to the message store.
+// Return the id number of the newly created item
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCreateItemL( TSmlDbItemUid& aUid, TInt aSize, TSmlDbItemUid aParent, 
+    const TDesC8& aMimeType, const TDesC8& /*aMimeVer*/, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN( "CMmsDataStore::DoCreateItemL" );
+    LOGGER_MSG_EC( "Parent folder: %d.", aParent );
+    
+    // Store some variables for further use
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure that we're in proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC( "Invalid state %d.", iCurrentState );
+        }
+        
+    TBool createFolder( EFalse );
+    LOG( aMimeType );
+    if ( aMimeType.Compare( KFolderMimeType() ) == 0 )  
+        {
+        createFolder = ETrue;
+        }
+    else if ( aMimeType.Compare( KMmsMimeType() ) != 0 )
+        {
+        User::RequestComplete( iCallerStatus, KErrNotSupported );
+        LOGGER_WRITE("Bad MIME type");
+        return;
+        }
+    
+    // Ensure that we've got enough disk space for the item
+    if ( iCodecClient->DiskSpaceBelowCriticalLevelL( aSize ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrDiskFull );
+        LOGGER_WRITE( "Disk full" );
+        return;
+        }
+        
+    if( createFolder )
+        {
+        if ( aParent != KMsvMyFoldersEntryIdValue )
+            {
+            User::RequestComplete( iCallerStatus, KErrNotSupported );
+            LOGGER_WRITE( "Bad parent folder" );
+            return;
+            }
+        SAFEDELETE( iDataBuffer );
+        iDataBuffer = CBufFlat::NewL( KDataBufferSize );
+        iCurrentState = EFolderCreating;
+        iCreatedUid = &aUid;
+        iWrittenDataLength = 0;
+        }
+    else
+        {
+        // There is some problems on chunked data adding, so get all data to internal buffer
+        iCreatedUid = &aUid;
+        iCurrentState = EMmsItemCreating;
+        iWriteCounter = 0;
+        iWrittenDataLength = 0;
+        if ( iDataBuffer )
+            {
+            iDataBuffer->ResizeL( aSize );
+            }
+        else
+            {
+            iDataBuffer = CBufFlat::NewL( KDataBufferSize );
+            iDataBuffer->ResizeL( aSize );
+            }
+        }
+        
+    iParentId = aParent; 
+    
+    // Signal we're complete
+    User::RequestComplete( iCallerStatus, KErrNone );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoCreateItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoReplaceItemL
+// Begin the replace operation, ensure that the item really exists
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoReplaceItemL( TSmlDbItemUid aUid, TInt aSize, TSmlDbItemUid aParent, 
+    TBool /*aFieldChange*/, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoReplaceItemL");
+    LOGGER_MSG_EC("Replacing item %d.", aUid);
+    LOGGER_MSG_EC("Parent folder: %d.", aParent);
+    
+    // Store some variables for further use
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC("Invalid state %d.", iCurrentState);
+        }
+
+    // Ensure that we've got enough disk space for the item
+    if ( iCodecClient->DiskSpaceBelowCriticalLevelL( aSize ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrDiskFull );
+        LOGGER_WRITE("Disk full");
+        return;
+        }
+             
+    // Find entry
+    CMsvEntry* entry(NULL);
+    TRAPD( err, entry = iMsvSession->GetEntryL( aUid ) );
+    if ( err != KErrNone )
+        {
+        User::RequestComplete( iCallerStatus, KErrNotFound );
+        LOGGER_MSG_EC("CMsvSession::GetEntryL failed with %d.", err)
+        return;
+        }
+
+    TMsvEntry tEntry = entry->Entry();
+    delete entry;
+    
+    // Check entry type
+    TBool updateFolder(EFalse);
+    if ( tEntry.iType == KUidMsvFolderEntry )
+        {
+        updateFolder = ETrue;
+        LOGGER_WRITE("Type: folder");
+        }
+        
+   if ( ( updateFolder && aParent != KMsvMyFoldersEntryIdValue )
+        || ( !updateFolder && !iMsvApi->ValidFolderL( aParent )
+        || ( aParent != tEntry.Parent() ) ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrNotSupported );
+        LOGGER_MSG_EC("Bad parent folder, message entry parent is %d", tEntry.Parent());
+        return;    
+        }           
+    
+    // Store these for further use
+    iParentId = aParent;
+    iCurrentId = aUid;
+    
+    if ( updateFolder )
+        {
+        SAFEDELETE( iDataBuffer );
+        iDataBuffer = CBufFlat::NewL( KDataBufferSize );
+        iCurrentState = EFolderUpdating;
+        iWrittenDataLength = 0;
+        }
+    else
+        {
+        iCurrentState = EMmsItemUpdating;
+        iWriteCounter = 0;
+        iWrittenDataLength = 0;
+        if ( iDataBuffer )
+            {
+            iDataBuffer->ResizeL( aSize );
+            }
+        else
+            {
+            iDataBuffer = CBufFlat::NewL( KDataBufferSize );
+            iDataBuffer->ResizeL( aSize );
+            }
+        }
+    
+    // Signal we're complete
+    User::RequestComplete( iCallerStatus, KErrNone );
+
+    LOGGER_LEAVEFN("CMmsDataStore::DoReplaceItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoReadItemL
+// Read specified amount of data from the temporary buffer
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoReadItemL( TDes8& aBuffer )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoReadItemL");
+    
+    if ( iCurrentState == EFolderOpen )
+        {   
+        // This is how much we've got left in the buffer
+        TInt left = iDataBuffer->Size() - iReadPosition;
+    
+        // Make sure that there's something to read
+        if ( left > 0 )
+            {
+            // This is how much there's space in the destination buffer
+            TInt destSize = aBuffer.MaxSize();
+
+            // This is how much we can read
+            TInt toRead = destSize < left ? destSize : left;
+
+            // Read the data from the buffer, then update the position
+            iDataBuffer->Read( iReadPosition, aBuffer, toRead );
+            iReadPosition += toRead;
+            }
+        else
+            {
+            LOGGER_WRITE("All data read");
+            User::Leave( KErrEof );
+            }
+        }
+    
+    else if ( iCurrentState == EMmsItemOpenFieldUpdate )
+        {
+        if ( iReadCounter++ == 0 )
+            {
+            TUint8 status = ResolveStatusBits( iUnread );
+            aBuffer.Append( &status, 1 );
+            }
+        else
+            {
+            LOGGER_WRITE("Field update done");
+            User::Leave( KErrEof );             
+            }   
+        }
+        
+    else if ( iCurrentState == EMmsItemOpen )
+        {
+        if ( iReadCounter++ == 0 )
+            {
+            TUint8 status = ResolveStatusBits( iUnread );
+            aBuffer.Append( &status, 1 );
+            iReadPosition = 0;
+            iLastDataChunk = EFalse;
+            iReadAllData = EFalse;
+            }
+        else if ( iReadAllData )
+            {
+            User::Leave( KErrEof );
+            }
+            
+        TInt error = ReadDataRecursively( aBuffer );
+        if ( error != KErrNone )
+            {
+            User::Leave( error );
+            }           
+        }
+    
+    else
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoReadItemL: bad state %d", iCurrentState);
+        User::Leave( KErrNotReady );
+        }   
+
+    LOGGER_LEAVEFN("CMmsDataStore::DoReadItemL");   
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoWriteItemL
+// Write specified amount of data to the temporary buffer
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoWriteItemL( const TDesC8& aData )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoWriteItemL");
+    LOGGER_MSG_EC("%d",iWriteCounter);
+    
+    TInt dataLength = aData.Length();
+    LOGGER_MSG_EC("Data length: %d", dataLength);
+    
+    if ( !( dataLength > 0 ) ) // Should never happen...
+        {
+        LOGGER_WRITE("Error: no data");
+        User::Leave( KErrArgument );
+        }
+
+    if ( iCodecClient->DiskSpaceBelowCriticalLevelL( dataLength ) )
+        {
+        LOGGER_WRITE("Error: disk full");
+        User::Leave( KErrDiskFull );
+        }
+        
+    TInt error( KErrNone );
+    
+    if ( iCurrentState == EFolderCreating || iCurrentState == EFolderUpdating )
+        {
+        // Add data to buffer
+        iDataBuffer->InsertL( iWrittenDataLength, aData );
+        iWrittenDataLength += aData.Size();
+        }
+      
+    else if ( iCurrentState == EMmsItemCreating )
+        {
+        if ( iWriteCounter++ == 0 )
+            {
+            iUnread = aData[0] & KMMS_Flag_Read ? EFalse : ETrue;
+            if ( dataLength > 1 )
+                {
+                TPtrC8 data = aData.Mid(1);
+                iDataBuffer->Write( iWrittenDataLength, data );
+                iWrittenDataLength += data.Length();
+                }
+            }
+        else
+            {
+            TPtrC8 data = aData.Mid(0);
+            iDataBuffer->Write( iWrittenDataLength, data );
+            iWrittenDataLength += dataLength;
+            }    
+        }
+        
+    else if ( iCurrentState == EMmsItemUpdating )
+        {
+        if ( iWriteCounter++ == 0 )
+            {
+            iUnread = aData[0] & KMMS_Flag_Read ? EFalse : ETrue;
+            if ( dataLength > 1 )
+                {
+                TPtrC8 data = aData.Mid(1);
+                iDataBuffer->Write( iWrittenDataLength, data );
+                iWrittenDataLength += data.Length();
+                }
+            else // just status update
+                {
+                UpdateMmsStatusL( iCurrentId, iUnread );
+                LOGGER_MSG_EC("Message status updated: %d", iUnread);
+                }    
+            }
+        else
+            {
+            TPtrC8 data = aData.Mid(0);    
+            iDataBuffer->Write( iWrittenDataLength, data );
+            iWrittenDataLength += dataLength;
+            }    
+        }
+        
+    else
+        {
+        LOGGER_MSG_EC("Wrong state %d", iCurrentState);
+        User::Leave( KErrNotReady );
+        }
+        
+    if ( error != KErrNone )
+        {
+        LOGGER_MSG_EC("iCodecClient->NextDataPart() failed with %d", error);
+        User::Leave( error );
+        }  
+
+    LOGGER_LEAVEFN("CMmsDataStore::DoWriteItemL");  
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCommitItemL
+// Commits item from temporary buffer to the message store
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCommitItemL( TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoCommitItemL");
+    
+    // Store some variables
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    TInt error(KErrNone);
+    
+    if ( iCurrentState == EFolderCreating || iCurrentState == EFolderUpdating )
+        {
+        error = iFolderObjectParser->ImportFolderXml( iDataBuffer->Ptr(0) );
+        if ( error != KErrNone )
+            {
+            User::RequestComplete( iCallerStatus, error );
+            LOGGER_MSG_EC("ImportFolderXml failed with %d", error);
+            return;
+            }
+         
+         const TDesC& name = iFolderObjectParser->GetName();
+         if ( name.Length() <= 0 )
+            {
+            User::RequestComplete( iCallerStatus, KErrNotSupported );
+            LOGGER_WRITE("Folder name is empty");
+            return;
+            }
+            
+         if ( iCurrentState == EFolderCreating )
+            {
+            TMsvId id;
+            error = iMsvApi->AddUserFolderL( id, name );
+            if ( error == KErrNone )
+                {
+                *iCreatedUid = id;
+                iCurrentId = id;
+                }
+            else
+                {
+                LOGGER_MSG_EC("iMsvApi->AddFolderL failed with %d", error);
+                }    
+            }
+         else
+            {
+            error = iMsvApi->UpdateUserFolderL( iCurrentId, name );
+            if ( error != KErrNone )
+                {
+                LOGGER_MSG_EC("iMsvApi->UpdateFolderL failed with %d", error);
+                }
+            }
+        }
+    else if ( iCurrentState == EMmsItemCreating )
+        {
+        LOGGER_WRITE("Create MMS item");
+        TMsvId newId(0);
+        TUint32 flags(0);
+        
+        error = iCodecClient->CreateNewMessageEntryL( iParentId, newId );
+        if ( !error )
+            {
+            iCodecClient->AddMML( *iDataBuffer, iParentId, flags, iUnread, newId, iMsvWait->iStatus );
+            // Wait until the message has been processed
+            iMsvWait->Start();
+            error = iMsvWait->iStatus.Int();
+            LOGGER_WRITE_1("error: %d", error);
+            LOGGER_WRITE_1("AddMML newId: %d", newId);
+            *iCreatedUid = newId;
+            iCurrentId = newId;
+            }
+        }
+    else if ( iCurrentState == EMmsItemUpdating )
+        {
+        if ( iWrittenDataLength > 0 ) // if no data then just field update
+            {
+            TUint32 flags(0);
+            iCodecClient->ReplaceMML( iCurrentId, *iDataBuffer, flags, iUnread, iMsvWait->iStatus );
+            iMsvWait->Start();
+            error = iMsvWait->iStatus.Int();
+            }
+        else
+            {
+            UpdateMmsStatusL( iCurrentId, iUnread );   
+            }
+        }
+    else
+        {
+        User::RequestComplete( iCallerStatus, KErrNotSupported );
+        LOGGER_MSG_EC("Bad state: %d", iCurrentState);
+        return;
+        }
+    
+    delete iDataBuffer;
+    iDataBuffer = NULL;
+    
+    if ( error == KErrNone ) // Update Change Finder
+        {
+        TMsvId service;
+        TMsvEntry msgEntry;
+        
+        // Inform ChangeFinder of added item
+        TSnapshotItem snapshotItem( iCurrentId, iParentId, iUnread );
+        error = iMsvSession->GetEntry( iCurrentId, service, msgEntry );
+        
+        if ( error == KErrNone )
+            {
+            snapshotItem.SetLastChangedDate( msgEntry.iDate );
+            if ( iCurrentState == EFolderCreating || iCurrentState == EFolderUpdating )
+                {
+                snapshotItem.SetFolderNameL( msgEntry.iDetails );
+                }
+            
+            if ( iCurrentState == EFolderCreating || iCurrentState == EMmsItemCreating )
+                {
+                iChangeFinder->ItemAddedL( snapshotItem );
+                }
+            else
+                {
+                iChangeFinder->ItemUpdatedL( snapshotItem );
+                }
+            }
+        else
+            {
+            LOGGER_MSG_EC( "CMsvSession::GetEntry failed with %d", error );
+            }
+        }
+    
+    // Send message if parent folder is Outbox
+    if ( iParentId == KMsvGlobalOutBoxIndexEntryId &&
+        iCurrentState == EMmsItemCreating &&
+        error == KErrNone )
+        {
+        LOGGER_WRITE("Sending message...");
+        iCodecClient->SendMML( iCurrentId, iMsvWait->iStatus );
+        iMsvWait->Start();
+        error = iMsvWait->iStatus.Int();
+        }
+    
+    LOGGER_WRITE_1("error: %d", error);
+    // We'll be waiting for next event, signal we're done
+    iCurrentState = EMmsOpenAndWaiting;
+    User::RequestComplete( iCallerStatus, error );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoCommitItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCloseItem
+// Closes open item in the data store
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCloseItem()
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoCloseItem");
+    SAFEDELETE(iDataBuffer);
+    if ( iCurrentState == EFolderOpen )
+        {
+        iCurrentState = EMmsOpenAndWaiting;
+        }
+    else if ( iCurrentState == EMmsItemOpen )
+        {
+        iCodecClient->ReleaseData();
+        iCurrentState = EMmsOpenAndWaiting; 
+        }
+    else if ( iCurrentState == EMmsItemOpenFieldUpdate )
+        {
+        iCurrentState = EMmsOpenAndWaiting;
+        }
+    else 
+        {
+        LOGGER_MSG_EC("Invalid state %d.", iCurrentState);
+        }
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoCloseItem");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoMoveItemL
+// Moves item from one folder to another in the message store
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoMoveItemL( TSmlDbItemUid aUid,
+    TSmlDbItemUid aNewParent, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoMoveItemL");
+    
+    LOGGER_MSG_EC("Moving item %d.", aUid);
+    
+    // Store some variables for further use
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+
+    // Check that we're in proper state
+    if ( iCurrentState != EMmsOpenAndWaiting ) 
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoMoveItemL, invalid state %d.", iCurrentState);
+        }
+
+    // Ensure that we have this item in the message store   
+    if ( !MmsItemExists( aUid ) )
+        {
+        User::RequestComplete( iCallerStatus, KErrNotSupported ); 
+        LOGGER_WRITE("MMS item not found");
+        return;
+        }
+    
+    iCodecClient->MoveMML( aUid, aNewParent, iMsvWait->iStatus );
+    iMsvWait->Start();  
+    
+    // Inform ChangeFinder of the moved item
+    TMsvId service;
+    TMsvEntry msgEntry;
+    User::LeaveIfError( iMsvSession->GetEntry( aUid, service, msgEntry ) );
+    TBool unread = msgEntry.Unread();
+    TSnapshotItem snapshotItem( aUid, aNewParent, unread );
+    iChangeFinder->ItemMovedL( snapshotItem );
+
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNone );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoMoveItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoDeleteItemL
+// Removes item from the message store
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoDeleteItemL( TSmlDbItemUid aUid, TRequestStatus& aStatus  )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoDeleteItemL");
+    LOGGER_MSG_EC("Deleting item %d.", aUid);
+    
+    // Store some variables for further use
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    TInt error(KErrNone);
+    
+    // Check that we're in proper state
+    if ( iCurrentState != EMmsOpenAndWaiting ) 
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoDeleteItemL, invalid state %d.", iCurrentState);        
+        }
+        
+    // Check if this is a user folder
+    if ( iMsvApi->FindUserFolderL( aUid ) )
+        {
+        LOGGER_WRITE("Folder");
+        error = DeleteAllMessagesInFolderL( aUid );
+        if ( error != KErrNone )
+            {
+            User::RequestComplete( iCallerStatus, error );    
+            LOGGER_MSG_EC("Deleting MMS messages in folder failed with %d", error); 
+            return;
+            }
+        error = iMsvApi->DeleteUserFolderL(aUid);  
+        if ( error != KErrNone )
+            {
+            // Note: folder is not deleted if contains other message items (like MMS)
+            // In this case DeleteUserFolderL returns KErrInUse.    
+            LOGGER_MSG_EC("Deleting folder failed with %d", error); 
+            }       
+        }
+    else if ( MmsItemExists( aUid ) )
+        {
+        // Tell CodecClient to delete this message
+        error = iCodecClient->DeleteMM( aUid );
+        if ( error != KErrNone )
+            {
+            User::RequestComplete( iCallerStatus, error );    
+            LOGGER_MSG_EC("CMmsCodecClient::DeleteMM failed with %d", error);   
+            return;
+            }
+        // Inform ChangeFinder of the removed item
+        iChangeFinder->ItemDeletedL( aUid );
+        }
+    else
+        {
+        User::RequestComplete( iCallerStatus, KErrNotFound ); 
+        LOGGER_MSG_EC("Item %d is not folder or MMS message", aUid);
+        return;
+        }
+    
+    LOGGER_WRITE_1("complete error: %d", error);
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, error );
+    LOGGER_LEAVEFN("CMmsDataStore::DoDeleteItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoSoftDeleteItemL
+// Soft delete isn't supported.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoSoftDeleteItemL( TSmlDbItemUid /*aUid*/, TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoSoftDeleteItemL"); 
+    
+    // Store some variables for further use
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNotSupported );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoSoftDeleteItemL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoDeleteAllItemsL
+// Deletes all items in the standard folders of message store
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoDeleteAllItemsL( TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoDeleteAllItemsL");
+    
+    // Store some variables for further use 
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+   // Check that we're in proper state
+    if ( iCurrentState != EMmsOpenAndWaiting ) 
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoDeleteAllItemsL, invalid state %d.", iCurrentState);
+        }
+        
+    TInt error(KErrNone);
+    TInt result(KErrNone);      
+    
+    // Delete all messages in the standard folders (except outbox)
+    error = DeleteAllMessagesInFolderL( KMsvGlobalInBoxIndexEntryId );
+    if ( error != KErrNone )
+        {
+        result = error;
+        }
+       
+    error = DeleteAllMessagesInFolderL( KMsvDraftEntryId );
+    if ( error != KErrNone )
+        {
+        result = error;
+        }   
+    
+    error = DeleteAllMessagesInFolderL( KMsvSentEntryId );
+    if ( error != KErrNone )
+        {
+        result = error;
+        }
+        
+    error = CleanUserFoldersL();
+    if ( error != KErrNone )
+        {
+        result = error;
+        }
+            
+    iChangeFinder->ResetL();
+    
+    User::RequestComplete( iCallerStatus, result );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoDeleteAllItemsL");
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DeleteAllMessagesInFolderL
+// Deletes all items in the specified folder in message store
+// -----------------------------------------------------------------------------
+TInt CMmsDataStore::DeleteAllMessagesInFolderL( TMsvId aId )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DeleteAllMessagesInFolderL");
+    LOGGER_MSG_EC("Folder: %d", aId);
+    
+    TInt error(KErrNone);
+    
+    // Get the root folder
+    CMsvEntry* msvEntry = iMsvSession->GetEntryL(aId);
+    CleanupStack::PushL(msvEntry);
+    
+    // Find all of it's childs
+    CMsvEntrySelection* messages = msvEntry->ChildrenWithTypeL(KUidMsvMessageEntry);
+    CleanupStack::PopAndDestroy(msvEntry);
+    CleanupStack::PushL(messages);
+        
+    TMsvId service;
+    TMsvEntry msg;
+    TMsvId id;
+    
+    // We are only interested of the MM content
+    for ( TInt index=0; index < messages->Count(); index++ )
+        {
+        id = messages->At( index );
+        LOGGER_MSG_EC("Message item %d:", id);
+        
+        error = iMsvSession->GetEntry( id, service, msg );
+        if ( error != KErrNone )
+            {
+            LOGGER_MSG_EC("GetEntry failed with %d", error);
+            break;
+            }
+        
+        if ( msg.iMtm == KUidMsgTypeMultimedia )
+            {
+            error = iCodecClient->DeleteMM( id );
+            if ( error != KErrNone )
+                {
+                LOGGER_MSG_EC("DeleteMM failed with %d", error);
+                break;
+                }
+            // Update Change Finder
+            iChangeFinder->ItemDeletedL( id );
+            LOGGER_WRITE("MMS message deleted");    
+            }
+        }
+    CleanupStack::PopAndDestroy(messages); 
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DeleteAllMessagesInFolderL");
+
+    return error;
+    }
+
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoHasSyncHistory
+// This method returns ETrue if Data Store has history information. 
+// Slow-sync will be used if Data Store does not have history information.
+// -----------------------------------------------------------------------------
+TBool CMmsDataStore::DoHasSyncHistory() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoHasSyncHistory");
+    LOGGER_LEAVEFN("CMmsDataStore::DoHasSyncHistory");      
+        
+    // iHasHistory is initialized in DoOpenL method
+    return iHasHistory;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoAddedItems
+// This method returns UIDs of added items. Those items are added after previous
+// synchronization with current synchronization relationship. 
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CMmsDataStore::DoAddedItems() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoAddedItems");  
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoAddedItems, invalid state %d.", iCurrentState);
+        }
+    
+    TInt error(KErrNone);
+
+    // Clear new-items array
+    iNewItems->Reset();
+  
+    // Search for new items
+    TRAP( error, iChangeFinder->FindNewItemsL(*iNewItems) )
+    if ( error != KErrNone )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoAddedItems, iChangeFinder->FindNewItemsL leaved with %d.", error);
+        }
+    
+    LOGGER_MSG_EC("New item count: %d.", iNewItems->ItemCount());
+    LOGGER_LEAVEFN("CMmsDataStore::DoAddedItems");      
+    
+    return *iNewItems;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoDeletedItems
+//
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CMmsDataStore::DoDeletedItems() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoDeletedItemsL");   
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoDeletedItems, invalid state %d.", iCurrentState);
+        }
+    
+    TInt error(KErrNone);
+    
+    // Clear deleted-items array
+    iDeletedItems->Reset();
+    
+    // Search for deleted items
+    TRAP( error, iChangeFinder->FindDeletedItemsL( *iDeletedItems ) );
+    if ( error != KErrNone )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoDeletedItems, iChangeFinder->FindDeletedItemsL leaved with %d.", error);
+        }           
+    
+    LOGGER_MSG_EC("Deleted item count: %d.", iDeletedItems->ItemCount());
+    LOGGER_LEAVEFN("CMmsDataStore::DoDeletedItemsL");
+    return *iDeletedItems;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoSoftDeletedItems
+// Not directly supported, equals to "hard" delete
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CMmsDataStore::DoSoftDeletedItems() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoSoftDeletedItems");
+    LOGGER_LEAVEFN("CMmsDataStore::DoSoftDeletedItems");
+
+    iSoftDeletedItems->Reset();
+    return *iSoftDeletedItems;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoModifiedItems
+// Finds all modified items in the data store
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CMmsDataStore::DoModifiedItems() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoModifiedItems");   
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoModifiedItems, invalid state %d.", iCurrentState);
+        }
+    
+    TInt error(KErrNone);
+    
+    // Clear updated-items array
+    iUpdatedItems->Reset();
+    
+    // Search for updated items
+    TRAP( error, iChangeFinder->FindChangedItemsL( *iUpdatedItems ) )
+    if ( error != KErrNone )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoModifiedItems, iChangeFinder->FindChangedItemsL leaved with %d.", error);
+        }
+    
+    LOGGER_MSG_EC("Modified item count: %d.", iUpdatedItems->ItemCount());
+    LOGGER_LEAVEFN("CMmsDataStore::DoModifiedItems");       
+    return *iUpdatedItems;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoMovedItems
+// Finds all moved items in the data store
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CMmsDataStore::DoMovedItems() const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoMovedItems");  
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoMovedItems, invalid state %d.", iCurrentState);
+        }
+    
+    TInt error(KErrNone);
+    
+    // Clear moved-items array
+    iMovedItems->Reset();
+    
+    // Search for moved items
+    TRAP( error, iChangeFinder->FindMovedItemsL( *iMovedItems ) );
+    if ( error != KErrNone )
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoMovedItems, iChangeFinder->FindMovedItemsL leaved with %d.", error);
+        }
+    
+    LOGGER_MSG_EC("Moved item count: %d.", iMovedItems->ItemCount());
+    LOGGER_LEAVEFN("CMmsDataStore::DoMovedItems");
+    return *iMovedItems;    
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoResetChangeInfoL
+// Resets change history in the data store. All content is considered
+// new in the data store point of view.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoResetChangeInfoL( TRequestStatus& aStatus )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoResetChangeInfoL");    
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Check that we're in proper state
+    if ( iCurrentState != EMmsOpenAndWaiting ) 
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoResetChangeInfoL, invalid state %d.", iCurrentState);
+        }   
+            
+    // Reset change info in ChangeFinder
+    iChangeFinder->ResetL();
+    iHasHistory = EFalse;
+    
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNone ); 
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoResetChangeInfoL");
+    }
+        
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCommitChangeInfoL
+// Commits change info. These items are no longer reported, when change
+// information is being queried.
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus, const MSmlDataItemUidSet& aItems )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoCommitChangeInfoL");
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting ) 
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState);
+        }
+
+    // Notify ChangeFinder
+    iChangeFinder->CommitChangesL(aItems);
+    iHasHistory = ETrue;
+        
+    // Signal we're done
+    User::RequestComplete(iCallerStatus, KErrNone);
+        
+    LOGGER_LEAVEFN("CMmsDataStore::DoCommitChangeInfoL");
+    }
+        
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::DoCommitChangeInfoL
+// Commits change info. There is no more nothing to report when change
+// information is being queried. 
+// -----------------------------------------------------------------------------
+void CMmsDataStore::DoCommitChangeInfoL(TRequestStatus& aStatus)
+    {
+    LOGGER_ENTERFN("CMmsDataStore::DoCommitChangeInfoL");
+    
+    iCallerStatus = &aStatus;
+    *iCallerStatus = KRequestPending;
+    
+    // Ensure that we're in a proper state
+    if ( iCurrentState != EMmsOpenAndWaiting ) 
+        {
+        LOGGER_MSG_EC("CMmsDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState);
+        }
+    
+    // Notify ChangeFinder
+    iChangeFinder->CommitChangesL();
+    iHasHistory = ETrue;
+        
+    // Signal we're done
+    User::RequestComplete( iCallerStatus, KErrNone );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::DoCommitChangeInfoL");
+    }
+        
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::RegisterSnapshotL
+// Sets Changefinder to compare against current message store content
+// -----------------------------------------------------------------------------
+void CMmsDataStore::RegisterSnapshotL() const
+    {
+    CSnapshotArray* snapshot = new (ELeave) CSnapshotArray( KSnapshotGranularity );
+    CleanupStack::PushL(snapshot);
+    
+    // Use only standard folders (except outbox)
+    RegisterFolderL(snapshot, KMsvGlobalInBoxIndexEntryId);
+    RegisterFolderL(snapshot, KMsvDraftEntryId);
+    RegisterFolderL(snapshot, KMsvSentEntryId);
+    RegisterFolderL(snapshot, KMsvGlobalOutBoxIndexEntryId);    
+    RegisterUserFoldersL(snapshot);
+    
+    // Set new snapshot to compare against
+    iChangeFinder->SetNewSnapshot(snapshot);
+
+    // Changefinder takes ownership of the snapshot
+    CleanupStack::Pop(snapshot);
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::RegisterFolderL
+// Adds a single folder into the snapshot array
+// -----------------------------------------------------------------------------
+TInt CMmsDataStore::RegisterFolderL(CSnapshotArray* aSnapshot, const TMsvId& aId) const
+    {
+    // Get the root folder
+    CMsvEntry* msvEntry = iMsvSession->GetEntryL(aId);
+    CleanupStack::PushL( msvEntry );
+    
+    // Find all of it's childs
+    CMsvEntrySelection* messages = msvEntry->ChildrenWithTypeL( KUidMsvMessageEntry );
+    CleanupStack::PopAndDestroy( msvEntry );
+    CleanupStack::PushL( messages );
+    
+    TMsvId id;
+    TMsvEntry msg;
+    
+    // We are only interested of the MM content
+    for ( TInt index=0; index<messages->Count(); index++ )
+        {
+        TInt result = iMsvSession->GetEntry( messages->At( index ), id, msg );
+        User::LeaveIfError( result );
+        
+        // We're only interested about the multimedia content
+        if ( msg.iMtm == KUidMsgTypeMultimedia )
+            {
+            // Create snapshot item
+            TKeyArrayFix key(iKey);
+            TSnapshotItem item( (TUint) msg.Id() );
+            
+            item.SetLastChangedDate( msg.iDate );
+            item.SetParentId( msg.Parent() );
+            item.SetUnread( msg.Unread() ? ETrue : EFalse );
+            
+            // Add to snapshot
+            aSnapshot->InsertIsqL( item, key );
+            }
+        }
+
+    CleanupStack::PopAndDestroy( messages );
+    return KErrNone;
+    }
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::MmsItemExists
+// Returns ETrue if MMS item exists in the message store, otherwise EFalse
+// -----------------------------------------------------------------------------    
+TBool CMmsDataStore::MmsItemExists( TMsvId aUid )
+    {
+    CMsvEntry* entry(NULL);
+    
+    // Try to open this item
+    TRAPD( error, entry = iMsvSession->GetEntryL( aUid ) );
+    if ( error != KErrNone )
+        {
+        return EFalse;
+        }
+        
+    TMsvEntry tEntry = entry->Entry();
+    TBool result(EFalse);
+    
+    if ( tEntry.iType == KUidMsvMessageEntry && tEntry.iMtm == KUidMsgTypeMultimedia )
+        {
+        result = ETrue;
+        }
+    
+    delete entry;
+    
+    return result;
+    }
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::ResolveStatusBits
+// Creates status bit field according to TMsvEntry parameter
+// -----------------------------------------------------------------------------
+TUint8 CMmsDataStore::ResolveStatusBits(TBool aUnread)
+    {
+    // Reset the status byte, then find the correct flags
+    TUint8 data(0);
+    
+    // Set status according to the Read/Unread information iCurrentEntry   
+    if ( aUnread )
+        {
+        // Status unset
+        data &= (~KMMS_Flag_Read);
+        }
+    else
+        { 
+        // Status set
+        data |= KMMS_Flag_Read;
+        }
+    
+    return data;
+    }   
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::ReadDataRecursively
+// Write specified amount of data to the temporary buffer
+// -----------------------------------------------------------------------------    
+TInt CMmsDataStore::ReadDataRecursively( TDes8& aBuffer )
+    {
+    LOGGER_ENTERFN("CMmsDataStore::ReadDataRecursively");
+    
+    TInt error(KErrNone);
+    
+    TInt freeBuffer = aBuffer.MaxLength() - aBuffer.Length();
+    
+    if ( freeBuffer == 0 )
+        {
+        LOGGER_WRITE("Destination buffer filled.");
+        return KErrNone;
+        }
+    
+    if ( iReadPosition == 0 )
+        {
+        if ( iLastDataChunk )
+            {
+            LOGGER_WRITE("All MMS data read");
+            iReadAllData = ETrue;
+            return KErrNone;
+            }
+        else
+            {
+            error = iCodecClient->GetNextDataPart( iReadDataChunk, iLastDataChunk );
+            if ( error != KErrNone )
+                {
+                LOGGER_MSG_EC("iCodecClient->GetNextDataPart failed with %d", error);
+                return error;
+                }
+            else
+                {
+                LOGGER_MSG_EC("iCodecClient->GetNextDataPart succeeded, length %d", iReadDataChunk.Length());
+                }   
+            }   
+        }
+        
+    TInt left = iReadDataChunk.Length() - iReadPosition;    
+    
+    if ( freeBuffer < left )
+        {
+        TPtrC8 data = iReadDataChunk.Mid(iReadPosition, freeBuffer);
+        aBuffer.Append(data);
+        iReadPosition += freeBuffer;
+        return KErrNone; 
+        }
+    else
+        {
+        if ( left > 0 )
+            {
+            TPtrC8 data = iReadDataChunk.Mid(iReadPosition, left);
+            aBuffer.Append(data);
+            }
+        error = iCodecClient->ReleaseData();
+        if ( error != KErrNone )
+            {
+            return error;
+            }
+        iReadPosition = 0;
+        return ReadDataRecursively( aBuffer );
+        }           
+    }   
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::UpdateMmsStatusL
+// Updates MMS message status
+// -----------------------------------------------------------------------------
+void CMmsDataStore::UpdateMmsStatusL( TMsvId aId, TBool aUnread )
+    {
+    CMsvEntry* msvEntry = iMsvSession->GetEntryL( aId );
+    const TMsvEntry& oldEntry = msvEntry->Entry();
+    
+    TMsvEntry newEntry( oldEntry );
+    newEntry.SetUnread( aUnread );
+    
+    CleanupStack::PushL( msvEntry );
+    msvEntry->ChangeL( newEntry );
+    CleanupStack::PopAndDestroy( msvEntry );   
+    }
+    
+// -----------------------------------------------------------------------------
+// CMmsDataStore::RegisterUserFoldersL
+// Adds user folder messages into the snapshot array
+// -----------------------------------------------------------------------------
+TInt CMmsDataStore::RegisterUserFoldersL( CSnapshotArray* aSnapshot ) const
+    {
+    LOGGER_ENTERFN("CMmsDataStore::RegisterUserFoldersL");     
+    
+    // Get the folder   
+    CMsvEntry* msvEntry = iMsvSession->GetEntryL( KMsvMyFoldersEntryIdValue );
+    CleanupStack::PushL(msvEntry);
+    
+    // Find all of it's childs
+    CMsvEntrySelection* folders = msvEntry->ChildrenWithTypeL( KUidMsvFolderEntry );
+    CleanupStack::PopAndDestroy( msvEntry ); 
+    CleanupStack::PushL( folders );
+
+    for ( TInt index = 0; index < folders->Count(); index++ )
+        {
+        TMsvId folderId = folders->At(index);
+        
+        if ( folderId != KMsvMyFoldersTemplatesFolderId )
+            {
+            TMsvId service;
+            TMsvEntry folderEntry;
+            TInt result = iMsvSession->GetEntry( folderId, service, folderEntry );
+            User::LeaveIfError( result );
+            
+            TKeyArrayFix key(iKey);
+            TBool unread(EFalse);
+            TSnapshotItem item( (TUint) folderId, folderEntry.Parent(), unread );
+            item.SetLastChangedDate( folderEntry.iDate );
+            item.SetFolderNameL( folderEntry.iDetails );
+            
+            aSnapshot->InsertIsqL( item, key );
+            
+            RegisterFolderL( aSnapshot, folderId );
+            }
+        }
+    
+    CleanupStack::PopAndDestroy( folders );
+    
+    // Register also MMS messages directly under My Folders
+    RegisterFolderL( aSnapshot, KMsvMyFoldersEntryIdValue );
+    
+    LOGGER_LEAVEFN("CMmsDataStore::RegisterUserFoldersL");     
+    
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsDataStore::CleanUserFoldersL
+// Cleans all user folders from MMS messages
+// -----------------------------------------------------------------------------
+TInt CMmsDataStore::CleanUserFoldersL() 
+    {
+    LOGGER_ENTERFN("CMmsDataStore::CleanUserFoldersL");      
+    
+    // Get the folder   
+    CMsvEntry* msvEntry = iMsvSession->GetEntryL( KMsvMyFoldersEntryIdValue );
+    CleanupStack::PushL(msvEntry);
+    
+    // Find all of it's childs
+    CMsvEntrySelection* folders = msvEntry->ChildrenWithTypeL( KUidMsvFolderEntry );
+    CleanupStack::PopAndDestroy( msvEntry ); 
+    CleanupStack::PushL( folders );
+    
+    TInt error(KErrNone);
+    TInt result(KErrNone);
+
+    for ( TInt index = 0; index < folders->Count(); index++ )
+        {
+        TMsvId folderId = folders->At(index);
+        
+        if ( folderId != KMsvMyFoldersTemplatesFolderId )
+            {
+            error = DeleteAllMessagesInFolderL(folderId);
+            if ( error != KErrNone )
+                {
+                LOGGER_MSG_EC("Deleting messages in folder failed with %d", error); 
+                result = error;
+                }
+            error = iMsvApi->DeleteUserFolderL( folderId );
+            if ( error != KErrNone && error != KErrInUse )
+                {
+                // Note: folder is not deleted if contains other message items (like MMS)
+                // In this case DeleteUserFolderL returns KErrInUse.
+                LOGGER_MSG_EC("iMsvApi->DeleteUserFolderL failed with %d", error);
+                result = error;
+                }
+            }
+        }
+    
+    CleanupStack::PopAndDestroy( folders );
+    
+    // Delete all messages directly under My Folders
+    DeleteAllMessagesInFolderL( KMsvMyFoldersEntryIdValue );
+    
+    LOGGER_LEAVEFN("CSmsDataStore::CleanUserFoldersL");
+    
+    return result;       
+    }
+