/***********************************************************************************************************************
PicoMite MMBasic

custom.c

<COPYRIGHT HOLDERS>  Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved. 
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
1.	Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2.	Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
    in the documentation and/or other materials provided with the distribution.
3.	The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed 
    on the console at startup (additional copyright messages may be added).
4.	All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed 
    by the <copyright holder>.
5.	Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software 
    without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

************************************************************************************************************************/

#include "MMBasic_Includes.h"
#include "Hardware_Includes.h"
bool optionsuppressstatus=0;
int TCP_PORT ;
//#define //DEBUG_printf printf
#define DEBUG_printf
const char httpheadersfail[]="HTTP/1.0 404\r\n\r\n";
TCP_SERVER_T *TCPstate=NULL;
jmp_buf recover;
static TCP_SERVER_T* tcp_server_init(void) {
    if(!TCPstate) {
        TCPstate = (TCP_SERVER_T*)calloc(1,sizeof(TCP_SERVER_T));
        memset(TCPstate,0,sizeof(TCP_SERVER_T));
    }
    if (!TCPstate) {
        //DEBUG_printf("failed to allocate state\r\n");
        return NULL;
    }
    for(int i=0;i<MaxPcb;i++){
        TCPstate->client_pcb[i]=NULL;
    }
    TCPstate->telnet_pcb_no=99;
    return TCPstate;
}
err_t tcp_server_close(void *arg, int pcb) {
    if(pcb==99)return ERR_OK;
    TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
    err_t err = ERR_OK;
    if (state->client_pcb[pcb] != 0) {
        tcp_arg(state->client_pcb[pcb], NULL);
        tcp_sent(state->client_pcb[pcb], NULL);
        tcp_recv(state->client_pcb[pcb], NULL);
        tcp_err(state->client_pcb[pcb], NULL);
        tcp_poll(state->client_pcb[pcb], NULL,0);
        err = tcp_close(state->client_pcb[pcb]);
        if (err != ERR_OK) {
            tcp_abort(state->client_pcb[pcb]);
            error("close failed %, calling abort", err);
            err = ERR_ABRT;
        }
        //DEBUG_printf("Close success %x on pcb %x\r\n",state->client_pcb[pcb],pcb);
        state->recv_len[pcb]=0;
        state->client_pcb[pcb]=NULL;
        FreeMemorySafe((void **)&state->buffer_recv[pcb]);
    }
    return err;
}

static err_t tcp_server_result(void *arg, int status) {
        return ERR_OK;
}

static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len, int pcb) {
    TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
    ////DEBUG_printf("tcp_server_sent %u\r\n", len);
    state->pcbopentime[pcb]=time_us_64();
    state->sent_len[pcb] += len;
    return ERR_OK;
}
static err_t tcp_server_sent0(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 0);
}
static err_t tcp_server_sent1(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 1);
}
static err_t tcp_server_sent2(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 2);
}
static err_t tcp_server_sent3(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 3);
}
static err_t tcp_server_sent4(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 4);
}
static err_t tcp_server_sent5(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 5);
}
static err_t tcp_server_sent6(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 6);
}
static err_t tcp_server_sent7(void *arg, struct tcp_pcb *tpcb, u16_t len) {
        return tcp_server_sent(arg, tpcb, len, 7);
}
err_t tcp_server_send_data(void *arg, struct tcp_pcb *tpcb, int pcb)
{
    TCP_SERVER_T *state = (TCP_SERVER_T*)arg;

//    state->sent_len[pcb] = 0;
//    if(pcb!=state->telnet_pcb_no)//DEBUG_printf("Writing %d bytes to client %x\r\n",state->to_send[pcb], (uint32_t)tpcb);
    // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you
    // can use this method to cause an assertion in debug mode, if this method is called when
    // cyw43_arch_lwip_begin IS needed
    int t;
    err_t err;
    uint64_t timestart=time_us_64()+5000000;
    while((t=tcp_sndqueuelen(tpcb))>6 && time_us_64()<timestart){
        //DEBUG_printf("Send queue %u\r\n", t);
        if(startupcomplete)cyw43_arch_poll();
    } 
    state->pcbopentime[pcb]=time_us_64();
    if(time_us_64()<timestart){
           err = tcp_write(tpcb, state->buffer_sent[pcb], state->to_send[pcb],  0);
    } else err=1;
    if (err != ERR_OK) {
        tcp_server_close(state,pcb);
    }
    return ERR_OK;
}
err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err, int pcb) {
//        static int count=0;
        TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
        if (!p) {
                return ERR_OK;
        }
        // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you
        // can use this method to cause an assertion in debug mode, if this method is called when
        // cyw43_arch_lwip_begin IS needed
//        cyw43_arch_lwip_check();
        if (p->tot_len > 0) {
                TCPreceived=1;
    	        if(!CurrentLinePtr){  // deal with requests when we don't want them
                        tcp_recved(tpcb, p->tot_len);
                        //DEBUG_printf("Sending 404 on pcb %d, rbuff address is %x ",pcb, (uint32_t)state->buffer_recv[pcb]);
                        state->sent_len[pcb]=0;
                        state->to_send[pcb]= state->total_sent[pcb] = strlen(httpheadersfail);
                        state->buffer_sent[pcb]=(unsigned char *)httpheadersfail;
                        if(state->client_pcb[pcb]){
                                tcp_server_send_data(state, state->client_pcb[pcb],pcb);
                                checksent(state,0, pcb);
                                tcp_server_close(state, pcb) ;
                        }
                } else {
                        OptionErrorSkip = 1;
                        if(state->buffer_recv[pcb]!=NULL){
                                FreeMemorySafe((void **)&state->buffer_recv[pcb]);
//                                MMPrintString("Internal error in tcp_server_recv - Attempting recovery");
                        }
                        state->buffer_recv[pcb]=GetMemory(p->tot_len);
                        state->inttrig[pcb]=1;
                        //DEBUG_printf("Tcp_HTTP_recv on pcb %d / %d\r\n",pcb, p->tot_len);
                        state->recv_len[pcb] = pbuf_copy_partial(p, state->buffer_recv[pcb] , p->tot_len, 0);
                        if(state->recv_len[pcb]!=p->tot_len) MMPrintString("Warning: WebMite Internal error");
                        for(int i=0;i<p->tot_len;i++)if(state->buffer_recv[pcb][i]==0)state->buffer_recv[pcb][i]=32;
                        tcp_recved(tpcb, p->tot_len);
                        state->pcbopentime[pcb]=time_us_64();
                }
        }
        //DEBUG_printf("Stack pointer is %x free space on heap %u\r\n",((uint32_t)__get_MSP()),getFreeHeap());
        pbuf_free(p);
        return ERR_OK;
}

err_t tcp_server_recv0(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,0);
}
err_t tcp_server_recv1(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,1);
}
err_t tcp_server_recv2(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,2);
}
err_t tcp_server_recv3(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,3);
}
err_t tcp_server_recv4(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,4);
}
err_t tcp_server_recv5(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,5);
}
err_t tcp_server_recv6(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,6);
}
err_t tcp_server_recv7(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        return tcp_server_recv(arg, tpcb, p, err,7);
}

static void tcp_server_err(void *arg, err_t err, int pcb) {
    if (err != ERR_ABRT) {
        //DEBUG_printf("tcp_client_err_fn %d\r\n", err);
        tcp_server_close(arg, pcb);
    }
}
static void tcp_server_err0(void *arg, err_t err) {
        tcp_server_err(arg, err, 0);
}
static void tcp_server_err1(void *arg, err_t err) {
        tcp_server_err(arg, err, 1);
}
static void tcp_server_err2(void *arg, err_t err) {
        tcp_server_err(arg, err, 2);
}
static void tcp_server_err3(void *arg, err_t err) {
        tcp_server_err(arg, err, 3);
}
static void tcp_server_err4(void *arg, err_t err) {
        tcp_server_err(arg, err, 4);
}
static void tcp_server_err5(void *arg, err_t err) {
        tcp_server_err(arg, err, 5);
}
static void tcp_server_err6(void *arg, err_t err) {
        tcp_server_err(arg, err, 6);
}
static void tcp_server_err7(void *arg, err_t err) {
        tcp_server_err(arg, err, 7);
}

static err_t tcp_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) {
    TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
    int pcb=0;
    if (err != ERR_OK || client_pcb == NULL) {
        //DEBUG_printf("Failure in accept\r\n");
        tcp_server_result(arg, err);
        return ERR_VAL;
    }
    for(pcb=0;pcb<=MaxPcb;pcb++){
        if(pcb==MaxPcb)MMPrintString("Warning: No free connections\r\n");
//        if(pcb==MaxPcb)error("No free connections");
        if(state->client_pcb[pcb]==NULL){
                state->client_pcb[pcb] = client_pcb;
                break;
        }
    }
    
    if(client_pcb->local_port==23 && Option.Telnet){
        starttelnet(client_pcb, pcb, arg) ;
        return ERR_OK;
    } else if(client_pcb->local_port==TCP_PORT && TCP_PORT){
        //DEBUG_printf("HTTP Client connected %x on pcb %d\r\n",(uint32_t)client_pcb,pcb);
        tcp_arg(client_pcb, state);
        state->keepalive[pcb]=0;
        tcp_arg(client_pcb, state);
        switch(pcb){
                case 0:
                        tcp_sent(client_pcb, tcp_server_sent0);
                        tcp_recv(client_pcb, tcp_server_recv0);
                        tcp_err(client_pcb, tcp_server_err0);
                        break;
                case 1:
                        tcp_sent(client_pcb, tcp_server_sent1);
                        tcp_recv(client_pcb, tcp_server_recv1);
                        tcp_err(client_pcb, tcp_server_err1);
                        break;
                case 2:
                        tcp_sent(client_pcb, tcp_server_sent2);
                        tcp_recv(client_pcb, tcp_server_recv2);
                        tcp_err(client_pcb, tcp_server_err2);
                        break;
                case 3:
                        tcp_sent(client_pcb, tcp_server_sent3);
                        tcp_recv(client_pcb, tcp_server_recv3);
                        tcp_err(client_pcb, tcp_server_err3);
                        break;
                case 4:
                        tcp_sent(client_pcb, tcp_server_sent4);
                        tcp_recv(client_pcb, tcp_server_recv4);
                        tcp_err(client_pcb, tcp_server_err4);
                        break;
                case 5:
                        tcp_sent(client_pcb, tcp_server_sent5);
                        tcp_recv(client_pcb, tcp_server_recv5);
                        tcp_err(client_pcb, tcp_server_err5);
                        break;
                case 6:
                        tcp_sent(client_pcb, tcp_server_sent6);
                        tcp_recv(client_pcb, tcp_server_recv6);
                        tcp_err(client_pcb, tcp_server_err6);
                        break;
                case 7:
                        tcp_sent(client_pcb, tcp_server_sent7);
                        tcp_recv(client_pcb, tcp_server_recv7);
                        tcp_err(client_pcb, tcp_server_err7);
                        break;
        }
        state->pcbopentime[pcb]=time_us_64();
    } //else DEBUG_printf("Attempted connection on port %d\r\n",client_pcb->local_port);
   int t=0;
    for(int i=0;i<MaxPcb;i++){
        if(state->client_pcb[i]==NULL){
                t++;
        } 
    }
    //DEBUG_printf("Connection still free %u\r\n", t);

    return ERR_OK;
}
static bool tcp_server_open(void *arg) {
    TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
    if(TCP_PORT && WIFIconnected){
        MMPrintString("Starting TCP server at ");
        MMPrintString(ip4addr_ntoa(netif_ip4_addr(netif_list)));
        MMPrintString(" on port ");
        PInt(TCP_PORT);PRet();
    }

    struct tcp_pcb *httppcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
    struct tcp_pcb *telnet_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
    if (!httppcb || !telnet_pcb) {
        //DEBUG_printf("failed to create pcbs\r\n");
        return false;
    }
    err_t err;
    if(TCP_PORT){
        err = tcp_bind(httppcb, NULL, TCP_PORT);
        if (err) {
                char buff[STRINGSIZE]={0};
                sprintf(buff,"failed to bind to port %d\n",TCP_PORT);
                if(!optionsuppressstatus)MMPrintString(buff);
                return false;
        }
        state->server_pcb = tcp_listen_with_backlog(httppcb, MaxPcb);
        if (!state->server_pcb) {
                //DEBUG_printf("failed to listen\r\n");
                if (httppcb) {
                tcp_close(httppcb);
                }
                return false;
        }
        tcp_arg(state->server_pcb, state);
        tcp_accept(state->server_pcb, tcp_server_accept);
    }
    if(Option.Telnet){    
        err = tcp_bind(telnet_pcb, NULL, 23);
        if (err) {
                char buff[STRINGSIZE]={0};
                sprintf(buff,"failed to bind to port %d\n",23);
                if(!optionsuppressstatus)MMPrintString(buff);
                return false;
        }
        state->telnet_pcb = tcp_listen_with_backlog(telnet_pcb, MaxPcb);
        if (!state->telnet_pcb) {
                //DEBUG_printf("failed to listen\r\n");
                if (telnet_pcb) {
                tcp_close(telnet_pcb);
                }
                return false;
        }
        tcp_arg(state->telnet_pcb, state);
        tcp_accept(state->telnet_pcb, tcp_server_accept);
    }


    return true;
}
void checksent(void *arg, int fn, int pcb){
  TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
  int loopcount=1000000;

  while(state->sent_len[pcb]!=state->total_sent[pcb]  && loopcount ){
      loopcount--;
      CheckAbort();
  }
  if(loopcount==0){
      if(fn)ForceFileClose(fn);
      tcp_server_close(state, pcb) ;
      MMPrintString("Warning: LWIP send data timeout\r\n");
  }
}
const char httpheaders[]="HTTP/1.1 200 OK\r\nServer:CPi\r\nConnection:close\r\nContent-type:";
const char httptail[]="\r\nContent-Length:";
const char httpend[]="\r\n\r\n";
const char httphtml[]="text/html\r\nContent-Length:";

void cleanserver(void){
    if(!TCPstate)return;
    TCP_SERVER_T *state = (TCP_SERVER_T*)TCPstate;
        for(int i=0 ; i<MaxPcb ; i++){
                if(state->client_pcb[i] && i!=state->telnet_pcb_no)tcp_server_close(state,i);
        }
}
void cmd_transmit(unsigned char *cmd){
    unsigned char *tp;
    int tlen;
    tp=checkstring(cmd, (unsigned char *)"CODE");
    if(tp){
    	getargs(&tp, 3, (unsigned char *)",");
        if(argc != 3)error("Argument count");
        TCP_SERVER_T *state = (TCP_SERVER_T*)TCPstate;
        char httpheaders[]="HTTP/1.0 404\r\n\r\n";
        int pcb = getint(argv[0],1,MaxPcb)-1;
        tlen=getint(argv[2],100,999);
        IntToStr(&httpheaders[9],tlen,10);
        httpheaders[12]='\r';
        state->to_send[pcb]=strlen(httpheaders);
        state->sent_len[pcb]=0;
        state->buffer_sent[pcb]=(unsigned char *)httpheaders;
        state->total_sent[pcb]=strlen(httpheaders);
        if(setjmp(recover) != 0)error("Transmit failed");
        if(state->client_pcb[pcb]){
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                checksent(state,0,pcb);
                tcp_server_close(state,pcb);
        }
//        {if(startupcomplete)ProcessWeb(0);}
        return;
    } 

    if((tp=checkstring(cmd, (unsigned char *)"FILE"))){
        int32_t fn;
        char *fname;
        char *ctype;
        char *outstr=GetTempMemory(STRINGSIZE);
        char p[10]={0};
        int FileSize; 
        UINT n_read;
    	getargs(&tp, 5, (unsigned char *)",");
        if(argc != 5)error("Argument count");
        TCP_SERVER_T *state = (TCP_SERVER_T*)TCPstate;
        int pcb = getint(argv[0],1,MaxPcb)-1;
        fname=(char *)getCstring(argv[2]);
        ctype=(char *)getCstring(argv[4]);
        strcpy(outstr,httpheaders);
        strcat(outstr,ctype);
        strcat(outstr,httptail);
        if(*fname == 0) error("Cannot find file");
        if (ExistsFile(fname)) {
                fn = FindFreeFileNbr();
                if (!BasicFileOpen(fname, fn, FA_READ))
                return;
                if(FatFSFileSystem)  FileSize = f_size(FileTable[fn].fptr);
                else FileSize = lfs_file_size(&lfs,FileTable[fn].lfsptr);
                IntToStr(p,FileSize,10);
                strcat(outstr,p);
                strcat(outstr,httpend);
//                int i=0;
                state->sent_len[pcb]=0;
                state->to_send[pcb]= state->total_sent[pcb] = strlen(outstr);
                state->buffer_sent[pcb]=(unsigned char *)outstr;
                if(setjmp(recover) != 0)error("Transmit failed");
                //DEBUG_printf("sending file header to pcb %d\r\n",pcb);
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                if(FileSize<TCP_MSS*4 && FileSize<FreeSpaceOnHeap()/4){
                        char *pBuf=GetTempMemory(FileSize);
                        if(filesource[fn]!=FLASHFILE)  f_read(FileTable[fn].fptr, pBuf, FileSize, &n_read);
                        else n_read=lfs_file_read(&lfs, FileTable[fn].lfsptr, pBuf, FileSize);
                        state->buffer_sent[pcb]=(unsigned char *)pBuf;
                        while(n_read>TCP_MSS){
                                state->to_send[pcb]=TCP_MSS;
                                state->total_sent[pcb]+=TCP_MSS;
                                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                                //DEBUG_printf("sending page to pcb %d\r\n",pcb);
                                n_read-=TCP_MSS;
                                state->buffer_sent[pcb]+=TCP_MSS;
                        }
                        state->to_send[pcb]=n_read;
                        state->total_sent[pcb]+=n_read;
                        tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                        //DEBUG_printf("sending page end to pcb %d\r\n",pcb);
                        checksent(state,0, pcb);
                } else {
                        char *pBuf=GetTempMemory(TCP_MSS);
                        while(1) {
                                if(
                                ((filesource[fn]==FLASHFILE) && (lfs_file_tell(&lfs,FileTable[fn].lfsptr)==lfs_file_size(&lfs,FileTable[fn].lfsptr)))
                                || ((filesource[fn]!=FLASHFILE) && f_eof(FileTable[fn].fptr))
                                ) break;
                                if(filesource[fn]!=FLASHFILE)  f_read(FileTable[fn].fptr, pBuf, TCP_MSS, &n_read);
                                else n_read=lfs_file_read(&lfs, FileTable[fn].lfsptr, pBuf, TCP_MSS);
                                state->to_send[pcb]=n_read;
                                state->buffer_sent[pcb]=(unsigned char *)pBuf;
                                state->total_sent[pcb]+=n_read;
                                //DEBUG_printf("sending file content to pcb %d\r\n",pcb);
                                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                                checksent(state,fn, pcb);
                                state->total_sent[pcb]=0;
                                state->sent_len[pcb]=0;
                        }
                }
                tcp_server_close(state,pcb);
//                {if(startupcomplete)ProcessWeb(0);}
                FileClose(fn);
        } else {
                state->to_send[pcb]= state->total_sent[pcb] = strlen(httpheadersfail);
                state->buffer_sent[pcb]=(unsigned char *)httpheadersfail;
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                checksent(state,0, pcb);
        }
        tcp_server_close(state,pcb);
//        {if(startupcomplete)ProcessWeb(0);}
        return;
    }
    if((tp=checkstring(cmd, (unsigned char *)"PAGE"))){
	MMFLOAT f;
        int32_t fn;
        int64_t i64;
         int i;
        int t;
        char *fname;
        char c;
        char vartest[MAXVARLEN];
        int vartestp;
        int FileSize;
        int buffersize=4096;
        char p[10]={0};
    	getargs(&tp, 5, (unsigned char *)",");
        if(argc<3)error("Argument count");
        char *outstr=GetTempMemory(STRINGSIZE);
        char *valbuf=GetTempMemory(STRINGSIZE);
        strcat(outstr,httpheaders);
        strcat(outstr,httphtml);
        TCP_SERVER_T *state = (TCP_SERVER_T*)TCPstate;
        int pcb = getint(argv[0],1,MaxPcb)-1;
        fname=(char *)getCstring(argv[2]);
        if(*fname == 0) error("Cannot find file");
        if(argc==5)buffersize=getint(argv[4],0,heap_memory_size);
        if (ExistsFile(fname)) {
                fn = FindFreeFileNbr();
                if (!BasicFileOpen(fname, fn, FA_READ)) return;
                if(filesource[fn]!=FLASHFILE) FileSize = f_size(FileTable[fn].fptr);
                else FileSize = lfs_file_size(&lfs,FileTable[fn].lfsptr);
                char *SocketOut=GetMemory(FileSize+buffersize);
                int SocketOutPointer=0;
                while(1) {
                        if(FileEOF(fn)) break;
                        c=FileGetChar(fn);
                        if(c==26)continue; //deal with xmodem padding
                        if(SocketOutPointer>FileSize+256)break;
                        if(c=='{'){ //start of variable definition
                                vartestp=0;
                                while(c!='}'){
                                        c=FileGetChar(fn);
                                        if(vartestp==0 && c=='{') break;
                                        if(c!='}')vartest[vartestp++]=c;
                                }
                                if(c=='{')SocketOut[SocketOutPointer++]=c; 
                                else {
                                        vartest[vartestp]=0;
                                        unsigned char *s, *st, *temp_tknbuf;
                                        temp_tknbuf = GetMemory(STRINGSIZE);
                                        strcpy((char *)temp_tknbuf, (char *)tknbuf);                                    // first save the current token buffer in case we are in immediate mode
                                        // we have to fool the tokeniser into thinking that it is processing a program line entered at the console
                                        st = GetMemory(STRINGSIZE);
                                        inpbuf[0] = 'r'; inpbuf[1] = '=';                               // place a dummy assignment in the input buffer to keep the tokeniser happy
                                        strcpy((char *)inpbuf + 2, (char *)vartest);
                                        tokenise(true);                                                 // and tokenise it (the result is in tknbuf)
                                        strcpy((char *)st, (char *)(tknbuf + 2 + sizeof(CommandToken)));
                                        t = T_NOTYPE;
                                        int os=OptionExplicit;
                                        OptionExplicit = false;
                                        evaluate(st, &f, &i64, &s, &t, false);
                                        OptionExplicit = os;
                                        if(t & T_NBR) {
                                                FloatToStr(valbuf, f, 0, STR_AUTO_PRECISION, ' ');   // set the string value to be saved
                                                for(i=0;i<strlen(valbuf);i++)SocketOut[SocketOutPointer++]=valbuf[i];
                                        } else if(t & T_INT) {
                                                IntToStr(valbuf, i64, 10); // if positive output a space instead of the sign
                                                for(i=0;i<strlen(valbuf);i++)SocketOut[SocketOutPointer++]=valbuf[i];
                                        } else if(t & T_STR) {
                                                for(i=1;i<=s[0];i++)SocketOut[SocketOutPointer++]=s[i];
                                        } 
                                        strcpy((char *)tknbuf, (char *)temp_tknbuf);// restore the saved token buffer
                                        FreeMemory(temp_tknbuf) ;
                                        FreeMemory(st);
                                }
                        } else 
                        SocketOut[SocketOutPointer++]=c;
                }
                FileClose(fn);
                SocketOut[SocketOutPointer++]=10;
                SocketOut[SocketOutPointer++]=13;
                SocketOut[SocketOutPointer++]=10;
                SocketOut[SocketOutPointer++]=13;
                SocketOut[SocketOutPointer]=0;
                IntToStr(p,strlen(SocketOut),10);
                strcat(outstr,p);
                strcat(outstr,httpend);
//
                if(setjmp(recover) != 0)error("Transmit failed");
                state->to_send[pcb] = state->total_sent[pcb] = strlen(outstr);
                state->sent_len[pcb]=0;
                state->buffer_sent[pcb]=(unsigned char *)outstr;
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                //DEBUG_printf("sending page header to pcb %d\r\n",pcb);
//
//                state->to_send[pcb]=strlen(SocketOut);
                state->buffer_sent[pcb]=(unsigned char *)SocketOut;
                int bufflen=strlen(SocketOut);
                while(bufflen>TCP_MSS){
                        state->to_send[pcb]=TCP_MSS;
                        state->total_sent[pcb]+=TCP_MSS;
                        tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                        //DEBUG_printf("sending page to pcb %d\r\n",pcb);
                        bufflen-=TCP_MSS;
                        state->buffer_sent[pcb]+=TCP_MSS;
                }
                state->to_send[pcb]=bufflen;
                state->total_sent[pcb]+=bufflen;
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                //DEBUG_printf("sending page end to pcb %d\r\n",pcb);
                checksent(state,0, pcb);
//
                FreeMemory((void *)SocketOut);
        } else {
                state->to_send[pcb] = state->total_sent[pcb] = strlen(httpheadersfail);
                state->sent_len[pcb]=0;
                state->buffer_sent[pcb]=(unsigned char *)httpheadersfail;
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                checksent(state,0,pcb);
        }
        tcp_server_close(state,pcb);
//        ProcessWeb(0);
        return;
    }
    error("Invalid option");
}
void open_tcp_server(void){
        tcp_server_init();
        TCP_PORT=Option.TCP_PORT;
        if (!TCPstate) {
                MMPrintString("Failed to create TCP server\r\n");
        }
        if (!tcp_server_open(TCPstate)) {
                MMPrintString("Failed to create TCP server\r\n");
        }
        return;
}

int cmd_tcpserver(void){
    unsigned char *tp;
            tp=checkstring(cmdline, (unsigned char *)"TCP INTERRUPT");
        if(tp){
                getargs(&tp, 1, (unsigned char *)",");
                if(argc!=1)error("Syntax");
                TCPreceiveInterrupt= (char *)GetIntAddress(argv[0]);
                InterruptUsed=true;
                TCPreceived=0;
                return 1;
        }
        tp=checkstring(cmdline, (unsigned char *)"TCP CLOSE");
        if(tp){
                TCP_SERVER_T *state = TCPstate;
                getargs(&tp, 1, (unsigned char *)",");
                if(argc!=1)error("Syntax");
                int pcb = getint(argv[0],1,MaxPcb)-1;
                tcp_server_close(state, pcb) ;
                return 1;
        }
        tp=checkstring(cmdline, (unsigned char *)"TRANSMIT");
        if(tp){
                cmd_transmit(tp);
        
                return 1;
        }

        tp=checkstring(cmdline, (unsigned char *)"TCP READ");
        if(tp){
                void *ptr1 = NULL;
                int64_t *dest=NULL;
                uint8_t *q=NULL;
                int size=0;
                TCP_SERVER_T *state = TCPstate;
                getargs(&tp, 3, (unsigned char *)",");
                if(argc!=3)error("Syntax");
                int pcb=getint(argv[0],1,MaxPcb)-1;
                ptr1 = findvar(argv[2], V_FIND | V_EMPTY_OK | V_NOFIND_ERR);
                if(g_vartbl[g_VarIndex].type & T_INT) {
                        if(g_vartbl[g_VarIndex].dims[1] != 0) error("Invalid variable");
                        if(g_vartbl[g_VarIndex].dims[0] <= 0) {      // Not an array
                                error("Argument 2 must be integer array");
                        }
                        size=(g_vartbl[g_VarIndex].dims[0] - g_OptionBase +1)*8;
                        dest = (long long int *)ptr1;
                        dest[0]=0;
                        q=(uint8_t *)&dest[1];
                } else error("Argument 2 must be integer array");
                if(!state->inttrig[pcb]){
                        memset(ptr1,0,size);
                        return 1;
                }
                if(size-8<state->recv_len[pcb])error("array too small");
                memcpy(q,state->buffer_recv[pcb],state->recv_len[pcb]);
                dest[0]=  state->recv_len[pcb];             
                state->inttrig[pcb]=0;
                return 1;
        }
        tp=checkstring(cmdline, (unsigned char *)"TCP SEND");
        if(tp){
                TCP_SERVER_T *state = (TCP_SERVER_T*)TCPstate;
                int64_t *dest=NULL;
                uint8_t *q=NULL;
                getargs(&tp, 3, (unsigned char *)",");
                if(!TCPstate)error("Server not open");
                if(argc != 3)error("Argument count");
                int pcb = getint(argv[0],1,MaxPcb)-1;
                parseintegerarray(argv[2],&dest,2,1,NULL,false);
                q=(uint8_t *)&dest[1];
//                int j=(g_vartbl[g_VarIndex].dims[0] - g_OptionBase);
                state->buffer_sent[pcb]=q;
                int bufflen=dest[0];
                state->to_send[pcb] = state->total_sent[pcb] = state->sent_len[pcb]=0;
                while(bufflen>TCP_MSS){
                        state->to_send[pcb]=TCP_MSS;
                        state->total_sent[pcb]+=TCP_MSS;
                        tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                        //DEBUG_printf("sending page to pcb %d\r\n",pcb);
                        bufflen-=TCP_MSS;
                        state->buffer_sent[pcb]+=TCP_MSS;
                }
                state->to_send[pcb]=bufflen;
                state->total_sent[pcb]+=bufflen;
                tcp_server_send_data(state, state->client_pcb[pcb], pcb);
                //DEBUG_printf("sending page end to pcb %d\r\n",pcb);
                checksent(state,0, pcb);
//                tcp_server_close(state,pcb);
//                ProcessWeb(0);
                return 1;   
        }
        return 0;
}
