/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 */

/*ad*
 * This is the FTP client extension for the NET+OS file system.
 *
 * It contains APIs that use the FTP client APIs to retrieve files from an FTP server 
 * and save it in the NET+OS file system, and to send files from the NET+OS file
 * system to an FTP server.
 *
 * The APIs are:
 *  <list type="unordered">
 *      <item>@link FCSaveFile  - Save a file in the file system</item>
 *      <item>@link FCSendFile  - Send a file to FTP server</item>
 *      <item>@link FCGetFileStatus  - Get file status</item>
 *  </list>					    
 *
 * The following definitions are used to access the file system:
 *      <list type="unordered">
 *          <item> @link _FTPC_RESTRICTED_PERMISSION - Enabled by default, this restricts user
 *                  access of the file system.</item>
 *          <item> @link _FTPC_RESTRICTED_FILE_CREATION - Disabled by default, this allows
 *                  a file to be overwritten. </item>
 *      </list>
 *
 * @external
 * @overview FTP Client & File System Interface
 * @category Networking:FTP_Client:File_System_Interface
 * @library filesys,ftpclnt
 * @include "ftpfile.h"
 * @platform all
 * @since 6.0
 * @see @link Networking:FTP_Client
 * @see @link SystemSWServices:fileSystem
*ad*/

#include <string.h>
#include <Npttypes.h>
#include <sysaccess.h>
#include <fs_api.h>
#include <fs.h>

#include <fclntapi.h>
#include "ftpfile.h"
#include <bsp_api.h>

/*ad*
 * The _FTPC_RESTRICTED_PERMISSION definition only allows users to create & open 
 * a file if they have the right permissions. If a directory is created with NAFS_FS_GROUP1, 
 * the user must have permissions for NAFS_FS_GROUP1 to access the directory.
 *
 * @external
 * @since 6.0
 * @category Networking:FTP_Client:File_System_Interface
 *
*ad*/
#define _FTPC_RESTRICTED_PERMISSION      1

/*ad*
 * When defined, _FTPc_RESTRICTED_FILE_CREATION allows users to create a file
 * only if it does not already exist. If this definition is 0, data will overwrite
 * an existing file.
 *
 * @external
 * @since 6.0
 * @category Networking:FTP_Client:File_System_Interface
 *
*ad*/
#define _FTPC_RESTRICTED_FILE_CREATION   0

#define _DEBUG_PRINTF   0

/* buffer size for file handling on each ftp client */
#define FTPC_FILE_BUFFER_LEN    4096

/*ad*
 * Parsing directory or file return status
 *
 * @external
 * @category Networking:FTP_Client:File_System_Interface
 * @since 6.0
 *
 * @name fc_parsing_status   "Parsing File Return Status"
 *
 * @param FILE_OK                 Directory or file was successfully parsed
 * @param INVALID_FILE_LEN        Unable to parse the file 
 * @param INVALID_DIRECTORY_LEN   Unable to parse the directory
*ad*/
#define FILE_OK                 0
#define INVALID_FILE_LEN        -1
#define INVALID_DIRECTORY_LEN   -2

#define _READ_PERM_OFFSET       2
#define _WRITE_PERM_OFFSET      16


enum {
    eFtpFileState_Error = -1,
    eFtpFileState_Close,
    eFtpFileState_Create,
    eFtpFileState_Open,
    eFtpFileState_Data
};

enum {
    eFtpFileAction_None,
    eFtpFileAction_Save,
    eFtpFileAction_Send
};


/*ad*
 * FTP client to file system interface control block.
 *
 * @external
 * @category Networking:FTP_Client:File_System_Interface
 * @since 6.0
 *
 * @param dir_path              The array to store the directory path.
 * @param file_name             The array to store the file name.
 * @param io_request            The I/O request control block.
 * @param client_handler        The FTP client's handle.
 * @param permission            The user's read and write permissions mask.
 * @param file_handler          The handle to a file.
 * @param file_offset           The offset in bytes from the start of a file.
 * @param file_size             The size of a file in bytes.
 * @param buffer                A general purpose buffer used as needed.
 * @param buffer_length         The size of the buffer.
 * @param error_status          The error status after a file I/O request is completed or terminated.
 * @param state                 The FTP client's current file I/O state.
 * @param action                The FTP client's current file I/O activity.
 * 
*ad*/

typedef struct {
    char    dir_path[NAFS_DIRECTORY_PATH_SIZE];
    char    file_name[NAFS_FILE_NAME_SIZE];

    NAFS_IO_REQUEST_CB  io_request;
    unsigned long       client_handler;
    unsigned int        permission;
    unsigned int        file_handler;
    unsigned int        file_offset;
    unsigned int        file_size;
    char                buffer[FTPC_FILE_BUFFER_LEN];
    int                 buffer_length;
    int                 error_status;
    int                 state;
    int                 action;

} FC_FILESYSTEM_T;

extern void FCSetFileSystem(unsigned long handler, unsigned long filesys_handler, void *callback);
extern unsigned long FCGetFileSystem(unsigned long handler);
extern void FCAbortFileAction(unsigned long handler);


/*
 * This routine parses the given fullPathName for directory path & filename. It looks for slashes 
 * for directory and filename separation. It searches the last slash for filename. 
 * The directory and filename will be copied into the given memory pointers.
 *
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param    fullPathName  pointer to the full file name path
 *
 * param    dirpath       pointer to the memory where directory path to be copied into
 * param    dirlen       pointer to the memory for the length of dirpath. It initially
 *                          tells the size of the dirpath. It is set to and returns the 
 *                          actual length of dirpath.
 * param    filename     pointer to the memory where filename is to be copied into.
 * param    filelen      pointer to the memory for the length of filename. It initially
 *                          tells the size of the filename. It is set to and returns the 
 *                          actual length of filename.
 *
 *                        
 * return  INVALID_DIRECTORY_LEN  Invalid directory size. Not enough memory to store the actual directory.
 * return  INVALID_FILE_LEN       Invalid filename size. Not enough memory to store the actual filename.
 * return  FILE_OK                The filename has been successfully obtained. 
 *
 */
static int parse_file_path(char * fullPathName, char * dirpath, int * dirlen, char * filename, int * filelen)
{
#define _ASCII_FSLASH   "/"  /* 0x2F */
#define _ASCII_BSLASH   "\\"

    char    *bufferPtr, *namePtr = NULL;
    int     i, len = 0, rc = FILE_OK;

    static char *slash[] = { _ASCII_FSLASH, _ASCII_BSLASH };   

    NA_ASSERT((int)fullPathName);
    NA_ASSERT((int)dirpath);
    NA_ASSERT((int)filename);

    for (i=0; i < 2; i++)
    {
        /* search for slash until the last slash */
        bufferPtr = fullPathName;
        while (bufferPtr != NULL) 
        {
            bufferPtr = strstr(bufferPtr, slash[i]);
            if (bufferPtr != NULL)
            {
                bufferPtr++;
                namePtr = bufferPtr;
            }
        }
        if (namePtr != NULL) 
        {   /* got filename */
            len = (namePtr - fullPathName)-1;
            break;
        }
    }
    /* no directory is given */
    if (namePtr == NULL) 
        namePtr = fullPathName;


    if (len > 0 && len < *dirlen)
    {
        strncpy(dirpath, fullPathName, len);
        dirpath[len] = '\0';
        if (strstr(dirpath, slash[i]) == NULL)
            strcat(dirpath, slash[i]);

        *dirlen = strlen(dirpath);
    }
    else if (len > *dirlen)
        rc = INVALID_DIRECTORY_LEN;

    if (strlen(namePtr) <= (unsigned int)*filelen)
        strcpy(filename, namePtr);
    else
        rc = INVALID_FILE_LEN;

    *filelen = strlen(namePtr);

    return rc;    
}

/*
 * Waits for io request status from the NETOS file system.
 *
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param    io_request       pointer to the file system io hander
 * param    byteTransferred  pointer to memory where number of bytes which have been processed
 *                              by the file system are copied to.
 * param    error_status     pointer to memory where error that is encountered by file 
 *                              system is copied to. 
 *                        
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 *
 * see link file_sys_errors
 * see link NAFS_IO_REQUEST_CB
 */
static int wait_io_status(NAFS_IO_REQUEST_CB * io_request, unsigned int * byteTransferred, int * error_status)
{
    unsigned int        requestType, status;
    int                 rc;

    do {    
        /* inquire file system io request */    
        rc = NAFSio_request_cb_status(io_request, &requestType, &status, 
                                      error_status, byteTransferred); 

        if (rc == NAFS_SUCCESS)
        {
            if (status == NAFS_IO_REQUEST_COMPLETED)
                break;
        }
        tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(10));

    } while (rc == NAFS_SUCCESS && (status == NAFS_IO_REQUEST_PENDING || 
            status == NAFS_IO_REQUEST_PROCESSING));

#if (_DEBUG_PRINTF > 0)
    if (*error_status != NAFS_SUCCESS)
        NA_DBPF("io_request_callback_fn Error: error status %d\n", *error_status);
#endif
    
    return  ((rc != NAFS_SUCCESS || *error_status != NAFS_SUCCESS) ? NAFTPC_FILESYSTEMERR : NAFTPC_NOERROR);
}

/*
 * Calls NETOS file system API to create a file
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param   pFile    pointer to FTP Client file system structure to be processed.
 * param   wait      tells whether it waits for process to be finished.
 *          <list type="unordered">
 *          <item> FTPC_NOWAIT </item>
 *          <item> FTPC_WAIT </item>
 *          </list>
 *
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 *
 * see link fc_wait_status
 * see link NAFTPC
 * see link NAFScreate_file
 * see link wait_io_status
 */
static int create_file(FC_FILESYSTEM_T * pFile, int wait)
{
    int             rc = NAFTPC_NOERROR;
    int             status, i;
    unsigned int    bytesTransferred;
    unsigned int    group_id;

    pFile->state = eFtpFileState_Create;

    for (group_id = NAFS_FS_GROUP1, i=0; group_id <= NAFS_FS_GROUP8; i++, group_id = group_id << i)
    {
        if ((pFile->permission & (NASYSACC_FS_GROUP1_READ << i)) != 0 &&
            (pFile->permission & (NASYSACC_FS_GROUP1_WRITE << i)) != 0)
        {
            break;
        }
    }

#if (_DEBUG_PRINTF > 0)
    printf("create_file: %s%s perm 0x%X group id %d\n", pFile->dir_path, pFile->file_name,
                        pFile->permission, group_id);
#endif

    status = NAFScreate_file(pFile->dir_path, pFile->file_name, pFile->permission,
                          group_id, &pFile->io_request);

    if (status != NAFS_SUCCESS)
    {
        NA_DBPF("create_file Error: NAFScreate_file returns %d\n", status);
        rc = NAFTPC_FILESYSTEMERR;        
    }
    else if (wait != FTPC_NOWAIT)
    {
        rc = wait_io_status(&pFile->io_request, &bytesTransferred, &pFile->error_status);
    }

    return rc;
}

/*
 * Calls NETOS file system API to open a file
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param   pFile    pointer to FTP Client file system structure to be processed.
 * param   wait      tells whether it waits for process to be finished.
 *          <list type="unordered">
 *          <item> FTPC_NOWAIT </item>
 *          <item> FTPC_WAIT </item>
 *          </list>
 *
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 *
 * see link fc_wait_status
 * see link NAFTPC
 * see link NAFSopen_file
 * see link wait_io_status
 */
static int open_file(FC_FILESYSTEM_T * pFile, int wait)
{
    int             rc = NAFTPC_NOERROR;
    int             status;
    unsigned int    bytesTransferred;

    pFile->state = eFtpFileState_Open;

#if (_DEBUG_PRINTF > 0)
    printf("open_file: %s %s perm 0x%X\n", pFile->dir_path, pFile->file_name, pFile->permission);
#endif

    status = NAFSopen_file(pFile->dir_path, pFile->file_name, &pFile->file_handler, 
                       pFile->permission, &pFile->io_request);

    if (status != NAFS_SUCCESS)
    {
        NA_DBPF("open_file Error: NAFSopen_file returns %d\n", status);
        rc = NAFTPC_FILESYSTEMERR;        
    }
    else if (wait != FTPC_NOWAIT)
    {
        rc = wait_io_status(&pFile->io_request, &bytesTransferred, &pFile->error_status);
    }

    return rc;
}

/*
 * Calls NETOS file system API to close a file
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param   pFile    pointer to FTP Client file system structure to be processed.
 * param   wait      tells whether it waits for process to be finished.
 *          <list type="unordered">
 *          <item> FTPC_NOWAIT </item>
 *          <item> FTPC_WAIT </item>
 *          </list>
 *
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 *
 * see link fc_wait_status
 * see link NAFTPC
 * see link NAFSclose_file
 * see link wait_io_status
 */
static int close_file(FC_FILESYSTEM_T * pFile, int wait)
{
    int             rc = NAFTPC_NOERROR;
    int             status;
    unsigned int    bytesTransferred;

    if (pFile->file_handler > 0)
    {
        /* if we already open a file, need to close it */
        pFile->state = eFtpFileState_Close;

#if (_DEBUG_PRINTF > 0)
        printf("close_file: %s %s perm 0x%X\n", pFile->dir_path, pFile->file_name, pFile->permission);
#endif
        status = NAFSclose_file(pFile->file_handler, pFile->permission, 
                          &pFile->io_request);
                          
        if (status != NAFS_SUCCESS)
        {
            NA_DBPF("close_file Error: NAFSclose_file returns %d\n", status);
            rc = NAFTPC_FILESYSTEMERR;
        }
        else if (wait != FTPC_NOWAIT)
        {
            rc = wait_io_status(&pFile->io_request, &bytesTransferred, &pFile->error_status);
        }
    }

    if (pFile->file_handler == 0 ||
        (rc == NAFTPC_NOERROR && wait != FTPC_NOWAIT))
    {
#if (_DEBUG_PRINTF > 0)
        printf("close_file: Done\n");
#endif
        pFile->file_handler = 0;
        pFile->file_offset = 0;
        pFile->action = eFtpFileAction_None;
    }
    return rc;
}

/*
 * Calls NETOS file system API to write data for a file
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param   pFile           Pointer to FTP Client file system structure to be processed.
 * param   bufferPtr       Pointer to data to be written to the file system.
 * param   bufferLength    Pointer to size of data in bufferPtr. 
 *                          It returns the actual size of data has been written
 *                          into the file system.
 * param   wait            Tells whether it waits for process to be finished.
 *          <list type="unordered">
 *          <item> FTPC_NOWAIT </item>
 *          <item> FTPC_WAIT </item>
 *          </list>
 *
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 *
 * see link fc_wait_status
 * see link NAFTPC
 * see link wait_io_status
 */
static int write_data(FC_FILESYSTEM_T * pFile, char * bufferPtr, int * bufferLength, int wait)
{
    unsigned int        byteTransferred;
    int                 status;
    int                 rc = NAFTPC_NOERROR;

    if (*bufferLength > 0)
    {
        pFile->state = eFtpFileState_Data;
        
        /* write the data into the file system */
        status = NAFSwrite_file(pFile->file_handler, pFile->permission, 
                      *bufferLength, pFile->file_offset, bufferPtr, 
                      &pFile->io_request, NAFS_ZERO_OFFSET_ERASE);
        if (status != NAFS_SUCCESS)
        {
            NA_DBPF("write_file Error: NAFSwrite_file returns %d", status);
            rc = NAFTPC_FILESYSTEMERR;        
        }
        else if (wait != FTPC_NOWAIT)
        {
            rc = wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);
            /* See whether all bytes in buffer have been written into the file system.
             * If not setup so that it will write again 
             */
            pFile->file_offset += byteTransferred;
            pFile->file_size += byteTransferred;

#if (_DEBUG_PRINTF > 0)
        printf("write_file: %s %s perm 0x%X size %d\n", pFile->dir_path, pFile->file_name, 
                                    pFile->permission, byteTransferred);
#endif
        }
    }
    return rc;
}
/*
 * Calls NETOS file system API to retrieve file data 
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param   pFile           Pointer to FTP Client file system structure to be processed.
 * param   bufferPtr       Pointer to memory where file data will be copied to.
 * param   bufferLength    Pointer to size of data in bufferPtr. 
 * param   wait            Tells whether it waits for process to be finished.
 *          <list type="unordered">
 *          <item> FTPC_NOWAIT </item>
 *          <item> FTPC_WAIT </item>
 *          </list>
 *
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 *
 * see link fc_wait_status
 * see link NAFTPC
 * see link wait_io_status
 */
static int read_data(FC_FILESYSTEM_T * pFile, char * bufferPtr, int bufferLength, int wait)
{
    unsigned int    byteTransferred;
    int             rc = NAFTPC_NOERROR;
    int             status;

    if (bufferLength > 0)
    {
        pFile->state = eFtpFileState_Data;
        /* read data from the file system */   
        status = NAFSread_file(pFile->file_handler, pFile->permission, 
                      bufferLength, pFile->file_offset, bufferPtr, 
                      &pFile->io_request);

        if (status != NAFS_SUCCESS)
        {
            NA_DBPF("read_data Error: NAFSread_file returns %d\n", status);
            rc = NAFTPC_FILESYSTEMERR;        
        }
        else if (wait != FTPC_NOWAIT)
        {
            rc = wait_io_status(&pFile->io_request, &byteTransferred, &pFile->error_status);
            /* See whether all bytes in buffer have been written into the file system.
             * If not, setup so that it will write again 
             */
            pFile->file_offset += byteTransferred;
            pFile->buffer_length = byteTransferred;
#if (_DEBUG_PRINTF > 0)
            printf("read_file: %s %s size %d\n", pFile->dir_path, pFile->file_name, byteTransferred);
#endif
        }
                
    }
    return rc;
}


/*
 * This is a callback function for NETOS file system to handle FTP client request.
 * 
 * This is used when FTP client APIs are called with FTPC_NOWAIT. This allows the 
 * FTP client request to be completed by separated process (thread).
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param io_request_cb Pointer to NETOS file system I/O request
 *
 */
static void io_request_callback_fn(void * io_request_cb)
{
    FC_FILESYSTEM_T         *pFile = NULL;
    NAFS_IO_REQUEST_CB      *io_request;
    char                    *sendPtr;
    int                     sendByte;
    int                     rc = NAFTPC_NOERROR;

    NA_ASSERT((int)io_request_cb);

    io_request = (NAFS_IO_REQUEST_CB *)io_request_cb;
    pFile = (FC_FILESYSTEM_T *)io_request->user_data;

    /* not found return */
    if (pFile == NULL) goto _io_request_callback_ret;

    NA_ASSERT((int)pFile->client_handler);    

    /* if we encounter error during a file creation,
     * we need to open a file since create a file returns 
     * error if a file is existed.
     */
    if ((pFile->io_request.status == NAFS_IO_REQUEST_COMPLETED && pFile->io_request.error_code == NAFS_SUCCESS) ||
        (pFile->state == eFtpFileState_Create && pFile->error_status == NAFS_DUPLICATE_DIR_ENTRY) ||
        (pFile->io_request.error_code == NAFS_END_OF_FILE))
    {
        /* The state we are in is already executed. We need to setup & execute the
         * next process.
         */
        switch (pFile->state)
        {
            case eFtpFileState_Create:  
                /* already create a file so let open a file */
                rc = open_file(pFile, FTPC_NOWAIT);
                break;
            case eFtpFileState_Open:
            case eFtpFileState_Data:
                if (pFile->action == eFtpFileAction_Save)
                {
                #if (_DEBUG_PRINTF > 0)
                    printf("SaveFile...");
                #endif
                    /* if we're able to get data from FTP server, write the data 
                     * into a file. We need to finish writing all data in the buffer
                     * to a file before get another buffer of data from FTP server.
                     */
                    if ((pFile->io_request.bytes_transferred >= (unsigned int)pFile->buffer_length) ||
                        pFile->buffer_length == 0)
                    {   /* we need to get data from FTP server */
                        pFile->file_offset += pFile->buffer_length;
                        pFile->file_size += pFile->buffer_length;

                        pFile->buffer_length = FCGetData(pFile->client_handler, pFile->buffer, 
                                                        sizeof pFile->buffer);
                    
                    #if (_DEBUG_PRINTF > 0)
                        printf("Got %d bytes", pFile->buffer_length);
                    #endif
                        sendByte = pFile->buffer_length;
                        sendPtr = pFile->buffer;
                    }
                    else
                    {   /* still data in the buffer */
                        pFile->file_offset += pFile->io_request.bytes_transferred;
                        pFile->file_size += pFile->io_request.bytes_transferred;
                        pFile->buffer_length -=  pFile->io_request.bytes_transferred;
                        sendByte = pFile->buffer_length;
                        sendPtr = pFile->buffer + pFile->io_request.bytes_transferred;
                    }

                    /* If we have data in the buffer, write it */
                    if (sendByte > 0)
                    {
                    #if (_DEBUG_PRINTF > 0)
                        printf("Write %d bytes", sendByte);
                    #endif
                        rc = write_data(pFile, sendPtr, &sendByte, FTPC_NOWAIT);
                    }
                    else if (pFile->buffer_length < 0)
                    {   /* close the file if we are done getting data from FTP server */
                        pFile->state = eFtpFileState_Close;
                        if (pFile->buffer_length == -1)
                            pFile->error_status = NAFS_READ_FAILED;
                            
                    }
                #if (_DEBUG_PRINTF > 0)
                    printf("\n");
                #endif
                }
                else if (pFile->action == eFtpFileAction_Send)
                {

                    /* send the complete data to the ftp server */
                    sendPtr = pFile->buffer;
                    sendByte = 0;

                #if (_DEBUG_PRINTF > 0)
                    if (pFile->buffer_length > 0)
                        printf("SendFile...Got %d bytes ", pFile->io_request.bytes_transferred);
                #endif

                    /* We need to finish sending all data in a buffer to FTP server
                     * before we read more data from the file system.
                     */
                    while (pFile->buffer_length > 0)
                    {
                        sendByte = FCPutData(pFile->client_handler, sendPtr, 
                                    pFile->io_request.bytes_transferred, 
                                    (pFile->io_request.error_code == NAFS_END_OF_FILE));

                        if (sendByte == -1)
                        {   /* make the pFile error */
                            pFile->state = eFtpFileState_Close;
                            pFile->error_status = NAFS_WRITE_FAILED;
                            pFile->buffer_length = sendByte;  
                            goto _io_request_callback_ret;
                        }
                        else if (sendByte == 0)
                        {
                        #if (_DEBUG_PRINTF > 0)
                            printf("..PutData=0..");
                        #endif
                            tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
                        }

                    #if (_DEBUG_PRINTF > 0)
                        printf("Write %d bytes ", sendByte);
                    #endif
                        sendPtr += sendByte;
                        pFile->io_request.bytes_transferred -= sendByte;
                        pFile->file_size += sendByte;
                        pFile->buffer_length = pFile->io_request.bytes_transferred;
                        pFile->file_offset += sendByte;
                    }
                    
                #if (_DEBUG_PRINTF > 0)
                    if (sendByte > 0)
                        printf("\n");
                #endif

                    if (pFile->io_request.error_code == NAFS_END_OF_FILE)
                    {   /* reach eof */
                        pFile->state = eFtpFileState_Close;
                    }
                    else if (pFile->buffer_length == 0)
                    {   /* read data from the file system */
                        pFile->buffer_length = sizeof pFile->buffer;
                        rc = read_data(pFile, pFile->buffer, pFile->buffer_length, FTPC_NOWAIT);
                    }
                }
                break;
            case eFtpFileState_Close:
            #if (_DEBUG_PRINTF > 0)
                printf("callback_fn: Done 0x%X - %d\n", pFile->file_handler, pFile->state);
            #endif
                if (pFile->buffer_length == -1) pFile->state = eFtpFileState_Error;
                pFile->action = eFtpFileAction_None;
                pFile->file_handler = 0;
                pFile->file_offset = 0;
                break;
            default:
                break;
        }
    }
    else
    {
        /* io request error */
        rc = NAFTPC_FILESYSTEMERR;
        pFile->error_status = pFile->io_request.error_code;

        /* close_file is already sent */
        if (pFile->state == eFtpFileState_Close)
        {
        #if (_DEBUG_PRINTF > 0)
            printf("callback_fn: Done %d\n", pFile->io_request.error_code);
        #endif
            pFile->action = eFtpFileAction_None;
            pFile->state = eFtpFileState_Error;
            pFile->file_handler = 0;
            pFile->file_offset = 0;
        }
        
#if (_DEBUG_PRINTF > 0)
        NA_DBPF("io_request_callback_fn Error: error status %d\n", pFile->io_request.error_code);
#endif      
    }

_io_request_callback_ret:
    if (pFile != NULL && (pFile->state == eFtpFileState_Close || rc != NAFTPC_NOERROR))
    {
        /* we need to close the data socket */
        FCAbortFileAction(pFile->client_handler);

        close_file(pFile, FTPC_NOWAIT);
        if (rc != NAFTPC_NOERROR && pFile->file_handler == 0)
        {
            pFile->state = eFtpFileState_Error;
        }
    }

    return;        
}

/*
 * Free the FTP client file system request block for the given FTP client
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param    handler           FTP client handler which is obtained from FCConnect 
 *                        
 * return  NAFTPC_FILESYSTEMERR    File System Error. Check error_status.
 * return  NAFTPC_NOERROR          No Error
 */
static int free_fileBlock(unsigned long handler)
{
    FC_FILESYSTEM_T         *pFile;

    int                     ret_code = NAFTPC_NOERROR;

    NA_ASSERT(handler);
    
    pFile = (FC_FILESYSTEM_T *)FCGetFileSystem(handler);

    if (pFile != NULL)
    {
        if (pFile->action != eFtpFileAction_None)
        {
            if (NAFSinit_io_request_cb(&pFile->io_request, NULL, (unsigned int)pFile) == NAFS_SUCCESS)
                ret_code = close_file(pFile, FTPC_WAIT);
            else 
                ret_code = NAFTPC_FILESYSTEMERR;
        }

        if (ret_code != NAFTPC_NOERROR)
        {
            NA_DBPF("free_fileBlock Error: Encounter file system error\n");
        }

#if (_DEBUG_PRINTF > 0)
        printf("free_fileBlock: Free 0x%X\n", (unsigned) pFile);
#endif
        free(pFile);
    }

    return ret_code;
}

/*
 * Allocate the FTP client file system request block for the given FTP client
 *
 * This will set the file system request block on the given FTP client handler.
 *
 * external
 * category Networking:FTP_Client:File_System_Interface
 * since 6.0 
 *
 * param    handler           FTP client handler which is obtained from FCConnect 
 *                        
 * return  NAFTPC_SYSERR      System error for lack of memory
 * return  NAFTPC_NOERROR     File system request block was successfull allocated and
 *                             set to the FTP client handler.
 */
static int allocate_fileBlock(unsigned long handler)
{
    FC_FILESYSTEM_T         *pFile;

    int                     ret_code = NAFTPC_SYSERR;

    NA_ASSERT((int)handler);

    pFile = (FC_FILESYSTEM_T *) malloc(sizeof(FC_FILESYSTEM_T));

    if (pFile != NULL)
    {
#if (_DEBUG_PRINTF > 0)
        printf("AllocateFileBlock: malloc 0x%X\n", (unsigned) pFile);
#endif
        FCSetFileSystem(handler, (unsigned long)pFile, (void *)free_fileBlock);

        pFile->file_handler = 0;
        pFile->state = eFtpFileState_Close;
        pFile->action = eFtpFileAction_None;

        pFile->client_handler = handler;

        ret_code = NAFTPC_NOERROR;
    }

    return ret_code;
}

/*ad*
 * Returns file system status. 
 *
 * The user should call this function to see whether @link FCSaveFile  or @link FCSendFile  have  
 * completed. It should be called without waiting.
 *
 * @external
 * @include "ftpfile.h"
 * @category Networking:FTP_Client:File_System_Interface
 * @since 6.0 
 *
 * @param    handler           FTP client handler which is obtained from FCConnect.
 * @param    error_status      pointer to memory where file system error status will 
 *                              be returned.
 *                        
 * @return  FTPC_FILE_BUSY     The given FTP client handler is still busy 
 *                              processing the file system.
 * @return  FTPC_FILE_ERROR     The given FTP client handler encounters error
 *                              on the file system. *error_status contains the
 *                              actual error status.
 * @return  FTPC_FILE_COMPLETED A file has been successfully saved or sent.
 *
 * @see @link FCConnect
 * @see @link FCSaveFile
 * @see @link FCSendFile
 * @see @link NAFTPC
 * @see @link file_sys_errors
*ad*/
int FCGetFileStatus(unsigned long handler, int * error_status)
{
    FC_FILESYSTEM_T *pFile;
    int             ret_code = FTPC_FILE_ERROR;

	NA_ASSERT((int)handler);

    /* we must have file system handler in the given FTP Client handler */
    pFile = (FC_FILESYSTEM_T *)FCGetFileSystem(handler);

    if (pFile != NULL)
    {
/*        if (pFile->error_status == NAFS_SUCCESS || pFile->error_status == NAFS_END_OF_FILE) */
        {
            if (pFile->action != eFtpFileAction_None)
                ret_code = FTPC_FILE_BUSY;
            else if (pFile->state == eFtpFileState_Error)
                ret_code = FTPC_FILE_ERROR;
            else
            {
                ret_code = FTPC_FILE_COMPLETED;
            }
        }

        *error_status = pFile->error_status;
    }

    return ret_code;
}
    
/*ad*
 * Save a file in the file system. @link FCSendFile should be called after this call completes to send
 * a file from the file system. 
 *
 * @external
 * @include "ftpfile.h"
 * @category Networking:FTP_Client:File_System_Interface
 * @since 6.0 
 *
 * @param   handler        FTP client handler which is obtained from FCConnect.
 * @param   filename       Pointer to a full path file name where a file will be created
 * @param   group_id       Group id of a file which will be created with.
 *          <list type="unordered">
 *          <item>FS_GROUP1</item>
 *          <item>FS_GROUP2</item>
 *          <item>FS_GROUP3</item>
 *          <item>FS_GROUP4</item>
 *          <item>FS_GROUP5</item>
 *          <item>FS_GROUP6</item>
 *          <item>FS_GROUP7</item>
 *          <item>FS_GROUP8</item>
 *          </list>
 * @param   wait           Tells the function to return when it finishes processing.
 *          <list type="unordered">
 *          <item>FTPC_WAIT</item>
 *          <item>FTPC_NOWAIT</item>
 *			</list>
 *                        
 * @return  NAFTPC_NOERROR          Success
 * @return  NAFTPC_FILESYSTEMERR    Error 
 *
 * @see @link FCConnect
 * @see @link FCRetrieveFile
 * @see @link fc_wait_status
 * @see @link group_masks
 * @see @link NAFTPC
*ad*/

int FCSaveFile(unsigned long handler, char * filename, unsigned int group_id, int wait)
{

    FC_FILESYSTEM_T     *pFile;
    int                 ret_code = NAFTPC_FILESYSTEMERR;
    int                 byteTransferred;
    int                 dirLength, fileLength;
    char                *bufferPtr;
    void                *io_request_function = NULL;

	NA_ASSERT((int)handler);
    NA_ASSERT((int)filename);
    NA_ASSERT((int)(group_id >= NAFS_FS_GROUP1 && group_id <= NAFS_FS_GROUP8));
    pFile = (FC_FILESYSTEM_T *)FCGetFileSystem(handler);
    if (pFile == NULL)
    {
        ret_code = allocate_fileBlock(handler);
        pFile = (FC_FILESYSTEM_T *)FCGetFileSystem(handler);
    }
   

    if (pFile == NULL || pFile->action != eFtpFileAction_None) goto _fcsavefile_ret;

    dirLength = sizeof pFile->dir_path;
    fileLength = sizeof pFile->file_name;

    /* parse filename & directory name */
    if (parse_file_path(filename, pFile->dir_path, &dirLength, 
                            pFile->file_name, &fileLength) != FILE_OK)
    {
        NA_ASSERT((int)dirLength <= NAFS_DIRECTORY_PATH_SIZE);
        NA_ASSERT((int)fileLength <= NAFS_FILE_NAME_SIZE);
        goto _fcsavefile_ret;
    }

#if (_DEBUG_PRINTF > 0)
    printf("FCSaveFile: %s %s\n", pFile->dir_path, pFile->file_name);
#endif

    if (wait == FTPC_NOWAIT)
        io_request_function = (void *)io_request_callback_fn; 

    /* initialize io request for file system interface */
    if (NAFSinit_io_request_cb(&pFile->io_request, 
                               (fs_io_request_callback_fn) io_request_function, 
                               (unsigned int)pFile) == NAFS_SUCCESS)
    {
        /* we are going to create and save a file in the file system */
        pFile->file_offset = 0;
        pFile->file_size = 0;
        pFile->buffer_length = 0;
#if (_FTPC_RESTRICTED_PERMISSION > 0)
        pFile->permission = (group_id << _READ_PERM_OFFSET) | 
                            (group_id << _WRITE_PERM_OFFSET);
#else
        pFile->permission = NASYSACC_FS_SUPER_USER;
#endif

        pFile->action = eFtpFileAction_Save;
        pFile->error_status = NAFS_SUCCESS;

        /* We need to create & open a file before we get data from the FTP server.
         * Get data from FTP server and then write into the file system until 
         * it's end of file.
         */
        ret_code = create_file(pFile, wait);

        /* if we are able to create a file and we don't need to wait for
         * io status, just return and let io_request_callback_fn 
         * handles error status.
         */
#if (_FTPC_RESTRICTED_FILE_CREATION == 0)
        if ((ret_code == NAFTPC_NOERROR ||  pFile->error_status == NAFS_DUPLICATE_DIR_ENTRY) && 
            (wait == FTPC_NOWAIT))
#else
        if (ret_code != NAFTPC_NOERROR || wait == FTPC_NOWAIT)
#endif
            goto _fcsavefile_ret;

        /* open a file even create file fails (file is already existed). */
        ret_code = open_file(pFile, wait);
        
        if (ret_code != NAFTPC_NOERROR || wait == FTPC_NOWAIT)
            goto _fcsavefile_ret;

        do {
            pFile->buffer_length = FCGetData(pFile->client_handler, pFile->buffer, 
                                            sizeof pFile->buffer);
    
            byteTransferred = pFile->buffer_length;
            bufferPtr = pFile->buffer;
            /* Write data in the file system. Make sure we complete all data
             * in the buffer.
             */
            while (pFile->buffer_length > 0)
            {
                ret_code = write_data(pFile, bufferPtr, &byteTransferred, wait);
                if (ret_code != NAFTPC_NOERROR)
                    goto _fcsavefile_ret;
                bufferPtr += byteTransferred;
                pFile->buffer_length -= byteTransferred;
                byteTransferred = pFile->buffer_length;
            }

            if (pFile->buffer_length == -1) {
                pFile->error_status = NAFS_READ_FAILED;
                ret_code = NAFTPC_FILESYSTEMERR;
            }
        } while (pFile->buffer_length >= 0);
    }
                        
_fcsavefile_ret:
    if (pFile != NULL && (ret_code != NAFTPC_NOERROR || wait != FTPC_NOWAIT))
    {   /* close the file if opened */
        close_file(pFile, wait);
        FCAbortFileAction(handler);
        if (ret_code != NAFTPC_NOERROR)
            pFile->state = eFtpFileState_Error;
    }
    return ret_code;
}

/*ad*
 * This function should be called after the @link FCStoreFile  function call to send
 * a file from the file system. 
 *
 * @external
 * @include "ftpfile.h"
 * @category Networking:FTP_Client:File_System_Interface
 * @since 6.0 
 *
 * @param   handler        FTP client handler which is obtained from FCConnect.
 * @param   filename       Pointer to a full path file name where a file is and 
 *                         will be sent.
 * @param   permission     User permission bit in order to retrieve a file. 
 *                         See sysAccess.h. The permisssion must be given permission
 *                         for both directory & a file. 
 * @param   wait           Tells the function to return when it finishes processing.
 *          <list type="unordered">
 *          <item>FTPC_WAIT</item>
 *          <item>FTPC_NOWAIT</item>
 *			</list>
 *                        
 * @return  NAFTPC_NOERROR          Success
 * @return  NAFTPC_FILESYSTEMERR    Error occurs
 *
 * @see @link FCConnect
 * @see @link FCStoreFile
 * @see @link securityLevels
 * @see @link fc_wait_status
 * @see @link NAFTPC
*ad*/
int FCSendFile(unsigned long handler, char * filename, unsigned int permission, int wait)
{

    FC_FILESYSTEM_T     *pFile;
    int                 ret_code = NAFTPC_FILESYSTEMERR;
    int                 dirLength, fileLength, sendByte;
    char                *bufferPtr;
    void                *io_request_function = NULL;

	NA_ASSERT((int)handler);
    NA_ASSERT((int)filename);

    pFile = (FC_FILESYSTEM_T *)FCGetFileSystem(handler);
    if (pFile == NULL)
    {
        ret_code = allocate_fileBlock(handler);
        pFile = (FC_FILESYSTEM_T *)FCGetFileSystem(handler);
    }
   
    if (pFile == NULL || pFile->action != eFtpFileAction_None) goto _fcsendfile_ret;

    dirLength = sizeof pFile->dir_path;
    fileLength = sizeof pFile->file_name;

    if (parse_file_path(filename, pFile->dir_path, &dirLength, 
                            pFile->file_name, &fileLength) != FILE_OK)
    {
        NA_ASSERT((int)dirLength <= NAFS_DIRECTORY_PATH_SIZE);
        NA_ASSERT((int)fileLength <= NAFS_FILE_NAME_SIZE);
        goto _fcsendfile_ret;
    }

#if (_DEBUG_PRINTF > 0)
    printf("FCSendFile: %s %s\n", pFile->dir_path, pFile->file_name);
#endif
    /* initialize io request for file system interface */
    if (wait == FTPC_NOWAIT)
        io_request_function = (void *)io_request_callback_fn;

    if (NAFSinit_io_request_cb(&pFile->io_request, 
                               (fs_io_request_callback_fn) io_request_function, 
                               (unsigned int)pFile) == NAFS_SUCCESS)
    {
        /* we are going to create and save a file in the file system */

        pFile->file_offset = 0;
        pFile->file_size = 0;
        pFile->buffer_length = 0;
        pFile->permission = permission;
        pFile->action = eFtpFileAction_Send;
        pFile->error_status = NAFS_SUCCESS;
        

        ret_code = open_file(pFile, wait);

        if (ret_code != NAFTPC_NOERROR || wait == FTPC_NOWAIT)
            goto _fcsendfile_ret;
        
        do {

            ret_code = read_data(pFile, pFile->buffer, sizeof pFile->buffer, wait);
    
            if (ret_code == NAFTPC_NOERROR || pFile->error_status == NAFS_END_OF_FILE)
            {
                bufferPtr = pFile->buffer;

                while (pFile->buffer_length > 0)
                {
                    sendByte = FCPutData(pFile->client_handler, bufferPtr, 
                                pFile->buffer_length, 
                                (pFile->error_status == NAFS_END_OF_FILE));

                    if (sendByte < 0)
                    {
                        ret_code = NAFTPC_FILESYSTEMERR;
                        pFile->error_status = NAFS_WRITE_FAILED;
                        goto _fcsendfile_ret;
                    }
                    else if (sendByte == 0) 
                    {
                    #if (_DEBUG_PRINTF > 0)
                        printf("..PutData=0..");
                    #endif
                        tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(20));
                    }

                #if (_DEBUG_PRINTF > 0)
                    printf("FCSendFile: FCPutData %d \n", sendByte);
                #endif
                    bufferPtr += sendByte;
                    pFile->buffer_length -= sendByte;
                    pFile->file_size += sendByte;
                }
            }
        } while (ret_code == NAFTPC_NOERROR);
    }                        

_fcsendfile_ret:
    if (pFile != NULL && (ret_code != NAFTPC_NOERROR || wait != FTPC_NOWAIT))
    {   /* close the file if opened */
        if (pFile->error_status == NAFS_END_OF_FILE)
            ret_code = NAFTPC_NOERROR;
        else if (ret_code != NAFTPC_NOERROR)
            pFile->state = eFtpFileState_Error;

        close_file(pFile, wait);
        FCAbortFileAction(handler);
    }
    return ret_code;
}



