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

/*ad*
 * This is an extension of e-mail services to support the NETOS file system.
 *
 * These mail services allow a user to send a mail with file attachments in
 * the file systam, and save file attachments in a mail to the file system.
 *
 * The mail service is handled by an HTTP server thread which supports POP3 and
 * SMTP. The interface between the HTTP server and the file system must be 
 * enabled to support these APIs. 
 *
 *                         
 * @external
 * @overview Email Service & File System Interface
 * @category Networking:Email_Services:File_System_Interface
 * @since 6.0
 * @include "mailfile.h"
 * @platform all
 * @library filesys,rphttpd,emailc
 * @see @link Networking:Web_Servers:Advanced_Web_Server:File_System_Interface
 * @see @url http://www.ietf.org/rfc/rfc1939.txt POP3:RFC 1939
 * @see @url http://www.ietf.org/rfc/rfc821.txt SMTP:RFC 821
 * @note These APIs must not be used with Blocking/WAIT mode in the web pages 
 *       and callback functions.
*ad*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysAccess.h>
#include <Npttypes.h>
#include <fs_api.h>
#include <AsExtern.h>
#include <fs.h>
#include <tx_api.h>
#include <mailcapi.h>
#include <mailfile.h>
#include <bsp_api.h>

/* debug printf */
#define _DEBUG_PRINTF   0 

int MCRetrMailFileSystem(unsigned long handle, int msgId, char * prefix, 
                         char * readBuffer, unsigned long * readLength, 
                         BOOLEAN includeHeader, int wait);


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

#define _READ_BIT_OFFSET    2
#define _WRITE_BIT_OFFSET   16
#define _FILE_TYPE_ALL      -1


extern void * MCGetServerData(unsigned long handle);
extern int MCSetupMailGlobals(unsigned long handle, char *fromAddr);
extern int MCSaveFileMessage(unsigned long handle, int indx, char *filePrefix, 
                             unsigned char group_id, void *callback, void *cookie);
extern int MCReadFileMessage(unsigned long handle, int indx, char *filePrefix,
                             unsigned char group_id, char *buffer, unsigned long *bufferLen, 
                             void *callback, void *cookie);
extern int MCIsMaxAttachments(void *theMessagePtr);
extern int MCIsMaxToAddr(void *theMessagePtr);
extern int MCIsMaxCopyAddr(void *theMessagePtr);

extern void * RmCreateMessage(void *theTaskDataPtr);

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

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

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

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

    if (*error_status != NAFS_SUCCESS)
        NA_DBPF("mc wait_io_status Error: error status %d\n", *error_status);
    
    return  ((rc != NAFS_SUCCESS || *error_status != NAFS_SUCCESS) ? NAMAILC_GENERALERROR : NAMAILC_OK);
}

/*
 * Parses the given full path into directory path and filename or the last directory 
 * name
 * 
 * category Networking:Email_Services:File_System_Interface
 * since 6.0 
 *
 * param   fullPathName   Pointer to full path name to be parsed
 * param   dirpath        Pointer to memory where directory path will be written to
 * param   dirlen         Pointer to the max size of dirpath and will be set with
 *                          the actual length of dirpath.
 * param   filename       Pointer to memory where filename or last direcotry
 *                          will be written to
 * param   filelen        Pointer to the max size of filename and will be set with
 *                          the actual length of filename.
 *
 *                        
 * return  FILE_OK    directory and dirname have been successfully obtained
 * return  INVALID_FILE_LEN       Invalid filename length
 * return  INVALID_DIRECTORY_LEN   Invalid directory length
 *
 */
static int mc_parse_file_path(char * fullPathName, char * dirpath, int * dirlen, char * filename, int * filelen)
{
    char    * bufferPtr, * namePtr = NULL;
    int     len=0, rc = FILE_OK;

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

    /* since file system only takes '/' not '\\', convert all '\\' to '/' */
    bufferPtr = fullPathName;
    while (strstr(bufferPtr, "\\") != NULL)
    {
        bufferPtr = strstr(bufferPtr, "\\");
        *bufferPtr = '/'; /* change it to forward slash */
        bufferPtr++;
    }

    /* search for slash until the last slash */
    bufferPtr = fullPathName;
    while (bufferPtr != NULL) 
    {
        bufferPtr = strstr(bufferPtr, "/");

        if (bufferPtr != NULL)
        {
            bufferPtr++;
            namePtr = bufferPtr;
        }
    }
    if (namePtr != NULL) 
    {   /* got filename */
        len = (namePtr - fullPathName)-1;
    }
    /* no directory is given */
    if (namePtr == NULL) 
        namePtr = fullPathName;


    if (len > 0 && len < *dirlen)
    {
        /* directory is begun with '/', skip '/' */
        if (strstr(fullPathName, "/") == fullPathName)
            strncpy(dirpath, fullPathName+1, (--len));
        else
            strncpy(dirpath, fullPathName, len);

        dirpath[len] = '\0';
        if (strstr(dirpath, "/") == NULL)
            strcat(dirpath, "/");  /* root directory requires "/" */

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

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

    *filelen = strlen(namePtr);

    return rc;    
}

/*
 * Check the directory permission for the given group id on the NETOS file system
 *
 * It makes sure the directory in the NETOS file system can be accessed by 
 * the given group id.
 * 
 * category Networking:Email_Services:File_System_Interface
 * since 6.0 
 *
 * param   dir_name       Pointer to full path directory name to be parsed
 * param   group_id       group id for the directory permission
 *
 *                        
 * return  NAMAILC_OK              The given group id is allowed to access the directory.
 * return  NAMAILC_GENERALERROR    No access
 * return  NAMAILC_INVALIDFILE     Invalid directory name
 *
 */
static int mc_check_groupId_access(char * dir_name, unsigned int group_id)
{
    int             error_status;
    unsigned int    byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            attached_filename[NAFS_DIRECTORY_PATH_SIZE];
    int             cur_dirlen, attached_filelen;
    unsigned int    dir_info_list_items;
    NAFS_IO_REQUEST_CB  io_request;
    int             rc = NAMAILC_GENERALERROR;
    unsigned int    permission;

    permission = (group_id << _READ_BIT_OFFSET) | (group_id << _WRITE_BIT_OFFSET);
    /* first we want to make sure the file is exist */
    cur_dirlen = sizeof cur_directory;
    attached_filelen = sizeof attached_filename;
    
    /* give me the directory and file name (or last sub-directory)*/
    if (mc_parse_file_path(dir_name, cur_directory, &cur_dirlen, 
                            attached_filename, &attached_filelen) != FILE_OK)
    {
        rc = NAMAILC_INVALIDFILE;
        goto _ret;
    }
    error_status = NAFSinit_io_request_cb(&io_request, NULL, 0);
    if (error_status != NAFS_SUCCESS) goto _ret;

    /* Get number of entry in the directory first.
     * Then allocate memory for the directory info
     * before get the directory listing info.
     * Error if no entry or no such directory.
     */
    error_status = NAFSdir_entry_count(cur_directory, permission, 
                        &dir_info_list_items, &io_request);

    if (error_status != NAFS_SUCCESS) goto _ret;

    if (mc_wait_io_status(&io_request, &byteTransferred, &error_status) != NAMAILC_OK ||
        error_status != NAFS_SUCCESS)
    {
        goto _ret;
    }

    rc = NAMAILC_OK;

_ret:
    return rc;
}


/*ad*
 * Add file attachments from the NETOS file system to a mail
 *
 * It checks and makes sure the file is currently in the NETOS file system. 
 * It then adds the file into the specified mail.
 *
 * @external
 * @include "mailfile.h"
 * @category Networking:Email_Services:File_System_Interface
 * @since 6.0 
 *
 * @param   handle        Identifier of the mail client that is returned by MCCreate
 * @param   mail_id       Mail id of the mail message that is returned by MCNewMail
 * @param   filename      A list (array) of pointers that point to the full filename 
 *                        path in the file system which will be attached in the mail
 *                        message. NULL must be used to indicate the end of the list.
 * @param   permission    Permission to access the file. 
 *                        
 * @return  ">=0"               Number of file attachment has been added in the mail.
 * @return  NAMAILC_SYNTAXERROR  Invalid argument.
 * @return  NAMAILC_NOMAILBOX    No mailbox is available.
 * @return  NAMAILC_BUFFERFULL   Max of attachments has been reached. Not allowing
 *                               any more attachments.
 * @return  NAMAILC_GENERALERROR Error is encountered when access the file in the file
 *                               system.
 *
 * @see @link securityLevels
 * @see @link MCSendMail
 * @see @link MCCancelMail
 * @see @link NAMAILC
 * 
 * @note HTTP server must support the NETOS file system.
*ad*/
int MCAdd_FileAttachment(unsigned long handle, unsigned long mail_id, char * filename[], unsigned int permission)
{
    int             rc = NAMAILC_SYNTAXERROR;
    int             error_status, status;
    unsigned int    byteTransferred;
    char            cur_directory[NAFS_DIRECTORY_PATH_SIZE];
    char            attached_filename[NAFS_DIRECTORY_PATH_SIZE];
    int             cur_dirlen, attached_filelen;
    unsigned int    i, dir_info_list_items;
    NAFS_IO_REQUEST_CB  io_request;
    NAFS_DIR_ENTRY_INFO *dir_info_list;
    int             file_index;

    NA_ASSERT(handle != 0);
    NA_ASSERT(mail_id != 0);
    NA_ASSERT(filename != NULL);
    NA_ASSERT(filename[0] != NULL);
    
    if (filename != NULL && filename[0] != NULL)
        NA_ASSERT(strlen(filename[0]) > 0);
    
    if (handle == 0 || mail_id == 0 || filename == NULL || 
        (filename[0] != NULL && strlen(filename[0]) == 0))
        goto _ret;

    file_index = 0;
    while (filename[file_index] != NULL)
    {
        rc = NAMAILC_GENERALERROR;

        /* first we want to make sure the file is exist */
        cur_dirlen = sizeof cur_directory;
        attached_filelen = sizeof attached_filename;

        /* give me the directory and file name */
        if (mc_parse_file_path(filename[file_index], cur_directory, &cur_dirlen, 
                                attached_filename, &attached_filelen) != FILE_OK)
        {
            rc = NAMAILC_INVALIDFILE;
            goto _ret;
        }

        error_status = NAFSinit_io_request_cb(&io_request, NULL, 0);
        if (error_status != NAFS_SUCCESS) goto _ret;

        /* Get number of entry in the directory first.
         * Then allocate memory for the directory info
         * before get the directory listing info.
         * Error if no entry or no such directory.
         */
        error_status = NAFSdir_entry_count(cur_directory, permission, 
                            &dir_info_list_items, &io_request);

        if (error_status != NAFS_SUCCESS) goto _ret;

        if (mc_wait_io_status(&io_request, &byteTransferred, &error_status) != NAMAILC_OK ||
            (dir_info_list_items == 0))
            goto _ret;

        dir_info_list = (NAFS_DIR_ENTRY_INFO *)calloc(dir_info_list_items, sizeof(NAFS_DIR_ENTRY_INFO));

        if (dir_info_list == NULL) goto _ret;
	
    #if (_DEBUG_PRINTF > 0)    
        printf("MCAdd_FileAttachment: calloc %p --->\n", dir_info_list);
    #endif
        memset(dir_info_list, 0, (dir_info_list_items * sizeof(NAFS_DIR_ENTRY_INFO)));

        /* Get info on the file's directory and make sure the file is
         * existed.
         */
        error_status = NAFSlist_dir(cur_directory, permission, dir_info_list, 
                                            dir_info_list_items, &io_request);

        if (error_status != NAFS_SUCCESS) goto _clean;

        status = mc_wait_io_status(&io_request, &byteTransferred, &error_status);
        if (status == NAMAILC_OK && error_status == NAFS_SUCCESS)
        {
            for (i=0; i < dir_info_list_items; i++)
            {
                if (dir_info_list[i].entry_type  == NAFS_FILE_TYPE_FILE &&
                    strcmp(dir_info_list[i].entry_name, attached_filename) == 0 &&
                    ((dir_info_list[i].group_id << _READ_BIT_OFFSET) & permission) != 0)
                {   /* found the file */
                    rc = NAMAILC_OK;
                    break;
                }
            }
        }
        
_clean:
        free (dir_info_list); 
    #if (_DEBUG_PRINTF > 0)    
        printf("MCAdd_FileAttachment: <--- free %p\n", dir_info_list);
    #endif
        if (rc != NAMAILC_OK) break;  /* error */
        file_index++;
    }

    if (rc == NAMAILC_OK)
        /* At this point, the file is in the local file system */
        rc = MCAdd_File(handle, mail_id, filename);

_ret:
    return rc;
        
}

/*ad*
 * Saves a mail message and file attachment in the NETOS file system
 *
 * @external
 * @include "mailfile.h"
 * @category Networking:Email_Services:File_System_Interface
 * @since 6.0 
 *
 * @param   handle        Identifier of the mail client that is returned by MCCreate
 * @param   msgId         Message Id for which message will be retrieved
 * @param   dir_path      Pointer to the path and prefix name of the email message
 *                         and attachment. For example: /RAMDRIVE/DIR/mailfile. 
 *                         An email message will be saved as dir_path with .txt extension 
 *                         and an attachment will be saved as dir_path with .001 
 *                         extension (for more attachments, the extension will be 
 *                         .002, .003, etc.). NULL for no file attachments will be
 *                         saved. 
 * @param   group_id      Group_id for the mail message file and attachments will be
 *                        created with. This is also used for permisssion access.
 * @param   includeHeader Includes mail header in mail message.
 *          <list type="unordered">
 *          <item>NAMAILC_NOHEADER - No header is included in the mail message.</item>
 *          <item>NAMAILC_HEADER   - Header is include in the body message.</item>
 *          </list>
 * @param   wait          Indicate whether it waits for the email message to be retrieved
 *          <list type="unordered">
 *          <item>NAMAILC_NOWAIT </item>
 *          <item>NAMAILC_WAIT </item>
 *          </list>
 *                        
 * @return  >0                    Number of files has been saved including the embil body message +
 *                                number of attachments if NAMAIL_WAIT is specified. 
 * @return  NAMAILC_OK            Successfully retrieving an email message or requesting email
 *                                retrieval.
 * @return  NAMAILC_NOMAILBOX     No mailbox is available or invalid argument.
 * @return  NAMAILC_MAILBOXBUSY   Busy
 * @return  NAMAILC_SYNTAXERROR   Invalid argument.
 *
 * @see @link mc_wait_status
 * @see @link MCRegisterRetr_Mail
 * @see @link MCRetrMail_Read
 * @see @link NAMAILC
 * @see @link group_masks
 *
 * @note This function does not work if the calling application is HTTP server and is 
 *       called with NAMAILC_WAIT (waiting for the email to be retrieved). 
 *       To retrieve an email message in Web Pages (HTTP Server), this function must be 
 *       called with NAMAILC_NOWAIT and the callback should be used to indicate an email 
 *       message has been retrieved.
 *       <list type="unordered">
 *          <item>HTTP server must support the file system in order to save attachments. </item>
 *       </list>
*ad*/
int MCRetrMail_Save(unsigned long handle, int msgId, char * dir_path, unsigned char group_id, BOOLEAN includeHeader, int wait)
{
    int             result = NAMAILC_SYNTAXERROR;
    
    NA_ASSERT(handle != 0);
    NA_ASSERT(msgId != 0);
    NA_ASSERT(dir_path != NULL);
    NA_ASSERT(group_id >= NAFS_FS_GROUP1 && group_id <= NAFS_FS_GROUP8);

    NA_ASSERT((includeHeader == NAMAILC_NOHEADER || includeHeader == NAMAILC_HEADER));
    NA_ASSERT((wait == NAMAILC_WAIT) || (wait == NAMAILC_NOWAIT));

    if (handle == 0 || msgId == 0 || dir_path == NULL || 
        group_id < NAFS_FS_GROUP1 || group_id > NAFS_FS_GROUP8 ||
        (includeHeader != NAMAILC_NOHEADER && includeHeader != NAMAILC_HEADER) ||
        (wait != NAMAILC_WAIT && wait != NAMAILC_NOWAIT))
        goto _ret;

    result = mc_check_groupId_access(dir_path, group_id);
    if (result != NAMAILC_OK) goto  _ret;

    result = MCRetrMailFileSystem(handle, msgId, dir_path, NULL, NULL, includeHeader, wait);

_ret:
    return result;
}

/*ad*
 * Reads a mail message and saves file attachment in the NETOS file system
 *
 * @external
 * @include "mailfile.h"
 * @category Networking:Email_Services:File_System_Interface
 * @since 6.0 
 *
 * @param   handle        Identifier of the mail client that is returned by MCCreate
 * @param   msgId         Message Id for which message will be retrieved
 * @param   attach_dir    Pointer to the path and prefix name of the any attachment
 *                         to be saved in the file system. For example: /RAMDRIVE/DIR/mailfile
 *                         The attachment will be saved as attach_path with .001 
 *                         extension (for more attachments, the extension will be 
 *                         .002, .003, etc.) 
 * @param   group_id      Group_id for attachments created in the file system.
 * @param   readBuffer    Pointer to memory for the email message
 * @param   readLength    Pointer to the size of readBuffer. After email has been retrieved,
 *                         readLength will contains the length of the email message in readBuffer.
 * @param   includeHeader Includes mail header in the body message.
 *          <list type="unordered">
 *          <item>NAMAILC_NOHEADER - No header is included in the body message.</item>
 *          <item>NAMAILC_HEADER   - Header is include in the body message.</item>
 *          </list>
 * @param   wait          Indicate whether it waits for the email message to be retrieved
 *          <list type="unordered">
 *          <item>NAMAILC_NOWAIT </item>
 *          <item>NAMAILC_WAIT </item>
 *          </list>
 *                        
 * @return  >=0                   Number of mail including attachments has been successfully retrieved
 *                                if NAMAIL_WAIT is specified. 0 indicates email body message is read
 *                                in the readBuffer with no attachment.
 * @return  NAMAILC_OK            Successfully retrieving an email message or requesting email
 *                                retrieval.
 * @return  NAMAILC_NOMAILBOX     No mailbox is available.
 * @return  NAMAILC_MAILBOXBUSY   Busy
 * @return  NAMAILC_SYNTAXERROR   Invalid argument.
 *
 * @see @link mc_wait_status
 * @see @link MCRegisterRetr_Mail
 * @see @link MCRetrMail_Read
 * @see @link MCRetrMail_CapturedAttachmentName
 * @see @link NAMAILC
 *
 * @note This function does not work if the calling application is HTTP server and is 
 *       called with NAMAILC_WAIT (waiting for the email to be retrieved). 
 *       To retrieve an email message in Web Pages (HTTP Server), this function must be
 *       called with NAMAILC_NOWAIT and the callback should be used to indicate an email 
 *       message has been retrieved.
*ad*/
int MCRetrMail_Read(unsigned long handle, int msgId, char * attach_dir, unsigned char group_id, char * readBuffer, unsigned long * readLength, BOOLEAN includeHeader, int wait)
{
    int             result = NAMAILC_SYNTAXERROR;

    NA_ASSERT(handle != 0);
    NA_ASSERT(msgId != 0);
    NA_ASSERT(readBuffer != NULL);
    NA_ASSERT(*readLength > 0);

    NA_ASSERT((includeHeader == NAMAILC_NOHEADER || includeHeader == NAMAILC_HEADER));
    NA_ASSERT((wait == NAMAILC_WAIT) || (wait == NAMAILC_NOWAIT));

    if (handle == 0 || msgId == 0 || 
        (includeHeader != NAMAILC_NOHEADER && includeHeader != NAMAILC_HEADER) ||
        (wait != NAMAILC_WAIT && wait != NAMAILC_NOWAIT))
        goto _ret;

    if (attach_dir != NULL && (group_id < NAFS_FS_GROUP1 || group_id > NAFS_FS_GROUP8))
        goto _ret;

    /* if want attachment to be saved in the File system, 
     * make sure the given group_id is ok.
     */
    if (attach_dir != NULL) {
        NA_ASSERT(group_id >= NAFS_FS_GROUP1 && group_id <= NAFS_FS_GROUP8);
        if (group_id < NAFS_FS_GROUP1 || group_id > NAFS_FS_GROUP8) goto _ret;

        result = mc_check_groupId_access(attach_dir, group_id);
        if (result != NAMAILC_OK) goto  _ret;
    }

    result = MCRetrMailFileSystem(handle, msgId, attach_dir, readBuffer, readLength, includeHeader, wait);

_ret:
    return result;
}




