/*******************************************************************************
        Samples\TCPIP\HTTP\post2a.c
        Z-World, 2000

        This is an alternate version of post2.c that uses the ZSERVER.LIB
        functionality to build the server table.  Comparison with post2.c
        shows how a program can be converted from using http_flashspec
        to ZSERVER.LIB functionality.

        This uses the post.c style form submission, and the ssi.c style
        dynamic pages to build a fully functional and audited contoller.
        When users first access the page, they enter their name in a form,
        that is then stored in a HTTP cookie. This is used to later build
        an audit trail of what changes each user makes.
*******************************************************************************/
#class auto

/***********************************
 * Configuration                   *
 * -------------                   *
 * All fields in this section must *
 * be altered to match your local  *
 * network settings.               *
 ***********************************/

/*
 * Pick the predefined TCP/IP configuration for this sample.  See
 * LIB\TCPIP\TCP_CONFIG.LIB for instructions on how to set the
 * configuration.
 */
#define TCPCONFIG 1

/*
 * TCP/IP modification - reduce TCP socket buffer
 * size, to allow more connections. This can be increased,
 * with increased performance, if the number of sockets
 * are reduced.  Note that this buffer size is split in
 * two for TCP sockets--1024 bytes for send and 1024 bytes
 * for receive.
 */
#define TCP_BUF_SIZE 2048

/*
 * Web server configuration
 */

/*
 * only one socket and server are needed for a reserved port
 */
#define HTTP_MAXSERVERS 1
#define MAX_TCP_SOCKET_BUFFERS 1


/*
 * Increase the number of spec entries
 */
#define SSPEC_MAXSPEC 19

/*
 * Don't use the FlashSpec structure
 */
#define HTTP_NO_FLASHSPEC

/*
 * Our web server as seen from the clients.
 * This should be the address that the clients (netscape/IE)
 * use to access your server. Usually, this is your IP address.
 * If you are behind a firewall, though, it might be a port on
 * the proxy, that will be forwarded to the Rabbit board. The
 * commented out line is an example of such a situation.
 */
#define REDIRECTHOST		_PRIMARY_STATIC_IP
//#define REDIRECTHOST	"proxy.domain.com:1212"


/********************************
 * End of configuration section *
 ********************************/

/*
 *  REDIRECTTO is used by each ledxtoggle cgi's to tell the
 *  browser which page to hit next.  The default REDIRECTTO
 *  assumes that you are serving a page that does not have
 *  any address translation applied to it.  
 *
 */

#define REDIRECTTO      "http://" REDIRECTHOST "/index.shtml"
#define REGISTERFORM    "http://" REDIRECTHOST "/register.html"

#memmap xmem
#use "dcrtcp.lib"
#use "http.lib"

#ximport "samples/tcpip/http/pages/form.html"      reg_form
#ximport "samples/tcpip/http/pages/ssi2.shtml"     index_html
#ximport "samples/tcpip/http/pages/rabbit1.gif"    rabbit1_gif
#ximport "samples/tcpip/http/pages/ledon.gif"      ledon_gif
#ximport "samples/tcpip/http/pages/ledoff.gif"     ledoff_gif
#ximport "samples/tcpip/http/pages/button.gif"     button_gif
#ximport "samples/tcpip/http/pages/showsrc.shtml"  showsrc_shtml
#ximport "samples/tcpip/http/post2a.c"             ssi_c

/* the default for / must be first */
const HttpType http_types[] =
{
   { ".shtml", "text/html", shtml_handler}, // ssi
   { ".html", "text/html", NULL},           // html
   { ".cgi", "", NULL},                     // cgi
   { ".gif", "image/gif", NULL}
};

#define MAX_FORMSIZE	64
typedef struct {
	char *name;
	char value[MAX_FORMSIZE];
} FORMType;
FORMType FORMSpec[2];

char led1[15];
char led2[15];
char led3[15];
char led4[15];

/*
 * The audit list - stores the last NUM_ENTRIES changes to the site
 */
#define NUM_ENTRIES	16
typedef struct {
	char who[HTTP_MAXNAME];
	char which;		/* which LED changed */
	char led1;		/* updated status of all LEDs */
	char led2;
	char led3;
	char led4;
} AUDITLine;
AUDITLine audit_list[NUM_ENTRIES];
int current_line;

char get_led_status(char *led)
{
   	if (strcmp(led,"ledon.gif")==0)
   		return 1;
   	else
   		return 0;
}

void AuditInitLine(AUDITLine* line)
{
	line->who[0] = '\0';
	line->led1 = get_led_status(led1);
	line->led2 = get_led_status(led2);
	line->led3 = get_led_status(led3);
	line->led4 = get_led_status(led4);
}

void AuditInit(void)
{
	int i;
	for(i=0;i<NUM_ENTRIES;i++) {
		AuditInitLine(&audit_list[i]);
	}
	current_line = 0;
}

/**
 * 	print the audit list.  Call by SSI exec command in "SSI2.shtml"
 * 	(ie, it holds the static areas of the HTML page).
 */
int audit_list_print(HttpState* state)
{
	int printline;
	AUDITLine* p;
	
	if(state->substate >= NUM_ENTRIES)
		return 1;
	
	printline = current_line - state->substate;
	if(printline < 0)
		printline += NUM_ENTRIES;


	p = &audit_list[printline];

	if('\0' == p->who[0]) {
		state->substate++;
		return 0;
	}
	
	sprintf(state->buffer, "<tr><td>%s</td><td>led%d</td><td>%c%c%c%c</td></tr>\r\n",
		p->who,
		p->which,
		1 == p->led1 ? 'O' : 'X',
		1 == p->led2 ? 'O' : 'X',
		1 == p->led3 ? 'O' : 'X',
		1 == p->led4 ? 'O' : 'X'
		);
	state->headerlen = strlen(state->buffer);
	state->headeroff = 0;
	state->substate++;

	return 0;
}

void add_audit(HttpState* state, char which)
{
	struct sockaddr sock_addr;

	current_line++;
	if(current_line >= NUM_ENTRIES)
		current_line = 0;

	AuditInitLine(&audit_list[current_line]);
	
	audit_list[current_line].which = which;

	strcpy(audit_list[current_line].who, state->cookie);
}

/*
 *  Instead of sending other text back from the cgi's
 *  we have decided to redirect them to the original page.
 *  the cgi_redirectto forms a header which will redirect
 *  the browswer back to the main page.
 *
 */

int led1toggle(HttpState* state)
{
   if('\0' == state->cookie[0]) {
   	cgi_redirectto(state,REGISTERFORM);
   	return 0;
   }
   
   if (strcmp(led1,"ledon.gif")==0)
      strcpy(led1,"ledoff.gif");
   else
      strcpy(led1,"ledon.gif");

   add_audit(state, 1);

   cgi_redirectto(state,REDIRECTTO);
   return 0;      
}

int led2toggle(HttpState* state)
{
   if('\0' == state->cookie[0]) {
   	cgi_redirectto(state,REGISTERFORM);
   	return 0;
   }

   if (strcmp(led2,"ledon.gif")==0)
      strcpy(led2,"ledoff.gif");
   else
      strcpy(led2,"ledon.gif");

   add_audit(state, 2);

      
   cgi_redirectto(state,REDIRECTTO);
   return 0;      
}

int led3toggle(HttpState* state)
{
   if('\0' == state->cookie[0]) {
   	cgi_redirectto(state,REGISTERFORM);
   	return 0;
   }

   if (strcmp(led3,"ledon.gif")==0)
      strcpy(led3,"ledoff.gif");
   else
      strcpy(led3,"ledon.gif");

   add_audit(state, 3);

   cgi_redirectto(state,REDIRECTTO);
   return 0;      
}

int led4toggle(HttpState* state)
{
   if('\0' == state->cookie[0]) {
   	cgi_redirectto(state,REGISTERFORM);
   	return 0;
   }

   if (strcmp(led4,"ledon.gif")==0)
      strcpy(led4,"ledoff.gif");
   else
      strcpy(led4,"ledon.gif");

   add_audit(state, 4);

   cgi_redirectto(state,REDIRECTTO);
   return 0;      
}

/*
 * parse the url-encoded POST data into the FORMSpec struct
 * (ie: parse 'foo=bar&baz=qux' into the struct
 */
int parse_post(HttpState* state)
{
	auto int retval;
	auto int i;

	// state->s is the socket structure, and state->p is pointer
	// into the HTTP state buffer (initially pointing to the beginning
	// of the buffer).  Note that state->p was set up in the submit
	// CGI function.  Also note that we read up to the content_length,
	// or HTTP_MAXBUFFER, whichever is smaller.  Larger POSTs will be
	// truncated.
	retval = sock_aread(&state->s, state->p,
	                    (state->content_length < HTTP_MAXBUFFER-1)?
	                     (int)state->content_length:HTTP_MAXBUFFER-1);
	if (retval < 0) {
		// Error--just bail out
		return 1;
	}

	// Using the subsubstate to keep track of how much data we have received
	state->subsubstate += retval;

	if (state->subsubstate >= state->content_length) {
		// NULL-terminate the content buffer
		state->buffer[(int)state->content_length] = '\0';

		// Scan the received POST information into the FORMSpec structure
		for(i=0; i<(sizeof(FORMSpec)/sizeof(FORMType)); i++) {
			http_scanpost(FORMSpec[i].name, state->buffer, FORMSpec[i].value,
			              MAX_FORMSIZE);
		}

		// Finished processing--returning 1 indicates that we are done
		return 1;
	}
	// Processing not finished--return 0 so that we can be called again
	return 0;
}

/*
 * Sample submit.cgi function
 */
int submit(HttpState* state)
{
	int i;
	
	if(state->length) {
		/* buffer to write out */
		if(state->offset < state->length) {
			state->offset += sock_fastwrite(&state->s,
					state->buffer + (int)state->offset,
					(int)state->length - (int)state->offset);
		} else {
			state->offset = 0;
			state->length = 0;
		}
	} else {
		switch(state->substate) {
		case 0:
			strcpy(state->buffer, "HTTP/1.0 200 OK\r\n");
			break;

		case 1:
			/* init the FORMSpec data */
			FORMSpec[0].value[0] = '\0';
			FORMSpec[1].value[0] = '\0';
			state->p = state->buffer;

			parse_post(state);
			
			state->substate++;
			return 0;

		case 2:
			http_setcookie(state->buffer, FORMSpec[0].value);
			break;

		case 3:
			strcpy(state->buffer, "\r\n\r\n<html><head><title>Results</title></head><body>\r\n");
			break;
			
		case 4:
			sprintf(state->buffer, "<p>Username: %s<p>\r\n<p>Email: %s<p>\r\n",
				FORMSpec[0].value, FORMSpec[1].value);
			break;

		case 5:
			strcpy(state->buffer, "<p>Go <a href=\"/\">home</a></body></html>\r\n");
			break;

		default:
			state->substate = 0;
			return 1;
		}

		state->length = strlen(state->buffer);
		state->offset = 0;
		state->substate++;
	}

	return 0;
}

void main()
{
	/* FORM stuff */
	sspec_addxmemfile("register.html", reg_form, SERVER_HTTP);
	sspec_addfunction("submit.cgi", submit, SERVER_HTTP);

	/* normal SSI button stuff */
	sspec_addxmemfile("/", index_html, SERVER_HTTP);
	sspec_addxmemfile("index.shtml", index_html, SERVER_HTTP);
	sspec_addxmemfile("showsrc.shtml", showsrc_shtml, SERVER_HTTP);
	sspec_addxmemfile("rabbit1.gif", rabbit1_gif, SERVER_HTTP);
	sspec_addxmemfile("ledon.gif", ledon_gif, SERVER_HTTP);
	sspec_addxmemfile("ledoff.gif", ledoff_gif, SERVER_HTTP);
	sspec_addxmemfile("button.gif", button_gif, SERVER_HTTP);

	sspec_addxmemfile("ssi.c", ssi_c, SERVER_HTTP);

	sspec_addvariable("led1", led1, PTR16, "%s", SERVER_HTTP);
	sspec_addvariable("led2", led2, PTR16, "%s", SERVER_HTTP);
	sspec_addvariable("led3", led3, PTR16, "%s", SERVER_HTTP);
	sspec_addvariable("led4", led4, PTR16, "%s", SERVER_HTTP);

	sspec_addfunction("led1tog.cgi", led1toggle, SERVER_HTTP);
	sspec_addfunction("led2tog.cgi", led2toggle, SERVER_HTTP);
	sspec_addfunction("led3tog.cgi", led3toggle, SERVER_HTTP);
	sspec_addfunction("led4tog.cgi", led4toggle, SERVER_HTTP);
	sspec_addfunction("audit", audit_list_print, SERVER_HTTP);

	strcpy(led1,"ledon.gif");
	strcpy(led2,"ledon.gif");
	strcpy(led3,"ledoff.gif");
	strcpy(led4,"ledon.gif");

	/* Init the audit history */
   	AuditInit();
   
	/* init FORM searchable names - must init ALL FORMSpec structs! */
	FORMSpec[0].name = "user_name";
	FORMSpec[1].name = "user_email";
	
	sock_init();
	http_init();
	tcp_reserveport(80);
		
	while (1) {
		http_handler();
	}
}

