/*
 * eRTOS TFTP Server
 * Copyright (c) 2000 Erick Engelke
 */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include <rtos.h>
#include <net.h>
#include <tftp.h>

#define TFTP_PORT 69
#define TFTP_BUF_LEN 1024

#define MAXPATHLEN 81

int _tftpd_readonly = 0;
//---------------------------------------------------------------------
// getblock - returns received length
static int getblock( char *filename, int blocknum, void *buffer )
{
    FILE *f;
    int result = 0;

    dos_enter();
    f = fopen( filename, "rb");
    if ( f != NULL ) {
        if ( 0 == fseek( f, (blocknum - 1) * 512, SEEK_SET ))
            result = fread( buffer, 1, 512, f );
        fclose( f );
    }
    dos_exit();
    return( result );
}

// putblock - returns 0 on success
static int putblock( char *filename, int blocknum, void *buffer, int length )
{
    FILE *f;
    int result = 0;

    dos_enter();
    f = fopen( filename, "a+b");
    if ( f != NULL ) {
        if ( 0 == fseek( f, (blocknum - 1) * 512, SEEK_SET ))
            fwrite( buffer, 1, length, f );
        else result = 3;    // disk full or alloc exceeded
        fclose( f );
    } else result = 3;
    dos_exit();

    return( result ? 3 : 0 );
}


#pragma argsused
void tftpdthread( DWORD remotehost )
{
    char *buf;
    unsigned buflen;
    int socklistening;
    static udp_Socket sock;
    DATA_Packet *d;
    ACK_Packet *a;
    RRQ_Packet *r;
    ERROR_Packet *e;
    char filename[ MAXPATHLEN ];
    int block;
    *filename = 0;
    if ( ( buf = kcalloc( TFTP_BUF_LEN, 1 )) == NULL )
        rt_halt( "TFTPD insufficient memory");


    d = a = r = e = buf;

    socklistening = 0;
    do {
        if ( socklistening == 0 ) {
            /* udp_open - use remote_IP=0, bind to first incoming IP address */
            /* if remotehost = 0  listen to anyone */
            /* otherwise directed listen */
            if (!udp_open( &sock, TFTP_PORT, remotehost, -1, NULL ))
                rt_halt("unable to open TFTP server socket");
            socklistening = 1;
        }
        rt_sleep( 10 );
        tcp_tick( NULL );

        /* look for input */
        if ( (buflen = sock_dataready( &sock )) == 0 ) {
            continue;   /* no input */
        }

        /* we received an TFTP request, process it */
        buflen = sock_fastread( &sock, buf, buflen );

        /* remote GET */
        if ( d->op2 == TFTP_RRQ ) {
            memset( filename, 0, MAXPATHLEN );
            strncpy( filename, r->filename, MAXPATHLEN - 1 );

            d->op2 = TFTP_DATA;
            block = 1;
            buflen = sizeof( ACK_Packet ) + getblock( filename, block, d->data );
            d->block = intel16( block );
        }
        else if ( d->op2 == TFTP_ACK ) {
            d->op2 = TFTP_DATA;
            block = intel16( d->block ) + 1;
            buflen = sizeof( ACK_Packet ) + getblock( filename, block, d->data );
            d->block = intel16( block );
        }
        /* remote PUT */
        else if ( d->op2 == TFTP_WRQ ) {
            memset( filename, 0, MAXPATHLEN );
            strncpy( filename, r->filename, MAXPATHLEN - 1 );
            unlink( filename );     // reset existing file
            d->op2 = TFTP_ACK;
            d->block = intel16( 0 );
            buflen = sizeof( ACK_Packet );
        }
        else if ( d->op2 == TFTP_DATA ) {
            if ( _tftpd_readonly != 0 ) {
               e->op2 = TFTP_ERROR;
               e->err1 = 0;
               e->err2 = 3;
               e->errmsg[0] = 0;
               buflen = sizeof( ERROR_Packet );
            } else {
                block = intel16( d->block );
                if ( putblock( filename, block, d->data, buflen - sizeof( ACK_Packet )) != 0 ) {
                    e->op2 = TFTP_ERROR;
                    e->err1 = 0;
                    e->err2 = 3;
                    e->errmsg[0] = 0;
                    buflen = sizeof( ERROR_Packet );
                } else {
                    d->op2 = TFTP_ACK;
                    buflen = sizeof( ACK_Packet );
                    // d->block = intel16( block ) ; // redundant
                }
            }
        }


        sock_fastwrite( &sock, buf, buflen );
        sock_close( &sock );

        /* open next tftpd server */
        socklistening = 0;

    } while( 1 );
}


