/*
 *  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
 *
 *
 * Edit History
 * 09/19/03  JZW    Fixed some autodoc problems
 * 01/09/06  MLC    Updated IPv6 support
 */

/*ad*
 * This is the interface between the Advanced Web Server and the file system. 
 * It is used to retrieve and store files by way of web pages and 
 * mail service (@link Networking:Email_Services).
 *
 * The calls it makes to the file system are asynchronous and have status 
 * completion calls to determine whether any file system activities have been
 * completed. Since real disks have latencies, the underlying file system
 * needs to support asynchronous calls, or create an operating system task
 * that can block on call completion. In this way, the HTTP server can 
 * continue to service other simultaneous HTTP requests.
 *
 * A file information block @link FILEINFOPTR  is used to pass information about 
 * the file between HTTP server and the file system. The file information block
 * is updated by the file system for a @link RpHSOpenFileStatus   call and is 
 * updated by HTTP server for a @link RpHSCreateFile   call.
 *
 * The file system needs to maintain a current byte position for all open files.
 * The byte positions are 1-relative and range from 1 to filesize. The current 
 * value of a byte position is that of the byte about to be read. The @link RpHSOpenFile  call
 * assumes that the byte position is set to the beginning. The @link RpHSReadFile   call
 * assumes the read is at the current byte position with the new byte position set 
 * at the current position plus bytes read. For example, a byte position of 10 means that 
 * the read pointer is after byte 49 and before byte 50. A read of 10 bytes will 
 * read bytes 50 to 59, and set the byte position to 60.
 *
 * The HTTP server only supports file upload with ENCTYPE="multipart/form-data". 
 * The HTML file upload input entry must be defined after all HTML input entries 
 * that require service before writing a file.
 * 
 * The user is only required to start the HTTP server and HTTP server will initialize
 * the intereface with the file system. These APIs are not for external use.
 * The file system must be initialized before the HTTP server starts.
 *
 * If this file is compiled with APP_FILE_SYSTEM defined, it will include the 
 * NETOS file system. Otherwise, users can modify this interface to customize 
 * the file system. 
 * Once APP_FILE_SYSTEM is defined, HTTP server and NETOS file system libraries
 * are required. 
 * 
 * The following definitions are used when HTTP server creates or uploads a file:
 *      <list type="unordered">
 *          <item> _HTTP_POST_FUNCTION_ENABLE - Enabled by default to prevent
 *                 HTTP server to send its own error response when error occurs
 *                 during uploading a file.</item>
 *          <item> _RESTRICTED_PERMISSION - Enabled by default to restrict user
 *                  permissions to access the file system.</item>
 *          <item> _RESTRICTED_FILE_CREATION - Disabled by default that allows
 *                  a file can be rewritten. </item>
 *      </list>
 *
 *
 * @external
 * @overview Advanced Web Server & File System Interface
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @include "http_awsapi.h"
 * @library filesys,rphttpd
 * @platform all
 * @since 6.0
 * @see @link _HTTP_POST_FUNCTION_ENABLE
 * @see @link _RESTRICTED_PERMISSION
 * @see @link _RESTRICTED_FILE_CREATION
 * @see @link Networking:Web_Servers:Advanced_Web_Server
*ad*/
#include "AsExtern.h"

#ifdef APP_FILE_SYSTEM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tx_api.h>
#include <Npttypes.h>
#include <sysaccess.h>
#include <fs_api.h>
#include <rss.h>
#include <fss.h>
#include <netosIo.h>

/* NETOS File System returns the file date in seconds since January 1, 1970
 * and HTTP calculates the date in seconds since January 1, 1900.
 * We need to get the offset in seconds for 70 years.
 */
#define kLeapYearOffset         0x166980 
#define kHttpStdYearInSecondsOffset    (0x83941500 + kLeapYearOffset)


/*ad*
 * The _HTTP_POST_FUNCTION_ENABLE definition allows the HTTP server to call a post processing 
 * function in the event an error occurs during file upload.
 *
 * If this definition is disabled, the HTTP server will send its own error response.
 *
 * This is only applied to file upload.
 *
 * @external
 * @since 6.0
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 *
*ad*/
#define _HTTP_POST_FUNCTION_ENABLE	1

/*ad*
 * This _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 permission for NAFS_FS_GROUP1 in order to access the directory.
 *
 * @external
 * @since 6.0
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 *
*ad*/
#define _RESTRICTED_PERMISSION     1

/*ad*
 * This _RESTRICTED_FILE_CREATION definition only allows users to create a file
 * when it does not already exist. If this definition is 0, the user will be allowed to
 * overwrite an existing file.
 *
 * @external
 * @since 6.0
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 *
*ad*/
#define _RESTRICTED_FILE_CREATION   0

/* enable printf for debugging */
#define _PRINTF_DEBUG   0

#if (_PRINTF_DEBUG > 0)
/* print out the processing state */
#define _PROCESS_NAME(a)    Print_ProcessName((a))
#else
#define _PROCESS_NAME(a)    
#endif


/*ad*
 * Define default group-id 
 *
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0
 *
*ad*/
#define DEFAULT_GROUP_ID        NAFS_FS_GROUP1	


/*ad*
 * Define default file extension
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0
 *
*ad*/
#define DEFAULT_FILE_EXTENSION  ".*"

/*ad*
 * The HTTP server uses the prefix "FS" for file system access
 *
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0
 * @external
 *
 * @note Do not change this prefix. The HTTP server accesses the file system
 * when the request path contains this prefix.
*ad*/
#define FILE_SYSTEM_PREFIX      "FS"  


#define _WRITE_BIT_OFFSET   16

#define IS_FILE_SYSTEM_BUSY(a)  (((int)(a) == NAFS_IO_REQUEST_CB_BUSY) || \
								((int)(a) == NAFS_FILE_CB_BUSY) || \
								((int)(a) == NAFS_DIR_PATH_CB_BUSY))
 
/*ad*
 * File system process status
 *
 * This enum is used to indicate the status of the io request process on the NETOS file system.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param eFileSystemStatus_Wait        Waiting for the io request process
 * @param eFileSystemStatus_Complete    Complete the io request process
 * @param eFileSystemStatus_Error       Error occurs on the io request process
 *
*ad*/
typedef enum {
    eFileSystemStatus_Wait,
    eFileSystemStatus_Complete,
    eFileSystemStatus_Error
} eFileSystemStatus;

/*ad*
 * File system process actions
 *
 * Used to indicate what process is being performed on the NET+OS file system.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param eFileSystemProcess_None           No process is performing
 * @param eFileSystemProcess_OpenFile       Open a file 
 * @param eFileSystemProcess_GetDir         Get a directory 
 * @param eFileSystemProcess_EntryCount     Get entry count on a directory
 * @param eFileSystemProcess_ListDir        List a directory
 * @param eFileSystemProcess_ReadFile       Read a file
 * @param eFileSystemProcess_WriteFile      Write a file
 * @param eFileSystemProcess_CreateFile     Create a file
 * @param eFileSystemProcess_CloseFile      Close a file
 * @param eFileSystemProcess_ClearFile      Clear a file
 * @param eFileSystemProcess_GetFileSize    Get a file's size
 * @param eFileSystemProcess_Max            Not used
 *
*ad*/
typedef enum {
    eFileSystemProcess_None,
    eFileSystemProcess_OpenFile,
    eFileSystemProcess_GetDir,
    eFileSystemProcess_EntryCount,
    eFileSystemProcess_ListDir,
    eFileSystemProcess_ReadFile,
    eFileSystemProcess_WriteFile,
    eFileSystemProcess_CreateFile,
    eFileSystemProcess_CloseFile,
    eFileSystemProcess_ClearFile,
    eFileSystemProcess_GetFileSize,
    eFileSystemProcess_Max
} eFileSystemProcess;

#define FILE_SYSTEM_NO_FILE       0x0
#define FILE_SYSTEM_CREATE_FILE   0x1
#define FILE_SYSTEM_OPEN_FILE     (0x1 << 1)

/*ad*
 * Structure for the File System Interface Connection Request
 *
 * This presents each read or write request for each connection.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 7.4 
 *
 * @param dir_path           Buffer that contains directory name
 * @param error_status       NETOS file system error status
*ad*/
typedef struct rpfs_connection_t {
    char                dir_path[NAFS_DIRECTORY_PATH_SIZE];
    int                 error_status;
} RPFS_CONNECTION_T;


/*ad*
 * Structure for the HTTP request for NET+OS file system access
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param dir_path           Pointer to buffer that contains directory name
 * @param file_name          Buffer that contains file name
 * @param parsing_buffer     Buffer for parsing the request directory or file name
 * @param io_request         I/O request for NETOS file system 
 * @param dir_info_list      Pointer to directory list. 
 *                           This will be dynamically allocated and free.
 * @param file_info          File information
 * @param dir_info_list_items Number of items in dir_info_list   
 * @param file_handler       File handler for accessing a file 
 * @param file_offset        File offset
 * @param permissions        User permissions
 * @param state              NETOS file system process action
 * @param file_status        Indicating whether a file is created or opened
 * @param process_function_cb Pointer to function for the file system process action
 * @param bytes_count        Store number of bytes temporary for write operation 
 * @param file_number        File Number given in create or open file
 * @param file_connection    Pointer to File System Interface Connection Structure
 *
*ad*/
typedef struct {
    char                * dir_path;
    char                file_name[NAFS_FILE_NAME_SIZE];
    char                parsing_buffer[NAFS_FILE_NAME_SIZE];

    NAFS_IO_REQUEST_CB  io_request;
    NAFS_DIR_ENTRY_INFO * dir_info_list;
    FILEINFO            file_info;
    unsigned int        dir_info_list_items;
    unsigned int        file_handler;
    unsigned int        file_offset;
    unsigned int        permissions;
    eFileSystemProcess  state;
    unsigned char       file_status;
	void 				* process_function_cb;
    int                 bytes_count;
    int                 file_number;
    RPFS_CONNECTION_T   * file_connection;
} RPFS_BUFFER_T;    

/*ad*
 * Structure for defining data type of an extension type and content-type.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param ext_type  Data type
 * @param ext_name  Pointer to extension name
 * @param ext_content_type  Pointer to user's content-type.
 *                          This allows user to specify its
 *                          own content-type (maximum lenght is 50 bytes).
 *                          Use NULL of standard content-type
 *                          predefined in Http server.
 *
*ad*/
typedef struct rpfs_ext_t {
    rpDataType          ext_type;
    char                * ext_name;
    char                * ext_content_type;
}RPFS_EXT_T;


/*ad*
 * This _XCONTENT_TYPE_ENABLE allows you to respond with user's defined content-type 
 * The current user's content-type is defined "application/x-local-exec".
 * You may change this content-type.
 *
 * This _XCONTENT_TYPE_ENABLE is enabled by default.
 * 
 * @external
 * @since 6.0
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 *
*ad*/
#define _XCONTENT_TYPE_ENABLE   1

#define _XCONTENT_TYPE_VALUE    (eRpDataTypeAll + 1)  
#define _XCONTENT_TYPE_EXT      DEFAULT_FILE_EXTENSION   
#define _XCONTENT_TYPE          "application/x-local-exec"

/*ad*
 * This gSupportedExt is a global variable which contains a list of
 * data type values, extension names and content types. Standard
 * content type is used if no content type is specified in the table.
 *
 * This list can be extended.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
*ad*/
static RPFS_EXT_T gSupportedExt[] = {
#if (_XCONTENT_TYPE_ENABLE > 0)    
    {(rpDataType) _XCONTENT_TYPE_VALUE,  _XCONTENT_TYPE_EXT, _XCONTENT_TYPE},
#endif
    {eRpDataTypeHtml,       ".html",    NULL},   
    {eRpDataTypeHtml,       ".htm",     NULL},    
    {eRpDataTypeImageGif,   ".gif",     NULL},    
    {eRpDataTypeText,       ".txt",     NULL},    
    {eRpDataTypeImageJpeg,  ".jpg",     NULL},    
    {eRpDataTypeImageJpeg,  ".jpeg",    NULL},   
    {eRpDataTypeXml,        ".xml",     NULL},    
    {eRpDataTypeWav,        ".wav",     NULL},    
};    
    

extern int getTheMaxNumberOfHttpConnections (void);

typedef void (* LIST_DIR_FUNC_T) (RPFS_BUFFER_T * pFileReq, char ** dataName, unsigned char * dataType);
typedef void (* READ_FILE_FUNC_T) (RPFS_BUFFER_T * pFileReq, char ** bufferPtr, unsigned int * length);
typedef void (* WRITE_FILE_FUNC_T) (RPFS_BUFFER_T * pFileReq, char ** bufferPtr, unsigned int * length);

static RPFS_CONNECTION_T * gRpFSConnection = NULL;
static RPFS_BUFFER_T   * gRpFSBuffer = NULL;     /* list of available file request buffers */
static unsigned int     gRpFSMaxBuffers = 0;    /* max number of file request buffers */
static unsigned			gRpFSDefaultGroupId = DEFAULT_GROUP_ID; /* default group id */

#if (_PRINTF_DEBUG > 0)
/*
 * Returns the http file system process string
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    processNum   File system process enum
 *                  
 *          <list type="unordered">
 *          <item> eFileSystemProcess_None</item>
 *          <item> eFileSystemProcess_OpenFile</item>
 *          <item> eFileSystemProcess_GetDir</item>
 *          <item> eFileSystemProcess_EntryCount</item>
 *          <item> eFileSystemProcess_ListDir</item>
 *          <item> eFileSystemProcess_ReadFile</item>
 *          <item> eFileSystemProcess_WriteFile</item>
 *          <item> eFileSystemProcess_CreateFile</item>
 *          <item> eFileSystemProcess_CloseFile</item>
 *          </list>
 *
 *                        
 * return  "process string"   string of file system process state
 *
 * see link eFileSystemProcess
 * 
 */
static char const * Print_ProcessName(eFileSystemProcess processNum)
{
    static char const * processName[eFileSystemProcess_Max] = 
    {
        "eFileSystemProcess_None",
        "eFileSystemProcess_OpenFile",
        "eFileSystemProcess_GetDir",
        "eFileSystemProcess_EntryCount",
        "eFileSystemProcess_ListDir",
        "eFileSystemProcess_ReadFile",
        "eFileSystemProcess_WriteFile",
        "eFileSystemProcess_CreateFile",
        "eFileSystemProcess_CloseFile",
        "eFileSystemProcess_ClearFile",
        "eFileSystemProcess_GetFileSize"
    };

    NA_ASSERT(processNum < eFileSystemProcess_Max);

    return processName[processNum];
}
#endif

/*
 * Returns file mime type 
 *
 * This function is used to setup the mime type for HTTP server.
 * It looks at the extension in the given filename and returns the mime type for the
 * appropriate extension. A list of extension is defined in gSupportedExt.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    filename      Pointer to file name. Must include an extension. "name.ext" 
 * param    dataType      Pointer to mime type which will be set according to
 *                         the extension of the filename.
 *                        
 * return eRpFileNotFound   File System is not initialized for HTTP. Must complete
 *                           RpFS_OpenFileSystem call.
 * return eRpNoError        Successfully retrieve the mime type for the given filename.
 *
 * see link RpErrorCode
 * see link rpDataType
 *
 * note You can add more data type for an extension. gSupportedExt table contains
 *      a list of data types.
 */
static RpErrorCode Get_FileMimeType(char const * filename, rpDataType * dataType, char ** mimePtr)
{
    RpErrorCode theResult = eRpFileNotFound;
    char        * ext;
    int         i;
    
    *mimePtr = NULL;
    *dataType = eRpDataTypeNone;
    
    /* get the externsion for the given file */
    ext = strstr(filename, ".");
    if (ext != NULL)
    {
        for (i=0; i < asizeof(gSupportedExt); i++)
        {
            if (strcmp(ext, gSupportedExt[i].ext_name) == 0)
            {
                *dataType = gSupportedExt[i].ext_type;
                *mimePtr = gSupportedExt[i].ext_content_type;
                theResult = eRpNoError;
                break;
            }
        }
    }
    
    return theResult;
}
/*
 * Returns the status of file system io request
 *
 * A file system process function must be called prior this function.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    io_request  File system io request for which a previous file system API
 *                      is associated with.
 *        
 * param    byteTransferred     Pointer to number of bytes that has been transferred.
 *
 * param    errorStatus         Pointer to memory where file system error status 
 *                               will be returned
 *
 *                        
 * return eFileSystemStatus_Error   File System is not initialized for HTTP. 
 * return eFileSystemStatus_Wait    The io request is still pending. 
 * return eFileSystemStatus_Complete  The io_request is completed.
 * return eFileSystemStatus_Error     The io_request encounters some error.
 *
 * see link NAFS_IO_REQUEST_CB
 * see link file_sys_errors
 *
 */
static eFileSystemStatus Get_IORequestStatus(NAFS_IO_REQUEST_CB * io_request, unsigned int * byteTransferred, int * errorStatus)
{
    eFileSystemStatus   ret_status = eFileSystemStatus_Error;
    unsigned int        requestType, status;
    int                 rc;
    
    /* inquire file system io request */    
    rc = NAFSio_request_cb_status(io_request, &requestType, &status, errorStatus, 
                                 byteTransferred); 
    if (rc == NAFS_SUCCESS)
    {
        if (status == NAFS_IO_REQUEST_PENDING || status == NAFS_IO_REQUEST_PROCESSING)
            ret_status = eFileSystemStatus_Wait;
        else if (status == NAFS_IO_REQUEST_COMPLETED || *errorStatus == NAFS_END_OF_FILE)
            ret_status = eFileSystemStatus_Complete;

    }
    /* no error on NAFS_END_OF_FILE errorStatus */
    if (*errorStatus != NAFS_SUCCESS && *errorStatus != NAFS_END_OF_FILE)
	{
		if (ret_status == eFileSystemStatus_Wait)
			*errorStatus = NAFS_SUCCESS;
        else
        {
            ret_status = eFileSystemStatus_Error;
#if (_PRINTF_DEBUG > 0)
	        printf("io_request status %d error %d\n", status, *errorStatus);    
#endif        
        }
	}
    return ret_status;
}

/*
 * Returns an unused or available Http file system structure buffer 
 *
 * This structure buffer is used to handle file transfer.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    theFileNumber  the file id
 *
 *                        
 * return  NULL            The file index is not present
 *          RPFS_BUFFER_T   Pointer to http file system buffer for the specified file id
 *
 */
static RPFS_BUFFER_T * New_FileSystemBuffer(int theFileNumber)
{
    RPFS_BUFFER_T * pFileReq = NULL;
    int             i;

    for (i=0; i < gRpFSMaxBuffers; i++)
    {
        if (gRpFSBuffer[i].state ==  eFileSystemProcess_None)
        {
            pFileReq = &gRpFSBuffer[i];
            pFileReq->file_number = theFileNumber;
            pFileReq->file_connection = &gRpFSConnection[theFileNumber];
            pFileReq->dir_path = gRpFSConnection[theFileNumber].dir_path;
            break;
        }
    }
        
    return pFileReq;
}

/*
 * Returns Http file system structure buffer 
 *
 * This structure buffer is used to handle file transfer.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    theFileNumber  the file id
 *
 *                        
 * return  NULL            The file index is not present
 *          RPFS_BUFFER_T   Pointer to http file system buffer for the specified file id
 *
 */
static RPFS_BUFFER_T * Get_FileSystemBuffer(int theFileNumber)
{
    RPFS_BUFFER_T * pFileReq = NULL;
    int             i;
    
    for (i=0; i < gRpFSMaxBuffers; i++)
    {
        if (gRpFSBuffer[i].file_number == theFileNumber && gRpFSBuffer[i].file_connection != NULL)
        {
            pFileReq = &gRpFSBuffer[i];
            break;
        }
    }
    return pFileReq;
}

/*
 * Returns Http file system Connection structure 
 *
 * This structure is used for each file system transfer per connection.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    theFileNumber  the file id
 *
 *                        
 * return  NULL            The file index is not present
 *          RPFS_BUFFER_T   Pointer to http file system buffer for the specified file id
 *
 */
static RPFS_CONNECTION_T * Get_FileSystemConnection(int theFileNumber)
{
    RPFS_CONNECTION_T * pConnection = NULL;
    
    if (theFileNumber <= getTheMaxNumberOfHttpConnections())
    {
        pConnection = &gRpFSConnection[theFileNumber];
    }

    return pConnection;
}

/*
 * Convert given char to hex decimal. It converts 2 characters into hexdecimal value.
 *
 * since 7.0
 *
 * param    ptr       Pointer to the characters which will be converted to hexdecimal.
 *
 * param    hexValue  Pointer to the memory where the hexdeciaml be stored.
 *
 *                        
 * return  0        Successfully get hexdecimal value.
 * return  -1       Unable to convert into hexdecimal value (Invalid character).
 *
 */
int Get_CharToHex(char * ptr, unsigned char * hexValue)
{
    
    int             i, rc = 0;
    unsigned char   ch, encode, value;

    value = 0;
    for (i=0; i < 2; i++)
    {     
        ch = (unsigned char) *(ptr + i); 
        if (ch >= 'a' && ch <= 'f')
            encode = ch - 'a' + 10 ;
        else if (ch >= 'A' && ch <= 'F')
            encode = ch - 'A' + 10;
        else if (ch >= '0' && ch <= '9')
            encode = ch - '0';
        else
        {
            rc = -1;
            break;
        }
        value = (value << 4) + encode;
    }

    *hexValue = value;

    return rc;
}
/*
 *  It parses the fullNamePath for the directory path and the file
 *  name. The fullNamePath must contains / (slash) to separate
 *  each directory and filename. If no slash is specified in the
 *  fullNamePath, only filename (no directory path) is specified.
 *  RpFS_Setcurrentdirectory() must be used prior this call to 
 *  set the directory path which a file will be created or
 *  opened from. 
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    fullNamePath       Pointer to a full file name path which will be
 *                              parsed the directory path and file name.
 *
 * param    dirName            Pointer to the directory path of a file 
 *                              given in fullNamePath.
 * param    dirLen             Size of the dirName
 *
 * param    fileName           Pointer to the file name given in fullNamePath.
 *
 * param    fileLen            Size of the fileName
 *
 *                        
 * return  NAFS_SUCCESS        Successfully parse directory path and file name.
 * return  NAFS_INVALID_VOLUME_NAME    No Volume name is specified in the fullNamePath.
 * return  NAFS_INVALID_FILE_NAME    No file name is specified in the fullNamePath.
 * return  NAFS_INVALID_VOLUME_NAME_LENGTH    Invalid volume name length
 * return  NAFS_INVALID_DIR_PATH_LENGTH    Invalid file name length
 *
 * see link file_sys_errors
 *
 */
static int Parse_FileNamePath(char * fullNamePath, char * dirName, unsigned dirLen, char * fileName, unsigned fileLen)
{

#define _IS_VALID_PATH(a, b)    (((a) != NULL) && (((a)+1) != b))
#define _ASCII_SLASH            "/" /* 0x2F */
#define _ASCII_SLASH_CHAR       '/' /* 0x2F */
#define _NO_SLASH_INDEX         1
#define _ENCODING_PREFIX        '%'  /* encoding prefix in a URL */

    int     rc = NAFS_SUCCESS;
    char    *filePtr, *pathPtr, *volumePtr, *dirPtr;
    char    *theNamePtr;
    int     length;
    int     i, j;
    unsigned char encodeValue;
    
    theNamePtr = fullNamePath;
    while (strstr(theNamePtr, "\\") != NULL)
    {
        theNamePtr = strstr(theNamePtr, "\\");
        *theNamePtr = _ASCII_SLASH_CHAR; /* change it to forward slash */
        theNamePtr++;
    }
    /* must skip RomPager file system prefix */
    theNamePtr = strstr(fullNamePath, FILE_SYSTEM_PREFIX);
    
    if (theNamePtr == NULL)
		/* not prefix is specified. Still need to continue for parsing the directory */
        theNamePtr = fullNamePath;
    else
    {	/* skip the prefix */
        theNamePtr += strlen(FILE_SYSTEM_PREFIX);
    }    

    if (theNamePtr == NULL)
    {   /* no filename is specified */
        rc = NAFS_INVALID_VOLUME_NAME;
        goto _Parse_FileNamePath_ret;
    }

    pathPtr = theNamePtr;        
    filePtr = pathPtr;
    /* Try to get the file name only (skip all /path/)
     * Parse until the last slash for file name
     */
    while (pathPtr != NULL) {
        pathPtr = strstr(pathPtr, _ASCII_SLASH);
        if (pathPtr != NULL)
        {
            pathPtr++;
            filePtr = pathPtr;
        }
    }
    /* make sure we have valid file name */
    if (filePtr == NULL || fileLen < strlen(filePtr))
    {   
        rc = NAFS_INVALID_FILE_NAME;
        goto _Parse_FileNamePath_ret;
    }

    /* get a volume_name */
    pathPtr = theNamePtr;
    if ((pathPtr = strstr(theNamePtr, _ASCII_SLASH)) == theNamePtr)
    {
        volumePtr = theNamePtr+1; /* skip slash */
    }
    else
        volumePtr = theNamePtr;
    
    if (strstr(volumePtr, _ASCII_SLASH) != NULL && _IS_VALID_PATH(volumePtr, filePtr))
    {   
       
        length = 0;
                        
        /* get directory path */        
        dirPtr = strstr(volumePtr, _ASCII_SLASH);
        
        if (_IS_VALID_PATH(dirPtr, filePtr))
        {   /* must be in /vol/path/filename format */
            dirPtr++;
            length = dirPtr - volumePtr;
        }
        else
        {   /* must be in /vol/filename format */
            length = filePtr - volumePtr;
            dirPtr = NULL;
        }
    
        if (length > dirLen)
        {
            rc = NAFS_INVALID_VOLUME_NAME_LENGTH;
            goto _Parse_FileNamePath_ret;
        }
        /* volume name */
        for (j=i=0; i <length; i++, j++)
        {
            if (*(volumePtr+i) == _ENCODING_PREFIX)
            {   /* get the encoded value  by minus the offset 0x30 */
                i++;
                if (Get_CharToHex( (volumePtr+i), &encodeValue) != 0)
                {
                    rc = NAFS_INVALID_VOLUME_NAME_LENGTH;
                    goto _Parse_FileNamePath_ret;
                }
                i++;
                dirName[j] = encodeValue;
            }
            else
            {
                dirName[j] = volumePtr[i];
            }
        }
        dirName[j] = '\0';
    
        /* copy the directory path if it's there */
        if (dirPtr != NULL)
        {
            /* make sure the length of the whole path is valid. */
            if ((length + (filePtr - dirPtr)) > dirLen)
            {
                rc = NAFS_INVALID_DIR_PATH_LENGTH;
                goto _Parse_FileNamePath_ret;
            }
            /* directory name */
            volumePtr = dirName + strlen(dirName);
            length = (filePtr - dirPtr);

            for (j=i=0; i <length; i++, j++)
            {
                if (*(dirPtr+i) == _ENCODING_PREFIX)
                {   /* get the encoded value  by minus the offset 0x30 */
                    i++;
                    if (Get_CharToHex( (dirPtr+i), &encodeValue) != 0)
                    {
                        rc = NAFS_INVALID_VOLUME_NAME_LENGTH;
                        goto _Parse_FileNamePath_ret;
                    }
                    i++;
                    volumePtr[j] = encodeValue;
                }
                else
                {
                    volumePtr[j] = dirPtr[i];
                }
            }
            volumePtr[j-_NO_SLASH_INDEX] = '\0';
        }
    }
           
    /* file name */
    length = strlen(filePtr);
    for (j=i=0; i <length; i++, j++)
    {
        if (*(filePtr+i) == _ENCODING_PREFIX)
        {   /* get the encoded value  by minus the offset 0x30 */
            i++;
            if (Get_CharToHex( (filePtr+i), &encodeValue) != 0)
            {
                rc = NAFS_INVALID_VOLUME_NAME_LENGTH;
                goto _Parse_FileNamePath_ret;
            }
            i++;
            fileName[j] = encodeValue;
        }
        else
        {
            fileName[j] = filePtr[i];
        }
    }
    fileName[j] = '\0';
    
_Parse_FileNamePath_ret:        
    return rc;
}        
/*
 * Gets user permission for the given group id 
 *
 * It derives permission from the given group id.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   groupId    group id 
 *          <list type="unordered">
 *              <item> NAFS_FS_GROUP1 </item> 
 *              <item> NAFS_FS_GROUP2 </item> 
 *              <item> NAFS_FS_GROUP3 </item> 
 *              <item> NAFS_FS_GROUP4 </item> 
 *              <item> NAFS_FS_GROUP5 </item> 
 *              <item> NAFS_FS_GROUP6 </item> 
 *              <item> NAFS_FS_GROUP7 </item> 
 *              <item> NAFS_FS_GROUP8 </item> 
 *          </list>
 * param   permission  pointer to user permission which will be returned for
 *                      given group id
 *
 * see link securityLevels
 * see link group_masks
 *                        
 */
static void Get_GroupIdToPermission(rpAccess groupId, unsigned int * permission)
{
    #define _READ_BIT_OFFSET    2

    rpAccess        accessCode = groupId;
    
    if (groupId == kRpPageAccess_Unprotected)
        accessCode = gRpFSDefaultGroupId;

    *permission = (accessCode << _READ_BIT_OFFSET) | (accessCode << _WRITE_BIT_OFFSET) | NASYSACC_LEVEL_R;
}    

/*
 * Validates the given group id 
 *
 * If the give group id is invalid, it will be set to the default group id.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    groupId   Pointer to group id which will be validated
 *
 *                        
 * return  0   Given groupId is a valid group  id
 * return  -1  Given groupId is not a valid group id and has been set to default group_id.
 *
 * see link group_masks
 *
 */
static int Set_ValidGroupId(rpAccess * groupId)
{
    int ret = -1;

	switch (*groupId)
	{
	    case NAFS_FS_GROUP1:
	    case NAFS_FS_GROUP2:
	    case NAFS_FS_GROUP3:
	    case NAFS_FS_GROUP4:
	    case NAFS_FS_GROUP5:
	    case NAFS_FS_GROUP6:
	    case NAFS_FS_GROUP7:
	    case NAFS_FS_GROUP8:
            ret = 0;
	        break;
	    default:
	        *groupId = gRpFSDefaultGroupId;
	        break;
	}
    return ret;
}


/*
 * Returns the current group id and permission 
 *
 * This is called when http is processing file system. The group id & permission 
 * are returned based on the username and password (realm) that http server has 
 * authenticated. If no authentication, a default group id will be returned and 
 * permission will not be set.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param    groupId    pointer to group id that will be returned
 *          <list type="unordered">
 *          <item> NAFS_FS_GROUP1 </item> 
 *          <item> NAFS_FS_GROUP2 </item> 
 *          <item> NAFS_FS_GROUP3 </item> 
 *          <item> NAFS_FS_GROUP4 </item> 
 *          <item> NAFS_FS_GROUP5 </item> 
 *          <item> NAFS_FS_GROUP6 </item> 
 *          <item> NAFS_FS_GROUP7 </item> 
 *          <item> NAFS_FS_GROUP8 </item> 
 *          </list>
 *
 * param    permissions    pointer to memory where user login permissions will be returned.
 *                        
 * return  0      Successfully get a group id
 * return  -1     Multiple realms. Unable to return a specific group id.
 *
 * see link group_masks
 * see link securityLevels
 *
 */
static int Get_LoginGroupIdnPermissions(rpAccess * groupId, unsigned int * permissions)
{
    void            * httpServerDataPtr;
    Unsigned16      timeout;
    StcpIpAddress   ipAddress;
    StcpIpAddress   remoteIp;
    char            * username, * password, * curUserName;
    int             userCount;
    rpAccess        thisAccess;
    int             ret_code = -1;
    
    extern int RpGetRemoteIpAddress(void *, StcpIpAddress *);
    extern void *getTheAllegroTaskDataPtr (void);  
    char    accountPassword[NASYSACC_STRLEN_PASSWORD + 4];

    if ((ret_code = Set_ValidGroupId(groupId)) != 0)
    {
        /* get the current login user */
        httpServerDataPtr = getTheAllegroTaskDataPtr();
        
        RpGetRemoteIpAddress(httpServerDataPtr, &remoteIp);
                
        curUserName = RpGetCurrentUserName(httpServerDataPtr);
        userCount = 0;
	    *groupId = gRpFSDefaultGroupId;

	    if (curUserName != NULL)
	    {
	        do {    
	            RpGetUserAttributes(httpServerDataPtr, userCount, &username,
	                        &password, &thisAccess, &ipAddress,
	                        &timeout);
	            userCount++;
                if (ipaddrzero(ipAddress.ss_family, &ipAddress) == False &&
                    ipaddrcmp(ipAddress.ss_family, &ipAddress, &remoteIp) == False)
                    continue;

	        } while (username != NULL && strcmp(username, curUserName) != 0);

            if (username != NULL && strcmp(username, curUserName) == 0 && 
                (ipaddrzero(ipAddress.ss_family, &ipAddress) == True || 
                 ipaddrcmp(ipAddress.ss_family, &ipAddress, &remoteIp) == True))
            {

                *permissions = NAgetSysAccess(username, accountPassword, &ipAddress);

            #if (_PRINTF_DEBUG > 0)
                printf("permission = 0x%X\n", *permissions);
                            
                if (thisAccess & NAFS_FS_GROUP1)
                {
                    printf("Group ID 1\n");
                }
                if (thisAccess & NAFS_FS_GROUP2)
                {
                    printf("Group ID 2\n");
                }
                if (thisAccess & NAFS_FS_GROUP3)
                {
                    printf("Group ID 3\n");
                }
                if (thisAccess & NAFS_FS_GROUP4)
                {
                    printf("Group ID 4\n");
                }
                if (thisAccess & NAFS_FS_GROUP5)
                {
                    printf("Group ID 5\n");
                }
                if (thisAccess & NAFS_FS_GROUP6)
                {
                    printf("Group ID 6\n");
                }
                if (thisAccess & NAFS_FS_GROUP7)
                {
                    printf("Group ID 7\n");
                }
            #endif /* (_PRINTF_DEBUG > 0) */
            
	            /* user must be logged in one of the following realm (group id) 
	             * If no realm is used, default realm will be set.
	             * But if multiple realms are set, return error and let the caller
	             * decide the group id
	             */
	            switch (thisAccess)
	            {
	                case kRpPageAccess_Unprotected:
	                    *groupId = gRpFSDefaultGroupId;
	                    ret_code = 0;
	                    break;
	                case NAFS_FS_GROUP1:
	                case NAFS_FS_GROUP2:
	                case NAFS_FS_GROUP3:
	                case NAFS_FS_GROUP4:
	                case NAFS_FS_GROUP5:
	                case NAFS_FS_GROUP6:
	                case NAFS_FS_GROUP7:
	                case NAFS_FS_GROUP8:
                        *groupId = thisAccess;
	                    ret_code = 0;
	                    break;
	                default:
	                    break;
	            }
            }
	    }
    }
    else
    {
        Get_GroupIdToPermission(*groupId, permissions);
    }
    return ret_code;
}    

#if (_RESTRICTED_PERMISSION == 0) 

/*
 * Gets group_id from the given write-permission 
 *
 * It derives group-id from the given write-permission.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   permission  Pointer to user permission which will be returned for
 *                      given group id
 *
 * param   group_id    group id 
 *          <list type="unordered">
 *          <item> NAFS_FS_GROUP1 </item> 
 *          <item> NAFS_FS_GROUP2 </item> 
 *          <item> NAFS_FS_GROUP3 </item> 
 *          <item> NAFS_FS_GROUP4 </item> 
 *          <item> NAFS_FS_GROUP5 </item> 
 *          <item> NAFS_FS_GROUP6 </item> 
 *          <item> NAFS_FS_GROUP7 </item> 
 *          <item> NAFS_FS_GROUP8 </item> 
 *          </list>
 *                        
 * return  True    group-id is successfully obtained from the given write-permission
 * return  False   Unable to obtain the group-id from the given write-permission
 *
 * see link securityLevels
 * see link group_masks
 *
 */
static Boolean Get_WritePermissionToGroupId(unsigned int permission, rpAccess * group_id)
{
    Boolean         rc = False;
    unsigned int    write_group;
    int             i;

    /* derive group id from permission */
    write_group = permission >> _WRITE_BIT_OFFSET;

    for (i=kRpPageAccess_Realm1; i <= kRpPageAccess_Realm8; i = i << 1)
    {
        if ((write_group & i) != 0)
        {
            *group_id = i;
            rc = True;
            break;
        }
    }
    
    return rc;
}
#endif

/*
 * This function is used to see whether the given permission allows the given
 * group-id for write access. 
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   groupId    group id 
 *          <list type="unordered">
 *          <item> NAFS_FS_GROUP1 </item> 
 *          <item> NAFS_FS_GROUP2 </item> 
 *          <item> NAFS_FS_GROUP3 </item> 
 *          <item> NAFS_FS_GROUP4 </item> 
 *          <item> NAFS_FS_GROUP5 </item> 
 *          <item> NAFS_FS_GROUP6 </item> 
 *          <item> NAFS_FS_GROUP7 </item> 
 *          <item> NAFS_FS_GROUP8 </item> 
 *          </list>
 *
 * param   permission  Permission to see whether the given group id is granted with
 *                      write access
 *
 * return  True    Write permission is allowed for the given group-id 
 * return  False   No write permission for the given group-id
 *
 * see link securityLevels
 * see link group_masks
 *
 */
static Boolean Is_WritePermission(rpAccess groupId, unsigned int permission)
{
    Boolean     isAllowed = False;
    Unsigned32  wPermission = groupId << _WRITE_BIT_OFFSET;

    if ((wPermission & permission) != 0)
        isAllowed = True;

    return isAllowed;
}    

/*
 * Handles appropriate file system process function for the given file request
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   pFileReq    pointer to file request indicating which file system 
 *                      process function will be executed.
 *
 * param   funcPtr     pointer to a function which is called to retrieve data
 *                      to handle file system process function.
 *
 *                        
 * return  NAFS_SUCCESS    Successfully perform file system process function.
 * return  <0              Error on accessing the file system 
 *
 * see link file_sys_errors
 * see link RPFS_BUFFER_T
 *
 */
static int Process_FileState(RPFS_BUFFER_T * pFileReq, void * funcPtr)
{
    int             rc = NAFS_SUCCESS;

	NA_ASSERT((int)pFileReq);

    pFileReq->io_request.arg2 = 0;  /* clean up */

    switch (pFileReq->state) {
        case eFileSystemProcess_OpenFile:
			pFileReq->io_request.arg2 = O_RDWR;  /* for yaffs. jhl */
            rc = NAFSopen_file(pFileReq->dir_path, pFileReq->file_name, &pFileReq->file_handler, 
                              pFileReq->permissions, &pFileReq->io_request);
            break;        
        case eFileSystemProcess_GetFileSize:
            rc  = NAFSopen_file_size(pFileReq->file_handler, pFileReq->permissions, &pFileReq->file_offset, &pFileReq->io_request);
            break;
            
        case eFileSystemProcess_ClearFile:
            rc = NAFSwrite_file(pFileReq->file_handler, pFileReq->permissions, 1, 0,
                                pFileReq->file_name, &pFileReq->io_request, NAFS_ERASE_DATA);

            break;
        case eFileSystemProcess_GetDir:
        {
            char          *pathPtr;
            unsigned char dataType;
            
            NA_ASSERT((int)funcPtr);
            /* call file system list-dir to get info on the current pathPtr */            
            if (funcPtr != NULL)
            {
                (*(LIST_DIR_FUNC_T) funcPtr)(pFileReq, &pathPtr, &dataType);
            }
            
            pFileReq->dir_info_list_items++; /* at least 1 entry */
            
            pFileReq->dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(pFileReq->dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));
    
            if (pFileReq->dir_info_list == NULL)
            {
                rc = NAFS_MALLOC_FAILED;
            }
            else
            {
                memset(pFileReq->dir_info_list, 0, (pFileReq->dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

                rc = NAFSlist_dir(pathPtr, pFileReq->permissions, 
                                pFileReq->dir_info_list, pFileReq->dir_info_list_items,
                                &pFileReq->io_request);
            }
            break;
        }
        case eFileSystemProcess_EntryCount:
        {
            char          *dataPtr;
            unsigned char dataType;
            
            NA_ASSERT((int)funcPtr);
            if (funcPtr != NULL)
            {
                /* Setup the dir info buffer for number of entries in the directory. */
                (*(LIST_DIR_FUNC_T) funcPtr)(pFileReq, &dataPtr, &dataType);
            }
            rc = NAFSdir_entry_count(dataPtr, pFileReq->permissions, 
                                &pFileReq->dir_info_list_items, &pFileReq->io_request);
            break;
        }
        case eFileSystemProcess_ListDir:
        {   
            char          *dataPtr, *mimePtr;
            unsigned char dataType;
            int           i;
            
            NA_ASSERT((int)funcPtr);
            
            if (funcPtr != NULL)
            {
                /* list-dir is successfully requested so let's get the info */
                (*(LIST_DIR_FUNC_T)funcPtr)(pFileReq, &dataPtr, &dataType);
            }
            for (i=0; i < pFileReq->dir_info_list_items; i++)
            {
                if (pFileReq->dir_info_list[i].entry_type ==  dataType && 
                    strcmp(pFileReq->dir_info_list[i].entry_name, dataPtr) == 0)
                {
					/* We should have group-id, file-date & file-size from get-dir.
					 * Let's setup file access=group-id. This will invoke user
					 * to supply a username/password in order to download this
					 * file.
					 * But if user overrides the file access for all users,
					 * we don't want to use the group_id as the file access. 
					 * We simply set this file without any file access 
					 * (no username/password requeired).
					*/
                    /* pFileReq->permissions = NASYSACC_FS_SUPER_USER;*/
                    pFileReq->file_info.FileAccess = pFileReq->dir_info_list[i].group_id;
                    pFileReq->file_info.FileDate =  pFileReq->dir_info_list[i].last_modified_time + kHttpStdYearInSecondsOffset;
                    pFileReq->file_info.FileSize =  pFileReq->dir_info_list[i].file_size;
                    
                    /* get the mime type. If we can't find any mime type from the predefined type,
                     * let use default type.
                     */
                    if (Get_FileMimeType(pFileReq->file_name, &pFileReq->file_info.FileType, &mimePtr) != eRpNoError)
                    {
                        /* get default type */
                        Get_FileMimeType(DEFAULT_FILE_EXTENSION, &pFileReq->file_info.FileType, &mimePtr);
                    }
                    
                    if (mimePtr != NULL)
                    {
                        /* user defines content-type so let use the one from this */
                        pFileReq->file_info.FileType = eRpDataTypeOther;
                        strcpy(pFileReq->file_info.FileMimeType, mimePtr);
                    }
                    
                    break;
                }
            }
            break;
        }
        case eFileSystemProcess_ReadFile:
        {
            char         *readPtr;
            unsigned int readLength;
            
            NA_ASSERT((int)funcPtr);
            if (funcPtr != NULL)
            {
                (*(READ_FILE_FUNC_T)funcPtr)(pFileReq, &readPtr, &readLength);
            }
            rc = NAFSread_file(pFileReq->file_handler, pFileReq->permissions, 
                      readLength, pFileReq->file_offset, 
                      readPtr, &pFileReq->io_request);
            break;
        }
        case eFileSystemProcess_WriteFile:
        {
            char            *writePtr;
            unsigned int    writeLength;
            
            NA_ASSERT((int)funcPtr);
            if (funcPtr != NULL)
            {
                (*(READ_FILE_FUNC_T)funcPtr)(pFileReq, &writePtr, &writeLength);
            }
            /* Used NAFS_RANDOM_WRITE. NAFS_ZERO_OFFSET_ERASE may not work for NAND flash */
            rc = NAFSwrite_file(pFileReq->file_handler, pFileReq->permissions, 
                      writeLength, pFileReq->file_offset, 
                      writePtr, &pFileReq->io_request, NAFS_RANDOM_WRITE);
            break;
        }
        case eFileSystemProcess_CreateFile:
    		pFileReq->io_request.arg2 = O_CREAT | O_TRUNC | O_RDWR;  /* for yaffs. mlc */
            rc = NAFScreate_file(pFileReq->dir_path, pFileReq->file_name, pFileReq->permissions,
                              pFileReq->file_info.FileAccess, &pFileReq->io_request);
            break;
        case eFileSystemProcess_CloseFile:
            rc = NAFSclose_file(pFileReq->file_handler, pFileReq->permissions, 
                      &pFileReq->io_request);
            break;
        default:
			break;
    }
    
    #if (_PRINTF_DEBUG > 0)
    printf("process [%s] - handler 0x%x error %d\n", _PROCESS_NAME(pFileReq->state), 
                    pFileReq->file_handler, rc);
    #endif
    return rc;
}    

/*
 * Gets the last directory name 
 *
 * This function is used to pass into Process_FileState function to 
 * retrieve data for list-dir process.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   pFileReq    pointer to RPFS_BUFFER_T which will be parsed to get the 
 *                       last directory name
 * param   dataName   pointer to an address that contains last directory name.
 *
 * param   dataType   pointer to directory data type
 *          <list type="unordered">
 *          <item> NAFS_FILE_TYPE_DIR. directory data type </item>
 *          </list>
 *
 */
static void Get_LastDirectoryName(RPFS_BUFFER_T * pFileReq, char ** dataName, unsigned char * dataType)
{

    char * pathPtr;
    char * dirPtr;
    
    NA_ASSERT((strlen(pFileReq->dir_path) > 0));
    
    pathPtr = pFileReq->dir_path;
    
    /* get the last / for the directory path */
    while (strlen(pathPtr) > 1 && (dirPtr = strstr(pathPtr, "/")) != NULL)
    {
        dirPtr++;
        pathPtr = dirPtr;
    }
    
    *dataType = NAFS_FILE_TYPE_DIR;
    *dataName = pathPtr;
}

/*
 * Removes the last directory name 
 *
 * This function is used to pass into Process_FileState function to retrieve
 * data for entry-count & get-dir processes for the last directory.
 * These processes must be passed with the full directory path without the inquire 
 * directory (last directory).
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   pFileReq   pointer to RPFS_BUFFER_T which will be parsed the full directory
 *                      path with the last directory removed. 
 * param   dataName  pointer to an address that contains directory path with last
 *                      directory excluded.
 *
 * param   dataType   pointer to directory data type
 *          <list type="unordered">
 *          <item> NAFS_FILE_TYPE_DIR </item>
 *          </list>
 *
 */
static void Remove_LastDirectoryName(RPFS_BUFFER_T * pFileReq, char ** dataName, unsigned char * dataType)
{

    char * pathPtr;
    char * dirPtr;
    
    NA_ASSERT((strlen(pFileReq->dir_path) > 0));
    
	/* This will remove the last directory and copy into the parsing_buffer */
    pathPtr = pFileReq->dir_path;

	/* search for last slash "/" in the full directory path */    
    while ((dirPtr = strstr(pathPtr, "/")) != NULL)
    {
        pathPtr = dirPtr+1;
    }
    strncpy(pFileReq->parsing_buffer, pFileReq->dir_path, (pathPtr - pFileReq->dir_path));
    pFileReq->parsing_buffer[(pathPtr - pFileReq->dir_path)] = '\0';
    
    *dataType = NAFS_FILE_TYPE_DIR;
    *dataName = pFileReq->parsing_buffer;
}

/*
 * Gets file name 
 *
 * This function is used to pass into Process_FileState function to 
 * retrieve data for list-dir process to get info about a file.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   pFileReq   Pointer to RPFS_BUFFER_T which contains the file name
 * param   dataName  Pointer to an address that contains the file name
 *
 * param   dataType   Pointer to memory where directory data type will be returned
 *          <list type="unordered">
 *          <item> NAFS_FILE_TYPE_FILE </item>
 *          </list>
 *
 */
static void Get_FileName(RPFS_BUFFER_T * pFileReq, char ** dataName, unsigned char * dataType)
{
    
    * dataType = NAFS_FILE_TYPE_FILE;
    * dataName = pFileReq->file_name;
}
/*
 * Get the full directory path of a file 
 *
 * This function is used to pass into Process_FileState function to 
 * retrieve data for entry-count & get-dir processes to get file info.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   pFileReq   pointer to RPFS_BUFFER_T which contains the directory path of a file
 * param   dataName  pointer to an address that contains the directory path
 *
 * param   dataType   pointer to directory data type
 *          <list type="unordered">
 *          <item> NAFS_FILE_TYPE_DIR. directory data type </item>
 *          </list>
 *
 */
static void Get_FileNameDirectory(RPFS_BUFFER_T * pFileReq, char ** dataName, unsigned char * dataType)
{

    * dataType = NAFS_FILE_TYPE_DIR;
    * dataName = pFileReq->dir_path;
}

/*
 * Sees whether the given path is root directory only
 *
 * The path should contain only a single slash for root directory.
 *
 * category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * since 6.0
 *
 * param   path  pointer to the directory path
 *
 * return  True    is only root directory
 * return  False   is not root directory
 *
 */
static Boolean Is_RootDirectory(char * path)
{
    char * dir;
    Boolean isRoot = False;
    
    dir = strstr(path, "/");
    
    if ((strlen(path) > 0 && dir == NULL) || strlen(dir) == 1)
        isRoot = True;
        
    return isRoot;
}    
#endif /* APP_FILE_SYSTEM */

/*ad*
 * This function is called once when the web server starts up.
 * This function should allocates and initializes any internal variables and processes.
 * The number of open files is specified by application via @link APP_FILE_SYSTEM_SIZE.
 *
 * This function allocates and initializes memory to handle NETOS file system if
 * NETOS file system is included.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theOpenFilesCount  Number of files will be opened simultaneous
 *
 *                        
 * @return  eRpNoError      File system interface was successfully initialized
 * @return  eRpOutOfMemory  No memory
 *
 * @see @link RpErrorCode
 *
*ad*/
RpErrorCode RpHSOpenFileSystem(int theOpenFilesCount) {    

#ifdef APP_FILE_SYSTEM
    RpErrorCode theResult = eRpOutOfMemory;
    int         i, nConnections;
    
#if (_PRINTF_DEBUG > 0)
    printf("------   RpFS_OpenFileSystem: %d -----\n", theOpenFilesCount);
#endif

    nConnections = getTheMaxNumberOfHttpConnections();
    gRpFSConnection = (RPFS_CONNECTION_T *)calloc(nConnections, sizeof(RPFS_CONNECTION_T));
    if (gRpFSConnection == NULL)
    {
#if (_PRINTF_DEBUG > 0)
        printf("------   RpFS_OpenFileSystem: out of memory -----\n");
#endif
        goto _ret;
    }

    for (i=0; i < nConnections; i++)
    {
        gRpFSConnection[i].dir_path[0] = '\0';
//        gRpFSConnection[i].error_status = 0;
    }
    

    /* allocate memory for file system handlers */    
    gRpFSBuffer = (RPFS_BUFFER_T *)calloc(theOpenFilesCount, sizeof(RPFS_BUFFER_T)); 
    if (gRpFSBuffer != NULL)
    {
        gRpFSMaxBuffers = theOpenFilesCount;
        for (i=0; i < theOpenFilesCount; i++)
        {
            gRpFSBuffer[i].dir_path = NULL;
            gRpFSBuffer[i].file_name[0] = '\0';
            gRpFSBuffer[i].dir_info_list = NULL;
            gRpFSBuffer[i].state = eFileSystemProcess_None;
			gRpFSBuffer[i].process_function_cb = NULL;
            gRpFSBuffer[i].file_status = FILE_SYSTEM_NO_FILE;
            gRpFSBuffer[i].file_number = -1;
            gRpFSBuffer[i].file_connection = NULL;
        }

        theResult = eRpNoError;
    }
    else
    {
        free(gRpFSConnection);
        gRpFSConnection  = NULL;
    }

_ret:
    return theResult;
#else /* APP_FILE_SYSTEM */

    return eRpNoError;

#endif
        
}

/*ad*
 *  The RpHSCloseFileSystem routine is called once when the web server finishes
 *	so the file system can deinitialize any internal variables and processes.
 *
 * This function frees all memory allocated in RpHSOpenFileSystem call for 
 * NETOS file system.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @return  eRpNoError              The file system interface was successfully closed.
 * @return  eRpFileSystemNotOpen    File system is not initialized.
 *
 * @see @link RpErrorCode
 * @see @link RpHSOpenFileSystem
 *
*ad*/
RpErrorCode RpHSCloseFileSystem(void) {

#ifdef APP_FILE_SYSTEM
    RpErrorCode theResult = eRpFileSystemNotOpen;
    
    #if (_PRINTF_DEBUG > 0)
    printf("------    RpFS_CloseFileSystem    ------\n");
    #endif
        
    if (gRpFSBuffer != NULL) {
        free (gRpFSBuffer);
        free (gRpFSConnection);
        gRpFSBuffer = NULL;
        gRpFSConnection = NULL;
        theResult = eRpNoError;
    }    
    return theResult;
#else /* APP_FILE_SYSTEM */

    return eRpNoError;
#endif    
}

/*ad*
 * The RpHSCreateFile routine is called to start creating a new file on the file system.
 * This function is responsible for all directory positioning since the full file name
 * from the URL will be passed in. An example file name is:
 * /FS/FirstDirectory/SecondDirectory/filename where /FS/ is used respresenting file
 * system access and the rest is directory path.
 * The file information block contains the rest of the information neccessary to create the file.
 * 
 * The RpHSCreateFileStatus call is used to check for completion.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber   File id 
 * @param   theFullNamePtr  Name of file to be created
 * @param   theFileInfoPtr  Pointer to a file info block
 *
 *                        
 * @return  eRpNoError              The process of creating a file was successfully started.
 * @return  eRpFileSystemNotOpen    Unable to create a file or File system is not initialized
 *
 * @see @link FILEINFOPTR
 * @see @link RpErrorCode
 * @see @link RpHSCreateFileStatus
 *
*ad*/
RpErrorCode RpHSCreateFile(int theFileNumber, char * theFullNamePtr, FILEINFOPTR theFileInfoPtr)
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode      theResult = eRpFileSystemNotOpen;
    RPFS_BUFFER_T    * pFileReq = NULL;

    pFileReq = New_FileSystemBuffer(theFileNumber);

    if (pFileReq != NULL)
    {   
        theResult = eRpNoError;
        /* parse the dir name and file name.
         * It will ignore /FS/ prefix and return only necessary directory path
         * and filename only.
         */
        pFileReq->file_connection->error_status = Parse_FileNamePath(theFullNamePtr, pFileReq->dir_path, 
                                            NAFS_DIRECTORY_PATH_SIZE, pFileReq->file_name, 
                                            NAFS_FILE_NAME_SIZE);
                              
        if (pFileReq->file_connection->error_status != NAFS_SUCCESS)
        {
#if (_PRINTF_DEBUG > 0)
            printf("RpHSCreateFile: unable to parse the directory path %s\n", theFullNamePtr);
#endif
            goto _createFile_ret;
        }

        if (strlen(pFileReq->dir_path) == 0)
        {
#if (_PRINTF_DEBUG > 0)
            printf("RpHSCreateFile: Missing directory path\n");
#endif
            pFileReq->file_connection->error_status = NAFS_INVALID_VOLUME_NAME;
            goto _createFile_ret;
        }
            
        /* if user did login with multiple access,
         * get the permissions from the multiple access and make
         * sure we have valid group id.
         */
        pFileReq->file_connection->error_status        = NAFS_SUCCESS;
        pFileReq->permissions         = NASYSACC_FS_SUPER_USER; 
        pFileReq->file_info.FileDate = theFileInfoPtr->FileDate;
        pFileReq->file_info.FileSize = theFileInfoPtr->FileSize;
        pFileReq->file_info.FileType = theFileInfoPtr->FileType;
        pFileReq->file_info.FileAccess = theFileInfoPtr->FileAccess;


        pFileReq->dir_info_list = NULL;
        pFileReq->file_offset = 0;
        
        /* initialize file system io request control */        
        pFileReq->file_connection->error_status = NAFSinit_io_request_cb(&pFileReq->io_request, NULL, 0);
        if (pFileReq->file_connection->error_status != NAFS_SUCCESS)
        {
#if (_PRINTF_DEBUG > 0)
            printf("RpHSCreateFile: NAFSinit_io_request_cb return error %d\n", pFileReq->file_connection->error_status);
#endif
            goto _createFile_ret;
        }
        
        /* In order to create a file, we need to determine which group id is going
         * to be created with the file.
         * 
         * First, we try to get the group-id (realm) & permission by query the current 
         * login username.
         *
         * If the login user is setup for multiple accesses, we must get & use the
         * group-id from the directory where a file is going to be created. We must
         * access the file system to return the information about the directory.
         *
         * Once this function is called, we must continue on RpHSCreateFileStatus
         * to obtain correct group id.
         */
         
        /* Get the current login user realm (group id) and permission.
         * We only want single access realm.
         */
        if (Get_LoginGroupIdnPermissions(&pFileReq->file_info.FileAccess, &pFileReq->permissions) != 0)
        {   
            /* We are unable to get group id from the login user. This may due to no user
             * is logged in or the user has multiple accesses.
             *
             * Use default group id for root (volume name) directory.
             * If not root directory, must get the directory group id from the file system.
             */
            if (Is_RootDirectory(pFileReq->dir_path))
            {
                pFileReq->file_info.FileAccess = gRpFSDefaultGroupId;
                pFileReq->state = eFileSystemProcess_CreateFile;
	    		pFileReq->process_function_cb = NULL;

                /* make sure we have any write permission. If so, use super-user
                 * permission.
                 */
                if (Is_WritePermission(kRpPageAccess_AllRealms, pFileReq->permissions) == True)
                    pFileReq->permissions         = NASYSACC_FS_SUPER_USER; 

            }
            else
            {
            /* User must contains multiple group-id so we must use the same group id as the  
             * directory.
             *
             * If the user logs in with different access code than the directory's group-id,
             * the lowest group-id of the user's login access code will be used.
             *
             * In order to get the current directroy group-id, we need 
             * to get dir on previous directory so it will return the current 
             * directory info.
             * 1. Get the number of items in the previous directory.
             * 2. Allocate the number of items in the directory.
             * 3. Get the directory listing.
             * 4. Setup the group-id for the current directory
             * 5. Create a file.
             * 6. Open a file.
             * 7. Write data into a file.
             * 8. Repeat 7 until no more data.
             * 9. Close a file and free memory.
             */

                pFileReq->permissions         = NASYSACC_FS_SUPER_USER;
                pFileReq->state = eFileSystemProcess_EntryCount;
	    		pFileReq->process_function_cb = Remove_LastDirectoryName;
            }
        }
        else
        {   /* group-id is ok */

#if (_RESTRICTED_PERMISSION == 0)
            /* we must make sure we have write permission set on the same group-id 
             * which a file will be created with. Use super-user so that it still allows
             * a file to be created with different group-id than directory's group-id.
            */
            if (Is_WritePermission(pFileReq->file_info.FileAccess, pFileReq->permissions) == True)
                pFileReq->permissions         = NASYSACC_FS_SUPER_USER;
#endif  
            pFileReq->state = eFileSystemProcess_CreateFile;
			pFileReq->process_function_cb = NULL;
        }
        
        pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);

#if (_PRINTF_DEBUG > 0)
        printf("\n\nRpFS_CreateFile: >>>>> index %d path = %s file = %s error = %d\n", 
                        theFileNumber, pFileReq->dir_path, pFileReq->file_name, 
                        pFileReq->file_connection->error_status);
#endif
        
    }
_createFile_ret:
    return theResult;
#else /* APP_FILE_SYSTEM */

    return eRpNoError;
#endif
}

/*ad*
 * The RpHSCreateFileStatus routine is called to determine whether the RpHSCreateFile
 * call has completed.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber          File id
 * @param   theCompletionStatusPtr Pointer to the create status which will be set
 *                                 indicating whether the file has been successfully
 *                                 created
 *          <list type="unordered">
 *          <item> ACTIONCOMPLETED - Finish creating file action </item>
 *          <item> ACTIONPENDING   - Waiting for completion of a file creation </item>
 *          </list>
 *
 *                        
 * @return  eRpNoError              No error when creating a file
 * @return  eRpFileSystemNotOpen    Unable to create a file or File system is not initialized
 * @return  eRpFileCreateError      Unable to create a file
 *
 * @see @link HttpFileAction
 * @see @link RpErrorCode
 * @see @link RpHSCreateFile
 *
*ad*/
RpErrorCode RpHSCreateFileStatus(int theFileNumber, int * theCompletionStatusPtr) 
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode     theResult = eRpFileSystemNotOpen;
    RPFS_BUFFER_T   * pFileReq = NULL;
    unsigned int    byteTransferred;
    unsigned int	status;
            
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL)
    {
        theResult = eRpNoError;
    
        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
        {
            /* get the file system io status for the previous file system process */    
            status = Get_IORequestStatus(&pFileReq->io_request, &byteTransferred, 
                                     &pFileReq->file_connection->error_status);
        }
		else if (IS_FILE_SYSTEM_BUSY(pFileReq->file_connection->error_status))
		{
			/* try to previous API call again */
            pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
			status = eFileSystemStatus_Wait;
		}
        else
        {   /* error occurs so skip any processing.
             * But no error is returned since http server sends its own
             * error message back to client instead of calling the
             * post-processing function (the web page initiates the upload)
             * to handle any error status.
             */
            status = eFileSystemStatus_Error;
        }
        
        *theCompletionStatusPtr = ACTIONPENDING;   
    
        /* if successfully created a file or even createFile returns error because,
         * the file is already exist, we want to continue 
         */
        if (status == eFileSystemStatus_Complete 
#if (_RESTRICTED_FILE_CREATION == 0)
            /* if a file is already existed */
            || (status != eFileSystemStatus_Wait && 
            pFileReq->state == eFileSystemProcess_CreateFile &&
            pFileReq->file_connection->error_status == NAFS_DUPLICATE_DIR_ENTRY) 
#endif
            )
        {   
            switch (pFileReq->state) {
                case eFileSystemProcess_EntryCount:
                    /* We now know number of entry in the previous directory.
                     * let's allocate memory and get the directory info
                     */
                    pFileReq->state = eFileSystemProcess_GetDir;
					pFileReq->process_function_cb = Remove_LastDirectoryName;
                    break;            
                case eFileSystemProcess_GetDir:
                    /* let's get the current directory info. */
                    pFileReq->state = eFileSystemProcess_ListDir;
					pFileReq->process_function_cb = Get_LastDirectoryName;
                    break;
                case eFileSystemProcess_ListDir:
                    /* let's create a file since we already get group-id for the directory
                     * where a file will be created.
                     */
                    {
                    rpAccess groupId = 0;
                    /* we must make sure we have write permission set on the same group-id 
                     * where a file will be created to. 
                     *
                     * If the given permission doesn't contain the group-id access for the 
                     * directory, we must derive the group-id from the user login permission
                     * and use it to create a file.
                     *
                     * Use super-user permission so that it still allows
                     * a file to be created with different group-id than directory's group-id.
                     * 
                     */

                    /* get the user login permissions */
                    Get_LoginGroupIdnPermissions(&groupId, &pFileReq->permissions);

                #if (_RESTRICTED_PERMISSION == 0)
                    /* the login permission has the write access permission for directory */
                    if (Is_WritePermission(pFileReq->file_info.FileAccess, pFileReq->permissions) == True)
                        pFileReq->permissions         = NASYSACC_FS_SUPER_USER;
                    /* derive group-id from the login write permission */
                    else if (Get_WritePermissionToGroupId(pFileReq->permissions, &pFileReq->file_info.FileAccess) == True)
                        pFileReq->permissions         = NASYSACC_FS_SUPER_USER;
                #endif  
                    }

                    pFileReq->state = eFileSystemProcess_CreateFile;
					pFileReq->process_function_cb = NULL;
                    break;
                case eFileSystemProcess_CreateFile:
                    /* file is created. 
                     * let's open the file
                     */
                    pFileReq->file_status |= FILE_SYSTEM_CREATE_FILE;
                    pFileReq->state = eFileSystemProcess_OpenFile;
					pFileReq->process_function_cb = NULL;
                    break;
                case eFileSystemProcess_OpenFile:
                    pFileReq->file_status |= FILE_SYSTEM_OPEN_FILE;
                    if (pFileReq->file_status & FILE_SYSTEM_CREATE_FILE)
                    {
                        /* a file is successfully opened
                         * Let's get the file size so that we can clear the whole file 
                         * before doing NAFS_RANDOM_WRITE.
                         */
                        pFileReq->state = eFileSystemProcess_GetFileSize;
		    			pFileReq->process_function_cb = NULL;
                        break;
                    }
                case eFileSystemProcess_GetFileSize:
                    if (pFileReq->file_status & FILE_SYSTEM_CREATE_FILE &&
                        pFileReq->file_offset > 0)
                    {
                        /* a file is successfully opened
                         * Let's clear the whole file before doing 
                         * NAFS_RANDOM_WRITE.
                         */
                        pFileReq->file_offset = 0;
                        pFileReq->state = eFileSystemProcess_ClearFile;
		    			pFileReq->process_function_cb = NULL;
                        break;
                    }
                 default: 
                    /* file has been successfully created/opened  */
                    pFileReq->file_status |= FILE_SYSTEM_OPEN_FILE;
                    pFileReq->file_connection->error_status = NAFS_SUCCESS;
                    *theCompletionStatusPtr = ACTIONCOMPLETED;
                    break;
            }
        	if (*theCompletionStatusPtr == ACTIONPENDING)
	            pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);

        }
        else if (status == eFileSystemStatus_Error)
        {
#if (_RESTRICTED_FILE_CREATION == 0)
            /* if the file is being opened, we need to wait and try again */
            if (pFileReq->state == eFileSystemProcess_OpenFile &&
                pFileReq->file_connection->error_status == NAFS_FILE_IS_OPEN)
            {
	            pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
            }
            else
#endif
            {
                /*
                 *  Although error occurs, let's tell http server create action is done 
                 *  and continues the write proccess.
                 */
                *theCompletionStatusPtr = ACTIONCOMPLETED;

			    #if (_HTTP_POST_FUNCTION_ENABLE == 0)
	            theResult = eRpFileCreateError;
			    #endif
            }
        }
    } 

#if (_HTTP_POST_FUNCTION_ENABLE == 0)
    if (theResult != eRpNoError && pFileReq != NULL)
    {
        /* free the file system buffer */
        if (pFileReq->dir_info_list != NULL) 
        {
            free(pFileReq->dir_info_list);
        }
        
		pFileReq->process_function_cb = NULL;
        pFileReq->dir_info_list = NULL;
        pFileReq->state = eFileSystemProcess_None;
        pFileReq->file_info.FileAccess = 0;
        pFileReq->parsing_buffer[0] = '\0';
        pFileReq->dir_path = NULL;
        pFileReq->file_status = FILE_SYSTEM_NO_FILE;
        pFileReq->file_connection = NULL;

    }
#endif
    
    return theResult;
#else /* APP_FILE_SYSTEM */
    *theCompletionStatusPtr = ACTIONCOMPLETED;   
    
    return eRpNoError;
#endif
}


/*ad*
 * The RpHSCloseFile routine is used to start closing a file.
 *
 * The RpHSCloseFileStatus call is used to detect the completion of the close.  
 * theCompleteFlag is used to signal whether all data was received from browser.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber   File id
 * @param   theCompleteFlag Complete flag indicator
 *          <list type="unordered">
 *          <item>  1      - File is completed </item>
 *          <item>  0      - File is incompleted </item>
 *          </list>
 *
 *                        
 * @return  eRpNoError              The process of closing a file was successfully started.
 * @return  eRpFileSystemNotOpen    File system is not initialized.
 *
 * @see @link RpErrorCode
 * @see @link RpHSCloseFileStatus
 *
*ad*/
RpErrorCode RpHSCloseFile(int theFileNumber, int theCompleteFlag)
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode     theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T   * pFileReq;
    int             rc;
    
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    if (pFileReq != NULL) 
    {
        if (pFileReq->file_status & FILE_SYSTEM_OPEN_FILE)
        {
            pFileReq->state = eFileSystemProcess_CloseFile;
		    pFileReq->process_function_cb = NULL;
            rc = Process_FileState(pFileReq, pFileReq->process_function_cb);

		    /* temporary save the close status. So that it can be closed */
            pFileReq->file_offset = rc;
        }
        
        theResult = eRpNoError; 
            
    }

    return theResult;
#else /* APP_FILE_SYSTEM */
    return eRpNoError;
#endif
}

/*ad*
 * The RpHSCloseFileStatus routine is called to determine whether a file has
 * been closed.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber          File id
 * @param   theCompletionStatusPtr Pointer to memory which will be set
 *                                 indicating whether the file has been successfully
 *                                 closed
 *          <list type="unordered">
 *          <item> ACTIONCOMPLETED - File was closed </item>
 *          <item> ACTIONPENDING   - Waiting for completion to close a file </item>
 *          </list>
 *
 *                        
 * @return  eRpNoError              No error on closing a file
 * @return  eRpFileSystemNotOpen    Unable to close a file or File system is not initialized
 *
 * @see @link RpErrorCode
 * @see @link HttpFileAction
 * @see @link RpHSCloseFile
 *
*ad*/
RpErrorCode RpHSCloseFileStatus(int theFileNumber,int * theCompletionStatusPtr) 
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode     theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T   * pFileReq;
    unsigned int    byteTransferred, status = eFileSystemStatus_Complete;
    int             error_status = NAFS_SUCCESS;
    
    
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL)
    {
        theResult = eRpNoError;
        
        *theCompletionStatusPtr = ACTIONCOMPLETED;

        if (pFileReq->file_status & FILE_SYSTEM_OPEN_FILE)
        {
		    if (IS_FILE_SYSTEM_BUSY(pFileReq->file_offset))
		    {
			    /* try to previous API call again */
                error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
		        if (IS_FILE_SYSTEM_BUSY(error_status))
                {
                    pFileReq->file_offset = error_status;
                }
                else
                {
                    pFileReq->file_offset = 0;
                }
			    status = eFileSystemStatus_Wait;
		    }
            else
		    {
	            /* get file system io request status */
    	        status = Get_IORequestStatus(&pFileReq->io_request, &byteTransferred, 
                                         &error_status);
            }
        }
        if (status == eFileSystemStatus_Wait)
        {
            *theCompletionStatusPtr = ACTIONPENDING;
        }
        else
        {
	        #if (_PRINTF_DEBUG > 0)
            printf("RpFS_CloseFileStatus: index %d io status %d error status %d<<<<<\n\n", 
                            theFileNumber, status, error_status);
            #endif
            if (pFileReq->file_connection->error_status == NAFS_SUCCESS && error_status != NAFS_SUCCESS)
            {
                /* Can't close a file */
                pFileReq->file_connection->error_status = error_status;
                printf("RpHSCloseFileStatus: index %d error %d\n", theFileNumber, error_status);
            
            }
        
            if (pFileReq->dir_info_list != NULL) 
            {
                free(pFileReq->dir_info_list);
            }
            
		    pFileReq->process_function_cb = NULL;
            pFileReq->dir_info_list = NULL;
            pFileReq->state = eFileSystemProcess_None;
            pFileReq->file_info.FileAccess = 0;
            pFileReq->parsing_buffer[0] = '\0';
            pFileReq->dir_path = NULL;
            pFileReq->file_status = FILE_SYSTEM_NO_FILE;
            pFileReq->file_connection = NULL;
            pFileReq->file_number = -1;

        }
    }
    return theResult;
#else /* APP_FILE_SYSTEM */

   *theCompletionStatusPtr = ACTIONCOMPLETED;
    return eRpNoError;
#endif
}


/*ad*
 * The RpHSWriteFile routine is called to start a write from the buffer provided
 * for the number of bytes in the count. This call is asynchronous and completes 
 * when the write has been started.  The RpHSWriteStatus call is used to detect 
 * the completion of the write.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber   File id
 * @param   theWritePtr     Pointer to the write buffer 
 * @param   theByteCount    Number of bytes to write
 *
 *                        
 * @return  eRpNoError              The process of writing a file has been successfully started.
 * @return  eRpFileSystemNotOpen    Unable to write data to a file or 
 *                                  File system is not initialized
 *
 * @see @link RpErrorCode
 * @see @link RpHSWriteFileStatus
 *
*ad*/
RpErrorCode RpHSWriteFile(int theFileNumber,char * theWritePtr, unsigned long theByteCount)
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode     theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T   * pFileReq;
    
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL)
    {
        theResult = eRpNoError;
        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
        {
            pFileReq->state = eFileSystemProcess_WriteFile;
            /* if no error in file system, writes to it. Otherwise, just flush it */
            /* Used NAFS_RANDOM_WRITE. NAFS_ZERO_OFFSET_ERASE may not work for NAND flash */
            if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
                pFileReq->file_connection->error_status = NAFSwrite_file(pFileReq->file_handler, pFileReq->permissions, 
                          theByteCount, pFileReq->file_offset, theWritePtr,
		                  &pFileReq->io_request, NAFS_RANDOM_WRITE);
        }
        else
        {
            pFileReq->bytes_count = theByteCount;
        }
        
    }


    return theResult;
#else /* APP_FILE_SYSTEM */

    return eRpNoError;
#endif
}


/*ad*
 *  The RpHSWriteStatus routine is called to determine whether the write
 *  has finished.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber              File id
 * @param   theCompletionStatusPtr     Pointer to the call status
 *          <list type="unordered">
 *          <item> ACTIONCOMPLETED     Write action is completed </item>
 *          <item> ACTIONPENDING       Waiting for write completion </item>
 *          </list>
 * @param   theBytesWrittenPtr         Pointer to the number of bytes that has been 
 *                                     written to
 *
 *                        
 * @return  eRpNoError              No error when writing file
 * @return  eRpFileSystemNotOpen    Unable to write a file or 
 *                                  File system is not initialized
 * @return  eRpFileCreateError      Unable to write a file
 *
 * @see @link HttpFileAction
 * @see @link RpErrorCode
 * @see @link RpHSWriteFile
 *
*ad*/
RpErrorCode RpHSWriteFileStatus(int theFileNumber, int * theCompletionStatusPtr, unsigned long * theBytesWrittenPtr)
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode      theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T    * pFileReq = NULL;
    unsigned int     byteTransferred = 0;
    unsigned int	 status;
    

    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL)
    {
	    theResult = eRpNoError;

        *theCompletionStatusPtr = ACTIONCOMPLETED;
        *theBytesWrittenPtr = 0;
        
		if (IS_FILE_SYSTEM_BUSY(pFileReq->file_connection->error_status))
		{
			/* make byteTransferred=0 so it pretents we didn't read anything.
			 * RpHSWriteFile should be called again 
			 */
			status = eFileSystemStatus_Complete;
			byteTransferred = 0;
		}
        else if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
        {
            /* get file system io request status */
            status = Get_IORequestStatus(&pFileReq->io_request, &byteTransferred, 
                                         &pFileReq->file_connection->error_status);
		}
        else
        {
            /* error occurs so skip any processing. */
            byteTransferred = pFileReq->bytes_count;
            status = eFileSystemStatus_Error;
		}
                
        if (status == eFileSystemStatus_Complete)
        {   /* data is successfully written so setup for next write */
            pFileReq->file_info.FileSize += byteTransferred;
            pFileReq->file_offset += byteTransferred;
            *theBytesWrittenPtr = byteTransferred;
        
        }
        else if (status == eFileSystemStatus_Wait)
            *theCompletionStatusPtr = ACTIONPENDING;
		else 
		{

	        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
            {
                pFileReq->file_info.FileSize += byteTransferred;
                pFileReq->file_offset += byteTransferred;
            }
            *theBytesWrittenPtr = byteTransferred;

#if (_HTTP_POST_FUNCTION_ENABLE == 0)
	        if (pFileReq->file_connection->error_status != NAFS_SUCCESS)
            {
//		        theResult = eRpFileCreateError;
		        theResult = eRpFileWriteError;
            }
#endif
#if (_PRINTF_DEBUG > 0)
            printf("RpHSWriteFileStatus: index %d bytes transferred %d io error status %d\n", 
                            theFileNumber, byteTransferred, pFileReq->file_connection->error_status);
#endif
        }        
    }                        
    return theResult;
#else /* APP_FILE_SYSTEM */
    *theBytesWrittenPtr = 0;
    *theCompletionStatusPtr = ACTIONCOMPLETED;

    return eRpNoError;
#endif
}

/*ad*
 *  The RpHSOpenFile routine is called to open an individual file.  The file byte
 *  position is set to 1 (the top of the file).  The open file call is responsible 
 *  for all directory positioning, since the full file name from the URL will be 
 *  passed in.  This call is asynchronous and completes when the open has been started.  
 *  The RpHSOpenFileStatus call is used to check for completion. An example file name
 *  is: /FS/MyFirstDirectory/TheSecondDirectory/MyFile.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber      File id
 * @param   theFullNamePtr     Pointer to full URL object name
 *
 * @return  eRpNoError  The process of opening a file has been successfully started.
 * @return  eRpFileSystemNotOpen    File system is not initialized
 *
 * @see @link RpErrorCode
 * @see @link RpHSOpenFileStatus
 *
*ad*/
RpErrorCode RpHSOpenFile(int theFileNumber, char * theFullNamePtr)
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode      theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T    * pFileReq = NULL;
    
    pFileReq = New_FileSystemBuffer(theFileNumber);

    if (pFileReq != NULL)
    {    
	    theResult = eRpNoError;

        /* parse the directory path & file name */        
        pFileReq->file_connection->error_status = Parse_FileNamePath(theFullNamePtr, pFileReq->dir_path, 
                              NAFS_DIRECTORY_PATH_SIZE, pFileReq->file_name, 
                              NAFS_FILE_NAME_SIZE);
                              
        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
        {                          
            pFileReq->permissions = NASYSACC_FS_SUPER_USER;
            pFileReq->file_offset = 0;
            /* intialize file system io request control */
            pFileReq->file_connection->error_status = NAFSinit_io_request_cb(&pFileReq->io_request, NULL, 0);
            
            /* We need to get at least group-id to setup the realm (file access) and file size.
             * 1. Get the number of items in the directory.
             * 2. Allocate the number of items in the directory.
             * 3. Get the directory listing.
             * 4. Setup the realm & file size if the file is in the directory.
             * 5. Open a file.
             * 6. Read a file.
             * 7. Repeat 6 until the end of file.
             * 8. Close a file and free the allocated memory.
             */
            if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
            {
                pFileReq->state = eFileSystemProcess_EntryCount;
				pFileReq->process_function_cb = Get_FileNameDirectory;
                pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
            }
                              
		    #if (_PRINTF_DEBUG > 0)
            printf("RpFS_OpenFile: >>>>> path = %s file = %s error = %d\n", 
                            pFileReq->dir_path, pFileReq->file_name, pFileReq->file_connection->error_status);
            #endif
        }

    }
    
    return theResult;
#else /* APP_FILE_SYSTEM */

    return eRpNoError;
#endif
}

/*ad*
 *  The RpHSOpenFileStatus routine is called to determine when the file has
 *  been opened.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber              File id
 * @param   theCompletionStatusPtr     Pointer to the open status which will be set
 *                                     indicating whether open is completed or not.
 *          <list type="unordered">
 *          <item> ACTIONCOMPLETED     Open action is completed </item>
 *          <item> ACTIONPENDING       Waiting for open completion </item>
 *          </list>
 * @param   theFileInfoPtr              Pointer to a file info block which contains
 *                                      file info on a file that has been successfully
 *                                      opened
 * @return  eRpNoError  No error when opening a file
 * @return  <0          Unable to open a file or file system is not initialized
 *
 * @see @link HttpFileAction
 * @see @link FILEINFOPTR
 * @see @link RpErrorCode
 *
*ad*/
RpErrorCode RpHSOpenFileStatus(int theFileNumber, int * theCompletionStatusPtr, FILEINFOPTR theFileInfoPtr)
{
#ifdef APP_FILE_SYSTEM 
    RpErrorCode     theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T   *pFileReq = NULL;
    unsigned int    byteTransferred;
    unsigned int	status;
    
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL)
    {
        theResult = eRpNoError;
        *theCompletionStatusPtr = ACTIONPENDING;
        
        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
        {
            /* check file system io request status */        
            status = Get_IORequestStatus(&pFileReq->io_request, &byteTransferred, 
                                         &pFileReq->file_connection->error_status);
        }
		else if (IS_FILE_SYSTEM_BUSY(pFileReq->file_connection->error_status))
		{
			/* try to previous API call again */
            pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
			status = eFileSystemStatus_Wait;
		}
        else
        {   /* error occurs so skip any processing. */
            status = eFileSystemStatus_Error;
        }
    
        if (status == eFileSystemStatus_Complete)
        {   
            /* 
             * if get entry count is ok, allocate memory for it and 
             * list the dir for the file info. Then, does an open.
             */
            switch (pFileReq->state) {
                case eFileSystemProcess_EntryCount:
                    /* let's allocate memory to get directory listing */
                    pFileReq->state = eFileSystemProcess_GetDir;
					pFileReq->process_function_cb = Get_FileNameDirectory;
                    pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
                    break;
                case eFileSystemProcess_GetDir:                
                    /* let's setup group-id, file size & file date
                     * Make sure it's a data file (not a directory)
                     */
                    pFileReq->state = eFileSystemProcess_ListDir;
					pFileReq->process_function_cb = Get_FileName;
                    pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);

                    if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
                    {	
						/*
						 * If the file access is same as the default file access, 
						 * no need to setup realm for HTTP server.
						 */
						if ((pFileReq->file_info.FileAccess & gRpFSDefaultGroupId) != 0)
                        {
	                       	theFileInfoPtr->FileAccess =  kRpPageAccess_Unprotected;
                            Get_GroupIdToPermission(pFileReq->file_info.FileAccess, &pFileReq->permissions);
                        }
						else
                        {
                            pFileReq->permissions = NASYSACC_FS_SUPER_USER;
                            
                        #if 0
                            pFileReq->permissions = 0;
                            /* This is only when you have external password function
                             * and you must know which realm is used for external password.
                             */
                                                        
                            /* we have setup kRpPageAccess_Realm for external password
                             * validation. Application supplies user password validation
                             * function to check username/password.
                             * If this file requires external password validation, we
                             * must use super-user-permission to open a file and
                             * let the http server to return error if external password
                             * validation fails.
                             */
                            if (pFileReq->file_info.FileAccess == kRpPageAccess_Realm3)
                            {
                                pFileReq->permissions = NASYSACC_FS_SUPER_USER;
                            }
                       #endif
                            /* set up the file access so that the http server will
                             * initial a challenge if user has not been validated.
                             */
	                       	theFileInfoPtr->FileAccess =  pFileReq->file_info.FileAccess; 
                       
                        }

                        theFileInfoPtr->FileDate =    pFileReq->file_info.FileDate;
                        theFileInfoPtr->FileSize =    pFileReq->file_info.FileSize;
                        theFileInfoPtr->FileType =    pFileReq->file_info.FileType;
                        strcpy(theFileInfoPtr->FileMimeType, pFileReq->file_info.FileMimeType);
                    }
                    break;
                case eFileSystemProcess_ListDir:
                    /* let's open the file */
                    pFileReq->state = eFileSystemProcess_OpenFile;
					pFileReq->process_function_cb = NULL;
                    pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
                    break;
                case eFileSystemProcess_OpenFile:
                    pFileReq->file_status |= FILE_SYSTEM_OPEN_FILE;
#if 0
                    if (pFileReq->file_status & FILE_SYSTEM_CREATE_FILE)
                    {
                        /* a file is successfully opened
                         * Let's clear the whole file before doing 
                         * NAFS_RANDOM_WRITE.
                         */
                        pFileReq->state = eFileSystemProcess_ClearFile;
		    			pFileReq->process_function_cb = NULL;
                        pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
                        break;
                    }
#endif
                default:
                    /* a file is successfully opened */
                    *theCompletionStatusPtr = ACTIONCOMPLETED;
                    pFileReq->file_connection->error_status = NAFS_SUCCESS;
                    break;
            }
        }
        else if (status == eFileSystemStatus_Error)
        {   
        #if (_PRINTF_DEBUG > 0)
            printf("RpHSOpenFileStatus: error %d\n", pFileReq->file_connection->error_status);
        #endif    
            /* return ok so that user will be prompted with username/password entry */
            if (pFileReq->file_connection->error_status == NAFS_NO_READ_PERMISSION)
            {   
                *theCompletionStatusPtr = ACTIONCOMPLETED;   
                theResult = eRpFileAccessError;
            } 
            /* if the file is being opened, we should try again */
            else if (pFileReq->file_connection->error_status == NAFS_FILE_IS_OPEN)
            {
                pFileReq->file_connection->error_status = Process_FileState(pFileReq, pFileReq->process_function_cb);
            }
            else
            {
                /* returns error. */
                *theCompletionStatusPtr = ACTIONCOMPLETED;   
                theResult = eRpFileOpenError;
            }
        }
    }
    
    if (theResult != eRpNoError && pFileReq != NULL)
    {
        /* free the file system buffer */
        if (pFileReq->dir_info_list != NULL) 
        {
            free(pFileReq->dir_info_list);
        }
        
		pFileReq->process_function_cb = NULL;
        pFileReq->dir_info_list = NULL;
        pFileReq->state = eFileSystemProcess_None;
        pFileReq->file_info.FileAccess = 0;
        pFileReq->parsing_buffer[0] = '\0';
        pFileReq->dir_path = NULL;
        pFileReq->file_status = FILE_SYSTEM_NO_FILE;
        pFileReq->file_connection = NULL;
		pFileReq->file_number = -1;
    }

    return theResult;
#else /* APP_FILE_SYSTEM */
    *theCompletionStatusPtr = ACTIONCOMPLETED;   

    return eRpNoError;
#endif
}


/*ad*
 *  The RpHSReadFile routine is called to start a read into the buffer provided
 *  for the number of bytes in the count.  The read takes place at the current
 *  file byte position with the file byte position being updated after the read
 *  completes.  This call is asynchronous and completes when the read has been
 *  started.  The SfsReadStatus call is used to detect the completion of the read.
 *
 * @external
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber   File id
 * @param   theReadPtr      Pointer to the read buffer 
 * @param   theByteCount    Number of bytes to read
 *
 *                        
 * @return  eRpNoError  The process of read a file has been successfully started.
 * @return  <0          Unable to open a file or file system is not initialized
 *
 * @see @link RpErrorCode
 * @see @link RpHSGetErrorStatus
 *
*ad*/
RpErrorCode RpHSReadFile(int theFileNumber, char * theReadPtr, unsigned long theByteCount)
{
#ifdef APP_FILE_SYSTEM 
    RpErrorCode     theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T   * pFileReq;
    unsigned long   remainingFileSize;
    unsigned long   bytesToRead;
    
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL) 
    {
        theResult = eRpNoError; 
        pFileReq->state = eFileSystemProcess_ReadFile;
    
        /* if no error is encountered, read the file. But error occurs, skip reading */
        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
        {   
            remainingFileSize = pFileReq->file_info.FileSize - pFileReq->file_offset;
            bytesToRead = (remainingFileSize > theByteCount) ? theByteCount : remainingFileSize;
        
            pFileReq->file_connection->error_status = NAFSread_file(pFileReq->file_handler, pFileReq->permissions, 
                  bytesToRead, pFileReq->file_offset, 
                  theReadPtr, &pFileReq->io_request);
        }              
    }
    return theResult;
#else
    return eRpNoError;
#endif
}

/*ad*
 *  The RpHSReadFileStatus routine is called to determine whether the read
 *  has finished.
 *
 * @external
 * @include "file.h"
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber              File id
 * @param   theCompletionStatusPtr     Pointer to the read status which will be set
 *                                      indicating whether a read is completed or not
 *          <list type="unordered">
 *          <item> READCOMPLETE        Data has been read </item>
 *          <item> ENDOFFILE           Data has been read and it's end of file </item>
 *          <item> READPENDING         The read is outstanding </item>
 *          </list>
 * @param   theBytesReadPtr            Pointer to number of bytes that actually read
 *
 *                        
 * @return  eRpNoError              No error when reading a file
 * @return  eRpFileSystemNotOpen    No File system supported
 * @return  eRpFileReadError        Cannot read a file
 *
 * @see @link RpErrorCode
 * @see @link HttpFileCompletion
 * @see @link RpHSGetErrorStatus
 *
*ad*/
RpErrorCode RpHSReadFileStatus(int theFileNumber, int * theCompletionStatusPtr, unsigned long * theBytesReadPtr)
{
#ifdef APP_FILE_SYSTEM
    RpErrorCode      theResult = eRpFileSystemNotOpen;
    
    RPFS_BUFFER_T    * pFileReq = NULL;
    unsigned int     byteTransferred;
    unsigned int	 status;
    
    pFileReq = Get_FileSystemBuffer(theFileNumber);
    
    if (pFileReq != NULL)
    {
        theResult = eRpNoError;
        *theBytesReadPtr = 0;
        
        if (pFileReq->file_connection->error_status == NAFS_SUCCESS)
		{
            /* check for file system io request status */
            status = Get_IORequestStatus(&pFileReq->io_request, &byteTransferred, 
                                         &pFileReq->file_connection->error_status);
		}
		else if (IS_FILE_SYSTEM_BUSY(pFileReq->file_connection->error_status))
		{
			/* RpHSReadFile should be called to read again  */
			status = eFileSystemStatus_Complete;
			byteTransferred = 0;
		}
        else
            /* error occurs so skip any processing. */
            status = eFileSystemStatus_Error;
        
        if (status == eFileSystemStatus_Complete)
        {   /* data is successfully obtained  */
            pFileReq->file_offset += byteTransferred;
            *theBytesReadPtr = byteTransferred;
            /* see whether we reach the eof */
            if (pFileReq->file_offset >= pFileReq->file_info.FileSize)
                *theCompletionStatusPtr = ENDOFFILE;
            else
                *theCompletionStatusPtr = READCOMPLETE;
                
		    #if (_PRINTF_DEBUG > 0)
            printf("RpFS_ReadFileStatus: index %d io status %d bytes %d\n", 
                        theFileNumber, status, byteTransferred);
            #endif        
        }
        else if (status == eFileSystemStatus_Wait)
            *theCompletionStatusPtr = READPENDING;
        else
        {
            /* no anymore data and returns error */
            *theCompletionStatusPtr = ENDOFFILE;
            theResult = eRpFileReadError;
        }
            
    }                        
    return theResult;
#else /* APP_FILE_SYSTEM */
    *theCompletionStatusPtr = ENDOFFILE;
    *theBytesReadPtr = 0;
    return eRpNoError;

#endif
}

#ifdef APP_FILE_SYSTEM
/*ad*
 * Sets the directory path on the NETOS file system for a specified file number 
 *
 * This function allows caller to set a directory path where a file 
 * will be created or opened. It's used prior to upload or download a file.
 *
 * @external
 * @include "file.h"
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber      File id
 * @param   dir_name           Pointer to directory path
 *
 * @return  eRpNoError  			The directory was successfully set.
 * @return  eRpFileSystemNotOpen    Invalid FileNumber or HTTP File System is not initialized
 *
 * @see @link RpErrorCode
 *
*ad*/
RpErrorCode RpHSSetCurrentDirectory(int theFileNumber, char * dir_name)
{
    RpErrorCode      theResult = eRpFileSystemNotOpen;
    RPFS_CONNECTION_T    * pConnection = NULL;

    NA_ASSERT((int)dir_name);
    NA_ASSERT((strlen(dir_name) < NAFS_DIRECTORY_PATH_SIZE));    
    
    pConnection = Get_FileSystemConnection(theFileNumber);
    
    if (pConnection != NULL)
    {
#if (_PRINTF_DEBUG > 0)
        printf("RpHSSetCurrentDirectory: setting %d file number: %s directory\n", theFileNumber, dir_name);
#endif
        strcpy(pConnection->dir_path, dir_name);
        theResult = eRpNoError;
    }
    
    return theResult;
}

/*ad*
 * Gets an error status for the given file number 
 *
 * Since this file system interface doesn't return any errors when errors occur 
 * in the NETOS file system, we need to keep track the error status on each file request. 
 * This function should be called as a post-processing function to indicate whether
 * download or upload is successfully completed.
 *
 * @external
 * @include "file.h"
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param   theFileNumber      File id
 * @param   errorStatus        Pointer to the memory where file system 
 *                             error will be returned
 *
 * @return  eRpNoError              The error status was returned successfully in errorStatus
 * @return  eRpFileSystemNotOpen    Invalid FileNumber or HTTP File System is not initialized
 *
 * @see @link RpErrorCode
 * @see @link file_sys_errors
 *
*ad*/
RpErrorCode RpHSGetErrorStatus(int theFileNumber, int * errorStatus)
{
    RpErrorCode      theResult = eRpFileSystemNotOpen;
    RPFS_CONNECTION_T    * pConnection = NULL;

    NA_ASSERT((int)errorStatus);
    
    pConnection = Get_FileSystemConnection(theFileNumber);
    if (pConnection != NULL)
    {
#if (_PRINTF_DEBUG > 0)
        printf("RpHSGetErrorStatus:  %d file number: %d error\n", theFileNumber, pConnection->error_status);
#endif
        theResult = eRpNoError;
        *errorStatus = pConnection->error_status;
    }
    return theResult;
}    

/*ad*
 * Sets a default group id
 *
 * This default group id is used for uploading file into the NETOS file system
 * when access permission information is not available from the logged in 
 * username and password. It's used for directory access permission and 
 * file creation group id.
 * 
 * It's also used for downloading a file from the NETOS file system. If a file 
 * can be accessed by this default group id, no authenication is required.
 *
 * @external
 * @include "file.h"
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @param    group_id       default group-id to be used 
 *		    <list type="unordered">
 *			<item>NAFS_FS_GROUP1 </item>
 *			<item>NAFS_FS_GROUP2 </item>
 *			<item>NAFS_FS_GROUP3 </item>
 *			<item>NAFS_FS_GROUP4 </item>
 *			<item>NAFS_FS_GROUP5 </item>
 *			<item>NAFS_FS_GROUP6 </item>
 *			<item>NAFS_FS_GROUP7 </item>
 *			<item>NAFS_FS_GROUP8 </item>
 *		    </list>
 *
 * @see @link group_masks
 *                        
*ad*/
void RpHSSetDefaultGroupId(unsigned group_id)
{
	
	NA_ASSERT((group_id >= NAFS_FS_GROUP1) && (group_id <= NAFS_FS_GROUP8));

	gRpFSDefaultGroupId = group_id;
}
    
/*ad*
 * Returns the default group id for accessing NETOS file system
 *
 * @external
 * @include "file.h"
 * @category Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @since 6.0 
 *
 * @return   "group_id"         Default group id
 *		    <list type="unordered">
 *			<item>NAFS_FS_GROUP1 </item>
 *			<item>NAFS_FS_GROUP2 </item>
 *			<item>NAFS_FS_GROUP3 </item>
 *			<item>NAFS_FS_GROUP4 </item>
 *			<item>NAFS_FS_GROUP5 </item>
 *			<item>NAFS_FS_GROUP6 </item>
 *			<item>NAFS_FS_GROUP7 </item>
 *			<item>NAFS_FS_GROUP8 </item>
 *		    </list>
 *
 * @see @link group_masks
 *
*ad*/
unsigned RpHSGetDefaultGroupId(void)
{

	return gRpFSDefaultGroupId;
}
#endif /* APP_FILE_SYSTEM */


