/*
    Copyright 2000 (c) by Reto Gaehler, Toni Kaufmann,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Net/old/TCP/nettcpHTTPServer.c,v $
    Author(s):             
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $      by: $Author: baderma $
 
 
    $Log: nettcpHTTPServer.c,v $
    Revision 1.3  2000/11/15 10:12:15  ruf
    Repository Reorganisation
 
    Revision 1.2  2000/04/03 17:45:30  gfa
    *** empty log message ***
 
    Revision 1.1  2000/03/31 17:50:38  gfa
    Merged with /Net from several term projects
 
*/

#include "nettcpSocket.h"
#include "NetDebug.h"
#include "nettcpHTTPServer.h"
#include "UserSupport.h"

Boolean	gbTerminate;
//**************************************************************************************************
//	the TCP echo server
//**************************************************************************************************
#ifdef MacOS
pascal void *tcpHTTPServerThread(ThreadArg arg)
#else
void tcpHTTPServerThread(ThreadArg arg)
#endif
{
  SocketPtr		pSocket = (SocketPtr)arg;
  int				nError = 0;
  void			*pTmp;
  int 			state = 0;
  int				header_length, content_length;
  int 			totreqlen = 0;
  int				nCount = 0;
  int				bytesReceived = 0;
  char			ch = 0;
  char 			tbuf[HTTP_TBUF_LEN];
  char 			response_header[HTTP_TBUF_LEN], response_message[HTTP_TBUF_LEN];
  char 			*tptr = NULL;

  /*
  * Read a complete HTTP request - header and body
  */
  for (nCount=0;nCount<HTTP_TBUF_LEN;nCount++)
  {
    tbuf[nCount]=0;
    response_header[nCount]=0;
    response_message[nCount]=0;
  }
  header_length = 0;
  tbuf[header_length] = '\0';
  state = kWorkerWaitingForCR1;
  do
  {
    bytesReceived = tcpRead(pSocket, &ch, sizeof(char));
    if ((bytesReceived == TCP_URGENTMODE) || (bytesReceived == TCP_NORMALMODE))
      continue;
    if (bytesReceived > 0)
    {

      // Put the character into the buffer.

      tbuf[header_length] = ch;
      header_length += 1;
      tbuf[header_length] = '\0';

      // Check that we still have space to include our null terminator.

      if (header_length >= HTTP_TBUF_LEN)
      {
        nError = -1;
      }

      // Do the magic state machine.  Note the use of
      // hardwired numbers for CR and LF.  This is correct
      // because the Internet standards say that these
      // numbers can't change.  I don't use \n and \r
      // because these values change between various C
      // compilers on the Mac.

      switch (ch)
      {
      case 13:
        switch (state)
        {
        case kWorkerWaitingForCR1:
          state = kWorkerWaitingForLF1;
          break;
        case kWorkerWaitingForCR2:
          state = kWorkerWaitingForLF2;
          break;
        default:
          state = kWorkerWaitingForCR1;
          break;
        }
        break;
      case 10:
        switch (state)
        {
        case kWorkerWaitingForLF1:
          state = kWorkerWaitingForCR2;
          break;
        case kWorkerWaitingForLF2:
          state = kWorkerDone;
          break;
        default:
          state = kWorkerWaitingForCR1;
          break;
        }
        break;
      default:
        state = kWorkerWaitingForCR1;
        break;
      }
    }
    else
    {
      nError = bytesReceived;
    }
  }
  while ( nError == TCP_NO_ERR && state != kWorkerDone );

  content_length = 0;
  totreqlen = header_length;

  if (nError == TCP_NO_ERR)
  {
    /* Find eventual request body */
    content_length=getContentLen(tbuf,header_length);

    if (content_length>0)
    {
      do
      {
        bytesReceived = tcpRead(pSocket, &ch, sizeof(char));
        if ((bytesReceived == TCP_URGENTMODE) || (bytesReceived == TCP_NORMALMODE))
          continue;
        if (bytesReceived > 0)
        {

          tbuf[totreqlen] = ch;
          totreqlen += 1;
          tbuf[totreqlen] = '\0';
        }
        else
        {
          nError = bytesReceived;
        }
      }
      while (nError == TCP_NO_ERR && totreqlen < header_length+content_length);
    }

    /* Interpretate HTTP requests */
    /******************************/

    /*
     * Root object "/"
     */
    if (nError != TCP_NO_ERR)
    {
      tcpClose(pSocket);
      return;
    }
    if (stringNCompare("GET / ", tbuf, 6) == EQUAL ||
        stringNCompare("GET /\n", tbuf, 6) == EQUAL ||
        stringNCompare("GET /\r", tbuf, 6) == EQUAL)
    {

      /* Create the response message */
      sprintf(response_message, "<H1>Topsy - Tiny high performance webserver</H1>\n");
      sprintf(&response_message[stringLength(response_message)], "A HTTP test server, listening at port %d.\n", 80);
      sprintf(&response_message[stringLength(response_message)], "<P>Log data encapsulated by HTML - <A HREF=\"/logdump\">/logdump</A>\n");
      sprintf(&response_message[stringLength(response_message)], "<BR>Log data readable by Excel - <A HREF=\"/logdump.xls\">/logdump.xls</A>\n");
      sprintf(&response_message[stringLength(response_message)], "<BR><FORM ACTION=\"/parameters\" METHOD=\"POST\">\n");
      sprintf(&response_message[stringLength(response_message)], "\t<INPUT TYPE=\"text\" NAME=\"textVariable1\" VALUE=\"Fill in something here\">\n");
      sprintf(&response_message[stringLength(response_message)], "\t<INPUT TYPE=\"submit\">\n");
      sprintf(&response_message[stringLength(response_message)], "</FORM>\n");

      /* Create response header */
      sprintf(response_header, "HTTP/1.0 200 OK\r\n");
      sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n");
      sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message));
      sprintf(&response_header[stringLength(response_header)], "\r\n");

    }
    else
      /*
       * Log object "/logdump" in HTML
       */
      if (stringNCompare("GET /logdump ", tbuf, 13) == EQUAL ||
          stringNCompare("GET /logdump\n", tbuf, 13) == EQUAL ||
          stringNCompare("GET /logdump\r", tbuf, 13) == EQUAL)
      {

        /* Create the response message */
        sprintf(response_message, "<H1>Dump of log data</H1>\n");
        sprintf(&response_message[stringLength(response_message)], "Donald = 0x4711\n");
        sprintf(&response_message[stringLength(response_message)], "<BR>\n");
        sprintf(&response_message[stringLength(response_message)], "Duck = 17\n");

        /* Create response header */
        sprintf(response_header, "HTTP/1.0 200 OK\r\n");
        sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n");
        sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message));
        sprintf(&response_header[stringLength(response_header)], "\r\n");

      }
      else
        /*
         * Log object "/logdump.xls" to EXCEL
         */
        if (stringNCompare("GET /logdump.xls ", tbuf, 17) == EQUAL ||
            stringNCompare("GET /logdump.xls\n", tbuf, 17) == EQUAL ||
            stringNCompare("GET /logdump.xls\r", tbuf, 17) == EQUAL)
        {

          /* Create the response message */
          sprintf(response_message, "Dump of log data \t Values\n");
          sprintf(&response_message[stringLength(response_message)], "Donald \t 0x4711\n");
          sprintf(&response_message[stringLength(response_message)], "Duck \t 17\n");

          /* Create response header */
          sprintf(response_header, "HTTP/1.0 200 OK\r\n");
          sprintf(&response_header[stringLength(response_header)], "Content-type: application/vnd.ms-excel\r\n");
          sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message));
          sprintf(&response_header[stringLength(response_header)], "\r\n");

        }
        else
          /*
           * Parameter object "/parameters" in HTML
           */
          if (stringNCompare("POST /parameters ", tbuf, 17) == EQUAL ||
              stringNCompare("POST /parameters\n", tbuf, 17) == EQUAL ||
              stringNCompare("POST /parameters\r", tbuf, 17) == EQUAL)
          {

            /* Create the response message */
            sprintf(response_message, "<H1>Parameters</H1>\n");
            if (header_length > 0)
              sprintf(&response_message[stringLength(response_message)], "%s\n", &tbuf[header_length]);
            else
              sprintf(&response_message[stringLength(response_message)], "No parameters available by means of POST\n");

            /* Create response header */
            sprintf(response_header, "HTTP/1.0 200 OK\r\n");
            sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n");
            sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message));
            sprintf(&response_header[stringLength(response_header)], "\r\n");

            //if ((tptr = strstr(tbuf, "kill")) != NULL)
            //{
            //	gbTerminate=TRUE;
            //}
          }
          else
          {
            /*
             * Unknown object requested
             */

            /* Create the response message */
            sprintf(response_message, "<H1>Error</H1>\n");
            sprintf(&response_message[stringLength(response_message)], "The requested object does not exist:\n");
            sprintf(&response_message[stringLength(response_message)], "<SP>\n");
            sprintf(&response_message[stringLength(response_message)], "<PRE>%s\n</PRE>\n", tbuf);

            /* Create response header */
            sprintf(response_header, "HTTP/1.0 404 Not found\r\n");
            sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n");
            sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message));
            sprintf(&response_header[stringLength(response_header)], "\r\n");

          }

    /*
     * Send HTTP response
     */

    nError = tcpWrite(pSocket,(void *)response_header,stringLength(response_header),FALSE);

    nError = tcpWrite(pSocket,(void *)response_message,stringLength(response_message),FALSE);

  }
  /*
  * Close connection to client process
  */
  tcpClose(pSocket);
  netdbgPrintf(NETDEBUG_TCP, "Terminate HTTP Server Thread\n");
}
//**************************************************************************************************
//	the TCP echo server
//**************************************************************************************************
#ifdef MacOS
pascal void *tcpHTTPDeamon(ThreadArg arg)
#else
void tcpHTTPDeamon(ThreadArg arg)
#endif
{
  IPAddressUon	uonSrcAddr;
  IPAddressUon	uonDstAddr;
  int				nError;
  SocketPtr		pServerSocket;
  SocketPtr		pSocket;
  ThreadId		echoID;
  void			*pTmp;
  char			cChar;

  uonSrcAddr.u32Addr = 0xC0A80202;		// 192.168.2.2
  uonDstAddr.u32Addr = 0xC0A80201;		// 192.168.2.1

  nError = tcpOpen(&uonSrcAddr,&uonDstAddr,80,TCP_ANY_FOREIGN_PORT,&pServerSocket);
  if (nError == TCP_NO_ERR)
  {
    nError = tcpControl(pServerSocket,TCP_CONTROL_LISTEN_QUEUE,(void*)10,NULL,&pTmp);
    if (nError == TCP_NO_ERR)
    {
      netdbgPrintf(NETDEBUG_TCP, "httpd is started\n");
      while (TRUE)
      {
        nError = tcpControl(pServerSocket,TCP_CONTROL_ACCEPT,NULL,NULL,&pSocket);
        if ((nError != TCP_NO_ERR) && (pSocket != NULL))
        {
          netdbgDisplay(NETDEBUG_TCP, "httpd failed\n");
          break;
        }
        else
          netdbgDisplay(NETDEBUG_TCP,"http client connected successfully\n");

        tcpControl(pSocket,TCP_CONTROL_SET_USER_OPTION,TCP_TCB_BUFFER,(void*)0,(void*)0);

        if (tmStart(&echoID,tcpHTTPServerThread,(ThreadArg)pSocket,"tcpHTTPServer") != TM_STARTOK)
          netdbgDisplay(NETDEBUG_TCP,"httpd error\n");
      }
    }
    else
      netdbgDisplay(NETDEBUG_TCP,"httpd error tcpControl\n");
  }
  else
    netdbgDisplay(NETDEBUG_TCP,"httpd error tcpOpen\n");
}
//**************************************************************************************************
//	getNum
//**************************************************************************************************
int getNum(unsigned char *pu8Buffer,unsigned short u16Len)
{
  unsigned char u8NumBuf[12];
  int nPos = 0;
  int nDigits = 0;
  int	nValue = -1;

  while ((*pu8Buffer == 32) || (*pu8Buffer == 9))
  {
    pu8Buffer++;
    u16Len--;
  }

  while ((*pu8Buffer >= '0') && (*pu8Buffer <= '9') && (nPos++ < u16Len))
  {
    u8NumBuf[nDigits++] = *pu8Buffer++;
  }
  u8NumBuf[nDigits] = 0;

  if (nDigits > 0)
    atoi(&nValue,u8NumBuf);
  return nValue;
}
//**************************************************************************************************
//	getContentLen
//**************************************************************************************************
int getContentLen(unsigned char *pu8Buffer,unsigned short u16Len)
{
  unsigned char u8Pattern1[] = "Content-Length:";
  unsigned char u8Pattern2[] = "Content-length:";
  int nPos = 0;
  int nRes;

  while (nPos < u16Len-sizeof(u8Pattern1)+1)
  {
    if (*pu8Buffer == 'C')
    {
      nRes = stringNCompare((const char*)pu8Buffer,(const char*)u8Pattern1,sizeof(u8Pattern1)-1);
      if (nRes == EQUAL)
        return getNum(pu8Buffer+sizeof(u8Pattern1)-1,u16Len-nPos);
      else
      {
        nRes = stringNCompare((const char*)pu8Buffer,(const char*)u8Pattern2,sizeof(u8Pattern2)-1);
        if (nRes == EQUAL)
          return getNum(pu8Buffer+sizeof(u8Pattern2)-1,u16Len-nPos);
      }
    }

    nPos++;
    pu8Buffer++;
  }
}
//**************************************************************************************************
//	tcpHTTPServerTest
//**************************************************************************************************
int
tcpHTTPServerTest(void)
{
  long			serverThreadId;
  int				nResult;

  gbTerminate=FALSE;

  if (tmStart(&serverThreadId,tcpHTTPDeamon,(ThreadArg)0,"HTTPDeamon") != TM_STARTOK)
    return TCP_GENERAL_ERROR;

  netdbgDisplay(NETDEBUG_TCP,"http-server started\n");

  do
  {
    tmYield();
  }
  while (gbTerminate==FALSE);

  netdbgDisplay(NETDEBUG_TCP,"exit http-server\n");

  return 0;
}
































