/*******************************************************************************
        console.c
        Z-World, 2000

        This program implements a web server and email client that can
        be operated over a serial link.  Web pages can be uploaded and
        downloaded, email can be sent, and the network configuration
        can be changed via the serial link.  See the chapter in the
        TCP/IP manual on this program for more information.  The "HELP"
        command gives more information about the available commands.
        Note that this program uses serial port C by default for the
        serial console.
*******************************************************************************/

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

/* Define the following to use port A, not C, for the console */
//#define SERIAL_PORT_A
 
/* Use the file system from flash */
#define FS_FLASH
 
/* Standard TCP/IP configuration */
#define MY_IP_ADDRESS	"10.10.6.112"
#define MY_NETMASK		"255.255.255.0"
#define MY_GATEWAY		"10.10.6.1"
#define MY_NAME			"test1"

/* Speed of the serial port */
#define SERIAL_PORT_SPEED	57600
/* Buffer sizes for the serial ports */
#ifdef SERIAL_PORT_A
	#define AINBUFSIZE			1023
	#define AOUTBUFSIZE			255
#else
	#define CINBUFSIZE			1023
	#define COUTBUFSIZE			255
#endif

/* Should input to the serial console be echoed by default? */
#define SERIAL_ECHO			1

/* Number of concurrent HTTP servers */
#define HTTP_MAXSERVERS 2
/* Number of TCP sockets (2 for HTTP + 1 for SMTP) */
#define MAX_TCP_SOCKET_BUFFERS 3
#define HTTP_MAXBUFFER 1024
/* Number of entries in the spec table */
#define SSPEC_MAXSPEC 64

/* Size of the filesystem, in 4k blocks */
#define FS_NUM_BLOCKS	64

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

#define HTTP_NO_FLASHSPEC
#memmap xmem
#use "dcrtcp.lib"
#use "md5.lib"
#use "filesystem.lib"
#use "http.lib"
#use "smtp.lib"

/************************************
 * Structures                       *
 ************************************/
typedef unsigned short int 	uint;
typedef unsigned long  int		ulong;
 
#define MAIL_FROM_SIZE 51
#define MAIL_SERV_SIZE 51
typedef struct {
	char	top; // used to mark the top of the struct 
	char	name[40];
	char	passwd[16];
	ulong	my_ip_addr;
	ulong	sin_mask;
	ulong gate_ip;
	char	echo;
	char  mail_from[MAIL_FROM_SIZE];
	char	mail_serv[MAIL_SERV_SIZE];
} SerIdentification;

/*****************************
 * End structure definitions *
 *****************************/

/***************
 * Global vars *
 ***************/

SerIdentification main_id;

/*******************
 * End global vars *
 *******************/

/* a global hash function */
void ser_backup_id(void);

/* macros for which serial port we are using */
#ifdef SERIAL_PORT_A
	#define SERopen		serAopen
	#define SERwrFlush	serAwrFlush
	#define SERrdFlush	serArdFlush
	#define SERpeek		serApeek
	#define SERputs		serAputs
	#define SERputc		serAputc
	#define SERrdUsed		serArdUsed
	#define SERwrUsed		serAwrUsed
	#define SERread		serAread
	#define SERwrite		serAwrite
#else
	#define SERopen		serCopen
	#define SERwrFlush	serCwrFlush
	#define SERrdFlush	serCrdFlush
	#define SERpeek		serCpeek
	#define SERputs		serCputs
	#define SERputc		serCputc
	#define SERrdUsed		serCrdUsed
	#define SERwrUsed		serCwrUsed
	#define SERread		serCread
	#define SERwrite		serCwrite
#endif

/*
 * Global defines for the serial console
 */
#define SER_INIT 					0
#define SER_READY					1
#define SER_GETREQ 				2
#define SER_PARSECMD 			3
#define SER_ECHO					4
#define SER_PUT					5
#define SER_PUT_READING			6
#define SER_PUT_B1				7
#define SER_PUT_B2				8
#define SER_GET					9
#define SER_GET_WRITING			10
#define SER_GET_B1				11
#define SER_GET_B2				12
#define SER_LIST					13
#define SER_LIST_FILES			14
#define SER_SET					15
#define SER_SET_IP				16
#define SER_SET_NETMASK			17
#define SER_SET_HOSTNAME		18
#define SER_SET_GATEWAY			19
#define SER_SET_MAIL				20
#define SER_SET_MAIL_FROM		21
#define SER_SET_MAIL_SERVER	22
#define SER_MAIL					23
#define SER_MAIL_READING		24
#define SER_MAIL_SENDING		25
#define SER_SHOW					26
#define SER_RESET					32
#define SER_RESET_ANSWER		33
#define SER_RESET_ALL			34
#define SER_HELP					35
#define SER_HELP_MAIL			36
#define SER_DELETE				37

#define SER_MAIN_TIMEOUT		60
#define SER_PUT_TIMEOUT			10

#define CMD_SIZE 101
#define BUFFER_SIZE 1024
#define PASSWORD_SIZE	256

#define SER_ERR_TIMEOUT			1
#define SER_ERR_BADCOMMAND		2
#define SER_ERR_BADPARAMETER	3
#define SER_ERR_NAMETOOLONG	4
#define SER_ERR_FILEEXISTS		5
#define SER_ERR_BADFILESIZE	6
#define SER_ERR_SAVINGFILE		7
#define SER_ERR_READINGFILE	8
#define SER_ERR_FILENOTFOUND	9
#define SER_ERR_MSGTOOLONG		10
#define SER_ERR_SMTPERROR		11
#define SER_ERR_BADPASSPHRASE	12
#define SER_ERR_CANCELRESET	13

typedef struct {
	long http_rambuffer;
	char* bufferend;
	FileNumber filenum;
	File f;

	char command[CMD_SIZE];
	char buffer[BUFFER_SIZE];
	char mailfrom[MAIL_FROM_SIZE];
	char mailserv[MAIL_SERV_SIZE];
	int buffernum;
	char* cmdptr;
	char* bufptr;
	int numparams;

	int spec;
	long num;
	long total;
	int getsawcr;

	char name[SSPEC_MAXNAME];

	char* mail_to;
	char* mail_subject;
	char* mail_body;
	
	int state;
	int nextstate;
	int laststate;
	int substate;
	int sawcr;
	int sawesc;
	int echo;

	long timeout;
} SerialState;

/* The main state structure */
static SerialState sstate;


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

/*
 * Init everything
 */
void dtp_consoleinit(void)
{
	sstate.state = SER_INIT;
	tcp_reserveport(80);
	http_init();	
}

void ser_ok(SerialState* state) {
	SERputs("OK\r\n");
	state->state = SER_READY;
}

void ser_error(SerialState* state, int errnum) {
	auto char errstr[7];

	SERputs("ERROR ");
	sprintf(errstr, "%04d  ", errnum);
	SERputs(errstr);

	if (errnum == SER_ERR_TIMEOUT)
		SERputs("Timed out.");
	else if (errnum == SER_ERR_BADCOMMAND)
		SERputs("Unknown command.");
	else if (errnum == SER_ERR_BADPARAMETER)
		SERputs("Bad or missing parameter.");
	else if (errnum == SER_ERR_NAMETOOLONG)
		SERputs("Filename too long.");
	else if (errnum == SER_ERR_FILEEXISTS)
		SERputs("File exists.");
	else if (errnum == SER_ERR_BADFILESIZE)
		SERputs("Bad file size.");
	else if (errnum == SER_ERR_SAVINGFILE)
		SERputs("Error saving file.");
	else if (errnum == SER_ERR_READINGFILE)
		SERputs("Error reading file.");
	else if (errnum == SER_ERR_FILENOTFOUND)
		SERputs("File not found.");
	else if (errnum == SER_ERR_MSGTOOLONG)
		SERputs("Mail message too long.");
	else if (errnum == SER_ERR_SMTPERROR)
		SERputs("SMTP server error.");
	else if (errnum == SER_ERR_BADPASSPHRASE)
		SERputs("Passphrases do not match!");
	else if (errnum == SER_ERR_CANCELRESET)
		SERputs("Reset cancelled.");
	SERputs("\r\n");		
	state->state = SER_READY;
}

int nullify(char* buffer) {
	auto int i;
	auto int looking;
	auto int num;

	looking = 0;
	num = 0;
	for (i = 0; buffer[i] != '\0'; i++) {
		if ((looking == 0) && (buffer[i] != ' ')) {
			looking = 1;
			num++;
		} else if ((looking == 1) && (buffer[i] == ' ')) {
			buffer[i] = '\0';
			looking = 0;
		}
	}
	return num;
}

char* get_param(char* buffer, int num) {
	auto int numnulls;
	auto int i;

	numnulls = 0;
	i = 0;
	// Skip to the right parameter
	while (numnulls < num) {
		if (buffer[i] == '\0') {
			numnulls++;
		}
		i++;
	}
	// Skip past initial spaces
	while (buffer[i] == ' ') {
		i++;
	}
	return (&buffer[i]);
}

/*
 * The main handler
 */
void ser_consolehandler(void) {
	auto char data;
	auto long len;
	auto int bytes;
	auto char* name;
	auto char* ptr;
	static SerialState* state;
	auto int sspec;
	auto long location;
	auto char s[20];

	#GLOBAL_INIT {
		state = &sstate;
	}

	if (state->state != state->laststate) {
		if ((state->state != SER_PUT_B1) && (state->state != SER_PUT_B2)) {
			// state changed; reset timeout
			state->laststate = state->state;
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
		}
	}

	if (chk_timeout(state->timeout)) {
		if (state->state <= SER_GETREQ) {
			// never mind--we're waiting for a command or initializing
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
		} else {
			// we timed out in one state for too long
			if (state->filenum != 0) {
				fclose(&state->f);
				fdelete(state->filenum);
			}
			ser_error(state, SER_ERR_TIMEOUT);
		}
	}
	
	if ((state->state == SER_GETREQ) || (state->state == SER_READY)) {
		http_handler();
	}
	switch (state->state) {
	case SER_INIT:
		state->echo = main_id.echo;
		state->sawcr = 0;

		strcpy(state->mailfrom, "");

		SERopen(SERIAL_PORT_SPEED);
		SERwrFlush();
		SERrdFlush();

		state->state = SER_READY;
		break;

	case SER_READY:
		state->command[0] = '\0';
		state->cmdptr = state->command;
		state->numparams = 0;
		state->sawesc = 0;
		state->state = SER_GETREQ;
		state->filenum = 0;
		break;

	case SER_GETREQ:
		if (SERrdUsed() > 0) {
			SERread(&data, 1, 0);
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
		} else {
			break;
		}

		if (state->sawesc > 0) {
			// Skip past escaped characters (starting with 1B, such as cursor
			// movement keys)
			state->sawesc--;
			break;
		} else if ((data == '\r') || ((data == '\n') && (state->sawcr == 0))) {
			if (data == '\r') {
				state->sawcr = 1;
			}
			if (state->echo) {
				SERputs("\r\n");
			}
			if ((state->cmdptr - state->command) < CMD_SIZE) {
				*(state->cmdptr) = '\0';
			} else {
				ser_error(state, SER_ERR_BADCOMMAND);
				break;
			}

			// find the command
			state->state = SER_PARSECMD;
			break;
		} else if ((data == '\n') && (state->sawcr == 1)) {
			state->sawcr = 0;
			break;
		} else if (state->sawcr == 1) {
			state->sawcr = 0;
		}
		// Handle the character we just got
		if (data == 0x08) {
			// Backspace character
			if (state->cmdptr > state->command) {
				state->cmdptr--;
				if (state->echo) {
					//SERwrite(&data, 1);
					SERputs("\x08 \x08");
				}
			}
		} else if (data == 0x1B) {
			// Escape character--skip past the next two characters
			state->sawesc = 2;
		} else if (data == 0x15) {
			// CTRL-U -- linefeed and clear the command buffer
			if (state->echo) {
				SERputs("\r\n");
			}
			state->cmdptr = state->command;
			state->command[0] = '\0';
		} else if (data >= 0x20) {
			if ((state->cmdptr - state->command) < CMD_SIZE) {
				*(state->cmdptr) = data;
			}
			state->cmdptr++;
			if (state->echo) {
				SERwrite(&data, 1);
			}
		}
		break;

	case SER_PARSECMD:
		state->numparams = nullify(state->command);
		if (state->numparams == 0) {
			state->state = SER_READY;
		} else if (strcmpi(get_param(state->command, 0), "echo") == 0) {
			state->state = SER_ECHO;
		} else if (strcmpi(get_param(state->command, 0), "put") == 0) {
			state->state = SER_PUT;
		} else if (strcmpi(get_param(state->command, 0), "get") == 0) {
			state->state = SER_GET;
		} else if (strcmpi(get_param(state->command, 0), "list") == 0) {
			state->state = SER_LIST;
		} else if (strcmpi(get_param(state->command, 0), "set") == 0) {
			state->state = SER_SET;
		} else if (strcmpi(get_param(state->command, 0), "mail") == 0) {
			state->state = SER_MAIL;
		} else if (strcmpi(get_param(state->command, 0), "show") == 0) {
			state->state = SER_SHOW;
		} else if (strcmpi(get_param(state->command, 0), "reset") == 0) {
			state->state = SER_RESET;
		} else if (strcmpi(get_param(state->command, 0), "help") == 0) {
			state->state = SER_HELP;
		} else if (strcmpi(get_param(state->command, 0), "delete") == 0) {
			state->state = SER_DELETE;
		} else {
			ser_error(state, SER_ERR_BADCOMMAND);
		}
		break;

	case SER_ECHO:
		if ((state->numparams == 2) &&
		    (strcmpi(get_param(state->command, 1), "on") == 0)) {
			ser_ok(state);
			state->echo = 1;
			ser_backup_id();
		} else if ((state->numparams == 2) &&
		           (strcmpi(get_param(state->command, 1), "off") == 0)) {
			ser_ok(state);
			state->echo = 0;
			ser_backup_id();
		} else {
			ser_error(state, SER_ERR_BADPARAMETER);
		}
		state->state = SER_READY;
		break;

	case SER_PUT:
		if ((state->numparams == 2) || (state->numparams == 3)) {
			if (strlen(get_param(state->command, 1)) < SSPEC_MAXNAME) {
				strcpy(state->name, get_param(state->command, 1));
			} else {
				// Not enough room to put the page name
				ser_error(state, SER_ERR_NAMETOOLONG);
				break;
			}
			if (sspec_findname(state->name, SERVER_HTTP) != -1) {
				ser_error(state, SER_ERR_FILEEXISTS);
				break;
			}
			state->bufferend = state->buffer;
			if (state->numparams == 2) {
				if ((state->filenum = fcreate_unused(&state->f)) == 0) {
					ser_error(state, SER_ERR_SAVINGFILE);
					break;
				}
				state->state = SER_PUT_READING;
			} else {
				state->num = strtol(get_param(state->command, 2), &ptr, 10);
				if ((*ptr == '\0') && (state->num < LONG_MAX) && (state->num > LONG_MIN)) {
					if ((state->filenum = fcreate_unused(&state->f)) == 0) {
						ser_error(state, SER_ERR_SAVINGFILE);
						break;
					}
					state->total = 0;
					state->timeout = set_timeout(SER_PUT_TIMEOUT);
					state->state = SER_PUT_B1;
				} else {
					ser_error(state, SER_ERR_BADFILESIZE);
				}
			}
		} else {
			ser_error(state, SER_ERR_BADPARAMETER);
		}
		break;

	case SER_PUT_READING:
		if (SERrdUsed() > 0) {
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
			SERread(&data, 1, 0);
		} else {
			break;
		}
		if ((data == 0x04) || (data == 0x1A)) {
			// Done getting the page (CTRL-D or CTRL-Z)
			if ((state->bufferend - state->buffer) > 0) {
				if (fwrite(&state->f, state->buffer, state->bufferend - state->buffer) < (state->bufferend - state->buffer)) {
					fclose(&state->f);
					fdelete(state->filenum);
					ser_error(state, SER_ERR_SAVINGFILE);
					break;
				}
			}
			fclose(&state->f);
			if (sspec_addfsfile(state->name, state->filenum, SERVER_HTTP) >= 0) {
				state->filenum = 0;
				ser_backup_id();
				ser_ok(state);
				break;
			} else {
				fdelete(state->filenum);
				state->filenum = 0;
				ser_error(state, SER_ERR_SAVINGFILE);
				break;
			}
		} else if (data == 0x03) {
			// Bail out (CTRL-C)
			fclose(&state->f);
			fdelete(state->filenum);
			state->filenum = 0;
			ser_ok(state);
			break;
		} else if ((data == '\r') || ((data == '\n') && (state->sawcr == 0))) {
			*(state->bufferend) = data;
			state->bufferend++;
			if (data == '\r') {
				state->sawcr = 1;
			}
			if (state->echo) {
				SERputs("\r\n");
			}
		} else if ((data == '\n') && (state->sawcr == 1)) {
			*(state->bufferend) = data;
			state->bufferend++;
			state->sawcr = 0;
		} else {
			*(state->bufferend) = data;
			state->bufferend++;
			if (state->echo) {
				SERwrite(&data, 1);
			}
		}
		if ((state->bufferend - state->buffer) == BUFFER_SIZE) {
			if (fwrite(&state->f, state->buffer, BUFFER_SIZE) < BUFFER_SIZE) {
				fclose(&state->f);
				fdelete(state->filenum);
				ser_error(state, SER_ERR_SAVINGFILE);
				break;
			}
		}
		break;

	case SER_PUT_B1:
		if (SERrdUsed() > 0) {
			state->timeout = set_timeout(SER_PUT_TIMEOUT);
			if ((state->sawcr == 1) && (SERpeek() == '\n')) {
				SERread(&data, 1, 0);
			}
			state->state = SER_PUT_B2;
		}
		break;

	case SER_PUT_B2:
		if (SERrdUsed() > 0) {
			state->timeout = set_timeout(SER_PUT_TIMEOUT);
			len = state->bufferend - state->buffer;
			state->buffernum = SERread(state->bufferend, (((BUFFER_SIZE - (int)len) < 256)?(BUFFER_SIZE - (int)len):256), 0);
			if ((len + state->buffernum) == BUFFER_SIZE) {
				if (fwrite(&state->f, state->buffer, BUFFER_SIZE) < BUFFER_SIZE) {
					fclose(&state->f);
					fdelete(state->filenum);
					ser_error(state, SER_ERR_SAVINGFILE);
					break;
				}
				state->bufferend = state->buffer;
			} else {
				state->bufferend += state->buffernum;
			}
			state->total += state->buffernum;
			
			if (state->total >= state->num) {
				if (state->bufferend > state->buffer) {
					if (fwrite(&state->f, state->buffer, state->bufferend - state->buffer) != (state->bufferend - state->buffer)) {
						fclose(&state->f);
						fdelete(state->filenum);
						ser_error(state, SER_ERR_SAVINGFILE);
						break;
					}
				}
				fclose(&state->f);
				if (sspec_addfsfile(state->name, state->filenum, SERVER_HTTP) >= 0) {
					state->filenum = 0;
					ser_backup_id();
					ser_ok(state);
					break;
				} else {
					fdelete(state->filenum);
					state->filenum = 0;
					ser_error(state, SER_ERR_SAVINGFILE);
					break;
				}
			}
		}
		break;

	case SER_GET:
		if ((state->numparams == 2) || (state->numparams == 3)) {
			if (strlen(get_param(state->command, 1)) < SSPEC_MAXNAME) {
				strcpy(state->name, get_param(state->command, 1));
			} else {
				// Not enough room to put the page name
				ser_error(state, SER_ERR_NAMETOOLONG);
				break;
			}
			if (((state->spec = sspec_findname(state->name, SERVER_HTTP)) == -1) ||
			    (sspec_gettype(state->spec) != SSPEC_FILE)) {
				// Didn't find the file
				ser_error(state, SER_ERR_READINGFILE);
				break;
			}
			state->num = 0;
			state->getsawcr = 0;
			if (state->numparams == 2) {
				state->state = SER_GET_WRITING;
			} else {
				state->num = strtol(get_param(state->command, 2), &ptr, 10);
				if ((*ptr == '\0') && (state->num < LONG_MAX) && (state->num > LONG_MIN)) {
					state->total = 0;
					sprintf(state->buffer, "%ld\r\n", sspec_getlength(state->spec));
					SERputs("LENGTH ");
					SERputs(state->buffer);
					state->state = SER_GET_B1;
				} else {
					ser_error(state, SER_ERR_BADFILESIZE);
				}
			}
		} else {
			ser_error(state, SER_ERR_BADPARAMETER);
		}
		break;

	case SER_GET_WRITING:
		if (SERwrUsed() == 0) {
			if (!state->echo) {
				bytes = sspec_readfile(state->spec, state->buffer, state->num, 200);
				if (bytes <= 0) {
					ser_error(state, SER_ERR_READINGFILE);
					break;
				}
				state->timeout = set_timeout(SER_MAIN_TIMEOUT);
				bytes = SERwrite(state->buffer, bytes);
				state->num += bytes;
				if (state->num >= sspec_getlength(state->spec)) {
					ser_ok(state);
				}
			} else {
				if (sspec_readfile(state->spec, &data, state->num, 1) == 1) {
					if ((data == '\r') || ((data == '\n') && (state->getsawcr == 0))) {
						SERputs("\r\n");
						if (data == '\r') {
							state->getsawcr = 1;
						}
					} else if ((data == '\n') && (state->getsawcr == 1)) {
						state->getsawcr = 0;
					} else {
						SERwrite(&data, 1);
					}
					state->num++;
				} else {
					ser_error(state, SER_ERR_READINGFILE);
					break;
				}
				if (state->num == sspec_getlength(state->spec)) {
					ser_ok(state);
				}
			}
		}
		break;

	case SER_GET_B1:
		if (SERwrUsed() == 0) {
			if ((state->sawcr == 1) && (SERpeek() == '\n')) {
				SERread(&data, 1, 0);
			}
			state->num = strtol(get_param(state->command, 2), &ptr, 10);
			if ((*ptr == '\0') && (state->num < LONG_MAX) && (state->num > LONG_MIN)) {
				state->total = 0;
				state->state = SER_GET_B2;
			} else {
				ser_error(state, SER_ERR_BADFILESIZE);
			}
		}
		break;

	case SER_GET_B2:
		if (SERwrUsed() == 0) {
			bytes = sspec_readfile(state->spec, state->buffer, state->total, 200);
			if (bytes <= 0) {
				ser_error(state, SER_ERR_READINGFILE);
				break;
			}
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
			if ((state->total + bytes) <= state->num) {
				SERwrite(state->buffer, bytes);
			} else if (state->total < state->num) {
				SERwrite(state->buffer, (int)(state->num - state->total));
			} else {
				ser_ok(state);
				break;
			}
			state->total += bytes;
			if (state->total >= sspec_getlength(state->spec)) {
				ser_ok(state);
			}
		}
		break;

	case SER_LIST:
		if (state->numparams != 2) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		if (strcmpi(get_param(state->command, 1), "files") == 0) {
			state->num = 0;
			state->state = SER_LIST_FILES;
		} else {
			ser_error(state, SER_ERR_BADPARAMETER);
		}
		break;

	case SER_LIST_FILES:
		if (SERwrUsed() == 0) {
			if ((state->num = sspec_findnextfile((int)state->num, SERVER_HTTP)) != -1) {
				name = sspec_getname((int)state->num);
				if (name != NULL) {
					sprintf(s, "%8d  ", sspec_getlength((int)state->num));
					SERputs(s);
					SERputs(name);
					SERputs("\r\n");
					state->timeout = set_timeout(SER_MAIN_TIMEOUT);
				}
				state->num++;
			} else {
				ser_ok(state);
			}
		}
		break;

	case SER_SET:
		if (state->numparams <= 1) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		if (strcmpi(get_param(state->command, 1), "ip") == 0) {
			state->state = SER_SET_IP;
		} else if (strcmpi(get_param(state->command, 1), "netmask") == 0) {
			state->state = SER_SET_NETMASK;
		} else if (strcmpi(get_param(state->command, 1), "hostname") == 0) {
			state->state = SER_SET_HOSTNAME;
		} else if (strcmpi(get_param(state->command, 1), "gateway") == 0) {
			state->state = SER_SET_GATEWAY;
		} else if (strcmpi(get_param(state->command, 1), "mail") == 0) {
			state->state = SER_SET_MAIL;
		} else {
			ser_error(state, SER_ERR_BADCOMMAND);
		}
		break;

	case SER_SET_IP:
		if (state->numparams != 3) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		tcp_config("MY_IP_ADDRESS",get_param(state->command,2));
		SERputs("MY_IP_ADDRESS set to: ");
		SERputs(inet_ntoa(state->buffer,my_ip_addr));
		SERputs("\r\n");
		ser_backup_id();
		ser_ok(state);
		break;

	case SER_SET_NETMASK:
		if (state->numparams != 3) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		tcp_config("MY_NETMASK",get_param(state->command,2));
		SERputs("MY_NETMASK set to: ");
		SERputs(inet_ntoa(state->buffer,sin_mask));
		SERputs("\r\n");
		ser_backup_id();
		ser_ok(state);
		break;

	case SER_SET_HOSTNAME:
		if (state->numparams != 3) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		strcpy(main_id.name,get_param(state->command,2));
		SERputs("HOSTNAME set to: ");
		SERputs(main_id.name);
		SERputs("\r\n");
		ser_backup_id();
		ser_ok(state);
		break;

	case SER_SET_GATEWAY:
		if (state->numparams != 3) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		tcp_config("MY_GATEWAY",get_param(state->command,2));
		SERputs("MY_GATEWAY set to: ");
		SERputs(inet_ntoa(state->buffer,_arp_gate_data[0].gate_ip));
		SERputs("\r\n");
		ser_backup_id();
		ser_ok(state);
		break;

	case SER_SET_MAIL:
		if (state->numparams != 4) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		if (strcmpi(get_param(state->command, 2), "from") == 0) {
			state->state = SER_SET_MAIL_FROM;
		} else if (strcmpi(get_param(state->command, 2), "server") == 0) {
			state->state = SER_SET_MAIL_SERVER;
		} else {
			ser_error(state, SER_ERR_BADCOMMAND);
		}
		break;

	case SER_SET_MAIL_FROM:
		if (strlen(get_param(state->command, 3)) < MAIL_FROM_SIZE) {
			strcpy(state->mailfrom, get_param(state->command, 3));
			strcpy(main_id.mail_from, get_param(state->command, 3));
			ser_backup_id();
			ser_ok(state);
		} else {
			ser_error(state, SER_ERR_BADPARAMETER);
		}
		break;

	case SER_SET_MAIL_SERVER:
		if (strlen(get_param(state->command, 3)) < MAIL_SERV_SIZE) {
			if (smtp_setserver(get_param(state->command, 3)) == SMTP_OK) {
				strcpy(main_id.mail_serv, get_param(state->command, 3));
				ser_backup_id();
				ser_ok(state);
			} else {
				ser_error(state, SER_ERR_BADPARAMETER);
			}
		} else {
			ser_error(state, SER_ERR_BADPARAMETER);
		}
		break;

	case SER_MAIL:
		if (state->numparams != 2) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		state->bufptr = state->buffer;
		state->state = SER_MAIL_READING;
		break;

	case SER_MAIL_READING:
		if (SERrdUsed() > 0) {
			SERread(&data, 1, 0);
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
		} else {
			break;
		}
		if ((data == 0x04) || (data == 0x1A)) {
			if ((state->bufptr - state->buffer) >= (BUFFER_SIZE - 1)) {
				ser_error(state, SER_ERR_MSGTOOLONG);
				break;
			}
			*state->bufptr = '\0';
			if ((ptr = memchr(state->buffer, '\r', BUFFER_SIZE)) != NULL) {
				*ptr = '\0';
				if (((ptr + 1 - state->buffer) < BUFFER_SIZE) && (*(ptr+1) == '\n')) {
					state->mail_body = ptr + 2;
				} else {
					state->mail_body = ptr + 1;
				}
			} else if ((ptr = memchr(state->buffer, '\n', BUFFER_SIZE)) != NULL) {
				*ptr = '\0';
				state->mail_body = ptr + 1;
			} else {
				// No newline, so only subject, with no body
				state->mail_body = state->bufptr;
			}
			smtp_sendmail(get_param(state->command, 1), state->mailfrom,
			              state->buffer, state->mail_body);
			state->state = SER_MAIL_SENDING;
			break;
		} else if (data == 0x03) {
			// Bail out (CTRL-C)
			ser_ok(state);
			break;
		}
		if ((data == '\r') || ((data == '\n') && (state->sawcr == 0))) {
			if (data == '\r') {
				state->sawcr = 1;
				// Put \r\n into the message
				if ((state->bufptr - state->buffer) < BUFFER_SIZE) {
					*state->bufptr = data;
				}
				state->bufptr++;
				data = '\n';
			}
			if (state->echo) {
				SERputs("\r\n");
			}
		} else if ((data == '\n') && (state->sawcr == 1)) {
			state->sawcr = 0;
		} else {
			if (state->echo) {
				SERwrite(&data, 1);
			}
		}
		if ((state->bufptr - state->buffer) < BUFFER_SIZE) {
			*state->bufptr = data;
		}
		state->bufptr++;
		break;

	case SER_MAIL_SENDING:
		if (smtp_mailtick() == SMTP_PENDING) {
			break;
		} else if (smtp_status() == SMTP_SUCCESS) {
			ser_ok(state);
		} else {
			ser_error(state, SER_ERR_SMTPERROR);
		}
		break;

	case SER_SHOW:
		SERputs("Current configuration:");
		SERputs("\r\n\tMY_IP_ADDRESS:\t");
		SERputs(inet_ntoa(state->buffer,my_ip_addr));
		SERputs("\r\n\tMY_NETMASK:\t");
		SERputs(inet_ntoa(state->buffer,sin_mask));
		SERputs("\r\n\tMY_GATEWAY:\t");
		SERputs(inet_ntoa(state->buffer,_arp_gate_data[0].gate_ip));
		SERputs("\r\n\tHOSTNAME:\t");
		SERputs(main_id.name);
		SERputs("\r\n\tMAIL_FROM:\t");
		SERputs(main_id.mail_from);
		SERputs("\r\n\tMAIL_SERV:\t");
		SERputs(main_id.mail_serv);
		SERputs("\r\n");
		ser_ok(state);
		break;

	case SER_RESET:
		if (state->numparams != 1) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		SERputs("Are you sure you want to reset to factory defaults? (y/n): ");

		state->bufptr = state->buffer;
		state->state = SER_RESET_ANSWER;
		break;

	case SER_RESET_ANSWER:
		if (SERrdUsed() > 0) {
			SERread(&data, 1, 0);
			state->timeout = set_timeout(SER_MAIN_TIMEOUT);
		} else {
			break;
		}
		if(state->sawcr == 1) {
			state->sawcr = 0;

			if ('\n' == data) {
				/* nothing - drop it */
				data = '\0';
			} else {
				/* character is ok */
			}
		}
		if('\0' == data) {
			/* empty char - drop it */
			break;
		} else if(('\r' == data) || ('\n' == data)) {
			/* end of line reached */
			if('\r' == data) {
				state->sawcr = 1;
			}
			if((state->bufptr - state->buffer) < BUFFER_SIZE) {
				*state->bufptr = '\0';
			}
			state->buffer[BUFFER_SIZE - 1] = '\0'; /* make sure we end in null */

			state->state = SER_RESET_ALL;
		} else {
			/* normal character - store it */
			if ((state->bufptr - state->buffer) < BUFFER_SIZE) {
				SERputc(data);
				*state->bufptr = data;
			}
			state->bufptr++;
		}
		break;

	case SER_RESET_ALL:
		SERputs("\r\n");
		if(1 != strlen(state->buffer)) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		if(('y' != state->buffer[0]) && ('Y' != state->buffer[0])) {
			ser_error(state, SER_ERR_CANCELRESET);
			break;
		}
		SERputs("reset!\r\n");

		/* reset it all! */
		strcpy(main_id.name,MY_NAME);
		main_id.echo = SERIAL_ECHO;
		strcpy(main_id.mail_from, "nobody@nowhere.com");
		strcpy(main_id.mail_serv, "10.10.6.1");
		smtp_setserver(main_id.mail_serv);

		sspec_init();
		fs_format(0, FS_NUM_BLOCKS, 1);

		/* backup the reset data to flash */		
		ser_backup_id();
		
		ser_ok(state);
		break;

	case SER_HELP:
		if (state->numparams <= 1) {
			SERputs("\r\nAvaliable commands: (type \"help <command>\" for more info)");
			SERputs("\r\necho\t- Turn on or off echoing of characters.");
			SERputs("\r\nput\t- Store a file for the web server.");
			SERputs("\r\nget\t- Get a file from the web server.");
			SERputs("\r\ndelete\t- Delete a file on the web server.");
			SERputs("\r\nlist\t- List files on the web server.");
			SERputs("\r\nset\t- Configure various options.");
			SERputs("\r\nmail\t- Send an e-mail.");
			SERputs("\r\nshow\t- Show current configuration.");
			SERputs("\r\nreset\t- Reset everything to factory defaults.");
			SERputs("\r\nhelp\t- This help screen.");
		} else if (strcmpi(get_param(state->command, 1), "echo") == 0) {
			SERputs("\r\necho:");
			SERputs("\r\n   \"echo off\" will stop the input characters");
			SERputs("\r\nfrom being echoed back.");
		} else if (strcmpi(get_param(state->command, 1), "put") == 0) {
			SERputs("\r\nput:");
			SERputs("\r\n   ASCII mode:");
			SERputs("\r\n      usage: \"put <filename>\"");
			SERputs("\r\n   The file is then sent, terminating with a ^D");
			SERputs("\r\n   or ^Z (0x04 or 0x1A).");
			SERputs("\r\n");
			SERputs("\r\n   BINARY mode:");
			SERputs("\r\n      usage: \"put <filename> <size in bytes>\"");
			SERputs("\r\n   The file is then sent, and must be exactly the");
			SERputs("\r\n   specified number of bytes in length.");
		} else if (strcmpi(get_param(state->command, 1), "get") == 0) {
			SERputs("\r\nget:");
			SERputs("\r\n   ASCII mode:");
			SERputs("\r\n      usage: \"get <filename>\"");
			SERputs("\r\n   The file is then sent, followed by the usual");
			SERputs("\r\n   OK message.");
			SERputs("\r\n");
			SERputs("\r\n   BINARY mode:");
			SERputs("\r\n      usage: \"get <filename> <size in bytes>\"");
			SERputs("\r\n   The message \"LENGTH <len>\" will be sent, indicating");
			SERputs("\r\n   length of the file to be sent, and then that number of");
			SERputs("\r\n   bytes will be send.");
		} else if (strcmpi(get_param(state->command, 1), "delete") == 0) {
			SERputs("\r\ndelete:");
			SERputs("\r\n   Usage: \"delete <filename>\"");
			SERputs("\r\nThe given file is deleted from the web server.");
		} else if (strcmpi(get_param(state->command, 1), "list") == 0) {
			SERputs("\r\nlist:");
			SERputs("\r\n   List all interally stored files for the web server.");
		} else if (strcmpi(get_param(state->command, 1), "set") == 0) {
			if (state->numparams > 2)
				if(strcmpi(get_param(state->command, 2), "mail") == 0) {
					state->state = SER_HELP_MAIL;
					break;
				}
			SERputs("\r\nset:");
			SERputs("\r\n   Usage: \"set <variable> <value>\"");
			SERputs("\r\nThe following variables can be set with this command:");
			SERputs("\r\nip\t\t- The IP address of the device (eg: w.x.y.z)");
			SERputs("\r\nnetmask\t\t- The netmask for the device (eg: 255.255.255.0)");
			SERputs("\r\ngateway\t\t- The gateway for the device (eg: w.x.y.z)");
			SERputs("\r\nhostname\t- The hostname of the device.");
			SERputs("\r\nmail\t\t- see \"help set mail\" for all options");
		} else if (strcmpi(get_param(state->command, 1), "mail") == 0) {
			SERputs("\r\nmail:");
			SERputs("\r\n   usage: \"mail <destination@where.com>\"");
			SERputs("\r\nThe first line of the message will be used as the");
			SERputs("\r\nsubject, and the other lines are the body. The body");
			SERputs("\r\nis terminated with a ^D or ^Z. (0x04 or 0x1A)");
		} else if (strcmpi(get_param(state->command, 1), "show") == 0) {
			SERputs("\r\nshow:");
			SERputs("\r\n   All configurable variables will be shown.");
		} else if (strcmpi(get_param(state->command, 1), "reset") == 0) {
			SERputs("\r\nreset:");
			SERputs("\r\n   This will reset the internal configuration to the");
			SERputs("\r\nfactory defaults. All settings, web pages, or other");
			SERputs("\r\ndata will be lost!");
		} else if (strcmpi(get_param(state->command, 1), "help") == 0) {
			SERputs("\r\nhelp:");
			SERputs("\r\n   Usage: \"help <command>\"");
			SERputs("\r\n   Used to find more information on various commands.");
		} else {
//			SERputs("\r\nUnknown command!\r\n");
			ser_error(state, SER_ERR_BADCOMMAND);
			break;
		}
		SERputs("\r\n");
		ser_ok(state);
		break;

	case SER_HELP_MAIL:
		if (state->numparams <= 3) {
			SERputs("\r\nset mail:");
			SERputs("\r\n   Configure options to the mail client.");
			SERputs("\r\nThe following options are avaliable:");
			SERputs("\r\nfrom\t- Set the from address.");
			SERputs("\r\nserver\t- Set the SMTP server to use.");
		} else if (strcmpi(get_param(state->command, 3), "from") == 0) {
			SERputs("\r\nset mail: from:");
			SERputs("\r\n   Set the from, or return address. This address");
			SERputs("\r\nwill be stamped on the outgoing e-mail, and should");
			SERputs("\r\nbe valid, incase the e-mail needs to be returned.");
		} else if (strcmpi(get_param(state->command, 3), "server") == 0) {
			SERputs("\r\nset mail: server:");
			SERputs("\r\n   The SMTP server to use. This should be an IP address.");
		} else {
			SERputs("\r\nUnknown command!\r\n");
			ser_error(state, SER_ERR_BADCOMMAND);
			break;
		}
		SERputs("\r\n");
		ser_ok(state);
		break;

	case SER_DELETE:
		if (state->numparams != 2) {
			ser_error(state, SER_ERR_BADPARAMETER);
			break;
		}
		sspec = sspec_findname(get_param(state->command, 1), SERVER_HTTP);
		if (sspec >= 0) {
			if (sspec_getfiletype(sspec) == SSPEC_FSFILE) {
				location = sspec_getfileloc(sspec);
				if (location == -1) {
					ser_error(state, SER_ERR_FILENOTFOUND);
					break;
				}
				fdelete((FileNumber)location);
				sspec_remove(sspec);
			} else {
				// Not a flash file
				ser_error(state, SER_ERR_FILENOTFOUND);
				break;
			}
		} else {
			// File not found
			ser_error(state, SER_ERR_FILENOTFOUND);
			break;
		}
		ser_ok(state);
		break;
		
	default:
		/* ERROR! */
		state->state = SER_INIT;
		break;
	}
}

/*
 * This should be called to backup our identification + IP into flash
 */
void ser_backup_id(void)
{
	auto int offset, len, retval;
	auto unsigned long temp;
	auto File f, *F;
	F = &f;
	
	main_id.top = 0xa5;
	main_id.my_ip_addr = my_ip_addr;
	main_id.sin_mask = sin_mask;
	main_id.gate_ip = _arp_gate_data[0].gate_ip;

	if (sspec_save() == -1) {
		printf("ERROR: No room to backup config table in flash FS!\n");
	}
}

void load_tables(void)
{
	if (sspec_restore() == -1) {
		fs_format(0, FS_NUM_BLOCKS, 1);
	} else {
		my_ip_addr = main_id.my_ip_addr;
		sin_mask = main_id.sin_mask;
		_arp_gate_data[0].gate_ip = main_id.gate_ip;
		smtp_setserver(main_id.mail_serv);
	}
}

main()
{
	/* init everything to basic values */
	strcpy(main_id.name,MY_NAME);
	main_id.echo = SERIAL_ECHO;
	strcpy(main_id.mail_from, "nobody@nowhere.com");
	strcpy(main_id.mail_serv, "10.10.6.1");
	smtp_setserver(main_id.mail_serv);

	/* init everything */
	sock_init();
	dtp_consoleinit();

	/* do we have a filesystem in flash? if so, try to mount it */
	if(fs_init(0, FS_NUM_BLOCKS)) {
		/* problem initializing the filesystem - format it? */
	}

	sspec_setsavedata((char*)&main_id, sizeof(main_id), NULL);

	/* check for duplicate files for our ID stuff, and load the tables */
	load_tables();
	
	/* run everything in a big loop */
	for(;;) {
		ser_consolehandler();
		tcp_tick(NULL);
	}
}
