/* ------------------------------------------------------------------------------------------------ */
/* FILE :		httpserver_netconn.c																*/
/*																									*/
/* CONTENTS :																						*/
/*				Code for the lwIP webserver with netconn.											*/
/*				This file contains all the webservices management.									*/
/*																									*/
/*																									*/
/* Copyright (c) 2013-2014, Code-Time Technologies Inc. All rights reserved.						*/
/*																									*/
/* Code-Time Technologies retains all right, title, and interest in and to this work				*/
/*																									*/
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS							*/
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF										*/
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL							*/
/* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR								*/
/* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,							*/
/* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR							*/
/* OTHER DEALINGS IN THE SOFTWARE.																	*/
/*																									*/
/*																									*/
/*	$Revision: 1.2 $																				*/
/*	$Date: 2014/04/28 21:32:16 $																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

#include <string.h>
#include "WebServer.h"

/* ------------------------------------------------------------------------------------------------ */

static void WebserverProcess(struct netconn *conn); 
static void WebserverTask(void *Arg);

/* ------------------------------------------------------------------------------------------------ */
													/* Out larger than in for SSI processing		*/
static char g_PageInBuf[16384-2048];				/* Buffer holding input data when manipulated	*/
static char g_PageOutBuf[16384];					/* Buffer holding output data when manipulated	*/
static char g_RqstBuf[8192];						/* Buffer holding the request string			*/

static struct Methods g_MyVerbs[] = {				/* Association of text to verbs					*/
	{"GET ",     V_GET},
	{"HEAD ",    V_HEAD},
	{"POST ",    V_POST},
	{"PUT ",     V_PUT},
	{"DELETE ",  V_DELETE},
	{"TRACE ",   V_TRACE},
	{"OPTIONS ", V_OPTIONS},
	{"CONNECT ", V_CONNECT},
	{"PATCH ",   V_PATCH}
};

#if (((OS_DEMO) == 12) || ((OS_DEMO) == 112))
  static FATFS g_FileSys;  
#endif

/* ------------------------------------------------------------------------------------------------ */
/* Initialization of the HTTP server																*/
/*																									*/
/* - Create and resume the HTTP server task															*/
/* ------------------------------------------------------------------------------------------------ */

void http_server_init()
{

	sys_thread_new("HTTP server", WebserverTask, NULL, WEBS_STACKSIZE, WEBS_PRIO);

	return;
}

/* ------------------------------------------------------------------------------------------------ */
/* HTTP server task																					*/
/*																									*/
/* - Create a new TCP connection																	*/
/* - Init the CGIs and SSIs																			*/
/* - Bind the network connection to port 80															*/
/* - Start listening on the connection																*/
/* - forever loop																					*/
/*   - Block until a new connection with the client is established									*/
/*   - Process the client request																	*/
/*   - Delete the new connection																	*/
/* ------------------------------------------------------------------------------------------------ */

static void WebserverTask(void *Arg)
{ 
struct netconn *Conn;
err_t           Error;
int             ii;
int             jj;
struct netconn *NewConn;
int             NetErr;

	Arg        = Arg;								/* To remove compiler warning					*/

	WebServerInit();

	jj = 0;											/* Make sure the SD/MMC card can be mounted		*/
	do {											/* If not, retry 10 second later over and over	*/
		ii = F_MNT();
		MTXLOCK_STDIO();
		if (ii != 0) {
			puts("**** ERROR: Failed to mount the SD/MMC card");
			puts("            Retrying in 10 s");
			TSKsleep(10*OS_TICK_PER_SEC);
			jj = 1;
		}
		else {
			if (jj != 0) {
				puts("\n*** OK: SD/MMC card mounted\n");
			}
		}
		MTXUNLOCK_STDIO();
	} while(ii != 0);

	MTXLOCK_STDIO();
	puts("The file system is ready");
	MTXUNLOCK_STDIO();

  #ifndef osCMSIS
   #if ((OX_EVENTS) != 0)
	EVTset(TSKgetID("A&E"), 2);						/* Report to Adam & Eve File System is ready	*/
   #endif
  #else
	extern osThreadId G_TaskMainID;
	osSignalSet(G_TaskMainID, 2);
  #endif

	Conn = netconn_new(NETCONN_TCP);				/* Create a new TCP connection handle			*/
	if (Conn != NULL) {

		Error = netconn_bind(Conn, NULL, 80);		/* Bind to port 80 (HTTP) with default IP addr	*/
		if (Error == ERR_OK) {
			netconn_listen(Conn);					/* Put the connection into LISTEN state			*/
			while(1) {								/* And we go on forever							*/
				NetErr = netconn_accept(Conn, &NewConn);	/* Accept any incoming connection		*/
				if (NetErr == ERR_OK) {
					WebserverProcess(NewConn);		/* Serve the connection							*/
					netconn_delete(NewConn);		/* Delete connection							*/
				}
			}
		}
		else {
			MTXLOCK_STDIO();
			printf("Cannot bind the connection to port 80");
			MTXUNLOCK_STDIO();
		}
		netconn_close(Conn);
		netconn_delete(Conn);
	}
	else {
		MTXLOCK_STDIO();
		printf("Cannot create a netconn connection");
		MTXUNLOCK_STDIO();
	}

	for (;;) {
		TSKselfSusp();
	}
}

/* ------------------------------------------------------------------------------------------------ */
/* Process a client request																			*/
/*																									*/
/* - Get the request																				*/
/* - Find the request verb/method																	*/
/* - Per Verb processing																			*/
/*																									*/
/*   - GET verb:																					*/
/*     - Open the requested file																	*/
/*     - if file opening OK																			*/
/*       - if file extension .stm or .shtm or .shtml												*/
/*         - Copy the file contents to a local buffer												*/
/*         - Perform the SSI substitution															*/
/*         - Request to output the buffer															*/
/*       - else																						*/
/*         - Request to output the file																*/
/*     - if file opening error																		*/
/*       - Check and process CGIs																	*/
/*       - Open the file returned by CGI processing													*/
/*       - if file open OK																			*/
/*         - Request to output the file																*/
/*     - if no valid output																			*/
/*       - Open the 404 error page file																*/
/*       - if file open OK																			*/
/*         - Request to output the file																*/
/*     - If request to output file																	*/
/*       - Copy file to output																		*/
/*     - If request to output buffer																*/
/*       - Copy buffer to output																	*/
/*     - Close file																					*/
/*																									*/
/*   - Other verbs:																					*/
/*     - print error message																		*/
/*																									*/
/* - Close connection																				*/
/* ------------------------------------------------------------------------------------------------ */

static void WebserverProcess(struct netconn *conn) 
{
int             CopyFile;
err_t           Error;
FILE_DSC_t      Fdsc;								/* Descriptor of the file requested by client	*/
int             FisOpen;							/* If the file to send back is open				*/
int             Fsize;								/* Number of bytes read from the file			*/
int             ii;									/* General purpose								*/
int             PageLen;							/* Length of the page to send back to client	*/
struct netbuf  *Rqst;								/* Buffer to the client request page			*/
char           *uri;
char            URIchar;							/* Character that was replaced by '\0'			*/
char           *URIend;								/* Index where the '\0' was inserted			*/
enum VerbTypes  VerbRqst;							/* Verb / method of the client request			*/

	F_INIT_FDSC(Fdsc);								/* To remove compiler warning					*/
	URIchar = '\0';
	Rqst    = NULL;
	Error   = netconn_recv(conn, &Rqst);			/* Read the data from the port					*/
	if ((Rqst   != NULL)							/* Got a valid request							*/
	&&  (Error  == ERR_OK)							/* No error getting the data					*/
	&&  (netconn_err(conn) == ERR_OK))  {			/* No error on the connection					*/
		ii = netbuf_len(Rqst);
		if (ii > sizeof(g_RqstBuf)-1) {
			ii = sizeof(g_RqstBuf)-1;
		}											/* Copy the request page locally				*/
		ii = netbuf_copy(Rqst, (void**)&g_RqstBuf[0], ii);
		g_RqstBuf[ii] = 0;							/* Terminate the input buffer					*/
		VerbRqst      = V_UNKNOWN_;					/* Assume we can't understand the verb			*/
		for(ii=0 ; ii<(sizeof(g_MyVerbs)/sizeof(g_MyVerbs[0])) ; ii++) {
			if (0 == strncmp(&g_RqstBuf[0], g_MyVerbs[ii].Text, strlen(g_MyVerbs[ii].Text))) {
				uri = strchr(&g_RqstBuf[0], ' ');	/* Isolate the resource name					*/
				if (uri != (char *)NULL) {
					while (*uri == ' '){			/* Skip the white spaces before the name		*/
						uri++;
					}
					URIend = strchr(uri, ' ');		/* Set uri to point to the file name			*/
					if (URIend != NULL) {			/* Terminate the string for the file name		*/
						URIchar = *URIend;
						*URIend = '\0';				/* Done by finding the last white space			*/
					}
					if (0 == strcmp(uri, "/")) {	/* Root remaps to the default page				*/
						uri = "/default.html";
						if (0 != F_OPEN(Fdsc, uri, FA_READ)) {	/* Check if default.html exists		*/
							F_CLOSE(Fdsc);			/* It exist, will use it						*/
						}
						else {						/* It does not exists, try with default.shtml	*/
							uri = "/default.shtml";
						}
					}
					VerbRqst = g_MyVerbs[ii].Verb;	/* This is the method to handle					*/
				}
				break;
			}
		}

		FisOpen  = 0;								/* The file to send back is not yet open		*/
		CopyFile = 0;								/* We will decide to either copy the file as is	*/
		PageLen  = 0;								/* or to output a processed buffer (SSI)		*/

		switch(VerbRqst) {							/* -------------------------------------------- */
		case V_GET:									/* GET											*/
			FisOpen = F_OPEN(Fdsc, uri, FA_READ);	/* Try opening the file							*/
			if (FisOpen == 0) {						/* The file did not open, it could be CGIs		*/
				uri = CGIprocess(uri);				/* Check and try processing CGIs				*/
				if (uri != NULL) {					/* Try opening result of CGI if was valid CGI	*/
					FisOpen = F_OPEN(Fdsc, uri, FA_READ);/* If URI is NULL, FisOpen already holds 0	*/
				}
			}
			break;

		case V_POST:								/* Place holders for unsupported methods		*/
			if (URIchar != '\0') {					/* Make sure to not try to analyse garbage		*/
				uri = POSTprocess(uri, URIend+1);
				if (uri != NULL) {					/* Try opening result of POST if was valid POST	*/
					FisOpen = F_OPEN(Fdsc, uri, FA_READ);/* If URI is NULL, FisOpen already holds 0	*/
				}
			}
			break;

		case V_HEAD:
			break;
		case V_PUT:
			break;
		case V_DELETE:
			break;
		case V_TRACE:
			break;
		case V_OPTIONS:
			break;
		case V_CONNECT:
			break;
		case V_PATCH:
			break;
		default:									/* V_UNKNOWN_									*/
			break;
		}

		if (FisOpen != 0) {							/* The file is open, we can process it now		*/
			F_PATCH_OPEN(Fdsc);						/* Used for in-memory to not modify fs.c		*/
			if ((NULL != strstr(uri, ".shtm"))		/* Check if the file contains SSIs				*/
			||  (NULL != strstr(uri, ".stm"))) {	/* SSI file extensions: .stm, .stml, .shtml		*/
				SSIvarUpdate();						/* Make sure the local variables are up to date	*/
													/* Copy the file contents in g_PageInBuf		*/
				F_READ(Fdsc, &g_PageInBuf[PageLen], sizeof(g_PageInBuf)-1, &Fsize);
				if ((int)Fsize == (sizeof(g_PageInBuf)-1)) {
					MTXLOCK_STDIO();
					puts("httpserver_netcomm.c: File too big for g_PageInBuf");
					MTXUNLOCK_STDIO();
				}
				g_PageInBuf[(int)Fsize] = '\0';		/* Terminate the input buffer as a string		*/
				PageLen = SSIexpand(&g_PageOutBuf[0], &g_PageInBuf[0]);	/* Process the SSIs			*/
			}
			else {									/* Not a SSI file, direct copy from the file	*/
				CopyFile = 1;						/* to the client								*/
			}
		}

		if ((CopyFile == 0)							/* When file not found, send the 404 error		*/
		&&  (PageLen  == 0)) {						/* Will be copied if opening successful			*/
			CopyFile = (0 != F_OPEN(Fdsc, "/404.html", FA_READ));
		}

		if (CopyFile != 0) {						/* Request to copy the file contents as is		*/
			F_PATCH_OPEN(Fdsc);						/* Used for in-memory to not modify fs.c		*/
			do {
				F_READ(Fdsc, &g_PageOutBuf[0], sizeof(g_PageOutBuf), &Fsize);
				netconn_write(conn, (const void *)&g_PageOutBuf[0], Fsize, NETCONN_COPY);
			} while (Fsize != 0);
		}
		else if (PageLen != 0) {					/* Request to send a processed buffer			*/
			netconn_write(conn, (const void *)&g_PageOutBuf[0], PageLen, NETCONN_COPY);
		}
		F_CLOSE(Fdsc);
	}

	netconn_close(conn);							/* Close connection socket						*/
	netbuf_delete(Rqst);							/* Return the buffer to the pool				*/

	return;
}

/* EOF */
