/*
* Copyright (c) 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:  HtiFtpServicePlugin implementation
*
*/


// INCLUDE FILES
#include "HtiFtpServicePlugin.h"
#include <HtiFtpBackupFakeBase.h>
#include <HtiDispatcherInterface.h>
#include <HtiLogging.h>

#include <e32property.h>
#include <hash.h>

// CONSTANTS
static const TUid KFtpServiceUid = { 0x1020DEC5 };
_LIT( KBackslash, "\\" );
_LIT( KRootPathFormat, "%c:\\" );
_LIT( KHtiFtpBackupFakeDllName, "HtiFtpBackupFake.dll" );
const static TInt KMinBufferSize = 1024;
const static TUint8 KUnicodeMask = 0x1;
const static TInt KFileSizeMsgSize = 5;

//error description
_LIT8( KErrDescrUnknownCmd, "unknown command" );
_LIT8( KErrDescrNoSpace, "no disk space" );
_LIT8( KErrDescrFailedRead, "failed read file" );
_LIT8( KErrDescrFailedWrite, "failed write file" );
_LIT8( KErrDescrFailedGetDir, "failed to read directory" );
_LIT8( KErrDescrEmptyDirname, "directory name empty" );
_LIT8( KErrDescrInvalidDirnameLength, "invalid directory name length" );
_LIT8( KErrDescrEmptyFilename, "file name empty" );
_LIT8( KErrDescrInvalidFilenameLength, "invalid file name length" );
_LIT8( KErrDescrFailedMkDir, "failed create directory" );
_LIT8( KErrDescrFailedRmDir, "failed remove directory" );
_LIT8( KErrDescrFailedDeleFile, "failed delete file" );
_LIT8( KErrDescrFailedRenameFile, "failed rename file" );
_LIT8( KErrDescrFailedCopyFile, "failed copy file" );
_LIT8( KErrDescrFailedMoveFile, "failed move file" );
_LIT8( KErrDescrInvalidStorArgument, "invalid arguments" );
_LIT8( KErrDescrFailedCreateFile, "invalid create file" );
_LIT8( KErrDescrFailedOpenFile, "failed open file" );
_LIT8( KErrDescrFailedCloseFile, "failed close file" );
_LIT8( KErrDescrInvalidDataMessage, "invalid data message" );
_LIT8( KErrDescrNoMemory, "no memory to send file" );
_LIT8( KErrDescrNoCancel, "nothing to cancel" );
_LIT8( KErrDescrBusy, "Busy" );
_LIT8( KErrDescrFailedCopyTcb, "Failed to copy to Tcb directories" );
_LIT8( KErrDescrInvalidForceArgs, "invalid arguments for setforce" );
_LIT8( KErrDescrInvalidChecksumArgs, "invalid arguments for checksum" );
_LIT8( KErrDescrInvalidFormatArgs, "invalid arguments for format" );
_LIT8( KErrDescrInvalidDriveListArgs, "invalid arguments for drive list" );
_LIT8( KErrDescrFailedFormat, "failed to format" );
_LIT8( KErrDescrNotSupported, "command not supported" );

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

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

/**
* CFtpHandlerAO implementation
*/
CFtpHandlerAO::CFtpHandlerAO( MFtpObserverAO* anObserver ):
    CActive( EPriorityStandard ),
    iObserver( anObserver ),
    iCancelFileMan( EFalse )
    {
    CActiveScheduler::Add( this );
    }

CFtpHandlerAO::~CFtpHandlerAO()
    {
    }

void CFtpHandlerAO::Start()
    {
    SetActive();
    }

MFileManObserver::TControl CFtpHandlerAO::NotifyFileManStarted()
    {
    return iCancelFileMan?
                MFileManObserver::EAbort :
                MFileManObserver::EContinue;
    }

MFileManObserver::TControl CFtpHandlerAO::NotifyFileManOperation()
    {
    return iCancelFileMan?
                MFileManObserver::ECancel :
                MFileManObserver::EContinue;
    }

MFileManObserver::TControl CFtpHandlerAO::NotifyFileManEnded()
    {
    return iCancelFileMan?
                MFileManObserver::EAbort :
                MFileManObserver::EContinue;
    }

void CFtpHandlerAO::RunL()
    {
    iObserver->FtpComplete( iStatus.Int() );
    }

void CFtpHandlerAO::DoCancel()
    {
    iCancelFileMan = ETrue;
    }

CProcessLogonAO::CProcessLogonAO( MFtpObserverAO* anObserver ):
    CActive( EPriorityStandard ),
    iObserver( anObserver )
    {
    CActiveScheduler::Add( this );
    }

CProcessLogonAO::~CProcessLogonAO()
    {
    Cancel();
    iProcess.Close();
    }

void CProcessLogonAO::Start( const TDesC& aCmdLine )
    {
    TInt err = iProcess.Create( KHtiFileHlp, aCmdLine );

    if ( err == KErrNone )
        {
        iProcess.Logon( iStatus );
        //start HtiFileHlp.exe
        iProcess.Resume();
        }
    else
        {
        TRequestStatus* pS = &iStatus;
        User::RequestComplete( pS, err );
        }
    SetActive();
    }

void CProcessLogonAO::RunL()
    {
    iObserver->FtpComplete( iStatus.Int() );
    }

void CProcessLogonAO::DoCancel()
    {
    iProcess.LogonCancel( iStatus );
    }

// Create instance of concrete ECOM interface implementation
CHtiFtpServicePlugin* CHtiFtpServicePlugin::NewL()
    {
    CHtiFtpServicePlugin* self = new (ELeave) CHtiFtpServicePlugin;
    CleanupStack::PushL (self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

// Constructor
CHtiFtpServicePlugin::CHtiFtpServicePlugin():
    iState( EIdle ),
    iSendBuffer( NULL ),
    iSendBufferDes( NULL, 0 ),
    iHandlerAO( NULL ),
    iProcessLogonAO( NULL )
    {
    }

CHtiFtpServicePlugin::~CHtiFtpServicePlugin()
    {
    HTI_LOG_FUNC_IN("~CHtiFtpServicePlugin");
    delete iSendBuffer;
    delete iHandlerAO;
    delete iProcessLogonAO;
    delete iFileMan;
    delete iBackupFake;
    iBackupFakeLib.Close();
    iFs.Close();
    HTI_LOG_FUNC_OUT("~CHtiFtpServicePlugin");
    }

// Second phase construction.
void CHtiFtpServicePlugin::ConstructL()
    {
    HTI_LOG_FUNC_IN("CHtiFtpServicePlugin::ConstructL");

    User::LeaveIfError( iFs.Connect() );
    iFileMan = CFileMan::NewL( iFs );

    HTI_LOG_FUNC_OUT("CHtiFtpServicePlugin::ConstructL");
    }

void CHtiFtpServicePlugin::InitL()
    {
    if ( iDispatcher->GetFreeMemory() < KMinBufferSize )
        {
        User::Leave( KErrNoMemory );
        }
    // just guess

    iBufferSize = iDispatcher->GetFreeMemory()/10;
    iBufferSize = iBufferSize < KMinBufferSize ?
                    KMinBufferSize :
                    iBufferSize;
    //test
    iBufferSize = 3*KMinBufferSize;
    }

TInt CHtiFtpServicePlugin::SendControlMsg( TFtpCommand aCmd,
                                           const TDesC8& aMsg  )
    {
    TInt err = KErrNone;
    HBufC8* temp = NULL;
    TRAP( err, temp = HBufC8::NewL( 1 + aMsg.Size() ) );
    if ( err == KErrNone )
        {
        temp->Des().Append( aCmd );
        temp->Des().Append( aMsg );
        err = iDispatcher->DispatchOutgoingMessage( temp,
                        KFtpServiceUid,
                        EFalse,
                        EHtiPriorityControl );
        if ( err != KErrNone )
            {
            delete temp;
            }
        }
    return err;
    }

inline TInt CHtiFtpServicePlugin::SendErrorMsg( TInt anError,
                                                const TDesC8& aMsg )
    {
    SetBURFakeState( EFalse ); // errors ignored
    return iDispatcher->DispatchOutgoingErrorMessage( anError,
                                               aMsg,
                                               KFtpServiceUid);
    }

TBool CHtiFtpServicePlugin::IsBusy()
    {
    return !( iState == EIdle || iState == EStorWait || iState == ERetrWait );
    }

void CHtiFtpServicePlugin::ProcessMessageL(const TDesC8& aMessage,
                THtiMessagePriority aPriority)
    {
    HTI_LOG_FUNC_IN("CHtiFtpServicePlugin::ProcessMessage");

    if ( IsBusy() )
        {
        //should not happend - service busy,deny request
        User::Leave( KErrInUse );
        }

    switch ( aPriority )
        {
        case EHtiPriorityData:
            {
            HandleDataMessageL( aMessage );
            }
            break;
        case EHtiPriorityControl:
            {
            if ( iState == EStorWait || iState == ERetrWait )
                {
                HandleCancelL( aMessage );
                }
            else
                {
                HandleControlMessageL( aMessage );
                }
            }
            break;
        default:
            HTI_LOG_TEXT("Unknown priority");
        }

    HTI_LOG_FUNC_OUT("CHtiFtpServicePlugin::ProcessMessage");
    }

void CHtiFtpServicePlugin::HandleControlMessageL(const TDesC8& aMessage)
    {
    TInt err = KErrNone;

    if ( aMessage.Length() > 0 )
        {
        TBool unicode = aMessage[0]&KUnicodeMask;
        HTI_LOG_FORMAT("cmd %d", aMessage[0] );

        switch ( aMessage[0] )
            {
            case EFtpSTOR:
            case EFtpSTOR_u:
                {
                //receive file
                //get filesize
                if ( aMessage.Length() > 5 )
                    {
                    iFileSize = aMessage[1] +
                                ( aMessage[2] << 8  ) +
                                ( aMessage[3] << 16 ) +
                                ( aMessage[4] << 24 );

                    HTI_LOG_FORMAT( "Filesize %d", iFileSize );
                    //get fileName
                    if ( GetFileNameL( aMessage.Mid( 5 ),
                                      unicode ) )
                        {
                        HandleReceiveFileL();
                        }
                    }
                else
                    {
                    HTI_LOG_TEXT("no file size or file name");
                    User::LeaveIfError( SendErrorMsg( KErrArgument,
                                            KErrDescrInvalidStorArgument) );

                    }
                }
                break;
            case EFtpRETR:
            case EFtpRETR_u:
                {
                //send file
                //get fileName and size
                if ( GetFileNameL( aMessage.Mid(1),
                                   unicode ) )
                    {
                    HandleSendFileL();
                    }
                }
                break;
            case EFtpLIST:
            case EFtpLIST_u:
            case EFtpLISTSIZES:
            case EFtpLISTSIZES_u:
                {
                if ( GetDirectoryL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    HandleListL( unicode,
                        ( KEntryAttHidden| KEntryAttSystem|KEntryAttNormal ),
                        ( aMessage[0] == EFtpLISTSIZES ||
                          aMessage[0] == EFtpLISTSIZES_u ) );
                    }
                }
                break;
            case EFtpListDetail:
            case EFtpListDetail_u:
                {
                if ( GetDirectoryL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    HandleListDetailL( unicode,
                        KEntryAttHidden| KEntryAttSystem|KEntryAttNormal);
                    }
                }
                break;
            case EFtpLISTDIR:
            case EFtpLISTDIR_u:
                {
                if ( GetDirectoryL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    HandleListL( unicode,
                                (KEntryAttMatchExclusive|
                                KEntryAttHidden|KEntryAttSystem|
                                KEntryAttDir), EFalse );
                    }
                }
                break;
            case EFtpListDirDetail:
            case EFtpListDirDetail_u:
                {
                if ( GetDirectoryL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    HandleListDetailL( unicode,
                            KEntryAttMatchExclusive|KEntryAttHidden|
                            KEntryAttSystem|KEntryAttDir);
                    }
                }
                break;
            case EFtpMKD:
            case EFtpMKD_u:
                {
                if ( GetDirectoryL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    if ( IsFileTcb( iFileName ) )
                        {
                        HandleTcbMkdL( iFileName );
                        }
                    else
                        {
                        err = iFs.MkDirAll( iFileName );
                        if ( err == KErrNone || err == KErrAlreadyExists )
                            {
                            User::LeaveIfError( SendControlMsg( EFtpOK,
                                                                KNullDesC8) );
                            }
                        else
                            {
                            User::LeaveIfError( SendErrorMsg( err,
                                                        KErrDescrFailedMkDir ) );

                            }
                        }
                    }
                }
                break;
            case EFtpRMD:
            case EFtpRMD_u:
                {
                if ( GetDirectoryL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    if ( IsFileTcb( iFileName ) )
                        {
                        HandleTcbRmdL( iFileName );
                        }
                    else
                        {
                        delete iHandlerAO;
                        iHandlerAO = NULL;

                        iHandlerAO = new(ELeave) CFtpHandlerAO( this );
                        err = iFileMan->RmDir( iFileName, iHandlerAO->iStatus );
                        if ( err == KErrNone)
                            {
                            iState = ERmdBusy;
                            iHandlerAO->Start();
                            }
                        else
                            {
                            delete iHandlerAO;
                            iHandlerAO = NULL;
                            User::LeaveIfError( SendErrorMsg( err,
                                                        KErrDescrFailedRmDir ) );
                            }
                        }
                    }
                }
                break;
            case EFtpDELE:
            case EFtpDELE_u:
                {
                if ( GetFileNameL( aMessage.Mid( 1 ),
                                   unicode ) )
                    {
                    if ( IsFileTcb( iFileName ) )
                        {
                        HandleTcbDeleteL( iFileName );
                        }
                    else
                        {
                        HandleDeleteL( iFileName );
                        }
                    }
                }
                break;
            case EFtpCANCEL:
                {
                User::LeaveIfError( SendErrorMsg( KErrArgument,
                                                KErrDescrNoCancel ) );
                }
                break;
            case EFtpSETFORCE:
                {
                if ( aMessage.Length() != 2 )
                    {
                    User::LeaveIfError( SendErrorMsg( KErrArgument,
                                                KErrDescrInvalidForceArgs ) );
                    }
                else
                    {
                    if ( aMessage[1] )
                        {
                        // Setting forced operations on
                        err = iBackupFakeLib.Load( KHtiFtpBackupFakeDllName );
                        HTI_LOG_FORMAT( "BackupFake DLL load returned %d", err );
                        if ( err == KErrNone && iBackupFakeLib.Type()[1] ==
                                KHtiFtpBackupFakeInterfaceUid )
                            {
                            HTI_LOG_TEXT( "BackupFake DLL found" );
                            TLibraryFunction entry = iBackupFakeLib.Lookup( 1 );
                            if ( entry != NULL )
                                {
                                iBackupFake = ( CHtiFtpBackupFakeBase* ) entry();
                                TRAP( err, iBackupFake->ConstructL( &iFs ) );
                                }
                            }
                        if ( err == KErrNone )
                            {
                            SendControlMsg( EFtpOK, KNullDesC8 );
                            }
                        else
                            {
                            User::LeaveIfError( SendErrorMsg( KErrNotSupported,
                                KErrDescrNotSupported ) );
                            }
                        }
                    else
                        {
                        // Setting forced operations off
                        delete iBackupFake;
                        iBackupFake = NULL;
                        iBackupFakeLib.Close();
                        SendControlMsg( EFtpOK, KNullDesC8 );
                        }
                    }
                }
                break;
            case EFtpCHECKSUM:
            case EFtpCHECKSUM_u:
                {
                if ( aMessage.Length() < 3 )
                    {
                    User::LeaveIfError(
                        SendErrorMsg( KErrArgument,
                                      KErrDescrInvalidChecksumArgs ) );
                    }
                else if ( GetFileNameL( aMessage.Mid( 2 ), unicode ) )
                    {
                    HandleCheckSumCalcL( (TAlgorithm)aMessage[1], iFileName );
                    }
                }
                break;
            case EFtpFORMAT:
                {
                if ( aMessage.Length() != 3 )
                    {
                    User::LeaveIfError( SendErrorMsg( KErrArgument,
                            KErrDescrInvalidFormatArgs ) );
                    }
                else
                    {
                    HandleFormat( aMessage[1], aMessage[2] );
                    }
                }
                break;
            case EFtpLISTDRIVES:
            case EFtpLISTDRIVES_u:
                {
                if ( aMessage.Length() != 1 )
                    {
                    User::LeaveIfError( SendErrorMsg( KErrArgument,
                        KErrDescrInvalidDriveListArgs ) );
                    }
                else
                    {
                    HandleListDrivesL( unicode );
                    }
                }
                break;
            case EFtpRENAME:
            case EFtpRENAME_u:
                {
                HandleRenameL( aMessage, unicode );
                }
                break;
            case EFtpCOPY:
            case EFtpCOPY_u:
                {
                HandleCopyL( aMessage, unicode );
                }
                break;
            case EFtpMOVE:
            case EFtpMOVE_u:
                {
                HandleMoveL( aMessage, unicode );
                }
                break;
            default:
                {
                //Error: unknown command
                User::LeaveIfError( SendErrorMsg( KErrArgument,
                                                KErrDescrUnknownCmd ) );
                }
            }
        }
    else
        {
        //error: empty request
        User::LeaveIfError( SendErrorMsg( KErrArgument, KErrDescrUnknownCmd ) );
        }
    }

void CHtiFtpServicePlugin::HandleDeleteL( const TDesC& aFilename )
    {
    HTI_LOG_FUNC_IN( "CHtiFtpServicePlugin::HandleDeleteL" );
    delete iHandlerAO;
    iHandlerAO = NULL;

    iHandlerAO = new(ELeave) CFtpHandlerAO( this );
    TInt err = iFileMan->Delete( aFilename, 0, iHandlerAO->iStatus );
    if ( err == KErrNone)
        {
        iState = EDeleBusy;
        iHandlerAO->Start();
        }
    else
        {
        HTI_LOG_FORMAT( "Delete failed %d", err );
        delete iHandlerAO;
        iHandlerAO = NULL;
        User::LeaveIfError( SendErrorMsg( err,
                                    KErrDescrFailedDeleFile ) );
        }
    HTI_LOG_FUNC_OUT( "CHtiFtpServicePlugin::HandleDeleteL" );
    }

void CHtiFtpServicePlugin::HandleRenameL( const TDesC8& aMessage, TBool aUnicode )
    {
    delete iHandlerAO;
    iHandlerAO = NULL;

    TInt originalLenngth = aMessage[1];

    GetFileNameL( aMessage.Mid( 1, originalLenngth + 1 ), aUnicode );

    //If last character is back slash remove it
    RemoveEndBackslash(iFileName);

    TFileName origName = iFileName;

    TInt newNamePos = originalLenngth + 2 ;
    TInt newNameLength = aMessage[newNamePos];
    GetFileNameL( aMessage.Mid( newNamePos, newNameLength + 1 ), aUnicode );

    RemoveEndBackslash( iFileName );

    if ( IsFileTcb( origName ) || IsFileTcb( iFileName ) )
        {
        HandleTcbRenameL( origName, iFileName );
        }
    else
        {
        iHandlerAO = new ( ELeave ) CFtpHandlerAO( this );
        TInt err = iFileMan->Rename( origName, iFileName,
                    CFileMan::EOverWrite, iHandlerAO->iStatus );

        if ( err == KErrNone )
            {
            iState = ERenameBusy;
            iHandlerAO->Start();
            }
        else
            {
            HTI_LOG_FORMAT( "Rename failed %d", err );
            delete iHandlerAO;
            iHandlerAO = NULL;
            User::LeaveIfError( SendErrorMsg(
                    err, KErrDescrFailedRenameFile ) );
            }
        }

    }

void CHtiFtpServicePlugin::RemoveEndBackslash( TFileName& aFileName  )
    {
    //If last character is back slash remove it
    if ( aFileName.Right( 1 ) == KBackslash )
        {
        aFileName.Delete( aFileName.Length() - 1, 1 );
        }
    }

void CHtiFtpServicePlugin::HandleCopyL( const TDesC8& aMessage, TBool aUnicode )
    {
    delete iHandlerAO;
    iHandlerAO = NULL;

    TInt originalLenngth = aMessage[1];

    GetFileNameL( aMessage.Mid( 1, originalLenngth + 1 ), aUnicode );
    TFileName origName = iFileName;

    TInt newNamePos = originalLenngth + 2 ;
    TInt newNameLength = aMessage[newNamePos];
    GetFileNameL( aMessage.Mid( newNamePos, newNameLength + 1 ), aUnicode );

    TInt msglen = aMessage.Length();
    TInt recurse = 1;
    if(msglen>originalLenngth+newNameLength+3)        
        {
        recurse = aMessage[newNamePos+1+newNameLength];
        }
    
    TInt err=0;
    if ( IsFileTcb( origName ) || IsFileTcb( iFileName ) )
        {
        HandleTcbCopyL( origName, iFileName );
        }
    else{
        iHandlerAO = new ( ELeave ) CFtpHandlerAO( this );
        
        if (recurse)
            {
            err = iFileMan->Copy( origName, iFileName,
                ( CFileMan::EOverWrite | CFileMan::ERecurse ),
                iHandlerAO->iStatus );
            }
        else
            {
            err = iFileMan->Copy( origName, iFileName,
                ( CFileMan::EOverWrite ),
                iHandlerAO->iStatus );
            }

        if ( err == KErrNone)
            {
            iState = ECopyBusy;
            iHandlerAO->Start();
            }
        else
            {
            HTI_LOG_FORMAT( "Rename failed %d", err );
            delete iHandlerAO;
            iHandlerAO = NULL;
            User::LeaveIfError( SendErrorMsg( err,
                                        KErrDescrFailedCopyFile ) );
            }
        }
    }

void CHtiFtpServicePlugin::HandleMoveL( const TDesC8& aMessage, TBool aUnicode )
    {
    delete iHandlerAO;
    iHandlerAO = NULL;

    TInt originalLenngth = aMessage[1];

    GetFileNameL( aMessage.Mid( 1, originalLenngth + 1 ), aUnicode );

    RemoveEndBackslash( iFileName );

    TFileName origName = iFileName;

    TInt newNamePos = originalLenngth + 2 ;
    TInt newNameLength = aMessage[newNamePos];
    GetFileNameL( aMessage.Mid( newNamePos, newNameLength + 1 ), aUnicode );
    // make sure destination ends with backslash - destination always directory
    if ( iFileName.Right( 1 ) != KBackslash )
        {
        iFileName.Append( KBackslash );
        }

    if ( IsFileTcb( origName ) || IsFileTcb( iFileName ) )
        {
        HandleTcbMoveL( origName, iFileName );
        }
    else
        {
        iHandlerAO = new ( ELeave ) CFtpHandlerAO( this );
        TInt err = iFileMan->Move( origName, iFileName,
                ( CFileMan::EOverWrite | CFileMan::ERecurse ),
                iHandlerAO->iStatus );

        if ( err == KErrNone )
            {
            iState = EMoveBusy;
            iHandlerAO->Start();
            }
        else
            {
            HTI_LOG_FORMAT( "Move failed %d", err );
            delete iHandlerAO;
            iHandlerAO = NULL;
            User::LeaveIfError( SendErrorMsg( err,
                                        KErrDescrFailedMoveFile ) );
            }
        }
    }

void CHtiFtpServicePlugin::HandleCancelL(const TDesC8& aMessage)
    {

    if ( aMessage.Length()>0 )
        {
        HTI_LOG_FORMAT("cmd %d", aMessage[0]);

        if ( aMessage[0] == EFtpCANCEL )
            {
            //handle cancel
            iFile.Close();

            if ( iState == EStorWait )
                {
                iFs.Delete(iFileName);
                }
            else if ( iState == ERetrWait )
                {
                iDispatcher->RemoveMemoryObserver(this);

                delete iSendBuffer;
                iSendBuffer = NULL;
                }
            //other states filtered out before

            delete iHandlerAO;
            iHandlerAO = NULL;

            iState = EIdle;

            User::LeaveIfError( SendControlMsg( EFtpOK, KNullDesC8) );
            }
        else
            {
            User::LeaveIfError( SendErrorMsg( KErrServerBusy,
                            KErrDescrBusy) );
            }
        }
    else
        {
        //send err
        User::LeaveIfError( SendErrorMsg( KErrArgument,
                            KErrDescrUnknownCmd ) );
        }
    }

void CHtiFtpServicePlugin::CopyUnicode( TDes& aTo, const TDesC8& aFrom )
{
    HTI_LOG_FUNC_IN("CHtiFtpServicePlugin::CopyUnicode");
    TInt len = aFrom.Length()>>1;
    aTo.SetLength( len );
    for ( TInt i = 0; i < len; ++i )
    {
        aTo[i] = (TUint16)aFrom[i<<1] + (((TUint16)aFrom[(i<<1)+1])<<8);
    }
    HTI_LOG_FUNC_OUT("CHtiFtpServicePlugin::CopyUnicode");
}

TBool CHtiFtpServicePlugin::GetFileNameL( const TDesC8& aFilename,
                                          TBool aToUnicode )
    {
    HTI_LOG_FUNC_IN("CHtiFtpServicePlugin::GetFileNameL");
    if ( aFilename.Length() > 1 )
        {
        TInt length = aFilename[0];
        TInt size = aToUnicode ? ( length * 2 ) : length;

        if ( ( size + 1 ) == aFilename.Size() )
            {
            if ( aToUnicode )
                {
                //const TPtrC8 ptr = aFilename.Mid(1).Ptr();
                //iFileName.Copy( (TUint16*)ptr, len );
                CopyUnicode( iFileName, aFilename.Mid( 1 ) );
                }
            else
                {
                iFileName.Copy( aFilename.Mid( 1, length ) );
                }

            HTI_LOG_TEXT( "filename:" );
            HTI_LOG_DES( iFileName );
            return ETrue;
            }
        else
            {
            User::LeaveIfError( SendErrorMsg( KErrArgument,
                                              KErrDescrInvalidFilenameLength ) );
            }
        }
    else
        {
        User::LeaveIfError( SendErrorMsg( KErrArgument,
                                          KErrDescrEmptyFilename ) );
        }

    HTI_LOG_FUNC_OUT("CHtiFtpServicePlugin::GetFileNameL");
    return EFalse;
    }

TBool CHtiFtpServicePlugin::GetDirectoryL( const TDesC8& aDirname,
                                           TBool aToUnicode )
    {
    HTI_LOG_FUNC_IN("CHtiFtpServicePlugin::GetDirectoryL");
    if ( aDirname.Length() > 1 )
        {
        TInt len = aDirname[0];
        TInt size = aToUnicode ? ( len * 2 ) : len;
        if ( ( size + 1 ) == aDirname.Size() )
            {
            if ( aToUnicode )
                {
                //const TUint8* ptr = aDirname.Mid(1).Ptr();
                //iFileName.Copy( (TUint16*)ptr, len );
                CopyUnicode( iFileName, aDirname.Mid(1) );
                }
            else
                {
                iFileName.Copy( aDirname.Mid( 1, len ) );
                }

            HTI_LOG_TEXT( "dir:" );
            HTI_LOG_DES( iFileName );
            if ( iFileName.Right( 1 ) != KBackslash )
                {
                iFileName.Append( KBackslash );
                }
            HTI_LOG_FUNC_OUT("CHtiFtpServicePlugin::GetDirectoryL");
            return ETrue;
            }
        else
            {
            User::LeaveIfError( SendErrorMsg( KErrArgument,
                                              KErrDescrInvalidDirnameLength ) );
            }
        }
    else
        {
        User::LeaveIfError( SendErrorMsg( KErrArgument,
                                          KErrDescrEmptyDirname ) );
        }

    HTI_LOG_FUNC_OUT("CHtiFtpServicePlugin::GetDirectoryL");
    return EFalse;
    }

void CHtiFtpServicePlugin::HandleListL( TBool aUnicodText,
                                        TUint aReadingAtt, TBool aSizes )
    {
    HTI_LOG_FUNC_IN("HandleListL");
    CDir* dir;
    TInt err = iFs.GetDir( iFileName, aReadingAtt, ESortNone, dir );
    if ( err != KErrNone )
        {
        User::LeaveIfError( SendErrorMsg( err, KErrDescrFailedGetDir ) );
        return;
        }

    CleanupStack::PushL( dir );
    //build list
    delete iSendBuffer;
    iSendBuffer = NULL;
    TInt bufferLen = dir->Count()*KMaxFileName;
    if ( aUnicodText )
        {
        bufferLen *= 2;
        }
    bufferLen += dir->Count();
    if ( aSizes )
        {
        bufferLen += 4 * dir->Count();
        }

    iSendBuffer = HBufC8::NewL( bufferLen );
    TInt dirNameLen = 0;
    for ( TInt i = 0; i < dir->Count(); ++i)
        {
        dirNameLen = (*dir)[i].iName.Length();
        iSendBuffer->Des().Append( dirNameLen );
        if ( aUnicodText )
            {
            iSendBuffer->Des().Append( (TUint8*)((*dir)[i].iName.Ptr()),
                                       dirNameLen*2 );
            }
        else
            {
            iSendBuffer->Des().Append( (*dir)[i].iName );
            }
        if ( aSizes )
            {
            TInt size = (*dir)[i].iSize;
            iSendBuffer->Des().Append( (TUint8*)(&size), 4 );
            }
        }

    err = iDispatcher->DispatchOutgoingMessage(iSendBuffer,
                        KFtpServiceUid,
                        EFalse,
                        EHtiPriorityControl);

    if (  err != KErrNone )
        {
        //wait for a memory
        iState = EListBusy;
        iDispatcher->AddMemoryObserver( this );
        }
    else
        {
        iSendBuffer = NULL;
        }

    CleanupStack::PopAndDestroy();//dir
    HTI_LOG_FUNC_OUT("HandleListL");
    }

void CHtiFtpServicePlugin::HandleListDetailL( TBool aUnicodText, TUint aReadingAtt)
    {
    HTI_LOG_FUNC_IN("HandleListDetailL");
    CDir* dir;
    TInt err = iFs.GetDir( iFileName, aReadingAtt, ESortNone, dir );
    if ( err != KErrNone )
        {
        User::LeaveIfError( SendErrorMsg( err, KErrDescrFailedGetDir ) );
        return;
        }

    CleanupStack::PushL( dir );
    //build list
    delete iSendBuffer;
    iSendBuffer = NULL;
    TInt bufferLen = dir->Count()*KMaxFileName;
    if ( aUnicodText )
        {
        bufferLen *= 2;
        }
    // 1 bytes for name length, 4 bytes for file size, 
    // 6 bytes for date time, 3 bytes for attributes(hide, readonly and system)
    bufferLen += (1 + 4 + 6 + 3) * dir->Count();

    iSendBuffer = HBufC8::NewL( bufferLen );
    TInt dirNameLen = 0;
    for ( TInt i = 0; i < dir->Count(); ++i)
        {
        dirNameLen = (*dir)[i].iName.Length();
        iSendBuffer->Des().Append( dirNameLen );
        if ( aUnicodText )
            {
            iSendBuffer->Des().Append( (TUint8*)((*dir)[i].iName.Ptr()),
                                       dirNameLen*2 );
            }
        else
            {
            iSendBuffer->Des().Append( (*dir)[i].iName );
            }
        TInt year = (*dir)[i].iModified.DateTime().Year();
        iSendBuffer->Des().Append((TUint8*)(&year), 2 );
        iSendBuffer->Des().Append( (*dir)[i].iModified.DateTime().Month()+1);
        iSendBuffer->Des().Append( (*dir)[i].iModified.DateTime().Day()+1);
        iSendBuffer->Des().Append( (*dir)[i].iModified.DateTime().Hour());
        iSendBuffer->Des().Append( (*dir)[i].iModified.DateTime().Minute());
        iSendBuffer->Des().Append( (*dir)[i].iModified.DateTime().Second());
        iSendBuffer->Des().Append( (*dir)[i].IsHidden());
        iSendBuffer->Des().Append( (*dir)[i].IsReadOnly());
        iSendBuffer->Des().Append( (*dir)[i].IsSystem());
        if((*dir)[i].IsDir() == EFalse)
            {
            TInt size = (*dir)[i].iSize;
            iSendBuffer->Des().Append( (TUint8*)(&size), 4 );
            }
        }

    err = iDispatcher->DispatchOutgoingMessage(iSendBuffer,
                        KFtpServiceUid,
                        EFalse,
                        EHtiPriorityControl);

    if (  err != KErrNone )
        {
        //wait for a memory
        iState = EListBusy;
        iDispatcher->AddMemoryObserver( this );
        }
    else
        {
        iSendBuffer = NULL;
        }

    CleanupStack::PopAndDestroy();//dir
    HTI_LOG_FUNC_OUT("HandleListDetailL");
    }

void CHtiFtpServicePlugin::HandleDataMessageL( const TDesC8& aMessage )
    {
    switch ( iState )
        {
        case EStorWait:
            {
            iState = EStorBusy;
            iCurrentOffset += aMessage.Size();

            TInt anError = iFile.Write( aMessage );
            if ( anError == KErrNone  )
                {
                HTI_LOG_FORMAT("received %d", iCurrentOffset);
                if ( iCurrentOffset == iFileSize )
                    {
                    HTI_LOG_TEXT("receiving is over");
                    //receiving is over
                    TInt err = iFile.Flush();
                    iFile.Close();
                    iState = EIdle;
                    if ( err != KErrNone )
                        {
                        //err
                        HTI_LOG_TEXT("failed to close file");
                        iFs.Delete(iFileName);
                        SendErrorMsg( err, KErrDescrFailedCloseFile );
                        }
                    else
                        {
                        //if file should be copied to TCB
                        //copy it to to TCB from temp location
                        if ( IsFileTcb( iFileName ) )
                            {
                            HandleTcbCopyL( KTmpFileName, iFileName );
                            }
                        else
                            {
                            SetBURFakeState( EFalse );
                            SendControlMsg( EFtpOK, KNullDesC8 );
                            }
                        }
                    }
                else
                    {
                    iState = EStorWait;
                    }
                }
            else
                {
                HTI_LOG_FORMAT("error writing file %d", anError);
                //abort operation and send err msg

                iFile.Close();
                iFs.Delete(iFileName);
                iState = EIdle;
                SendErrorMsg( anError, KErrDescrFailedWrite );
                }
            }
            break;
        default:
            //do nothing
            User::LeaveIfError( SendErrorMsg( KErrArgument,
                                              KErrDescrInvalidDataMessage ) );
            break;
        }
    }

void CHtiFtpServicePlugin::HandleReceiveFileL()
    {
    HTI_LOG_FUNC_IN("HandleReceiveFileL");

    // create file
    TInt err = KErrNone;

    //if file should be copied to TCB
    //first copy it to temp location
    if ( IsFileTcb( iFileName ) )
        {
        err = iFile.Replace( iFs, KTmpFileName, EFileWrite );
        }
        else
        {
        err = iFile.Replace(
            iFs, iFileName, EFileWrite | EFileShareExclusive );
        }

    if ( err != KErrNone )
        {
        err = iFile.Replace(
            iFs, iFileName, EFileWrite | EFileShareAny );
        }

    if ( err != KErrNone )
        {
        err = iFile.Replace(
            iFs, iFileName, EFileWrite | EFileShareReadersOrWriters );
        }

    if ( err != KErrNone )
        {
        if ( SetBURFakeState( ETrue ) == KErrNone )
            {
            err = iFile.Replace(
                iFs, iFileName, EFileWrite | EFileShareExclusive );
            }
        }

    if ( err != KErrNone )
        {
        HTI_LOG_TEXT("failed create file");
        SendErrorMsg( err, KErrDescrFailedCreateFile );
        return;
        }

    //check that there is enough disk space
    err = iFile.SetSize( iFileSize );
    if ( err != KErrNone )
        {
        HTI_LOG_TEXT("not enough space");
        SendErrorMsg( err, KErrDescrNoSpace );
        iFile.Close();
        iFs.Delete( iFileName );
        return;
        }

    // init receiving byte counter
    iCurrentOffset = 0;
    iState = EStorWait;

    //send ok and
    User::LeaveIfError( SendControlMsg( EFtpOK, KNullDesC8 ) );
    //...wait for data messages
    HTI_LOG_FUNC_OUT("HandleReceiveFileL");
    }

void CHtiFtpServicePlugin::HandleSendFileL()
    {
    //open it
    TInt err = iFile.Open( iFs, iFileName, EFileRead | EFileShareAny );

    if ( err != KErrNone )
        {
        err = iFile.Open( iFs, iFileName, EFileRead | EFileShareReadersOnly );
        }

    if ( err != KErrNone )
        {
        err = iFile.Open( iFs, iFileName, EFileRead | EFileShareReadersOrWriters );
        }

    if ( err != KErrNone )
        {
        if ( SetBURFakeState( ETrue ) == KErrNone )
            {
            err = iFile.Open(
                iFs, iFileName, EFileRead | EFileShareReadersOnly );
            }
        }

    if ( err != KErrNone )
        {
        HTI_LOG_FORMAT("failed open file %d", err);
        SendErrorMsg( err, KErrDescrFailedOpenFile );
        return;
        }

    //send file size
    err = iFile.Size( iFileSize );
    if ( err != KErrNone )
        {
        HTI_LOG_TEXT("failed get filesize");
        User::LeaveIfError( SendErrorMsg( err, KErrDescrFailedOpenFile ) );
        }

    TBuf8<KFileSizeMsgSize> fileSizeMsg;
    fileSizeMsg.Append( (TUint8*)(&iFileSize), 4 );
    User::LeaveIfError( SendControlMsg( EFtpFILESIZE, fileSizeMsg ) );

    iCurrentOffset = 0;
    //start sending
    //always observe when sending
    iDispatcher->AddMemoryObserver( this );

    //create handler for following RFile::Write
    delete iHandlerAO;
    iHandlerAO = NULL;
    iHandlerAO = new(ELeave) CFtpHandlerAO( this );

    //start operation
    ReadToBuffer();
    }

void CHtiFtpServicePlugin::ReadToBuffer()
    {
    HTI_LOG_FUNC_IN("ReadToBuffer");
    //check that iHandlerAO valid
    if ( iHandlerAO )
        {
        if ( !iHandlerAO->IsActive() )
            {
            //dispatch messages in the outgoing queue until run out of memory
            TInt err;
            //allocate memory and read from file
            delete iSendBuffer;
            iSendBuffer = NULL;

            TRAP( err, iSendBuffer = HBufC8::NewL( iBufferSize ) );
            if ( err == KErrNone )
                {
                HTI_LOG_TEXT("read file");
                iState = ERetrBusy;
                iSendBufferDes.Set( iSendBuffer->Des() );
                iFile.Read( iSendBufferDes, iHandlerAO->iStatus  );
                iHandlerAO->Start();
                }
            else
                {
                //go to idle state
                HTI_LOG_TEXT("impossible to alloc mem");
                iDispatcher->RemoveMemoryObserver( this );

                delete iHandlerAO;
                iHandlerAO = NULL;

                iFile.Close();
                iState = EIdle;
                //try to send err message
                SendErrorMsg( KErrNoMemory, KErrDescrNoMemory );
                }
            }
        else
            {
            //error, ignore
            HTI_LOG_TEXT("ERROR: Call ReadBuffer with active handler");
            }
        }
    else
        {
        //error
        HTI_LOG_TEXT("ERROR: Call ReadBuffer with no handler");
        //go to idle
        iState = EIdle;
        }
    HTI_LOG_FUNC_OUT("ReadToBuffer");
    }

void CHtiFtpServicePlugin::SendBuffer()
    {
    HTI_LOG_FUNC_IN("SendBuffer");
    if ( iSendBuffer )
        {
        HTI_LOG_FORMAT("iCurrentOffset %d bytes", iCurrentOffset);
        //send buffer
        TInt err = iDispatcher->DispatchOutgoingMessage( iSendBuffer,
                        KFtpServiceUid,
                        EFalse,
                        EHtiPriorityData);

        if ( err == KErrNone )
            {
            HTI_LOG_TEXT("message was dispatched");
            //message was dispatched
            iSendBuffer = NULL;
            //check do we have to continue
            if ( iCurrentOffset >= iFileSize )
                {
                HTI_LOG_TEXT( "over sending" );

                delete iHandlerAO;
                iHandlerAO = NULL;

                iFile.Close();
                iState = EIdle;

                iDispatcher->RemoveMemoryObserver( this );
                SetBURFakeState( EFalse );
                }
            else
                {
                //read and send next package
                ReadToBuffer();
                }
            }
        else if ( err == KErrNoMemory )
            {
            //wait for memory
            //observer should be active
            iState = ERetrWait;
            }
        else if ( err != KErrNone )
            {
            HTI_LOG_FORMAT("error dispatching outgoing message %d", err );
            //some err, abort operation
            delete iSendBuffer;
            iSendBuffer = NULL;

            delete iHandlerAO;
            iHandlerAO = NULL;

            iFile.Close();

            iState = EIdle;

            iDispatcher->RemoveMemoryObserver( this );
            SetBURFakeState( EFalse );
            }
        }
    else
        {
        //really weird error, go to idle
        HTI_LOG_TEXT("ERROR: SendBuffer with empty iSendBuffer");
        iState = EIdle;
        SetBURFakeState( EFalse );
        }
    HTI_LOG_FUNC_OUT("SendBuffer");
    }

void CHtiFtpServicePlugin::FtpComplete( TInt anError)
    {
    HTI_LOG_FUNC_IN("FtpComplete");
    HTI_LOG_FORMAT("error %d", anError);

    //NOTE: can't leave from here
    switch ( iState )
        {
        case ERmdBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedRmDir );
                }
            delete iHandlerAO;
            iHandlerAO = NULL;

            iState = EIdle;
            }
            break;
        case EDeleBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                if ( SetBURFakeState( ETrue ) != KErrNone )
                    {
                    // Force was not requested, not supported OR already tried.
                    SendErrorMsg( anError, KErrDescrFailedDeleFile );
                    }
                else
                    {
                    // try delete again
                    TRAPD( err, HandleDeleteL( iFileName ) );
                    if ( err == KErrNone )
                        {
                        break;
                        }
                    else
                        {
                        SendErrorMsg( err, KErrDescrFailedDeleFile );
                        }
                    }
                }
            delete iHandlerAO;
            iHandlerAO = NULL;

            iState = EIdle;
            }
            break;
        case ERenameBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedRenameFile );
                }

            delete iHandlerAO;
            iHandlerAO = NULL;

            iState = EIdle;
            }
            break;
        case ECopyBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedCopyFile );
                }

            delete iHandlerAO;
            iHandlerAO = NULL;

            iState = EIdle;
            }
            break;
        case EMoveBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedMoveFile );
                }

            delete iHandlerAO;
            iHandlerAO = NULL;

            iState = EIdle;
            }
            break;
        case EMoveTcbBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedMoveFile );
                }

            delete iProcessLogonAO;
            iProcessLogonAO = NULL;

            iState = EIdle;
            }
            break;
        case ERenameTcbBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedRenameFile );
                }

            delete iProcessLogonAO;
            iProcessLogonAO = NULL;

            iState = EIdle;
            }
            break;
        case EDeleTcbBusy:
            {
            //send OK message
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                if ( SetBURFakeState( ETrue ) != KErrNone )
                    {
                    // Force was not requested, not supported OR already tried.
                    SendErrorMsg( anError, KErrDescrFailedDeleFile );
                    }
                else
                    {
                     // try delete again
                    TRAPD( err, HandleTcbDeleteL( iFileName ) );
                    if ( err == KErrNone )
                        {
                        break;
                        }
                    else
                        {
                        SendErrorMsg( err, KErrDescrFailedDeleFile );
                        }
                    }
                }
            delete iProcessLogonAO;
            iProcessLogonAO = NULL;

            iState = EIdle;
            }
            break;
        case EMkdTcbBusy:
            {
            if ( anError == KErrNone || anError == KErrAlreadyExists )
                {
                SendControlMsg( EFtpOK, KNullDesC8);
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedMkDir );
                }
            delete iProcessLogonAO;
            iProcessLogonAO = NULL;

            iState = EIdle;

            }
            break;
        case ERmdTcbBusy:
            {
            if ( anError == KErrNone || anError == KErrAlreadyExists )
                {
                SendControlMsg( EFtpOK, KNullDesC8);
                }
            else
                {
                SendErrorMsg( anError, KErrDescrFailedRmDir );
                }
            delete iProcessLogonAO;
            iProcessLogonAO = NULL;

            iState = EIdle;

            }
            break;

        case EStorBusy:
            {
            if ( anError == KErrNone  )
                {
                HTI_LOG_FORMAT("received %d", iCurrentOffset);
                if ( iCurrentOffset == iFileSize )
                    {
                    HTI_LOG_TEXT("receiveing is over");
                    //receiveing is over
                    delete iHandlerAO;
                    iHandlerAO = NULL;

                    iState = EIdle;

                    TInt err = iFile.Flush();
                    iFile.Close();
                    if ( err != KErrNone )
                        {
                        //err
                        HTI_LOG_TEXT("failed to close file");
                        iFs.Delete(iFileName);
                        SendErrorMsg( err, KErrDescrFailedCloseFile );
                        }
                    else
                        {
                        //if file should be copied to TCB
                        //copy it to to TCB from temp location
                        if ( IsFileTcb( iFileName ) )
                            {
                            TRAP( err, HandleTcbCopyL(
                                KTmpFileName, iFileName ) );
                            if ( err != KErrNone )
                                {
                                SendErrorMsg( anError, KErrDescrFailedCopyTcb );
                                iFs.Delete( KTmpFileName );
                                delete iProcessLogonAO;
                                iProcessLogonAO = NULL;
                                iState = EIdle;
                                }
                            }
                        else
                            {
                            SetBURFakeState( EFalse );
                            SendControlMsg( EFtpOK, KNullDesC8 );
                            }
                        }
                    }
                else
                    {
                    iState = EStorWait;
                    //busy state is over
                    iDispatcher->Notify(KErrNone);
                    }
                }
            else
                {
                HTI_LOG_FORMAT("error writing file %d", anError);
                //abort operation and send err msg
                delete iHandlerAO;
                iHandlerAO = NULL;

                iFile.Close();
                iFs.Delete(iFileName);
                iState = EIdle;
                SendErrorMsg( anError, KErrDescrFailedWrite );
                }
            }
            break;

        case EStorTcbBusy:
            {
            if ( anError == KErrNone )
                {
                SetBURFakeState( EFalse );
                SendControlMsg( EFtpOK, KNullDesC8 );
                }
            else
                {
                if ( SetBURFakeState( ETrue ) != KErrNone )
                    {
                    // Force was not requested, not supported OR already tried.
                    HTI_LOG_FORMAT("error copy to tcb %d", anError);
                    //abort operation and send err msg
                    SendErrorMsg( anError, KErrDescrFailedCopyTcb );
                    }
                else
                    {
                    // try copy again
                    TRAPD( err, HandleTcbCopyL( KTmpFileName, iFileName ) );
                    if ( err == KErrNone )
                        {
                        break;
                        // don't continue, this method will be called again
                        // by the AO after copying is tried
                        }
                    else
                        {
                        SendErrorMsg( err, KErrDescrFailedCopyTcb );
                        }
                    }
                }

            //delete temp file
            HTI_LOG_TEXT("delete tmp file");
            TInt err = iFs.Delete(KTmpFileName);
            if ( err != KErrNone )
                {
                HTI_LOG_FORMAT("error delete tmp file %d", err);
                }

            delete iProcessLogonAO;
            iProcessLogonAO = NULL;
            iState = EIdle;
            }
            break;

        case ERetrBusy:
            {
            if ( anError == KErrNone )
                {
                HTI_LOG_FORMAT("read %d bytes", iSendBuffer->Size());
                iCurrentOffset += iSendBuffer->Size();

                SendBuffer();
                }
            else
                {
                HTI_LOG_FORMAT("failed read file %d", anError);
                //error reading file
                //abort operation and send err msg
                delete iSendBuffer;
                iSendBuffer = NULL;

                delete iHandlerAO;
                iHandlerAO = NULL;

                iFile.Close();

                iState = EIdle;
                iDispatcher->RemoveMemoryObserver( this );

                SendErrorMsg( anError, KErrDescrFailedRead );
                }
            }
            break;
        default:
            {
            //some error, should not be called
            HTI_LOG_TEXT("invalid state for FtpComplete");
            }
        }
    HTI_LOG_FUNC_OUT("FtpComplete");
    }

void CHtiFtpServicePlugin::NotifyMemoryChange( TInt aAvailableMemory )
    {
    HTI_LOG_FUNC_IN("NotifyMemoryChange");
    switch ( iState )
        {
        case ERetrWait:
            {
            if ( iSendBuffer )
                {
                if ( aAvailableMemory >= iSendBuffer->Size() )
                    {
                    //continue sending
                    SendBuffer();
                    }
                }
            else
                {//impossible
                //nothing to send
                //just reset
                HTI_LOG_TEXT("invalid state for mem");

                delete iHandlerAO;
                iHandlerAO = NULL;
                iFile.Close();
                iDispatcher->RemoveMemoryObserver(this);
                iState = EIdle;
                }
            }
            break;
        case EListBusy:
            {
            if ( iSendBuffer )
                {
                if ( aAvailableMemory >= iSendBuffer->Size() )
                    {
                    //send buffer
                    TInt err = iDispatcher->DispatchOutgoingMessage(
                                    iSendBuffer,
                                    KFtpServiceUid,
                                    EFalse,
                                    EHtiPriorityData);
                    if ( err != KErrNone )
                        {
                        //error, reset
                        HTI_LOG_TEXT("failed to send LIST");
                        delete iSendBuffer;
                        }
                    iSendBuffer = NULL;
                    iDispatcher->RemoveMemoryObserver(this);
                    iState = EIdle;
                    }
                }
            else
                {//impossible
                //nothing to send
                //just reset
                HTI_LOG_TEXT("invalid state for mem");
                iDispatcher->RemoveMemoryObserver(this);
                iState = EIdle;
                }
            }
            break;
        default:
            //some error, should not be called
            HTI_LOG_TEXT("invalid state for mem");
            //iDispatcher->RemoveMemoryObserver(this);
            //iState = EIdle;
        }
    HTI_LOG_FUNC_OUT("NotifyMemoryChange");
    }

TBool CHtiFtpServicePlugin::IsFileTcb( const TDesC& aFilename )
    {
    return aFilename.FindF( KHtiTcbSys ) == KPathOffset ||
           aFilename.FindF( KHtiTcbResource ) == KPathOffset;
    }

void CHtiFtpServicePlugin::HandleTcbDeleteL( const TDesC& aFilename )
    {
    HTI_LOG_FUNC_IN("HandleTcbDeleteL");
    HBufC* cmdLine = HBufC::NewLC( aFilename.Length() + 2 );

    //make command line "d|filename"
    cmdLine->Des().Copy( KHtiFileHlpDeleteCmd );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aFilename );

    HTI_LOG_DES( *cmdLine );

    delete iProcessLogonAO;
    iProcessLogonAO = NULL;

    iProcessLogonAO = new(ELeave) CProcessLogonAO( this );
    iProcessLogonAO->Start( *cmdLine );

    iState = EDeleTcbBusy;

    CleanupStack::PopAndDestroy( cmdLine );
    HTI_LOG_FUNC_OUT("HandleTcbDeleteL");
    }

void CHtiFtpServicePlugin::HandleTcbCopyL( const TDesC& aFromFilename,
                                           const TDesC& aToFilename )
    {
    HTI_LOG_FUNC_IN("HandleTcbCopyL");
    HBufC* cmdLine = HBufC::NewLC( aFromFilename.Length() + aToFilename.Length() + 3 );

    //make command line "c|fromfilename|tofilename"
    cmdLine->Des().Copy( KHtiFileHlpCopyCmd );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aFromFilename );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aToFilename );

    HTI_LOG_DES( *cmdLine );

    delete iProcessLogonAO;
    iProcessLogonAO = NULL;

    iProcessLogonAO = new(ELeave) CProcessLogonAO( this );
    iProcessLogonAO->Start( *cmdLine );

    iState = EStorTcbBusy;

    CleanupStack::PopAndDestroy( cmdLine );
    HTI_LOG_FUNC_OUT("HandleTcbCopyL");
    }

void CHtiFtpServicePlugin::HandleTcbMkdL( const TDesC& aDirname )
    {
    HTI_LOG_FUNC_IN("HandleTcbMkdL");
    HBufC* cmdLine = HBufC::NewLC( aDirname.Length() + 2 );

    //make command line "m|dirname"
    cmdLine->Des().Copy( KHtiFileHlpMkdCmd );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aDirname );

    HTI_LOG_DES( *cmdLine );

    delete iProcessLogonAO;
    iProcessLogonAO = NULL;

    iProcessLogonAO = new(ELeave) CProcessLogonAO( this );
    iProcessLogonAO->Start(*cmdLine);

    iState = EMkdTcbBusy;

    CleanupStack::PopAndDestroy(cmdLine);
    HTI_LOG_FUNC_OUT("HandleTcbMkdL");
    }

void CHtiFtpServicePlugin::HandleTcbRmdL( const TDesC& aDirname )
    {
    HTI_LOG_FUNC_IN("HandleTcbRmdL");
    HBufC* cmdLine = HBufC::NewLC( aDirname.Length() + 2 );

    //make command line "r|dirname"
    cmdLine->Des().Copy( KHtiFileHlpRmdCmd );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aDirname );

    HTI_LOG_DES( *cmdLine );

    delete iProcessLogonAO;
    iProcessLogonAO = NULL;

    iProcessLogonAO = new ( ELeave ) CProcessLogonAO( this );
    iProcessLogonAO->Start( *cmdLine );

    iState = ERmdTcbBusy;

    CleanupStack::PopAndDestroy( cmdLine );
    HTI_LOG_FUNC_OUT("HandleTcbRmdL");
    }

void CHtiFtpServicePlugin::HandleTcbRenameL(
        const TDesC& aTargetName, const TDesC& aDestName )
    {
    HTI_LOG_FUNC_IN("HandleTcbRenameL");
    HBufC* cmdLine = HBufC::NewLC( aTargetName.Length() + 4 + aDestName.Length());

    //make command line "r|dirname"
    cmdLine->Des().Copy( KHtiFileHlpRenameCmd );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aTargetName );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aDestName );

    HTI_LOG_DES( *cmdLine );

    delete iProcessLogonAO;
    iProcessLogonAO = NULL;

    iProcessLogonAO = new ( ELeave ) CProcessLogonAO( this );
    iProcessLogonAO->Start( *cmdLine );

    iState = ERenameTcbBusy;

    CleanupStack::PopAndDestroy( cmdLine );
    HTI_LOG_FUNC_OUT("HandleTcbRenameL");
    }

void CHtiFtpServicePlugin::HandleTcbMoveL(
        const TDesC& aTargetName, const TDesC& aDestName )
    {
    HTI_LOG_FUNC_IN("HandleTcbRenameL");
    HBufC* cmdLine = HBufC::NewLC( aTargetName.Length() + 4 + aDestName.Length());

    //make command line "r|dirname"
    cmdLine->Des().Copy( KHtiFileHlpMoveCmd );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aTargetName );
    cmdLine->Des().Append( KHtiFileHlpDelim );
    cmdLine->Des().Append( aDestName );

    HTI_LOG_DES( *cmdLine );

    delete iProcessLogonAO;
    iProcessLogonAO = NULL;

    iProcessLogonAO = new ( ELeave ) CProcessLogonAO( this );
    iProcessLogonAO->Start( *cmdLine );

    iState = EMoveTcbBusy;

    CleanupStack::PopAndDestroy( cmdLine );
    HTI_LOG_FUNC_OUT("HandleTcbRenameL");
    }

TInt CHtiFtpServicePlugin::SetBURFakeState( TBool aOn )
    {
    HTI_LOG_FUNC_IN( "CHtiFtpServicePlugin::SetBURFakeStateL" );
    TInt err = KErrNone;

    if ( iBackupFake == NULL )
        {
        // Foreced operations not requested or not supported
        err = KErrNotSupported;
        }

    else if ( aOn )
        {
        HTI_LOG_TEXT( "Calling ActivateBackup()" );
        err = iBackupFake->ActivateBackup();
        }

    else
        {
        HTI_LOG_TEXT( "Calling DeactivateBackup()" );
        err = iBackupFake->DeactivateBackup();
        }

    HTI_LOG_FUNC_OUT( "CHtiFtpServicePlugin::SetBURFakeStateL" );
    return err;
    }

void CHtiFtpServicePlugin::HandleCheckSumCalcL( TAlgorithm aAlgorithm,
                                                const TDesC& aFilename )
    {
    HTI_LOG_FUNC_IN( "CHtiFtpServicePlugin::HandleCheckSumCalcL" );

    RFile file;
    TInt err = file.Open( iFs, aFilename, EFileRead | EFileShareReadersOnly );
    if ( err )
        {
        HTI_LOG_FORMAT( "Error opening file, err: %d", err );
        SendErrorMsg( err, KErrDescrFailedOpenFile );
        return;
        }

    CleanupClosePushL( file );
    switch ( aAlgorithm )
        {
        case EMD5:
            {
            HTI_LOG_TEXT( "Using MD5 algorithm" );
            const TInt KBufSize( 1024 );
            TInt size = 0;
            TInt offset = 0;

            file.Size( size );

            HBufC8* buf = HBufC8::NewMaxLC( KBufSize );
            TPtr8 filePtr( buf->Des() );

            CMD5* md5 = CMD5::NewL();
            CleanupStack::PushL( md5 );

            while ( offset < size - KBufSize )
                {
                User::LeaveIfError( file.Read( filePtr, KBufSize ) );
                md5->Hash( filePtr );
                offset += filePtr.Length();
                }

            file.Read( filePtr, size - offset );
            filePtr.SetLength( size - offset );
            md5->Hash( filePtr );

            filePtr.Set( NULL, 0, 0 );
            HBufC8* result = md5->Hash( filePtr ).AllocL();
            CleanupStack::PushL( result );

            HTI_LOG_TEXT( "Got following MD5:" );
            HTI_LOG_DES( *result );

            User::LeaveIfError(
                iDispatcher->DispatchOutgoingMessage( result,
                                                      KFtpServiceUid,
                                                      EFalse,
                                                      EHtiPriorityControl ) );
            CleanupStack::Pop( result ); // do not delete, ownership transfered
            CleanupStack::PopAndDestroy( 2 ); // md5, buf
            break;
            }

        default:
            {
            HTI_LOG_FORMAT( "Invalid algorithm: %d", aAlgorithm );
            SendErrorMsg( KErrNotFound, KErrDescrInvalidChecksumArgs );
            break;
            }
        }
    CleanupStack::PopAndDestroy(); // file

    HTI_LOG_FUNC_OUT( "CHtiFtpServicePlugin::HandleCheckSumCalcL" );
    }

void CHtiFtpServicePlugin::HandleFormat( const TUint8 aDrive, const TUint8 aMode )
    {
    HTI_LOG_FUNC_IN( "CHtiFtpServicePlugin::HandleFormat" );

    // Convert the format mode: in HTI protocol 0 means full format and 1
    // (or anything else currently) means quick format
    TUint formatMode = 0;
    if ( aMode )
        formatMode = ESpecialFormat | EQuickFormat;
    else
        formatMode = ESpecialFormat | EFullFormat;

    // Create the drive name (drive letter and colon)
    TDriveName drive;
    drive.Append( aDrive );
    drive.Append( _L( ":" ) );

    // Check that HTI is not running from the drive that is to be formatted
    RProcess thisProcess;
    if ( thisProcess.FileName().FindF( drive ) == 0 )
        {
        HTI_LOG_FORMAT( "HTI running from drive %S cannot format", &drive );
        SendErrorMsg( KErrInUse, KErrDescrFailedFormat );
        HTI_LOG_FUNC_OUT( "CHtiFtpServicePlugin::HandleFormat" );
        return;
        }

    // Activate backup/restore to close apps that might have files open.
    // Ignore errors - try to format even if backup fake is not supported.
    SetBURFakeState( ETrue );

    // Start the format
    HTI_LOG_FORMAT( "Starting to format drive %S", &drive );
    HTI_LOG_FORMAT( "Format mode = %d", formatMode );
    TInt remainingTracks = 0;
    RFormat format;
    TInt err = format.Open( iFs, drive, formatMode, remainingTracks );
    if ( err == KErrNone )
        {
        HTI_LOG_FORMAT( "Formatting %d tracks", remainingTracks );
        while ( remainingTracks && err == KErrNone )
            {
            err = format.Next( remainingTracks );
            HTI_LOG_FORMAT( "Tracks remaining: %d", remainingTracks );
            }
        }
    format.Close();

    // Deactivate backup/restore - errors ignored.
    SetBURFakeState( EFalse );

    if ( err == KErrNone )
        {
        SendControlMsg( EFtpOK, KNullDesC8 );
        }

    else
        {
        SendErrorMsg( err, KErrDescrFailedFormat );
        }

    HTI_LOG_FUNC_OUT( "CHtiFtpServicePlugin::HandleFormat" );
    }

void CHtiFtpServicePlugin::HandleListDrivesL( TBool aUnicode )
    {
    HTI_LOG_FUNC_IN( "CHtiFtpServicePlugin::HandleListDrivesL" );
    TInt driveCount = 0;
    TDriveList driveList;

    User::LeaveIfError( iFs.DriveList( driveList, KDriveAttAll ) );

    // Buffer for the drive list that is returned
    CBufFlat* driveListBuf = CBufFlat::NewL( 256 );
    CleanupStack::PushL( driveListBuf );
    TInt bufPos = 0;

    for ( TInt i = 0; i < KMaxDrives; i++ )
        {
        if ( driveList[i] )
            {
            HTI_LOG_FORMAT( "Found drive number %d", i );
            TVolumeInfo volInfo;
            if ( iFs.Volume( volInfo, i ) == KErrNone )
                {
                driveCount++;
                // root path with length byte
                TChar driveLetter;
                iFs.DriveToChar( i, driveLetter );
                HTI_LOG_FORMAT( "Got volume info for drive %c",
                        TUint( driveLetter ) );
                TBuf<3> rootPathBuf;
                rootPathBuf.AppendFormat( KRootPathFormat,
                        TUint( driveLetter ) );
                TBuf8<1> lengthBuf;
                lengthBuf.Append( rootPathBuf.Length() );
                TBuf8<6> rootPathBuf8;
                if ( aUnicode )
                    {
                    rootPathBuf8.Append( ( TUint8* ) rootPathBuf.Ptr(),
                            rootPathBuf.Length() * 2 );
                    }
                else
                    {
                    rootPathBuf8.Copy( rootPathBuf );
                    }
                driveListBuf->ExpandL( bufPos, rootPathBuf8.Length() + 1 );
                driveListBuf->Write( bufPos, lengthBuf, 1 );
                bufPos++;
                driveListBuf->Write( bufPos, rootPathBuf8 );
                bufPos += rootPathBuf8.Length();

                // media type 1 byte
                TBuf8<8> tmpBuf;
                tmpBuf.Append( volInfo.iDrive.iType );
                driveListBuf->ExpandL( bufPos, tmpBuf.Length() );
                driveListBuf->Write( bufPos, tmpBuf );
                bufPos += tmpBuf.Length();

                // UID 4 bytes
                tmpBuf.Copy( ( TUint8* ) ( &volInfo.iUniqueID ), 4 );
                driveListBuf->ExpandL( bufPos, tmpBuf.Length() );
                driveListBuf->Write( bufPos, tmpBuf );
                bufPos += tmpBuf.Length();

                // size 8 bytes
                tmpBuf.Copy( ( TUint8* ) ( &volInfo.iSize ), 8 );
                driveListBuf->ExpandL( bufPos, tmpBuf.Length() );
                driveListBuf->Write( bufPos, tmpBuf );
                bufPos += tmpBuf.Length();

                // free space 8 bytes
                tmpBuf.Copy( ( TUint8* ) ( &volInfo.iFree ), 8 );
                driveListBuf->ExpandL( bufPos, tmpBuf.Length() );
                driveListBuf->Write( bufPos, tmpBuf );
                bufPos += tmpBuf.Length();

                // name with length byte
                HBufC8* driveNameBuf8 = NULL;
                TInt driveNameLength = volInfo.iName.Length();
                lengthBuf.Zero();
                lengthBuf.Append( driveNameLength );
                driveListBuf->ExpandL( bufPos, 1 );
                driveListBuf->Write( bufPos, lengthBuf, 1 );
                bufPos++;
                if ( driveNameLength > 0 )
                    {
                    if ( aUnicode )
                        {
                        driveNameBuf8 = HBufC8::NewL( driveNameLength * 2 );
                        driveNameBuf8->Des().Append(
                                ( TUint8* ) volInfo.iName.Ptr(),
                                driveNameLength * 2 );
                        }
                    else
                        {
                        driveNameBuf8 = HBufC8::NewL( driveNameLength );
                        driveNameBuf8->Des().Append( volInfo.iName );
                        }
                    HTI_LOG_FORMAT( "Drive name: %S", &volInfo.iName );
                    driveListBuf->ExpandL( bufPos, driveNameBuf8->Length() );
                    driveListBuf->Write( bufPos, driveNameBuf8->Ptr(),
                        driveNameBuf8->Length() );
                    bufPos += driveNameBuf8->Length();
                    delete driveNameBuf8;
                    driveNameBuf8 = NULL;
                    }
                }
            }
        }

    // All drives added - write number of drives to the beginning of buffer
    TBuf8<1> countBuf;
    countBuf.Append( ( TUint8* ) ( &driveCount ), 1 );
    driveListBuf->InsertL( 0, countBuf, 1 );

    // Make a copy of the buffer to iSendBuffer
    delete iSendBuffer;
    iSendBuffer = NULL;
    iSendBuffer = driveListBuf->Ptr( 0 ).AllocL();
    CleanupStack::PopAndDestroy(); // driveListBuf

    // ...and send it away
    TInt err = iDispatcher->DispatchOutgoingMessage(
        iSendBuffer, KFtpServiceUid, EFalse, EHtiPriorityControl );

    if (  err != KErrNone )
        {
        //wait for memory
        iState = EListBusy;
        iDispatcher->AddMemoryObserver( this );
        }
    else
        {
        iSendBuffer = NULL; // ownership transferred
        }

    HTI_LOG_FUNC_OUT( "CHtiFtpServicePlugin::HandleListDrivesL" );
    }


// End of File
