/*******************************************************************************
        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.
*******************************************************************************/

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

/*
 * You must change the following values to whatever
 * your local IP address and netmask are. Contact your
 * network administrator for these numbers.
 */
#define MY_IP_ADDRESS   "10.10.6.100"
#define MY_NETMASK      "255.255.255.0"

/*
 * 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

/*
 * 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		MY_IP_ADDRESS
//#define REDIRECTHOST	"proxy.domain.com:1212"

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

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

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

#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 */
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 one token 'foo=bar', matching 'foo' to the name field in
 * the struct, and storing 'bar' into the value
 */
void parse_token(HttpState* state)
{
	int i, len;

	for(i=0; i<HTTP_MAXBUFFER; i++) {
		if(state->buffer[i] == '=')
			state->buffer[i] = '\0';
	}
	state->p = state->buffer + strlen(state->buffer) + 1;
	
	for(i=0; i<(sizeof(FORMSpec)/sizeof(FORMType)); i++) {
		if(!strcmp(FORMSpec[i].name,state->buffer)) {
			len = (strlen(state->p)>MAX_FORMSIZE) ? MAX_FORMSIZE - 1: strlen(state->p);
			strncpy(FORMSpec[i].value,state->p,1+len);
			FORMSpec[i].value[MAX_FORMSIZE - 1] = '\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)
{
	int retval;

	while(1) {
		retval = sock_fastread(&state->s, state->p, 1);
		if(0 == retval) {
			*state->p = '\0';
			parse_token(state);
			
			return 1;
		}

		/* should this only be '&'? (allow the eoln as valid text?) */
		if((*state->p == '&') || (*state->p == '\r') || (*state->p == '\n')) {
			/* found one token */
			*state->p = '\0';
			parse_token(state);
			
			state->p = state->buffer;
		} else {
			state->p++;
		}
			
		if((state->p - state->buffer) > HTTP_MAXBUFFER) {
			/* input too long */
			return 1;
		}
	}

	return 0; /* end of data - loop again to give it time to write more */
}

/*
 * 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;
}

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();
	}
}

#nodebug