/*
* Copyright (c) 2007-2008 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:  Skin server's active data owner in backup/restore.
*
*/


#include <f32file.h>

#include "aknssrvactivebackupdataclient.h"
#include "AknsSrvUtils.h"

#include "AknsDebug.h"

// Maximum size of header info.
const TUint KMaxHeaderSize = 256;

// number of bytes to read from file per block
const TUint KCheckSumBlockSize = 128;

// Default size of restore/backup stream size in bytes.
const TUint KArbitraryNumber=1024;

// 8 bytes
const TUint K8Bytes = 8;
// 16 bytes
const TUint K16Bytes = 16;
// Length of extension (including preceding dot).
const TInt KExtensionLength = 4;

_LIT( KSkinRootDir,":\\resource\\skins\\" );

// ======== MEMBER FUNCTIONS ========

// Skin files are backed up in the following format.
// filename contains drive and path in addition to filename.
// <checksum><filesize><filenamelen><filename>

// ---------------------------------------------------------------------------
// Symbian constructor.
// ---------------------------------------------------------------------------
//
CAknsSrvActiveBackupDataClient* CAknsSrvActiveBackupDataClient::NewL( RFs& aFsSession )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::NewL" );
    CAknsSrvActiveBackupDataClient* self =
        new( ELeave ) CAknsSrvActiveBackupDataClient( aFsSession );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );

    return self;
    }

// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CAknsSrvActiveBackupDataClient::~CAknsSrvActiveBackupDataClient()
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::destructor" );
    iFileArray.Reset();
    iFileArray.Close();
    iFile.Close();
    delete iBuffer;
    }

// ---------------------------------------------------------------------------
// Prepare for backup and restore ( no actions).
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::PrepareForBURL( TInt /*aBackupStateValue*/ )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::PrepareForBURL" );
    }

// ---------------------------------------------------------------------------
// Inform that all data has been backed up or restored.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::AllSnapshotsSuppliedL()
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::AllSnapshotsSuppliedL" );
    // Finalize and cleanup.
    return;
    }

// ---------------------------------------------------------------------------
// Not supported.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::ReceiveSnapshotDataL(
    TDriveNumber /*aDrive*/, TDesC8& /*aBuffer*/, TBool /*aLastSection*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::ReceiveSnapshotDataL" );
    User::Leave( KErrNotSupported );
    }

// ---------------------------------------------------------------------------
// Make a guess about data size.
// ---------------------------------------------------------------------------
//
TUint CAknsSrvActiveBackupDataClient::GetExpectedDataSize(
    TDriveNumber /*aDrive*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetExpectedDataSize" );
    // we have no idea at this point - we even don't know what is to be
    // backed up yet
    return KArbitraryNumber;
    }

// ---------------------------------------------------------------------------
// Not supported.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::GetSnapshotDataL(
    TDriveNumber /*aDrive*/, TPtr8& /*aBuffer*/, TBool& /*aFinished*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetSnapshotDataL" );
    User::Leave( KErrNotSupported );
    }

// ---------------------------------------------------------------------------
// Initialize for backup - collect to-be-backed-up-files.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::InitialiseGetBackupDataL(
    TDriveNumber aDrive)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::InitialiseGetBackupDataL" );
    DoInitialiseGetBackupDataL( aDrive );
    }

// ---------------------------------------------------------------------------
// Run state machine for backup. Each file is opened and streamed to the
// BUR engine.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::GetBackupDataSectionL(
    TPtr8& aBuffer, TBool& aFinished)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL" );
    // Make sure that the buffer is empty and starts from the beginning
    aBuffer.SetLength(0);

    // don't assume they set it to false
    aFinished = EFalse;
    // any files to backup
    if( iFileArray.Count() == 0 )
        {
        // nothing to backup - just return the finished flag
        aFinished = ETrue;
        // clear the list and stop.
        iFileArray.Reset();
        AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL no files" );
        return;
        }

    // run the state machine
    while( ETrue )
        {
        switch( iBackupState )
            {
            // open a file for processing
            case EBackupNoFileOpen:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL state %d", iBackupState );
                if( iFileIndex >= iFileArray.Count() )
                    {
                    // all files have been processed - send the finished flag
                    aFinished = ETrue;
                    // clear the list and stop.
                    iFileArray.Reset();
                    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL all processed" );
                    return;
                    }

                // open file to send
                TInt rc=iFile.Open( iFsSession,
                                    iFileArray[iFileIndex].FullName(),
                                    ( EFileRead | EFileShareExclusive | EFileStream ) );
                if( rc != KErrNone )
                    {
                    // there's nothing we can do if we can't open the file
                    // so we just skip it
                    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL skip file" );
                    ++iFileIndex;
                    break;
                    }
                iBackupState = EBackupOpenNothingSent;
                break;
                }
            // nothing sent (so far) for this file - send the header info
            case EBackupOpenNothingSent:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL state %d", iBackupState );
                TInt fileSize;
                TInt retValue = iFile.Size( fileSize );
                if( retValue != KErrNone || fileSize == 0 )
                    {
                    // empty or unreadable - skip this file
                    iBackupState = EBackupEndOfFile;
                    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL skip file2" );
                    break;
                    }

                // build the header - this is an instance member because it
                // has to persist over multiple calls to this method
                TPtr headerPtr = iBuffer->Des();

                // get the checksum - only grab last 4 bytes - enough to be satisfied that
                // the backup and restore worked ok
                TUint64 checksum = CheckSumL( iFile ) & 0xffffffff;

                // build the header - note NOT AppendFormat(); wipes out previous content.
                // <no of files><checksum><filesize><filenamelen><filename>
                headerPtr.Format(_L("%8x%8lx%8x%8x"),
                    iFileArray.Count(),
                    checksum,
                    fileSize,
                    iFileArray[iFileIndex].FullName().Length());
                headerPtr.Append( iFileArray[iFileIndex].FullName() );

                // we need it to look like an 8bit buffer
                TPtr8 headerPtr8(
                    (TUint8*)headerPtr.Ptr(),
                    headerPtr.Size(),
                    headerPtr.Size() );

                // Check how much room is left in the buffer.
                // it starts out empty when we get it from the BUE
                TInt available = aBuffer.MaxSize() - aBuffer.Size();

                // Check is there enough room for the whole header.
                TBool enoughRoom = headerPtr8.Size() < available;

                // append the header to the buffer (only till it's full)
                aBuffer.Append(
                    headerPtr8.Ptr(),
                    enoughRoom ? headerPtr8.Size() : available) ;

                // decide what needs to happen next
                // if complete then we need data, otherwise we need to put
                // the rest of the header in the next chunk
                if( enoughRoom )
                    {
                    iBackupState = EBackupOpenAllHeaderSent;
                    }
                else
                    {
                    // we need to keep track of how much of the header has
                    // been sent so that we only send the reminder on the next
                    // iteration
                    iHeaderSent = available;
                    iBackupState = EBackupOpenPartHeaderSent;
                    }

                // if the buffer's full we need to return control to the backup engine
                // Because the finishedFlag is not set, the BUE will process this
                // chunk and then ask for another
                if( aBuffer.Size() == aBuffer.MaxSize() )
                    {
                    return;
                    }
                break;
                }
            // need to send the rest of the header
            case EBackupOpenPartHeaderSent:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL state %d", iBackupState );
                // get back the header - this is already loaded with the necessary info
                // from the previous state we were in
                TPtr headerPtr = iBuffer->Des();

                // we need it to look like an 8bit buffer
                TPtr8 headerPtr8(
                    (TUint8*)headerPtr.Ptr(),
                    headerPtr.Size(),
                    headerPtr.Size() );

                // Check how many bytes have we yet to send.
                TInt bytesRemaining = headerPtr.Size() - iHeaderSent;

                // Check how much room in the buffer.
                TInt available = aBuffer.MaxSize() - aBuffer.Size();

                // enough, if not send as much as we can
                TBool enoughRoom = bytesRemaining < available;
                aBuffer.Append(
                    headerPtr8.Ptr() + iHeaderSent,
                    enoughRoom ? bytesRemaining : available );

                if( enoughRoom )
                    {
                    iHeaderSent = 0; // ready for next header
                    iBackupState = EBackupOpenAllHeaderSent;
                    }
                else
                    {
                    iHeaderSent += available; // ready to do round again
                    // state remains as EBackupOpenPartHeaderSent
                    }

                // if the buffer's full we need to return control to the backup engine
                // Because the finishedFlag is not set, the BUE will process this
                // chunk and then ask for another
                if( aBuffer.Size() == aBuffer.MaxSize() )
                    {
                    return;
                    }
                break;
                }
            // need to send some data
            case EBackupOpenAllHeaderSent:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL state %d", iBackupState );
                // how many bytes can we send
                TInt available = aBuffer.MaxSize() - aBuffer.Size();

                // create a buffer for this data (plus one for PtrZ)
                HBufC8* transferBuffer = HBufC8::NewLC( available + 1 );
                TPtr8 bufferToSend = transferBuffer->Des();

                // get the data
                User::LeaveIfError( iFile.Read( bufferToSend, available ) );

                // Check how much did we actually read.
                TInt bytesRead = bufferToSend.Size();

                // EOF
                if( bytesRead == 0 )
                    {
                    CleanupStack::PopAndDestroy( transferBuffer );
                    iBackupState = EBackupEndOfFile;
                    break;
                    }

                // add it to the aBuffer
                aBuffer.Append( bufferToSend.PtrZ(), bytesRead );

                // tidy up
                CleanupStack::PopAndDestroy( transferBuffer );

                // if the buffer's full we need to return control to the backup engine
                if( aBuffer.Size() == aBuffer.MaxSize() )
                    {
                    return;
                    }
                break;
                }
            // At the end of the current file.
            case EBackupEndOfFile:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::GetBackupDataSectionL state %d", iBackupState );

                // how many bytes can we send
                if ( aBuffer.Size() != 0 )
                    {
                    TInt available = aBuffer.MaxSize() - aBuffer.Size();
                    // pad the end of the buffer with NULL.
                    HBufC8* transferBuffer = HBufC8::NewLC( available + 1 );
                    TPtr8 bufferToSend = transferBuffer->Des();
                    bufferToSend.FillZ();
                    aBuffer.Append( bufferToSend.PtrZ(), available );
                    CleanupStack::PopAndDestroy( transferBuffer );
                    if( aBuffer.Size() != aBuffer.MaxSize() )
                        {
                        // Sanity check
                        User::Leave( KErrGeneral );
                        }
                    }
                // Close file and move on to next file.
                iFile.Close();
                ++iFileIndex;
                // Start all over again.
                iBackupState = EBackupNoFileOpen;
                break;
                }
            default:
                {
                // not reachable
                return;
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// Initialize restore.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::InitialiseRestoreBaseDataL(
    TDriveNumber /*aDrive*/ )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::InitialiseRestoreBaseDataL" );
    // this is the first state of the restore state machine
    iRestoreState = ERestoreNumberOfFiles;
    // to keep track in the state machine whether any data was actually sent
    iDataRestored = EFalse;
    iFilesRestored = 0;
    }

// ---------------------------------------------------------------------------
// Run state machine for restore. Receive stream from BUR engine and turn it
// to file(s).
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL(
    TDesC8& aBuffer, TBool aFinished )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL" );
    // convert the buffer - this is KMaxHeaderSize=256
    TInt bufferIndex;
    TPtr bufPtr = iBuffer->Des();

    // used to walk the buffer
    // got a new buffer - because each time this method is called, we have a
    // fresh chunk of data
    bufferIndex = 0;

    // to mark when the state machine is through
    TBool done = EFalse;

    // check whether this is an empty restore
    if( aFinished && !iDataRestored )
        {
        // we have to do this and not rely on aFinishedFlag alone, because
        // if aFinished is used, we'll process the last state of the machine
        // which does tidyup, except that if there was no data, no tidyup should
        // be done
        AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL empty restore" );
        return;
        }

    // run the machine
    do
        {
        // Check how many bytes are there available in the buffer for processing.
        TInt bytesAvailable = aBuffer.Length() - bufferIndex;
        // the reason why we are testing finishedFlag is because we must
        // make sure we re-enter the machine to do the tidyup
        if( bytesAvailable <= 0 && !aFinished )
            {
            // ran out of data in the chunk
            // so we return and wait for more data to arrive
            return;
            }

        if( aFinished &&
            iRestoreState != ERestoreComplete &&
            iRestoreState != ERestoreExpectMoreData )
            {
            // ran out of data early
            // will be ERestoreComplete if data not aligned on 128
            // will be ERestoreExpectMoreData if data aligned on 128
            User::Leave( KErrCorrupt );
            }
        // yep there was some data in the chunk if we got here
        if( bytesAvailable > 0 )
            {
            iDataRestored = ETrue;
            }
        switch( iRestoreState )
            {
            case ERestoreNumberOfFiles: // 16 bytes
                {
                TInt bytesToGet = bytesAvailable >= K16Bytes ? K16Bytes : bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // stick it in the heap buffer
                bufPtr.Zero();
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == K8Bytes )
                    {
                    // If file size is now read, read file name size next.
                    iRestoreState = ERestoreExpectChecksum;
                    }

                break;
                }
            case ERestoreExpectChecksum:
                {
                // first of all, process the heap buffer to extract the checksum
                TLex lex( bufPtr );
                lex.SkipSpace();
                lex.Val( iNumberofFiles, EHex );

                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // if we have 16 bytes, get them;
                // otherwise get what we have available.
                // if not complete then we need to go around again to get the rest
                TInt bytesToGet = bytesAvailable >= K16Bytes ? K16Bytes : bytesAvailable;

                // extract the bytes
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // stick it in the heap buffer
                bufPtr.Zero();
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == 8 )
                    {
                    // If complete checksum is available, move on to filesize.
                    iRestoreState = ERestoreExpectFileSize;
                    }
                else
                    {
                    // Otherwise continue reading the checksum.
                    iRestoreState = ERestoreExpectMoreChecksum;
                    }
                break;
                }
            // checksum overran buffer on first/subsequent read.
            case ERestoreExpectMoreChecksum:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // Check how many more bytes do we want.
                TInt bytesNeeded=( K8Bytes - bufPtr.Length() ) << 1;
                TInt bytesToGet=
                    bytesAvailable >= bytesNeeded ? bytesNeeded : bytesAvailable;

                // extract the bytes
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // append it to the heap buffer
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == K8Bytes )
                    {
                    // If complete checksum is now available, move on to filesize.
                    iRestoreState = ERestoreExpectFileSize;
                    }
                else
                    {
                    // same state : ERestoreExpectMoreChecksum;
                    }
                break;
                }
            case ERestoreExpectFileSize: // 16 bytes
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // first of all, process the heap buffer to extract the checksum
                TLex lex( bufPtr );
                lex.SkipSpace();
                lex.Val( iChecksum, EHex );

                // now start getting the file size
                TInt bytesToGet = bytesAvailable >= K16Bytes ? K16Bytes : bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // stick it in the heap buffer
                bufPtr.Zero();
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == K8Bytes )
                    {
                    // If file size is now read, read file name size next.
                    iRestoreState = ERestoreExpectFileNameSize;
                    }
                else
                    {
                    // Otherwise continue reading the file size.
                    iRestoreState = ERestoreExpectMoreFileSize;
                    }
                break;
                }
            // file size didn't fit into one buffer
            case ERestoreExpectMoreFileSize:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // Check how many more bytes do we want.
                TInt bytesNeeded = ( K8Bytes - bufPtr.Length() ) << 1;
                TInt bytesToGet = bytesAvailable >= bytesNeeded ? bytesNeeded : bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // append it to the heap buffer
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == K8Bytes )
                    {
                    // If file size is now read, read file name size next.
                    iRestoreState = ERestoreExpectFileNameSize;
                    }
                else
                    {
                    // same state ERestoreExpectMoreFileSize;
                    }
                break;
                }
            // the size of the file name to restore
            case ERestoreExpectFileNameSize:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // first of all, process the heap buffer to extract the file size
                TLex lex( bufPtr );
                lex.SkipSpace();
                lex.Val( iFileSize, EHex );

                // now start getting the file name size
                TInt bytesToGet = bytesAvailable >= K16Bytes ? K16Bytes : bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // stick it in the heap buffer
                bufPtr.Zero();
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == K8Bytes )
                    {
                    // If filename size is now read, read filename next.
                    iRestoreState = ERestoreExpectFileName;
                    }
                else
                    {
                    // Otherwise, continue reading the filename size.
                    iRestoreState = ERestoreExpectMoreFileNameSize;
                    }
                break;
                }
            // the file name size overran the buffer
            case ERestoreExpectMoreFileNameSize:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // Check how many more bytes do we want.
                TInt bytesNeeded = ( K8Bytes - bufPtr.Length() ) << 1;
                TInt bytesToGet = bytesAvailable >= bytesNeeded ? bytesNeeded : bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // append it to the heap buffer
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == K8Bytes )
                    {
                    // If filename size is now read, read filename next.
                    iRestoreState = ERestoreExpectFileName;
                    }
                else
                    {
                    // same state ERestoreExpectMoreFileNameSize;
                    }
                break;
                }
            case ERestoreExpectFileName:  // the name of the file to restore
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // first of all, process the heap buffer to extract the file name size
                TLex lex( bufPtr );
                lex.SkipSpace();
                lex.Val( iFileNameSize, EHex );

                // now start getting the file name
                TInt bytesToGet = bytesAvailable >= iFileNameSize << 1 ? iFileNameSize << 1:bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // stick it in the heap buffer
                bufPtr.Zero();
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == iFileNameSize )
                    {
                    // If filename is now read, start reading the file content.
                    iRestoreState = ERestoreExpectData;
                    }
                else
                    {
                    // Otherwise, continue reading the file.
                    iRestoreState = ERestoreExpectMoreFileName;
                    }
                break;
                }
            // the file name overran the buffer
            case ERestoreExpectMoreFileName:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // Check how many more bytes do we want.
                TInt bytesNeeded = ( iFileNameSize  -bufPtr.Length() ) << 1;
                TInt bytesToGet = bytesAvailable >= bytesNeeded ? bytesNeeded : bytesAvailable;
                TPtrC8 ptr8 = aBuffer.Mid( bufferIndex, bytesToGet );
                bufferIndex += bytesToGet;

                // convert to 16 bit
                TPtr ptr16(
                    (TUint16*) ptr8.Ptr(),
                    bytesToGet >> 1,
                    bytesToGet >> 1 );

                // append it to the heap buffer
                bufPtr.Append( ptr16 );

                if( bufPtr.Length() == iFileNameSize )
                    {
                    // If filename is now read, start reading the file content.
                    iRestoreState = ERestoreExpectData;
                    }
                else
                    {
                    // same state ERestoreExpectMoreFileName;
                    }
                break;
                }
            // now for the data
            case ERestoreExpectData:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                iFileOpenError = EFalse;
                // iFileSize counts down
                TInt bytesToGet = bytesAvailable >= iFileSize ? iFileSize : bytesAvailable;

                if ( iFsSession.IsValidName( bufPtr ) &&
                     !AknsSrvUtils::IsFile( iFsSession, bufPtr ) &&
                     RestoreFileInPrivateDirectoryL( bufPtr ) == KErrNone )
                    {
                    // write it
                    TInt fileErr = iFile.Write( aBuffer.Mid( bufferIndex, bytesToGet ) );
                    AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL file write %d", fileErr );
                    }
                else
                    {
                    iFileOpenError = ETrue;
                    }

                // update our counters
                bufferIndex += bytesToGet;
                iFileSize -= bytesToGet;

                // Check if finished now.
                if( iFileSize == 0 )
                    {
                    // If file is now written, restore is complete (for this file).
                    iRestoreState = ERestoreComplete;
                    }
                else
                    {
                    // Otherwise, continue reading and writing the file.
                    iRestoreState = ERestoreExpectMoreData;

                    if ( iFileOpenError )
                        {
                        return;
                        }
                    }

                break;
                }
            // will almost certainly exceed a single buffer
            case ERestoreExpectMoreData:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // Check how much more.
                TInt bytesToGet = bytesAvailable >= iFileSize ? iFileSize : bytesAvailable;

                // write it
                if (!iFileOpenError)
                    {
                    TInt fileErr = iFile.Write( aBuffer.Mid( bufferIndex, bytesToGet ) );
                    AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL file write %d", fileErr );
                    }

                // update the counters
                bufferIndex += bytesToGet;
                iFileSize -= bytesToGet;

                // Check if finished now.
                if( iFileSize == 0 )
                    {
                    // If file is now written, restore is complete (for this file).
                    iRestoreState = ERestoreComplete;
                    }
                else
                    {
                    // same state ERestoreExpectMoreData;
                    }
                break;
                }
            // file completely restored
            case ERestoreComplete:
                {
                AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL state %d", iRestoreState );
                // calculate the checksum
                if(!iFileOpenError)
                    {
                    TUint64 cksum = CheckSumL( iFile ) & 0xffffffff;

                    // validate that the checksum matches
                    if( cksum != iChecksum )
                        {
                        AKNS_TRACE_DEBUG2("CAknsSrvActiveBackupDataClient::RestoreBaseDataSectionL checksum fail %d %d", cksum, iChecksum );
                        User::Leave( KErrCorrupt );
                        }

                    // Done with the file, so close it.
                    iFile.Close();
                    }
                iFilesRestored++;

                // end of data - Check if another file is to be restored.
                iRestoreState = ERestoreNumberOfFiles;
                iFileOpenError = EFalse;

                // Each file should end at the end of buffer.
                aFinished = ETrue;
                return;
                }
            default:
                break;
            }
        } while(!done);
    }

// ---------------------------------------------------------------------------
// Incremental restoration is not supported.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::InitialiseRestoreIncrementDataL(
    TDriveNumber /*aDrive*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::InitialiseRestoreIncrementDataL" );
    User::Leave( KErrNotSupported );
    }

// ---------------------------------------------------------------------------
// Incremental restoration is not supported.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::RestoreIncrementDataSectionL(
    TDesC8& /*aBuffer*/, TBool /*aFinished*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreIncrementDataSectionL" );
    User::Leave( KErrNotSupported );
    }

// ---------------------------------------------------------------------------
// Called when restore is complete - sets data back to initial state.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::RestoreComplete(TDriveNumber /*aDrive*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreComplete" );
    // Possibly delete some extra data.
    iDataRestored = EFalse;
    iRestoreState = ERestoreNumberOfFiles;
    }

// ---------------------------------------------------------------------------
// Tidy up when operation is over.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::TerminateMultiStageOperation()
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::TerminateMultiStageOperation" );
    iFileArray.Reset();
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TUint CAknsSrvActiveBackupDataClient::GetDataChecksum(TDriveNumber /*aDrive*/)
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::GetDataChecksum" );
    // not required - not implemented
    return KArbitraryNumber;
    }

// ---------------------------------------------------------------------------
// Initialize backup.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::InitialiseGetProxyBackupDataL(
    TSecureId /*aSID*/, TDriveNumber aDrive )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::InitialiseGetProxyBackupDataL" );
    DoInitialiseGetBackupDataL( aDrive );
    }

// ---------------------------------------------------------------------------
// Initializes restore state machine.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::InitialiseRestoreProxyBaseDataL(
    TSecureId /*aSID*/, TDriveNumber /*aDrive*/ )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::InitialiseRestoreProxyBaseDataL" );
    // this is the first state of the restore state machine
    iRestoreState = ERestoreNumberOfFiles;
    // to keep track in the state machine whether any data was actually sent
    iDataRestored = EFalse;
    }

// ---------------------------------------------------------------------------
// Creates a checksum for the files.
// ---------------------------------------------------------------------------
//
TUint64 CAknsSrvActiveBackupDataClient::CheckSumL(const RFile& aOpenFile) const
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::CheckSumL" );
    // scoot through the database file building the checksum
    TInt seekPos = 0; // rewind first
    User::LeaveIfError( aOpenFile.Seek( ESeekStart, seekPos ) );
    TUint64 total = 0;
    HBufC8* block = HBufC8::NewL( KCheckSumBlockSize );
    CleanupStack::PushL( block );
    TPtr8 ptr=block->Des();
    while( ETrue )
        {
        User::LeaveIfError( aOpenFile.Read( ptr ) );
        TInt len = ptr.Length();
        if( len == 0 )
            {
            break;
            }
        // calculate the checksum
        for( TInt i = 0; i < len; ++i )
            {
            TUint64 carry = total&(0x8000000000000000ULL);
            total<<=1;
            if( carry )
                {
                total|=1;
                }
            TUint in = ptr[i];
            total += in;
            }
        };
    CleanupStack::PopAndDestroy( block );
    // restore file position
    seekPos = 0;
    User::LeaveIfError( aOpenFile.Seek( ESeekStart, seekPos ) );
    return total;
    }

// ---------------------------------------------------------------------------
// Scan directory for files.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::ScanDirectoryForSkinFilesL(
    const TDesC& aRootDir )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::ScanDirectoryForSkinFilesL" );
    CDirScan *dirScan = CDirScan::NewLC( iFsSession );
    dirScan->SetScanDataL(
        aRootDir,
        KEntryAttNormal | KEntryAttHidden | KEntryAttSystem |
        KEntryAttDir,
        ESortNone );

    // Fetch all directories and files from root.
    CDir* entryList = NULL;
    TParse parse;
    for(;;)
        {
        TRAPD( err, dirScan->NextL( entryList ) );

        // Stop in error case, or if no more data.
        if (!entryList  || ( err != KErrNone) )
            {
            break;
            }

        for (TInt i=0; i < entryList->Count(); i++)
            {
            TEntry entry = (*entryList)[i];
            const TDesC& dir = dirScan->FullPath();
            parse.Set( entry.iName, &dir, NULL );
            if ( !entry.IsDir() )
                {
                iFileArray.Append( parse );
                }
            }
        delete entryList;
        }
    AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::ScanDirectoryForSkinFilesL noFiles=%d", iFileArray.Count() );

    // Destroy the list.
    CleanupStack::PopAndDestroy( dirScan );
    }

// ---------------------------------------------------------------------------
// Re-creates backed-up file in private directory.
// ---------------------------------------------------------------------------
//
TInt CAknsSrvActiveBackupDataClient::RestoreFileInPrivateDirectoryL(
    const TDesC& aFileName )
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreFileInPrivateDirectoryL" );
    _LIT( KAknsStaticPath, "\\private\\10207114\\import" );
    _LIT( KAknsSknExtension, ".SKN" );
    TBool parseError = EFalse;
    TInt fileErr = KErrNone;

    HBufC* fileNameBuffer = HBufC::NewL( KMaxFileName );
    TParsePtrC fileName( aFileName );
    TPtr bufferPtr = fileNameBuffer->Des();

    // First append drive and static path.
    if ( fileName.DrivePresent() )
        {
        bufferPtr.Append( fileName.Drive() );
        }
    else
        {
        AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreFileInPrivateDirectoryL parse error1" );
        parseError = ETrue;
        }
    if ( !parseError )
        {
        bufferPtr.Append( KAknsStaticPath );
        }

    if ( fileName.PathPresent() && !parseError )
        {
        // Take path without the trailing backslash.
        TPtrC path = fileName.Path().Left( fileName.Path().Length() - 1 );

        // Locate last backslash.
        TChar backslash('\\');
        TInt bsLoc = path.LocateReverse( backslash );

        // Append skin PID to the directory.
        bufferPtr.Append( fileName.Path().Mid( bsLoc ) );

        if ( fileName.ExtPresent() )
            {
            // Last, append filename. Now string is complete.
            bufferPtr.Append( fileName.NameAndExt() );

            // switch the extension and check for file existance.
            bufferPtr.Replace(
                bufferPtr.Length() - KExtensionLength,
                KExtensionLength,
                KAknsSknExtension );
            if ( !AknsSrvUtils::IsFile( iFsSession, bufferPtr ) )
                {
                // There is no matching .skn-file, do not restore the graphics file.
                fileErr = KErrNotFound;
                }

            // switch back the graphics file extension.
            bufferPtr.Replace(
                bufferPtr.Length() - KExtensionLength,
                KExtensionLength,
                fileName.Ext() );
            }
        else
            {
            AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreFileInPrivateDirectoryL parse error2" );
            parseError = ETrue;
            }
        }
    else
        {
        AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::RestoreFileInPrivateDirectoryL parse error3" );
        parseError = ETrue;
        }

    if( !parseError && fileErr == KErrNone )
        {
        if ( AknsSrvUtils::IsFile( iFsSession, bufferPtr ) )
            {
            // Do not restore, if the file already exists.
            fileErr = KErrAlreadyExists;
            }
        else
            {
            // Create the file.
            fileErr = iFile.Replace(
                iFsSession,
                bufferPtr,
                ( EFileWrite | EFileShareExclusive | EFileStream ) );
            }
        }

    AKNS_TRACE_DEBUG1("CAknsSrvActiveBackupDataClient::RestoreFileInPrivateDirectoryL parse ret=%d", fileErr );
    if ( fileErr != KErrNone )
        {
        // If error creating or parsing the file, skip to next one.
        return fileErr;
        }
    if ( parseError )
        {
        return KErrArgument;
        }
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// C++ constructor.
// ---------------------------------------------------------------------------
//
CAknsSrvActiveBackupDataClient::CAknsSrvActiveBackupDataClient(
    RFs& aFsSession ) : iFsSession( aFsSession )
    {
    }

// ---------------------------------------------------------------------------
// 2nd phase constructor.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::ConstructL()
    {
    AKNS_TRACE_DEBUG("CAknsSrvActiveBackupDataClient::ConstructL" );
    iBuffer=HBufC::NewL( KMaxHeaderSize );
    }

// ---------------------------------------------------------------------------
// Do initialise get backup data.
// ---------------------------------------------------------------------------
//
void CAknsSrvActiveBackupDataClient::DoInitialiseGetBackupDataL(
    TDriveNumber aDrive )
    {
    // this is the index of the file being processed - point to the beginning
    iFileIndex=0;
    // the first state of the backup state machine
    iBackupState=EBackupNoFileOpen;

    TFileName path;
    TChar driveLetter;
    RFs::DriveToChar( aDrive, driveLetter );
    path.Append( driveLetter );
    path.Append( KSkinRootDir );
    // Store the directories and filenames.
    ScanDirectoryForSkinFilesL( path );
    }

// End of file
