/*
		flashlog2.c

		Copyright (c) 2000, Zworld

		This program can be used for boards that have a second flash.
		It is set up to work on the TCP/IP Dev Kit, the NetGateway and
		the Ethernet Core.

		The program logs users that request a controller's page.  The
		log can be viewed online.

		This program differs from flashlog.c in its use of the flash
		file system to do the logging instead of the direct flash
		write routines.

		This program works on boards with a second flash device;
		currently the RCM2100, TCP/IP development kit and INTELLICOM.
*/

#memmap xmem

#define MY_IP_ADDRESS	"10.10.6.100"
#define MY_NETMASK		"255.255.255.0"
#define MY_GATEWAY		"10.10.6.1"

//#define REDIRECTTO		"http://www.myhost.com"
#define REDIRECTTO		"http://" MY_IP_ADDRESS

/*
 *		FORM_ERROR_BUF is required for the zserver form interface in
 *		init_dateform()
 *
 */

#define FORM_ERROR_BUF 	256

/*
 *		Use Flash for the file system instead of RAM.
 *
 */

#define FS_FLASH

#use "dcrtcp.lib"
#use "http.lib"
#use "filesystem.lib"


/*
 *		User macros that specify the offset and size of the flash.
 *		They are used below in fs_init
 *
 */

#define FORMAT 
#define OFFSET 	0L
#define BLOCKS 	64
#define WEAR		1
#define LOGFILE	1

/*
 *		Load files into flash
 *
 */

#ximport "samples/tcpip/http/pages/log.shtml"	index_shtml
#ximport "samples/tcpip/http/pages/rabbit1.gif"	rabbit1_gif
#ximport "samples/tcpip/http/flashlog2.c"			flashlog_c

/*
 *		log_cgi(HttpState*)
 *
 *		This cgi function is called from a log.shtml #exec ssi.
 *		Each time the page is requested it calls this function which
 *		records the IP address and time of the access.
 *
 */

File logfile;

int log_cgi(HttpState* state)
{
	int x;
	char buffer[512];
	int my_xpc;
	struct tm time;
	sockaddr sock_addr;

	memset(buffer,0,sizeof(buffer));

	x=sizeof(sock_addr);
	getpeername((sock_type*)&state->s,&sock_addr,&x);

	tm_rd(&time);
	sprintf(buffer, "%02d/%02d/%04d %02d:%02d:%02d - %d.%d.%d.%d<br>",
		time.tm_mon,
		time.tm_mday,
		time.tm_year+1900,
		time.tm_hour,
		time.tm_min,
		time.tm_sec,
		*(((char*)&sock_addr.s_ip)+3),
		*(((char*)&sock_addr.s_ip)+2),
		*(((char*)&sock_addr.s_ip)+1),
		*(((char*)&sock_addr.s_ip)+0));

	if(fwrite(&logfile,buffer,strlen(buffer))==0) {
		printf("error writing to file");
		/* handle error... */
	}
		
   return 1;
}

/*
 *		int resetlog_cgi(HttpState*)
 *
 *		This cgi function clears the log.
 *
 */

int resetlog_cgi(HttpState* state)
{
	fclose(&logfile);
	fdelete(LOGFILE);
	if(fcreate(&logfile,LOGFILE)) {
		printf("error recreating the log file\n");
		/* handle error... */
	}
	
	cgi_redirectto(state,REDIRECTTO);
   return 0;      
}

/*
 *		int printlog_cgi(HttpState*)
 *
 *		This cgi function prints the contents of the log.  It uses
 *		the HttpState substate, buffer, offset, and p variables to
 *		maintain the current state.
 *
 */

#define PRTLOG_INIT		0
#define PRTLOG_HEADER	1
#define PRTLOG_PRTITEM	2
#define PRTLOG_FOOTER	3

const char prtlog_header[] =
	"<html><head><title>prtlog.cgi</title></head><body>\r\n" \
	"<h2>Web Log</h2>\r\n";

const char prtlog_error[] =
	"<br><br><h3>file system error</h3></body></html>\r\n";

const char prtlog_footer[] = "</body></html>\r\n";	

int printlog_cgi(HttpState* state)
{
	auto int bytes_written, bytes_read;
	auto long end;
	auto File prtlog;
	
	switch(state->substate) {
		case PRTLOG_INIT:
			state->p=&prtlog_header[0];
			state->substate=PRTLOG_HEADER;

			// intentionally no break
		
		case PRTLOG_HEADER:
			/*
			 *		Send the top of the page.
			 *
			 */
			 
			if(*state->p) {
				bytes_written=sock_fastwrite(&state->s,state->p,strlen(state->p));
				if(bytes_written>0)
					state->p+=bytes_written;
			} else {
				state->p=NULL;
				state->offset=0;
				state->substate=PRTLOG_PRTITEM;
			}
			break;
			
		case PRTLOG_PRTITEM:
			/*
			 *		Open the file, get its end, read as many bytes
			 *		as we can into the HttpState buffer, and write
			 *		them to the socket.
			 *
			 *		We keep doing this until we reach the end of the
			 *		file.  At that point we set things up to send
			 *		the bottom of the page.
			 *
			 */
		
			if(fopen_rd(&prtlog,LOGFILE)) {
				state->offset=0;
				state->p=&prtlog_error[0];
				state->substate=PRTLOG_FOOTER;
				break;
			}

			if(fseek(&prtlog,0L,SEEK_END)) {
				state->offset=0;
				state->p=&prtlog_error[0];
				state->substate=PRTLOG_FOOTER;
				fclose(&prtlog);
				break;
			}
			end=ftell(&prtlog);

			if(end<=state->offset) {
				state->offset=0;
				state->p=&prtlog_footer[0];
				state->substate=PRTLOG_FOOTER;
			} else {
				fseek(&prtlog,state->offset,SEEK_SET);
				bytes_read=fread(&prtlog,state->buffer,sizeof(state->buffer));

				if(bytes_read==0) {
					state->offset=0;
					state->p=&prtlog_error[0];
					state->substate=PRTLOG_FOOTER;
				} else {
					bytes_written=sock_fastwrite(&state->s,state->buffer,bytes_read);
					state->offset+=bytes_written;
				}
			}

			fclose(&prtlog);
			break;
			
		case PRTLOG_FOOTER:
			/*
			 *		Send the bottom of the page.
			 *
			 */
			 
			if(*state->p) {
				bytes_written=sock_fastwrite(&state->s,state->p,strlen(state->p));
				if(bytes_written>0)
					state->p+=bytes_written;
			} else
				return 1;			// done

			break;
	}
	return 0;
}

const HttpType http_types[] =
{
   { ".shtml", "text/html", shtml_handler},	// ssi
   { ".cgi", "", NULL},                     	// cgi
   { ".gif", "image/gif", NULL},					// gif
   { ".c", "text/plain", NULL}
};

const HttpSpec http_flashspec[] = 
{
   { HTTPSPEC_FILE,  "/",              index_shtml,   NULL, 0, NULL, NULL},
   { HTTPSPEC_FILE,  "/index.shtml",   index_shtml,   NULL, 0, NULL, NULL},
   { HTTPSPEC_FILE,  "/rabbit1.gif",   rabbit1_gif,   NULL, 0, NULL, NULL},
   { HTTPSPEC_FILE,  "flashlog.c",  	flashlog_c,    NULL, 0, NULL, NULL},

   { HTTPSPEC_FUNCTION, "/log.cgi",			0, log_cgi, 		0, NULL, NULL},
   { HTTPSPEC_FUNCTION, "/printlog.cgi",	0, printlog_cgi,	0, NULL, NULL},
   { HTTPSPEC_FUNCTION, "/resetlog.cgi",	0, resetlog_cgi,	0, NULL, NULL},
};

/*
 *		void setdate();
 *
 *		Change the board time to the user specified time
 *		and redirect the user to the front page.
 *
 */

int hour, minute, second, month, day, year, date_lock;
 
int set_date(HttpState* state)
{
	auto struct tm time;
	
	time.tm_sec=second;
	time.tm_min=minute;
	time.tm_hour=hour;
	time.tm_mon=month;
	time.tm_mday=day;
	time.tm_year=year-1900;

	tm_wr(&time);
	SEC_TIMER=mktime(&time);

	date_lock=0;

	cgi_redirectto(state,REDIRECTTO);
   return 0;      
}


/*
 *		void update_date()
 *
 *		Update the date from the board clock
 *
 */

void update_date()
{
	auto struct tm time;

	#GLOBAL_INIT { date_lock=0; }

	if(date_lock) return;
	
	tm_rd(&time);

	month=time.tm_mon;
	day=time.tm_mday;
	year=time.tm_year+1900;
	hour=time.tm_hour;
	minute=time.tm_min;
	second=time.tm_sec;
}

/*
 *		void lock_date()
 *
 *		Lock the date structure so we can update it atomically.
 *
 */

void lock_date()
{
	date_lock=1;
}

/*
 *		void init_dateform()
 *
 *		Initialize the date form using ZServer primatives.
 *
 */

void init_dateform()
{
    auto int var,form;
    static FormVar dateform[7];

    /*
     *	Set up the date form.  To set up your own form you follow a
     *	similar set of steps.  First call sspec_addform.  Then add
     *	the variables creating the variable and adding it to the form.
     *	If you want to restrict the values of the variables you can
     *	use the sspec_setfvlen, sspec_setfvrange, or define a custom
     *	validation function with sspec_fvcheck.
     *
     */
     
    form = sspec_addform("date.html", dateform, 7, SERVER_HTTP);

    // Set the title of the form
    sspec_setformtitle(form, "Set Date");

    var = sspec_addvariable("hour", &hour, INT16, "%02d", SERVER_HTTP);
    var = sspec_addfv(form, var);
    sspec_setfvname(form, var, "Hour");
    sspec_setfvlen(form, var, 2);
    sspec_setfvrange(form, var, 0, 24);

    var = sspec_addvariable("minute", &minute, INT16, "%02d", SERVER_HTTP);
    var = sspec_addfv(form, var);
    sspec_setfvname(form, var, "Minute");
    sspec_setfvlen(form, var, 2);
    sspec_setfvrange(form, var, 0, 60);

    var = sspec_addvariable("second", &second, INT16, "%02d", SERVER_HTTP);
    var = sspec_addfv(form, var);
    sspec_setfvname(form, var, "Second");
    sspec_setfvlen(form, var, 2);
    sspec_setfvrange(form, var, 0, 60);

    var = sspec_addvariable("month", &month, INT16, "%02d", SERVER_HTTP);
    var = sspec_addfv(form, var);
    sspec_setfvname(form, var, "month");
    sspec_setfvlen(form, var, 2);
    sspec_setfvrange(form, var, 0, 12);

    var = sspec_addvariable("day", &day, INT16, "%02d", SERVER_HTTP);
    var = sspec_addfv(form, var);
    sspec_setfvname(form, var, "day");
    sspec_setfvlen(form, var, 2);
    sspec_setfvrange(form, var, 0, 31);

    var = sspec_addvariable("year", &year, INT16, "%04d", SERVER_HTTP);
    var = sspec_addfv(form, var);
    sspec_setfvname(form, var, "year");
    sspec_setfvlen(form, var, 4);
    sspec_setfvrange(form, var, 1990, 2010);

    /*
     *	lock_date sets a flag that disables the automatic updating
     *	of the hour, minute, second, month, day, and year variables.
     *	The prolog gets called after the form is parsed correctly,
     *	but before the variables are updated.  The set_date function
     *	gets called after the variables are updated.  The lock_date
     *	function is necessary because the update_date function in
     *	main could be called between the time the variables are
     *	updated and the prolog completes its processing.
     *
     */

    var = sspec_addfunction("lock_date", lock_date, SERVER_HTTP);
	 sspec_setformprolog(form, var);
    var = sspec_addfunction("set_date", set_date, SERVER_HTTP);
	 sspec_setformepilog(form, var);

}

/*
 *		main()
 *
 *		Initialize the flash, set up the date form and start the web
 *		server.
 *
 */

main()
{
#ifdef FORMAT	
	fs_format(OFFSET,BLOCKS,WEAR);
	fcreate(&logfile,LOGFILE);
#else
	fs_init(OFFSET,BLOCKS);
	fopen_wr(&logfile,LOGFILE);
#endif

	sock_init();
	http_init();
	init_dateform();

	tcp_reserveport(80);			// mark port 80 as a server port.
	
	for(;;) {
		update_date();
		http_handler();
	}
}


