// File: httpserver.c
// Modification history:
//$ FA(01/06/2001): Comment MMpull() after call to SCexeccomm()
//$ FA(06/06/2001): Include baselib.h 


#include <stdio.h>
#include "../include/vscol.h"
#ifdef VERSION_WIN
#include <windows.h>
#include <windowsx.h>
#include <winsock.h>
#include <wininet.h>
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#endif
#include "../include/kernel.h"

#include "../listlab.h"
#include "../scol.h"
#include "../baselib.h"

#include "../scolobj.h"
#include "../include/socket.h"

#define PKG_NAME "HTTPserver.pkg2.0"

int maxpostsize;
int SCsendFull(mmachine m);
extern int ScolSuper;

/* Globals */
#ifdef VERSION_WIN
extern HWND hscol;
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
#define SOCKET int
#define SOCKADDR_IN struct sockaddr_in
#define INVALID_SOCKET -1
#define HANDLE FILE*
#define INVALID_HANDLE_VALUE NULL
#define closesocket close
#endif
int  WM_HTTPSVR;
int  HTTPserver_type;
int  HTTPcon_type;

/* HTTPserver object: [I port, I con.count, I in, I out] */
#define HTTPserver_name "HTTPSERVER"
#define HTTPserver_nrfl 2
#define HTTPserver_size 4
#define HTTPSVR_PORT    0
#define HTTPSVR_CNB     1
#define HTTPSVR_IN      2
#define HTTPSVR_OUT     3

/* HTTPcon: [S buffer, I flags, I socket, HTTPserver server, I ip(MSW), I ip(LSW)] */
#define HTTPcon_name    "HTTPCON"
#define HTTPcon_nrfl    3
#define HTTPcon_size    7
#define HTTPCON_BUF     0
#define HTTPCON_FLAGS   1
#define HTTPCON_SOCK    2
#define HTTPCON_SVR     3
#define HTTPCON_IPA     4
#define HTTPCON_IPB     5
#define HTTPCON_FILE	6

/* values for HHPCON_FLAGS */
#define IS_INPUT    (1<<1)
#define IS_OUTPUT   (2<<1)
#define IS_CLOSED   (4<<1)
#define IS_ASYNC    (8<<1)
#define IS_LINGER   (16<<1)

#define HTTP_TERM    "\r\n\r\n"

/* maximum pending connections in queue for listen() */
#define _SOCK_BACKLOG 5


/*
  fun [Chn, I, fun [HTTPcon u0 S] S, u0] HTTPserver
*/
#define MFAILED MMpull(m), MMpull(m), MMpull(m), MMset(m,0,NIL)

int MstartHTTPserver(mmachine m)
{
  SOCKET sock;
  SOCKADDR_IN sin;
  long argp=1;
  int opt=1;
  int ptr,k,port;
  
  port=MMget(m,2)>>1;
  
  /* create a connectionless HTTP/IP socket */
  sock=socket(AF_INET,SOCK_STREAM,0);
  if (sock==INVALID_SOCKET)
    {
      MMechostr(MSKDEBUG,"startHTTPserver(%d): socket() failed\n",port);
      MFAILED;
      return 0;
    }
  
#ifdef VERSION_WIN
  /* socket will trigger the WM_HTTPSVR message, handled by SCOLHttpEvent() */
  if (WSAAsyncSelect(sock,hscol,WM_HTTPSVR,FD_ACCEPT|FD_READ|FD_WRITE|FD_CLOSE)==SOCKET_ERROR)
    {
	  closesocket(sock);
      MMechostr(MSKDEBUG,"startHTTPserver(%d): WSAAsyncSelect() failed\n",port);
      MFAILED;
      return 0;
    }
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
  opt=1;
  setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
  ioctl (sock, FIONBIO, &argp);
#endif
#ifdef VERSION_MAC
  TODO
#endif
  /* bind socket to the 'sin' incoming inet mask */
  sin.sin_family = AF_INET;
  sin.sin_port   = htons((short)port);
  sin.sin_addr.s_addr = SCKbindadress();
  if (bind(sock,(struct sockaddr*)&sin,sizeof(sin))!=0)
    {
	  closesocket(sock);
      MMechostr(MSKRUNTIME,"startHTTPserver(%d): bind() failed, port busy/protected\n",port);
      MFAILED;
      return 0;
    }
  
  /* start listening */
  if (listen(sock,_SOCK_BACKLOG)!=0)
    {
	  closesocket(sock);
      MMechostr(MSKDEBUG,"startHTTPserver(%d): listen() failed\n",port);
      MFAILED;
      return 0;
    }
#if defined(VERSION_X11) || defined(VERSION_NOX)
  if (SCKaddsock(sock,SC_TYPHTTPSRV)) return -1;
#endif
  MMechostr(MSKRUNTIME,"HTTPserver: setup OK, (%d) listening on port %d\n",sock,port);
  
  /* push channel now (for OBJcreate) */
  if ((k=MMpush(m,MMget(m,3)))) return k;
  
  /* creates new HTTPserver object (tuple) */
  if ((k=MMpush(m,port<<1))) return k;
  if ((k=MMpush(m,0))) return k;
  if ((k=MMpush(m,0))) return k;
  if ((k=MMpush(m,0))) return k;
  if ((k=MMpush(m,HTTPserver_size*2))) return k;
  if ((k=MBdeftab(m))) return k;
	
  /*	if ((ptr=MMmallocCLR(m,HTTPserver_size,TYPETAB))==NIL) return MERRMEM;
	MMstore(m,ptr,HTTPSVR_PORT,port<<1);*/
  /* HTTPSVR_CNB = 0 */
  /* HTTPSVR_IN  = 0 */
  /* HTTPSVR_OUT = 0 */
  /*	if ((k=MMpush(m,ptr*2+1))) return k;*/
  
  if ((k=OBJcreate(m,HTTPserver_type,sock,NIL,NIL))) return k;
  
  /* HTTPserver object is on top of the stack, push reflex fun & u0 */
  if ((k=MMpush(m,MMget(m,2)))) return k;
  if ((k=MMpush(m,MMget(m,2)))) return k;
  if ((k=OBJaddreflex(m,HTTPserver_type,0))) return k;
  
  /* pull the HTTPserver object */
  ptr=MMpull(m);
  /* pull u0, fun reflex & I port */
  MMpull(m);
  MMpull(m);
  MMpull(m);
  /* return the HTTPserver object (overwrite channel) */
  MMset(m,0,ptr);
  return 0;
}
#undef MFAILED


/*
  fun [HTTPserver] I
*/
int McloseHTTPserver(mmachine m)
{
  int ptr;
  
  if ((ptr=MMget(m,0))==NIL) return 0;
  OBJdelTM(m,HTTPserver_type,ptr);
  MMset(m,0,0);
  return 0;
}


/*
  fun [HTTPcon S] I
*/
int _onWrite(mmachine m, int sock); /* proto */
int MHTTPsend(mmachine m)
{
  int k,ptr,flags;
  
  ptr=MMget(m,1)>>1;
  flags=MMfetch(m,ptr,HTTPCON_FLAGS);
  if ((ptr==NIL) || (flags & IS_CLOSED) ||(MMget(m,0)==NIL))
    {
      MMpull(m);
      MMset(m,0,1<<1);
      return 0;
    }
  
  if ((k=MMpush(m,MMfetch(m,ptr,HTTPCON_BUF)))) return k;
  if ((k=MMpush(m,MMget(m,1)))) return k;
  if ((k=MBstrcat(m))) return k;
  ptr=MMget(m,2)>>1;
  MMstore(m,ptr,HTTPCON_BUF,MMpull(m));
  /* pull HTTPcon & S params */
  MMpull(m);
  MMpull(m);
  _onWrite(m,MMfetch(m,ptr,HTTPCON_SOCK)>>1);
  return MMpush(m,0);
}



int MHTTPsendFile(mmachine m)
{
  HANDLE f;
  int s,p;
  int filename;
  
  filename=MMpull(m);
  p=MMget(m,0)>>1;
  
  if ((p==NIL)||(MMfetch(m,p,HTTPCON_FILE)!=NIL)||(filename==NIL))
    {
      MMset(m,0,NIL);
      return 0;
    }
  
#ifdef VERSION_WIN
  f=CreateFile(MMstartstr(m,filename>>1),GENERIC_READ,FILE_SHARE_READ,NULL,
	       OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
  f=fopen(MMstartstr(m,filename>>1),"rb");
#endif
#ifdef VERSION_MAC
  TODO
#endif
  //  MMechostr(MSKDEBUG,"Opening the file %s : %d\n",MMstartstr(m,filename>>1),f);
  if (f==INVALID_HANDLE_VALUE)
    {
      MMechostr(MSKDEBUG,"Cannot Open file %s\n",MMstartstr(m,filename>>1));
      MMset(m,0,NIL);
      return 0;
    }
  
  s=MMmalloc(m,2,TYPEBUF); if (s==NIL) return MERRMEM;
  *(HANDLE*)MMstart(m,s)=f;
  p=MMget(m,0)>>1;
  MMstore(m,p,HTTPCON_FILE,s+s+1);
  MMset(m,0,0);
  _onWrite(m,MMfetch(m,p,HTTPCON_SOCK)>>1);
  return 0;
}


/*
  fun [HTTPcon] I
*/
int _onDisconnect(mmachine m, int sock); /* proto */
int McloseHTTPcon(mmachine m)
{
  int ptr,flags;
  
  ptr=MMpull(m)>>1;
  flags=MMfetch(m,ptr,HTTPCON_FLAGS);
  if ((ptr!=NIL) && (flags & IS_INPUT))
    {
      _onDisconnect(m,MMfetch(m,ptr,HTTPCON_SOCK)>>1);
      return MMpush(m,0);
    }
  if ((ptr==NIL) || (flags & IS_CLOSED)) return MMpush(m,1<<1);
  
  MMstore(m,ptr,HTTPCON_FLAGS,flags|IS_LINGER);
  _onWrite(m,MMfetch(m,ptr,HTTPCON_SOCK)>>1);
/*  _onDisconnect(m,MMfetch(m,ptr,HTTPCON_SOCK)>>1);*/
  return MMpush(m,0);
}


/*
  fun [HTTPcon fun [HTTPcon u0] u1 u0] HTTPcon
*/
int MrflHTTPclose(mmachine m)
{
  return OBJaddreflex(m,HTTPcon_type,1);
}

/*
  fun [HTTPserver fun [HTTPcon u0] u1 u0] HTTPserver
*/
int MrflHTTPaccept(mmachine m)
{
  return OBJaddreflex(m,HTTPserver_type,1);
}


/*
  fun [HTTPserver] [I I I]
*/
int MgetHTTPstats(mmachine m)
{
  int ptr,tup;
  
  if (MMget(m,0)==NIL) return 0;
  if ((tup=MMmallocCLR(m,3,TYPETAB))==NIL) return MERRMEM;
  ptr=MMget(m,0)>>1;
  MMstore(m,tup,0,MMfetch(m,ptr,HTTPSVR_CNB));
  MMstore(m,tup,1,MMfetch(m,ptr,HTTPSVR_IN));
  MMstore(m,tup,2,MMfetch(m,ptr,HTTPSVR_OUT));
  MMset(m,0,tup*2+1);
  return 0;
}

/*
  fun [HTTPcon] S
*/
int MgetHTTPclientIP(mmachine m)
{
  int ptr,a,b,k;
  char ip[32];
  
  ptr=MMpull(m)>>1;
  if (ptr==NIL) return MMpush(m,NIL);
  
  a=MMfetch(m,ptr,HTTPCON_IPA)>>1;
  b=MMfetch(m,ptr,HTTPCON_IPB)>>1;
  k=(a&0xffff)+((b&0xffff)<<16);
  SCKgetstringbyip2(ip,k);
  return Mpushstrbloc(m,ip);
}


/*
  fun [HTTPcon] I
*/
int MgetHTTPbufferSize(mmachine m)
{
  int ptr,size;
  
  if ((ptr=MMget(m,0)>>1)==NIL) return 0;
  size=MMsizestr(m,MMfetch(m,ptr,HTTPCON_BUF));
  MMset(m,0,size<<1);
  return 0;
}

int MsetHTTPmaxpostsize(mmachine m)
{
	int p;
	p=MMget(m,0);
	if (p==NIL) MMset(m,0,maxpostsize<<1);
	else maxpostsize=p>>1;
	return 0;
}


/*
  This event should only occur for client connections (HTTPcon object)
*/
int _onDisconnect(mmachine m, int sock)
{
  int k;
  
  if (OBJbeginreflex(m,HTTPcon_type,sock,1)==0)
    if ((k=OBJcallreflex(m,0))) return k;
  OBJdelTH(m,HTTPcon_type,sock);
  return 0;
}

extern int ScolSuper;
extern int maxsock;

/*
  Create new HTTPcon object for incoming connection
*/
int myerrorsock()
{
#ifdef VERSION_WIN
  return WSAGetLastError()-WSABASEERR;
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
  return errno;
#endif
#ifdef VERSION_MAC
  TODO
#endif
}

int _onAccept(mmachine m, int socksvr)
{
  SOCKADDR_IN sin;
  SOCKET sock;
  int k,ptr,mag,port,a;
  long argp=1;
  
  /* search the corresponding HTTPserver object */
  ptr=OBJfindTH(m,HTTPserver_type,socksvr);
  if (ptr==NIL)
    {
      MMechostr(MSKDEBUG,"HTTPserver: accept: server object not found !?\n");
      return 0;
    }
  mag=MMfetch(m,ptr,OFFOBJMAG)>>1;
  port=MMfetch(m,mag,HTTPSVR_PORT)>>1;
  
  /* get the connection socket */
  k=sizeof(sin);
  sock=accept((SOCKET)socksvr,(struct sockaddr*)&sin,&k);
  if (sock==INVALID_SOCKET) {
    MMechostr(MSKDEBUG,"HTTPserver(%d): accept failed (WSABASEERR+%d)\n",port,
	      myerrorsock());
    return 0;
  }
  
#if defined(VERSION_X11) || defined(VERSION_NOX)
  ioctl (sock, FIONBIO, &argp);
  if (SCKaddsock(sock,SC_TYPHTTP)) return -1;
#endif
//	MMechostr(MSKDEBUG,"HTTPserver(%d): accept %d OK\n",port,sock);
  /* update statistics */
  MMstore(m,mag,HTTPSVR_CNB,MMfetch(m,mag,HTTPSVR_CNB)+2*1);
  
  /* save HTTPserver object ptr (reflexes needed further) */
  if (MMpush(m,ptr*2+1)) return MERRMEM;
  /* push HTTPserver channel */
  if (MMpush(m,MMfetch(m,MMget(m,0)>>1,OFFOBJCHN))) return MERRMEM;
  
  /* build new HTTPcon object */
  if ((k=Mpushstrbloc(m,""))) return k;
  if ((k=MMpush(m,IS_INPUT))) return k;
  if ((k=MMpush(m,sock<<1))) return k;
  if ((k=MMpush(m,MMfetch(m,MMget(m,4)>>1,OFFOBJMAG)))) return k;
  a=(int)(sin.sin_addr.s_addr);
  if ((k=MMpush(m,(a&0xffff)<<1))) return k;
  if ((k=MMpush(m,((a>>16)&0xffff)<<1))) return k;
  //  if ((k=MMpush(m,(int)sin.sin_addr.S_un.S_un_w.s_w1<<1))) return k;
  //  if ((k=MMpush(m,(int)sin.sin_addr.S_un.S_un_w.s_w2<<1))) return k;
  if ((k=MMpush(m,NIL))) return k;
  if ((k=MMpush(m,HTTPcon_size*2))) return k;
  if ((k=MBdeftab(m))) return k;
  if ((k=OBJcreate(m,HTTPcon_type,sock,NIL,NIL))) return k;
  
  /* inherit the HTTPserver reflex */
  if ((k=MMpush(m,MMfetch(m,MMget(m,1)>>1,OFFOBJREF0)))) return k;
  // MMechostr(1,"callback read is =%x\n",MMget(m,0));
  if ((k=MMpush(m,MMfetch(m,MMget(m,2)>>1,OFFOBJUSER0)))) return k;
  // MMechostr(1,"param read is =%x\n",MMget(m,0));
  if ((k=OBJaddreflex(m,HTTPcon_type,0))) return k;

  /* inherit the HTTPserver accept */
  if ((k=MMpush(m,MMfetch(m,MMget(m,1)>>1,OFFOBJREF0+2)))) return k;
  //  MMechostr(1,"callback accept is =%x\n",MMget(m,0));
  if ((k=MMpush(m,MMfetch(m,MMget(m,2)>>1,OFFOBJUSER0+2)))) return k;
  //  MMechostr(1,"param accept is =%x\n",MMget(m,0));
  if ((k=OBJaddreflex(m,HTTPcon_type,2))) return k;
  
  /* flush HTTPcon & HTTPserver objects from the stack */
  MMpull(m);
  MMpull(m);

  /* appel de la callback accept */
  //  MMechostr(1,"callback accept sock=%d\n",sock);
  if (OBJbeginreflex(m,HTTPcon_type,sock,2)==0)
    {
      //  MMechostr(1,"call callback accept\n"); 
      if ((k=OBJcallreflex(m,0))) return k;
    }
  //  MMechostr(1,"call callback end\n"); 

  return 0;
}

int _onWrite(mmachine m, int sock)
{
  char* buf;
  int ptr,svr,k,size,flags,sent;
  char buffile[2048];
  HANDLE file;
  int res;
  
  SCKclrwrite(sock);
  /* search for corresponding HTTPcon object */
  //  MMechostr(MSKDEBUG,"_onWrite %d\n",sock);
  if ((ptr=OBJfindTH(m,HTTPcon_type,sock))==NIL) return 0;
  ptr=MMfetch(m,ptr,OFFOBJMAG)>>1;
 
  /* fetch buffer info */
  flags=MMfetch(m,ptr,HTTPCON_FLAGS);
  if ((flags & IS_INPUT) || (flags & IS_CLOSED)) return 0;
  k=MMfetch(m,ptr,HTTPCON_BUF)>>1;
  if (k==NIL) return 0;
  buf=MMstartstr(m,k);
  size=MMsizestr(m,k);
  
  //  MMechostr(MSKDEBUG,"try send buffer %d\n",size);
  if (size==0) sent=0;
  else sent=send(sock,buf,size,0);
  //  MMechostr(MSKDEBUG,"HTTPserver: sent on %d %d/%d\n",sock,sent,size);
  if (sent<0)
    {
#ifdef VERSION_WIN
      if (WSAGetLastError()!=WSAEWOULDBLOCK)
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
	if (errno!=EAGAIN)
#endif
#ifdef VERSION_MAC
  TODO
#endif
	  {
	    _onDisconnect(m,sock);
	    return 0;
	  }
      sent=0;
    }
  if (sent<size)  SCKsetwrite(sock);
  /* update statistics */
  svr=MMfetch(m,ptr,HTTPCON_SVR)>>1;
  if (svr!=NIL) MMstore(m,svr,HTTPSVR_OUT,MMfetch(m,svr,HTTPSVR_OUT)+sent*2);
  if ((!(flags & IS_ASYNC))&&(sent>=size))
    {
       /* All sent & mode is synchronous: close connection */
       _onDisconnect(m,sock);
    }
  else if ((sent>=size)&&(MMfetch(m,ptr,HTTPCON_FILE)!=NIL))
    {
      
      file=*(HANDLE*)MMstart(m,MMfetch(m,ptr,HTTPCON_FILE)>>1);
      do
	{
#ifdef VERSION_WIN
	  ReadFile(file,buffile,2048,&res,NULL);
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
	  res=fread(buffile,1,2048,file);
#endif
#ifdef VERSION_MAC
  TODO
#endif
	  //	  MMechostr(MSKDEBUG,"read %d bytes from file\n",res);
	  if (res) sent=send(sock,buffile,res,0);
	  else sent=0;
	  if (sent<0)
	    {
#ifdef VERSION_WIN
	      if (WSAGetLastError()!=WSAEWOULDBLOCK)
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
		if (errno!=EAGAIN)
#endif
#ifdef VERSION_MAC
  TODO
#endif
		  {
		    _onDisconnect(m,sock);
		    return 0;
		  }
	      sent=0;
	    }
	} while ((res)&&(sent==res));
      if (res)
	{
	  SCKsetwrite(sock);
	  //	  MMechostr(MSKDEBUG,"sent only %d/%d\n",sent,res);
	  if ((k=MMpush(m,ptr*2+1))) return k;
	  if ((k=Mpushstrblocn(m,buffile+sent,res-sent))) return k;
	  ptr=MMget(m,1)>>1;
	  MMstore(m,ptr,HTTPCON_BUF,MMpull(m));
	  MMpull(m);
	}
      else
	{
	  //	  MMechostr(MSKDEBUG,"end of file\n");
	  _onDisconnect(m,sock);
	}
    }
  else if ((sent==size)&&(flags&IS_LINGER))
	{
	  //	  MMechostr(MSKDEBUG,"end of file\n");
	  _onDisconnect(m,sock);
	}
  else    
    {
//      MMechostr(MSKDEBUG,"send: %d left (%d/%d)\n",size-sent,sent,size);
	  /* save HTTPcon ptr in the stack */
      if ((k=MMpush(m,ptr*2+1))) return k;
      /* push [buffer pos len] for substr */
      if ((k=MMpush(m,MMfetch(m,MMget(m,0)>>1,HTTPCON_BUF)))) return k;
      if ((k=MMpush(m,sent<<1))) return k;
      if ((k=MMpush(m,(size-sent)<<1))) return k;
      //		MMechostr(MSKDEBUG,"substr '%s' %d %d : ",MMstartstr(m,MMget(m,2)>>1),sent,size-sent);
      if ((k=MBsubstr(m))) return k;
      //		MMechostr(MSKDEBUG,"'%s'\n",MMstartstr(m,MMget(m,0)>>1));
      /* store new substr'ed buffer */
      k=MMpull(m);
      MMstore(m,MMget(m,0)>>1,HTTPCON_BUF,k);
      /* pull HTTPcon ptr */
      MMpull(m);
    }
  return 0;
}

/* compte le nombre d'objets d'un certain type */
int countHTTPasync(mmachine m)
{
  int p,q,r,n,f;
  n=0;
  p=MMgetglobal(m,OFFSCOBJ);
  while(p!=NIL)
    {
      p>>=1;
      q=MMfetch(m,p,OFFLVAL)>>1;
      if ((q!=NIL)&&(MMfetch(m,q,OFFOBJTYP)==HTTPcon_type*2))
	  {
		  r=MMfetch(m,q,OFFOBJMAG)>>1;
		  f=0;
		  if (r!=NIL) f=MMfetch(m,r,HTTPCON_FLAGS);
		  if ((f&IS_OUTPUT)&&(f&IS_ASYNC)) n++;
	  }
      p=MMfetch(m,p,OFFLNEXT);
    }
  return n;
}


#define MAX_BUFFER 16384
#define MFAILED MMpull(m), MMpull(m), MMpull(m), MMpull(m)

int _onRead(mmachine m, int sock)
{
  char buffer[MAX_BUFFER];
  char *req,*pos,*term;
  int k,ptr,ans,recved,reqlen,f;
  
  //  MMechostr(MSKDEBUG,"_on read %d\n",sock);
  recved=recv((SOCKET)sock,buffer,MAX_BUFFER-1,0);
//  MMechostr(MSKDEBUG,"_on read receive %d\n",recved);
  if (recved==0) {
    /* 'gracefull' disconnection */
    _onDisconnect(m,sock);
    return 0;
  }

  if (recved<0)
    {
#ifdef VERSION_WIN
      if (WSAGetLastError()!=WSAEWOULDBLOCK)
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
	if (errno!=EAGAIN)
#endif
#ifdef VERSION_MAC
  TODO
#endif
	  _onDisconnect(m,sock);
      return 0;
    }
  
  buffer[recved]=0; /* buffer is now a C string */
  
  //	MMechostr(MSKDEBUG,"received=\n%s\n",buffer);
  
  /* prepare for reflex */
  if (OBJbeginreflex(m,HTTPcon_type,sock,0)) return 0;
  /* update statistics */
  ptr=MMfetch(m,MMget(m,1)>>1,HTTPCON_SVR)>>1;
  MMstore(m,ptr,HTTPSVR_IN,MMfetch(m,ptr,HTTPSVR_IN)+recved*2);
  /* append recv buffer to HTTPcon buffer */
  if ((k=MMpush(m,MMfetch(m,MMget(m,1)>>1,HTTPCON_BUF)))) return k;
  if ((k=Mpushstrblocn(m,buffer,recved))) return k;
  if ((k=MBstrcat(m))) return k;
  MMstore(m,MMget(m,2)>>1,HTTPCON_BUF,MMget(m,0));
  
  req=MMstartstr(m,MMget(m,0)>>1);
  reqlen=MMsizestr(m,MMget(m,0)>>1);
  term=strstr(req,HTTP_TERM);

  /* verification de la taille max du tampon de requete */
  if ((maxpostsize)&&(reqlen>maxpostsize))
    {
      MMechostr(MSKDEBUG,"POST overflow: %d/%d\n",reqlen,maxpostsize);
      MFAILED;
      _onDisconnect(m,sock);
      return 0;
    }
  
  /* stack contains: RflFunc, HTTPcon, rfluser0, answer string */
  
  /* waiting for normal HTTP_TERM marker */
  if (term!=NULL)
  {
      /* check for 'content-length' data (for POST method) */
      *term=0;
      //		MMechostr(MSKDEBUG,"header=%s\n",req);
      if ((pos=strstr(req,"ength:"))!=NULL &&
		  sscanf(pos+6,"%d",&k)>0)
	  {
		  /* calculate post data size (in read buffer) */
		  ptr=reqlen-(int)(term-req)-strlen(HTTP_TERM);
//		  MMechostr(MSKDEBUG,"POST: read %d/%d bytes\n",ptr,k);
		  if ((maxpostsize)&&(k>maxpostsize))
		  {
			  MMechostr(MSKDEBUG,"POST overflow: %d/%d\n",k,maxpostsize);
			  MFAILED;
			  _onDisconnect(m,sock);
			  return 0;
		  }
		  if (ptr<k)
		  {
			  /* there are bytes left to read, see you next time */
			  *term=HTTP_TERM[0];
			  MFAILED;
			  return 0;
		  }
	  }
      *term=HTTP_TERM[0];
      
      /* set default connection : output, empty buffer */
      if ((k=Mpushstrbloc(m,""))) return k;
      ptr=MMget(m,3)>>1;
      MMstore(m,ptr,HTTPCON_BUF,MMpull(m));
      MMstore(m,ptr,HTTPCON_FLAGS,IS_OUTPUT|IS_ASYNC);
      /* trigger HTTPcon reflex */
      if ((k=OBJcallreflex(m,1))) return k;
      
      /* get answer string */
      ans=MMget(m,-2);
      /* user may want to keep asynchronous (stack is clean, exit) */
      if (ans==NIL)
	  {
		  if ((!ScolSuper)&&(countHTTPasync(m)>maxsock))
		  {
			  MMechostr(MSKRUNTIME,"HTTP Full\n");
			  _onDisconnect(m,sock);
			  if (!ScolSuper)
			  {
				  if (k=SCexeccomm(m,"_fullserver")) return k;
//				  MMpull(m);
			  }
			  return 0;
		  }
		  return 0;
	  }
      
      if ((ptr=OBJfindTH(m,HTTPcon_type,sock))==NIL) return 0;
      ptr=MMfetch(m,ptr,OFFOBJMAG)>>1;
      /* connection is synchronous, buffer is in ptr */
      MMstore(m,ptr,HTTPCON_BUF,ans);
	  f=MMfetch(m,ptr,HTTPCON_FLAGS);
      MMstore(m,ptr,HTTPCON_FLAGS,f&(~IS_ASYNC)); /* sync mode */
      //		MMechostr(MSKDEBUG,"sync answer (%d)\n",MMsizestr(m,ans>>1));
      _onWrite(m,sock);
  }
  else 
  {
      /* no terminator found, wait for more bytes */
      MFAILED;
  }
  return 0;
}
#undef MFAILED


/*
  WSA socket event handler (for WM_HTTPSVR)
    dispatch messages for incoming and current client connections
*/
#ifdef VERSION_WIN
int SCOLHttpEvent (mmachine m,HWND hwnd,unsigned msg,UINT wParam,LONG lParam,int *ret)
{
	int err;
	
//	MMechostr(MSKDEBUG,"ACCEPT:%d READ:%d WRITE:%d CLOSE:%d\n",FD_ACCEPT,FD_READ,FD_WRITE,FD_CLOSE);
//	MMechostr(MSKDEBUG,"HTTPserver: event %d on socket %d\n",WSAGETSELECTEVENT(lParam),wParam);
	
	err=WSAGETSELECTERROR(lParam);
	if (err!=0) {
//		MMechostr(MSKDEBUG,"HTTPserver: event: error WSABASEERR+%d\n",err-WSABASEERR);
		_onDisconnect(m,wParam);
		return 0;
	}
	
	switch (WSAGETSELECTEVENT(lParam)) {
		case FD_ACCEPT: return _onAccept(m,wParam);
		case FD_READ:   return _onRead(m,wParam);
		case FD_WRITE:  return _onWrite(m,wParam);
		case FD_CLOSE:  return _onDisconnect(m,wParam);
	}
	return 0;
}
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
int SCOLHttpEvent (mmachine m,int hSock,int NetEvent,int NetError)
{
  //  MMechostr(MSKDEBUG,"HTTPevent %d %d %d\n",hSock,NetEvent,NetError);
  if (NetError)
    {
//      MMechostr(MSKDEBUG,"HTTPserver: error\n");
      _onDisconnect(m,hSock);
      return 0;
    }
  
  switch (NetEvent) {
  case FD_ACCEPT: return _onAccept(m,hSock);
  case FD_READ:   return _onRead(m,hSock);
  case FD_WRITE:  return _onWrite(m,hSock);
  case FD_CLOSE:  return _onDisconnect(m,hSock);
  }
  return 0;
}
#endif
#ifdef VERSION_MAC
  int SCOLHttpEvent (mmachine m,int hSock,int NetEvent,int NetError) TODO
#endif

/* HTTPserver destructor */
int HTTPserver_destroy(mmachine m, int handsys, int objm)
{
	MMechostr(MSKRUNTIME,"HTTPserver: closing server on port %d\n",MMfetch(m,objm>>1,HTTPSVR_PORT)>>1);
	closesocket((SOCKET)handsys);
#if defined(VERSION_X11) || defined(VERSION_NOX)
	SCKdelsock(handsys);
#endif
	return 0;
}


/* HTTPcon destructor */
int HTTPcon_destroy(mmachine m, int handsys, int objm)
{
//  MMechostr(MSKDEBUG,"HTTPcon: destroy: closing socket %d\n",handsys);
  MMstore(m,objm>>1,HTTPCON_BUF,0);
  MMstore(m,objm>>1,HTTPCON_FLAGS,IS_CLOSED);
  MMstore(m,objm>>1,HTTPCON_SVR,0);
  closesocket((SOCKET)handsys);
  if (MMfetch(m,objm>>1,HTTPCON_FILE)!=NIL)
#ifdef VERSION_WIN
    CloseHandle(*(HANDLE*)MMstart(m,MMfetch(m,objm>>1,HTTPCON_FILE)>>1));
#endif
#if defined(VERSION_X11) || defined(VERSION_NOX)
    fclose(*(HANDLE*)MMstart(m,MMfetch(m,objm>>1,HTTPCON_FILE)>>1));
  SCKdelsock(handsys);
#endif
    
  return 0;
}


/* SCOL declaration tables */

#define N_HTTPserver_PKG 13

char* m_HTTPserver_name[N_HTTPserver_PKG]=
{"HTTPserver","HTTPcon","startHTTPserver", "closeHTTPserver",
 "HTTPsend","closeHTTPcon","rflHTTPclose","getHTTPstats",
 "getHTTPclientIP", "getHTTPbufferSize", "HTTPsendFile", "setHTTPmaxpostsize",
 "rflHTTPaccept"
};


int (*m_HTTPserver_fun[N_HTTPserver_PKG])(mmachine m)=
{NULL,NULL,
 MstartHTTPserver,
 McloseHTTPserver,
 MHTTPsend,
 McloseHTTPcon,
 MrflHTTPclose,
 MgetHTTPstats,
 MgetHTTPclientIP,
 MgetHTTPbufferSize,
 MHTTPsendFile,
 MsetHTTPmaxpostsize,
 MrflHTTPaccept
};

int m_HTTPserver_narg[N_HTTPserver_PKG]=
{TYPTYPE,TYPTYPE,
 4,
 1,
 2,
 1,
 3,
 1,
 1,
 1,
 2,
 1,
 3
};

char* m_HTTPserver_type[N_HTTPserver_PKG]=
{NULL,NULL,
 "fun [Chn I fun [HTTPcon u0 S] S u0] HTTPserver",
 "fun [HTTPserver] I",
 "fun [HTTPcon S] I",
 "fun [HTTPcon] I",
 "fun [HTTPcon fun [HTTPcon u0] u1 u0] HTTPcon",
 "fun [HTTPserver] [I I I]",
 "fun [HTTPcon] S",
 "fun [HTTPcon] I",
 "fun [HTTPcon P] I",
 "fun [I] I",
 "fun [HTTPserver fun [HTTPcon u0] u1 u0] HTTPserver"
};


/* plugin startup */


int SCOLloadHTTPserver(mmachine m)
{
  int k;

  maxpostsize=0;
  k=PKhardpak(m,PKG_NAME,N_HTTPserver_PKG,
	      m_HTTPserver_name,m_HTTPserver_fun,m_HTTPserver_narg,m_HTTPserver_type);
  
  /* rergister objects */
  HTTPserver_type=OBJregister(HTTPserver_nrfl,0,HTTPserver_destroy,HTTPserver_name);
  HTTPcon_type   =OBJregister(HTTPcon_nrfl   ,0,HTTPcon_destroy,   HTTPcon_name);
  
#ifdef VERSION_WIN
  /* windoz event stuff */
  WM_HTTPSVR=OBJgetUserEvent();
  OBJdefEvent(WM_HTTPSVR,SCOLHttpEvent);
#endif
  return k;
}
