#include <sqlext.h>
#include <netdb.h>
#include <utime.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include "smtp.h"

#include <string.h>

#include "cgi.h"
#include <regex.h>

#include "safestring.h"
#include "cfg.h"
#include "clog.h"

#include "util.h"
#include "timer.h"

extern char SENDMAIL[];

int GetParam(char * params, char _name[], char value[], int valuelen, char ** end /*= NULL*/)
					// *end is the first character after the end of the value
{
//	logfmt( FLOG_MAX, "::GetParam( params='%s', name='%s', sz=%d )", params, _name, valuelen );

	char *c, *cbeg, *cend, *vc;
	unsigned int	ch, len;
	char name[50];
	value[0] = 0;

	snprintf( name, sizeof(name), "%s=", _name );

//	logfmt( FLOG_MAX, "name=%s, params=%s", name, params );


	if ( strncasecmp( name, params, strlen(name) ) == 0 )
	{
		cbeg = params + strlen( name );
		cend = params + strcspn( params, "&;" );
//		logfmt( FLOG_MAX, "cbeg=%s, len=%d", cbeg, cend-cbeg );
	}
	else
	{
		snprintf( name, sizeof(name), "&%s=", _name );
		c = strcasestr( params, name );
		if (!c )
		{
			snprintf( name, sizeof(name), " %s=", _name );
			c = strcasestr( params, name );
		}
		if (!c )
		{
			snprintf( name, sizeof(name), "\n%s=", _name );
			c = strcasestr( params, name );
		}
		if (!c)
			return 0;

		cbeg = c + strlen( name );
		c++;
		cend = c + strcspn( c, "&;" );
//logfmt( FLOG_MAX, "" );
	}

	// Ready to deal with '%' and '+'	-- urldecode

	c = cbeg; vc = value;

	char save = *cend;
	*cend = 0;
	urldecode( cbeg, value, valuelen );
	*cend = save;


    // Strip trailing spaces
//logfmt( FLOG_MAX, "" );

	while ( strlen(value)>0 && value[ strlen(value)-1 ] == ' ' )
		value[ strlen(value)-1 ] = 0;

	if (end)
		*end = cend;

	return 1;
}



bool initcgilib(int log = 0)
{
	return true;
}

char * strlower( const char * str, char * res )
{
	int len = strlen(str);

	for ( int i=0; i<=len; i++ )
	{
		res[i] = tolower( str[i] );
	}

	return res;
}



/* Class definitions */


C_CGI_Form::C_CGI_Form( char * _dir, char * _template_file, char * _global_template/*=NULL*/, int _argc/*=0*/, char **_argv/* = NULL*/ )

	{

		//logfmt( FLOG_MAX, "C_CGI_Form constructor" );

		sequence = SEQ_NONE;

		dir = strdup( _dir );

		if (_global_template)
			global_template = strdup(_global_template);
		else
			global_template = NULL;

		if ( getenv("SCRIPT_NAME") )
		{
			argc = _argc; argv = _argv;
			char pars[16096]; pars[sizeof(pars)-1] = 0;

			if ( !fgets( pars, sizeof(pars), stdin ) )
			{
				method = METHOD_GET;

				char * qs = getenv("QUERY_STRING");
				if ( qs && (strlen(qs)>0) )
					params = strdup( qs );
				else
				{
					params = strdup( "" );
		//			LOG( "No parameters", LOG_FATAL );
				}
			}
			else	// method POST
			{
				method = METHOD_POST;

				//log.logfmt( FLOG_MAX, "strlen(pars)=%d, pars=%s", strlen(pars), pars );
				params = strdup(pars);
				if (!params)
				{
					logfmt( FLOG_MAX, "strdup failed" );
					params = strdup("");
				}
			}

                       // log.logfmt( FLOG_MAX, pars );

// Uncomment below is you want to see what is being passed from the browser

//			if ( params[0] ) //LOG( params, LOG_COMMAND );
//				log.logfmt( FLOG_MAX, "%s", params );
		}
		else
		{
			method = METHOD_NONE;
			params = strdup( "" );
		}

		template_file = strdup(_template_file);
		nlists = 0;


#ifdef WITH_LANGUAGE

	language[0] = 0;

	char tmp_lang[10];

	GetParam( "lang", tmp_lang, sizeof(tmp_lang) );

	if ( reg_match( tmp_lang, "[[:alpha:]_-]+" ) )
	{
		logfmt( FLOG_MAX, "Language given as a parameter: %s", tmp_lang );

		char expires[50] = "", domain[50] = "chiefsoft.com";

		// determine the domain name

		char * server_name = getenv( "SERVER_NAME" );
		if ( server_name )
		{
			strsafecpy( domain, server_name, sizeof(domain) );
			tolower( domain );
			if ( strncasecmp( domain, "www.", 4 )==0 )
				memmove( domain, &domain[4], strlen(domain)-4+1 );
		}
		else
			logfmt( logWARNING, "Environment variable SERVER_NAME not found" );


        int cookie_duration = 365*24*60;        // default is now 24 hours x 365 = 1 year
		time_t now = time( NULL ); now += cookie_duration*60;   // cookie_duration is in minutes
		strftime( expires, sizeof(expires), "; expires=%a, %d-%b-%Y %X GMT", gmtime( &now ) );

		logfmt( FLOG_MAX, "DEBUG" );

        send_header( "Set-Cookie: lang=%s; path=/; domain=%s%s", tmp_lang, domain, expires );
		logfmt( FLOG_MAX, "DEBUG" );
        logfmt( FLOG_MAX, "Set-Cookie: lang=%s; path=/; domain=%s%s", tmp_lang, domain, expires );
		logfmt( FLOG_MAX, "DEBUG" );

        strsafecpy( language, tmp_lang, sizeof( language ) );
		logfmt( FLOG_MAX, "DEBUG" );
	}
	else
	{
		char * cookie = getenv( "HTTP_COOKIE" );
		if ( cookie )
		{
			::GetParam( cookie, "lang", tmp_lang, sizeof( tmp_lang ) );

			if ( reg_match( tmp_lang, "[[:alpha:]_-]+" ) )
			{
				logfmt( FLOG_MAX, "Language found in a cookie: %s", tmp_lang );
		        strsafecpy( language, tmp_lang, sizeof( language ) );
			}
		}

	}
#endif
};



C_CGI_Form::~C_CGI_Form()
{
	for ( int i=0; i<nlists; i++ )
		if ( lists[i] ) delete lists[i]; // maybe zero out lists[i].form?

	free( dir );
	free( template_file );
	free( params );
	if ( global_template ) free( global_template );

}


void 	CatFile( char * fname, FILE * fout )
{
	FILE * f = fopen( fname, "r" );

	if (!f)
	{
		logfmt( logWARNING, "Couldn't open included file '%s'", fname );

		return;
	}

	char buf[1024];
	int read;

	do {
		read = fread( buf, 1, sizeof(buf), f );
		fwrite( buf, 1, read, fout );

	} while ( read==sizeof(buf) );

	fclose( f );
}

char * C_CGI_Form::FormFile( char * fname )
{
	logfmt( FLOG_MAX, "CGI Parsing: %s", fname );

	char * result = NULL;
	struct stat stats;
	if ( stat(fname, &stats)==-1 )
	{
		logfmt( FLOG_MAX, "stat() error: %s, '%s'", strerror(errno), fname );
//		delete filename;
//		return 0;
		return strdup("Template read error");

	}

	FILE * f = fopen( fname, "r" );

//	delete filename;

	if (!f)
	{
		logfmt( logERROR, "Could not read template '%s'", fname );

		return strdup("Template read error");
	}

	char * buf = (char *)malloc(stats.st_size+1);
	if (!buf)
	{
		fclose( f );
		logfmt( logERROR, "Could not allocate %d bytes form template '%s'", stats.st_size+1, fname );
		return 0;
	}

	fread( buf, 1, stats.st_size, f );
	fclose( f );

	buf[stats.st_size] = 0;


	// Find out if this template has a parent and insert it into parent as necessary

	char ret[100], *start = buf;

	if ( reg_match( buf, "^([[:alnum:][:punct:]-]+\\[[[:alnum:]_]+\\]).*", ret, sizeof(ret) ) )
	{
		logfmt( FLOG_MAX, "TODO: Parent template: %s", ret );

		start += strlen(ret);

		char pfname[100], varname[50], pfname_full[200];

		reg_match( ret, "^([[:alnum:][:punct:]-]+)\\[[[:alnum:]_]+\\]$", pfname, sizeof(pfname) );
		reg_match( ret, "^[[:alnum:][:punct:]-]+\\[([[:alnum:]_]+)\\]$", varname, sizeof(varname) );

		logfmt( FLOG_MAX, "file=%s, name=%s", pfname, varname  );

		LetStr( varname, start );

		free(buf);

		snprintf( pfname_full, sizeof(pfname_full), TMPL_PREFIX"/tmpl/%s", pfname );

		logfmt( FLOG_MAX, "Loading Template '%s'", pfname_full );

		buf = FormFile( pfname_full );

		return buf;
	}

	logfmt( FLOG_MAX, "Template '%s' loaded: %d bytes", fname, stats.st_size );


	// Last step:  parse and substutute

	result = RFormBuffer( start );
	free( buf );

	logfmt( FLOG_MAX, "Returning from FormFile()" );

	return result;
}



int regall( const char * regex, const char * string, size_t nmatch, regmatch_t pmatch[] )
{
	regex_t compiled;
	int r;

	r = regcomp( &compiled, regex, REG_EXTENDED );
	if ( r ) return r;

	r = regexec( &compiled, string, nmatch, pmatch, 0 );

	regfree( &compiled );

	return r;
}


void C_CGI_Form::GetNextVariable( TList * &cur, char * &c1, char * &c, char * listname, int listi  )
{
		c = strstr( c1, "{{" );

		if (!c)
		{
			cur = Add( cur, c1, strlen(c1) ); // end
			return;
		}
//	LOG( "BP", LOG_MAX );

		cur = Add( cur, c1, c-c1 );
		c += 2;

//	LOG( "BP", LOG_MAX );
		// Check for recursive variables, i.e., {{text{{var2}}}}
		// Find the corresponding right bracket '}}'

		char *c2;
		int level = 1, maxlevel = 1;

		char * b1 = c, *b2;
		for ( c2=b1; c2<c+strlen(c); c2++ )
		{
//			logfmt(FLOG_MAX, "*c2=%c", *c2 );
			if ( *c2=='{' && *(c2+1)=='{' )
			{
				level++; c2++;
				if ( level>maxlevel )
					maxlevel = level;
			}
			else
			if ( *c2=='}' && *(c2+1)=='}' )
			{
				level--; c2++;
//				logfmt(FLOG_MAX, "level=%d", level );
			}
			if (!level) break;
		}
//	LOG( "BP", LOG_MAX );

		if ( c2==c1+strlen(c1) )	// no closing }}
		{
			cur = Add( cur, c, strlen(c) ); // end
			c = 0;
			return;
		}
//	LOG( "BP", LOG_MAX );

		c2--; *c2 = 0; c2+=2;

//		logfmt( FLOG_MAX, "Looking at '%s'", c );

		if ( maxlevel>1 ) // recursive call needed
		{
//			char *newbuf = new char[ c2-b1 ];
//			memcpy( newbuf, b1, c2-b1-1 );
//			newbuf[ c2-b1-1 ] = 0;

//			LOG( "Recursive call", LOG_MAX );
			c = RFormBuffer( c, listname, listi );

//			delete newbuf;
		}

		c1 = c2;
}

char * C_CGI_Form::RFormBuffer( char * buf, char * listname /*= NULL*/, int listi /*= 0*/ )
{
	// parse and substutute

	char * c = buf, *c1=buf;

	TList *top = new TList, *cur = top;

//	LOG( "BP", LOG_MAX );

	do {
		c = strstr( c1, "{{" );
//		logfmt( FLOG_MAX, c );

		if (!c)
		{
			cur = Add( cur, c1, strlen(c1) ); // end
			break;
		}
//	LOG( "BP", LOG_MAX );

		cur = Add( cur, c1, c-c1 );
		c += 2;

//	LOG( "BP", LOG_MAX );
		// Check for recursive variables, i.e., {{text{{var2}}}}
		// Find the corresponding right bracket '}}'

		char *c2;
		int level = 1, maxlevel = 1;

		char * b1 = c, *b2;
		for ( c2=b1; c2<c+strlen(c); c2++ )
		{
//			logfmt(FLOG_MAX, "*c2=%c", *c2 );
			if ( *c2=='{' && *(c2+1)=='{' )
			{
				level++; c2++;
				if ( level>maxlevel )
					maxlevel = level;
			}
			else
			if ( *c2=='}' && *(c2+1)=='}' )
			{
				level--; c2++;
//				logfmt(FLOG_MAX, "level=%d", level );
			}
			if (!level) break;
		}
//	LOG( "BP", LOG_MAX );

		if ( c2==c1+strlen(c1) )	// no closing }}
		{
			cur = Add( cur, c, strlen(c) ); // end
			break;
		}
//	logfmt( FLOG_MAX,  "BP, *c2=%s, c=%s", c2, c );

		c2--; *c2 = 0; c2+=2;

//		logfmt( FLOG_MAX, "Looking at '%s'", c );

		if ( maxlevel>1 ) // recursive call needed
		{
//			char *newbuf = new char[ c2-b1 ];
//			memcpy( newbuf, b1, c2-b1-1 );
//			newbuf[ c2-b1-1 ] = 0;

//			LOG( "Recursive call", LOG_MAX );
			c = RFormBuffer( c, listname, listi );

//			delete newbuf;
		}

		// resolve result which should have no recursive variables

//	LOG( "BP", LOG_MAX );

		c1 = c2;

//		char var[20000];

		if (c[0])	// non-empty variable
		{
			// Let's see if a special command is given
			if ( c[0] == '#' )
			{
				regmatch_t match[2*10];
				regex_t	compiled;

				if ( !regall( "^#include file=\"([[:alnum:][:punct:] ]+)\"$", c, 2, match ) )
				{
					char fname[50];
					int len = match[1].rm_eo-match[1].rm_so;
					if ( len>=sizeof(fname) )
						len = sizeof(fname)-1;		// to be safe

					strncpy( fname, &c[ match[1].rm_so ],  len );
					fname[ len ] = 0;

					logfmt( FLOG_MAX, "Including file '%s'", fname );

					char filename[100];
					snprintf( filename, sizeof(filename), TMPL_PREFIX"/%s", fname );

					char * tt = FormFile( filename );
					cur = Add( cur, tt, strlen(tt) );
					delete tt;
				}
				else
				if ( !regall( "^#field_([[:alpha:]]+)[[:space:]]*\\([[:space:]]*([[:alnum:]_]+)[[:space:]]*,[[:space:]]*\"(.*)\"[[:space:]]*,[[:space:]]*([[:digit:]]+)[[:space:]]*,[[:space:]]*([[:digit:]]+)[[:space:]]*\\)[[:space:];]*$", c, 6, match ) )
				{
					char name[50], description[200], size[10], maxlen[10], type[20];

					strnsafecpy( type, c+match[1].rm_so, match[1].rm_eo-match[1].rm_so, sizeof(type) );
					strnsafecpy( name, c+match[2].rm_so, match[2].rm_eo-match[2].rm_so, sizeof(name) );
					strnsafecpy( description, c+match[3].rm_so, match[3].rm_eo-match[3].rm_so, sizeof(description) );

					strnsafecpy( size, c+match[4].rm_so, match[4].rm_eo-match[4].rm_so, sizeof(size) );
					strnsafecpy( maxlen, c+match[5].rm_so, match[5].rm_eo-match[5].rm_so, sizeof(maxlen) );

					LetStr( "__func_type", type );
					LetStr( "__func_name", name );
					LetStr( "__func_description", description );
					LetStr( "__func_size", size );
					LetStr( "__func_maxlen", maxlen );

					logfmt( FLOG_MAX, "field_%s( %s, %s, %s, %s )", type, name, description, size, maxlen );

					char filename[100];
					snprintf( filename, sizeof(filename), TMPL_PREFIX"/%s", "field_text.func" );

					char * tt = FormFile( filename );
					cur = Add( cur, tt, strlen(tt) );
					delete tt;
				}



				if ( regcomp( &compiled, "^#global file=\"([[:alnum:][:punct:] ]+)\"$", REG_EXTENDED ) != 0 )
				{
					logfmt( logWARNING, "regcomp returned non-0, ignoring command" );
				}
				else
				{
					size_t r = regexec( &compiled, c, 2, match, 0 );
					if (r)
						;
//						{ logfmt( logWARNING, "Command detected, but no match" ); }
					else
					{
						char fname[50];
						int len = match[1].rm_eo-match[1].rm_so;
						if ( len>=sizeof(fname) )
							len = sizeof(fname)-1;		// to be safe

						strncpy( fname, &c[ match[1].rm_so ],  len );
						fname[ len ] = 0;

						GlobalTemplate( fname );
						ParseGlobalTemplate();
					}

				}

				regfree( &compiled );
			}
			else
//	if (false)
			if ( c[0]=='\\' )	// talking lists now
			{
				//logfmt( FLOG_MAX, "talking lists now" );
				char listname[50];
//				strsafecpy( listname, c+1, sizeof(listname) );

				int len = strcspn( c+1, " }" );
				strnsafecpy( listname, c+1, len, sizeof(listname) );

				// Locate the list in the form object

				int j;
				for (j=0; j<nlists; j++)
					if ( strcasecmp( lists[j]->listname, listname )==0 ) break;

				if (j==nlists)
				{
//					logfmt( logWARNING, "List '%s' not found in form object", listname );
//					continue;
				}
				else
					logfmt( FLOG_MAX, "Processing list %s[%d]", listname, lists[j]->n );


//				logfmt( FLOG_MAX, "c -> '%s'", c );


				bool only_if_present =  *(c+1+len) != 0;

//				logfmt( FLOG_MAX, "only_if_present = '%d'", only_if_present );

				c += strlen(c)+2;	// set c to after {{\list 1}}  // ???? = c2
// crash  ^^^
//				c1++;

				char temp[100], temp1[100];

				snprintf( temp, sizeof(temp), "{{/%s}}", listname );
				char * listend = strcasestr( c, temp ); // c1
				if (!listend)
				{
					logfmt( logWARNING, "End of list '%s' not found", temp );
					continue;
//					fprintf(fout, c1); // end
//					break;
				}

				*listend = 0;

				c1 = listend + strlen(temp); // point to after the list closing command

				// is there an 'else' section ??	{{//list}}
				snprintf( temp, sizeof(temp), "{{//%s}}", listname );
				char * celse = strcasestr( c1, temp );


				// 06-16-2002

				snprintf( temp1, sizeof(temp1), "{{\\%s", listname );
                                char * cnextinst = strcasestr( c1, temp1 );

				if ( cnextinst && (cnextinst<celse) ) 	// 'else' section belongs to the next occurence of the list
					celse = NULL;

				// end 06-16-2002


				if ( only_if_present && j<nlists && !lists[j]->n )
				{
					if (!celse) continue;
					*celse = 0;

					char * tt = RFormBuffer( c1 );
					cur = Add( cur, tt, strlen(tt) );
					delete tt;

					c1 = celse + strlen(temp);

					continue;
				}

				if (celse) c1 = celse + strlen(temp);


				// if there is no such list drop it

				if ( j==nlists )
				{
					logfmt( FLOG_MAX, "List '%s' not found in form object. Skipping...", listname );
					continue;
				}


				// now parse

				char * c2;

				// Skip all child lists before {{...}}

				c2 = strstr( c, "{{\\" );
				char * c3 = strstr( c, "{{...}}" );
				if ( c2 && c3 && c2<c3 )
				{
					c2 += 3;
					char skiplist[30];
					int n = strcspn( c2, " }" );
					if (n<sizeof(skiplist))
					{
						strncpy( skiplist, c2, n );
						skiplist[n] = 0;

						logfmt( FLOG_MAX, "Childlist '%s'. Skip.", skiplist );

						char temp[40];
						snprintf( temp, sizeof(temp), "{{/%s}}", skiplist );

//						logfmt( FLOG_MAX, "Looking for '%s'", temp );

						c2 = strcasestr( c2, temp );
						if (c2)
							c2 += n+5;

//						logfmt( FLOG_MAX, "Continuing at '%s'", c2 );

					}
				}
				else
					c2 = c;

				if (!c2)
					c2 = c;


				c2 = strstr( c2, "{{...}}" );
				if (!c2)	// no elements, list used as a block
				{
					logfmt( FLOG_MAX, "List '%s' does not contain any {{...}} -> used as a block", listname );
					char * tt = RFormBuffer( c );
					cur = Add( cur, tt, strlen(tt) );
					delete tt;
					continue;
				}


				*c2 = 0;
				char * tt = RFormBuffer( c );
				cur = Add( cur, tt, strlen(tt) );
				delete tt;
				c = c2+7;

				c2 = strstr( c, "{{/...}}" );
				if (!c2)	// no element close directive
				{
					logfmt( logWARNING, "Element close directive for '%s' not found", listname );

					char * tt = RFormBuffer( c );
					cur = Add( cur, tt, strlen(tt) );
					delete tt;

					continue;	// this is correct
				}
				*c2 = 0;


				//logfmt( FLOG_MAX, "Processing list '%s' 5", listname );

				//logfmt( FLOG_MAX, "Calling FormBuffer() for '%s'", c );

				for ( int i=0; i< lists[j]->n; i++ )
				{
					char * ctemp = strdup( c );

					if (ctemp)
					{
						char * tt = RFormBuffer( ctemp, listname, i );
						cur = Add( cur, tt, strlen(tt) );
						delete tt;
						free( ctemp );
					}
				}

				tt = RFormBuffer( c2+8 );
				cur = Add( cur, tt, strlen(tt) );
				delete tt;

				continue;
			}

			else
			{
				char * c5;

				if (!listname)
				{
//					logfmt( FLOG_MAX, "Getting variable '%s'", c);

//					GetVariable( c, var, sizeof(var) );
//					cur = Add( cur,  var, strlen(var) );

					c5 = GetVariable( c );
					if (c5)
					{									// 11-03-2002 Parse variable contents recursively
						char * temp = strdup( c5 );		// to prevent damage to the buffer
						char * c = RFormBuffer( temp );
						free( temp );					// 11-03-2002 This stuff rocks da house

						cur = Add( cur,  c, strlen(c) );
						delete c;
					}
				}
				else
				{
					char buf[100];
					snprintf( buf, sizeof(buf), "__list_%s_%d_%s",
							listname, listi, c );

//					logfmt( FLOG_MAX, "Fetching variable '%s'", buf );
					c5 = GetVariable( buf );
					if (!c5)
						c5 = GetVariable( c );
					if (c5)
						cur = Add( cur, c5, strlen(c5) );

				}

				if ( *c1=='/' && *(c1+1)=='\\' && *(c1+2)=='{' )
				{
					c1 += 2;
					GetNextVariable( cur, c1, c, listname, listi  );
					if (!c5)
					{
						if ( c5 = GetVariable( c ) )
							cur = Add( cur,  c5, strlen(c5) );
					}
				}

			}
		}

//		c1 += 2;

	} while ( 1 );

//	LOG( "BP3", LOG_MAX );

//	PrintList( top );

	ULONG len=0; cur = top;

	while (cur->text)
	{
		len += strlen(cur->text);
		cur = cur->next;
	}

//	logfmt( FLOG_MAX, "Allocating %lu bytes", len+1 );

	char * ret = new char[len+1];

	if (!ret) logfmt( logWARNING, "Not enough memory allocating %lu bytes", len );

	cur = top; ULONG pos=0;
	TList * prev;

	while ( cur && cur->text)
	{
//		logfmt( FLOG_MAX, "Copying %d chars at %d..", strlen( cur->text ), pos );
		memcpy( &ret[pos], cur->text, strlen( cur->text ) );
		pos += strlen( cur->text );

		delete cur->text;
		prev = cur;
		cur = cur->next;
		delete prev;
	}
//	LOG( "BP5", LOG_MAX );

	ret[pos] = 0;



//		Fold();

	return ret;
}


char *  C_CGI_Form::FormBuffer( char * buf )
{
	char * ret;
	char * c = strdup( buf );

	ret = RFormBuffer( c );
	free( c );

	return ret;
}



void C_CGI_Form::ParseGlobalTemplate( char * fname /*= NULL*/ )
{
	char * global_template = fname?fname:this->global_template;


	logfmt( FLOG_MAX, "ParseGlobalTemplate( \"%s\" )", global_template );
	struct stat stats;
	if ( stat(global_template, &stats)==-1 )
	{
		logfmt( FLOG_MAX, "stat() error: %s, '%s'", strerror(errno), global_template );
		return;
	}

	char * buf = (char *)malloc(stats.st_size+1);

	if (!buf)
	{
		logfmt( logERROR, "Could not allocate %d bytes for global template %s", stats.st_size+1, global_template );
		return;
	}

	FILE * f = fopen( global_template, "r" );
	if (!f)
	{
		logfmt( logERROR, "Could not read global template '%s'", global_template );
		return;
	}

	fread( buf, 1, stats.st_size, f );
	buf[stats.st_size] = 0;
	fclose( f );

	int i;
	char s1[30], s2[30], *c1, *c2;

	for (i=1; 1; i++ )
	{
		snprintf( s1, sizeof(s1), "<!--section%d-->", i );
		snprintf( s2, sizeof(s2), "<!--/section%d-->", i );
		c1 = strstr(buf, s1);
		c2 = strstr(buf, s2);
		if (!c1 || !c2) break;
		c1 += strlen(s1);

		snprintf( s2, sizeof(s2), "section%d", i );

		char * buf = new char[c2-c1+1];
		memcpy( buf, c1, c2-c1 );
		buf[c2-c1] = 0;
		char * temp = RFormBuffer( buf  );
		delete buf;
		LetStr( s2, temp );
		free( temp );

//		LetStr( s2, c1, c2-c1 );

//		logfmt( FLOG_MAX, "%s=%s", s2, GetVariable(s2) );

	}

	logfmt( FLOG_MAX, "%d sections found", i-1 );

	free( buf );
}


bool C_CGI_Form::ParseNewTemplate( char * fname /*= NULL*/ )
		// returns false if this seems to be wrong format template
{
	char * global_template = fname?fname:this->global_template;

	logfmt( FLOG_MAX, "ParseNewTemplate( \"%s\" )", global_template );
	struct stat stats;
	if ( stat(global_template, &stats)==-1 )
	{
//		delete filename;

		logfmt( FLOG_MAX, "stat() error: %s, '%s'", strerror(errno), global_template );
		return true;
	}

	char * buf = (char *)malloc(stats.st_size+1);

	if (!buf)
	{
//		delete filename;
		logfmt( logERROR, "Could not allocate %d bytes for global template %s", stats.st_size+1, global_template );
		return true;
	}

	FILE * f = fopen( global_template, "r" );

//	delete filename;

	if (!f)
	{
		logfmt( logERROR, "Could not read global template '%s'", global_template );
		return true;
	}

	fread( buf, 1, stats.st_size, f );
	buf[stats.st_size] = 0;
	fclose( f );


	logfmt( FLOG_MAX, "New template loaded '%s', size=%d bytes", global_template, stats.st_size );

	int i;
	char name[50], *c1, *c2;

	c1 = buf;
	if ( c1[0]!='[' )
	{
		c1 = strstr( c1, "\n[" );
		if (c1) c1++;
	}

	bool result = true;

	if ( !c1 )
		result = false;

	while (c1)
	{
		c1++;
		c2 = strchr( c1, ']' );
		strnsafecpy( name, c1, c2-c1, sizeof(name) );
		c2++;
		c1 = c2+strspn( c2, " \t\r\n" );  // ignore initial spaces

		if ( *c1=='[' )  // blank variable
		{
			LetStr( name, "" );
			continue;
		}

		c2 = strstr( c1, "\n[" );
		if (c2)
		{
			*c2 = 0;
			c2++;
		}

		char * c = (c2?c2:(buf+stats.st_size)) -1;

		while ( c>=c1 && strchr( " \t\r\n", *c ) )
		{
			*c = 0; c--;
		}

		LetStr( name, c1 = RFormBuffer(c1) );
		free( c1 );

//		logfmt( FLOG_MAX, "FOUND:  %s, len = '%d'", name, strlen(GetVariable(name)) );

		c1 = c2;
	}


	free( buf );

	return result;
}


bool C_CGI_Form::Form( FILE * fout )
{
	fprintf( fout, "Content-Type: text/html; charset=UTF-8\n\n");

	if (global_template) ParseGlobalTemplate();

CTimer timer;

	char * c = FormFile( template_file );

logfmt( logINFO, "FormFile() took %.3f sec", timer.elapsed()/1e6 );
logfmt( logINFO, "strlen(c)=%d", strlen(c) );

	int r;
	if ( r = ferror( fout ) ) logfmt( logWARNING, "ferror() returned %d: %s", r, strerror(r) );
	fprintf( fout, "%s", c );
logfmt( FLOG_MAX, "DEBUG" );
	if ( r = ferror( fout ) ) logfmt( logWARNING, "ferror() returned %d: %s", r, strerror(r) );
	fflush( fout );
logfmt( FLOG_MAX, "DEBUG" );
	delete c;

logfmt( logINFO, "fprintf()/fflush() took %.3f sec", timer.elapsed()/1e6 );

	return true;
}

bool C_CGI_Form::Form( FILE * fout, char * template_file )
{
	char * c = FormFile( template_file );
	fprintf( fout, c ); fflush( fout );
	delete c;

	return true;
}


int 	C_CGI_Form::GetParam( char name[], char *value, int valuelen, char ** end /*= NULL*/, char * start /*= NULL*/ )
{
//	logfmt( FLOG_MAX, "C_CGI_Form::GetParam( name='%s', sz=%d )", name, valuelen );
//	printf("C_CGI_Form() on params=%s \n", params);

	int result;
	char * qs;

	if (start)
		result = ::GetParam( start, name, value, valuelen, end );
	else
	{
		qs = getenv("QUERY_STRING");

		//logfmt( FLOG_MAX, "qs='%s'", qs );

		if ( !qs || !(result = ::GetParam( qs, name, value, valuelen, end ) ) )
			result = ::GetParam( params, name, value, valuelen, end );
	}

//	if ( !value[0] )


//	logfmt( FLOG_MAX, "GetParam returns '%s'='%s', qs='%s'", name, value, qs );

	return result;
}

int	C_CGI_Form::GetParamMul( char name[], TList ** list )
{
	char value[100];
	int	r, i=0;

	char * qs = params;

	logfmt( FLOG_MAX, "params=%s", qs );
//	getenv("QUERY_STRING"); if (!qs) qs = params;

	char *start = qs, * end;

	TList *top=NULL, *cur=NULL, *tmp;

	do {
		r = GetParam( name, value, sizeof(value), &end, start );
		logfmt( FLOG_MAX, "%s[%d]=%s", name, i, value );
		start = end;
		if (r)
		{
			tmp = new TList;
			tmp->text = strdup( value );
			tmp->next = NULL;

			if (cur)
			{
				cur->next = tmp;
				cur = cur->next;
			}
			else
				top = cur = tmp;

			i++;
		}
	} while (r);

	if (list) *list = top;
	return i;
}


int 	C_CGI_Form::GetCommand( char * commands[], int n )
{
	char cmd[30];

	cmd[0] = 0;

/*	LOG( argv[1], LOG_MAX );
	if (argc>1)
	{
		::GetParam( argv[1], "action", cmd, sizeof(cmd) );
		if (cmd[0])
			logfmt( FLOG_MAX, "Command given as a parameter: %s", cmd);
	}*/

/*
	char * qs = getenv("QUERY_STRING");
	if (qs)
	{
		::GetParam( qs, "action", cmd, sizeof(cmd) );
		if (cmd[0])
			logfmt( FLOG_MAX, "Command given as a parameter: %s", cmd);
	}

	if ( !cmd[0] )*/

	GetParam( "action", cmd, sizeof(cmd) );

	if ( !cmd[0] )
	{
		char * c;

		c = strcasestr( params, ".x=" );
		if (!c)
			return -1;

		char * c1 = strchr( c, '&' );
		if (!c1) return -1;


		c1++;
		c = strchr( c1, '.' );
		if (!c) return -1;


		if (c-c1 < sizeof(cmd) )
			strncpy( cmd, c1, c-c1 );
		else
			return -1;

		cmd[c-c1] = 0;
	}

	//logfmt( FLOG_MAX, "command=%s", cmd);   // LOG

	int i;

	for ( i=0; i<n; i++ )
	{
		if ( strcasecmp(cmd, commands[i])==0 )
			break;
	}

	if (i==n)
		return -1;

	return i;

}


int 	C_CGI_Form::GetScreen( char * screens[], int n, const char * scrs /*= NULL*/ )
{
	char cmd[30];

	if ( !scrs )
	{

	char * qs = getenv("QUERY_STRING");
	if (qs)
	{
		::GetParam( qs, "screen", cmd, sizeof(cmd) );
		if (cmd[0])
			logfmt( FLOG_MAX, "Screen given as a parameter: %s", cmd);
	}

	if (!cmd[0])
		GetParam( "screen", cmd, sizeof(cmd) );

	if ( !cmd[0] )
		return -1;

	}
	else
		strsafecpy( cmd, scrs, sizeof(scrs) );

	int i;

	for ( i=0; i<n; i++ )
	{
		if ( strcasecmp(cmd, screens[i])==0 )
			break;
	}

	if (i==n)
		return -1;

	return i;

}


void C_CGI_Form::Flag( char * var, char * explanation /*= NULL*/ )
{
	char buf[500], s[500];

	snprintf( buf, sizeof(buf), "c%s", var );
	LetStr( buf, GetVariable("syntax") );

	snprintf( buf, sizeof(buf), "err_%s", var );

	if ( CGI_FLAG_CLEAR==explanation )
		LetStr( buf, "" );
	else
		LetStrf( buf, "<a href='#' onClick=\"alert('%1$s');return false;\" TABINDEX=-1><img src='/image/gif/point.gif' align=middle border=0 width=10 height=15 alt='%1$s'></a>", explanation?explanation:"Invalid input" );

	logfmt( FLOG_MAX, "Flagged %s", buf );
}


void C_CGI_Form::Flag( CGI_Variable * var )
{
	if ( var && var->pattern )
		Flag( var->name, var->pattern->explanation );
}


void	C_CGI_Form::MatchVariable( CGI_Variable * var, bool optional /*= false*/, bool flag_if_mismatch /*= true*/ )
{
	char s[500];
	char buf[500];
	regex_t	compiled;
	char smatch[500], fc;
	unsigned int i;
	int	n = 0;
	ULONG * t = (ULONG*)var->target;
	double amount;

//	typedef *ULONG PULONG;
//	(ULONG *) t[100];
//	t = var->target;

	if ( var->type != CGI_CHECKBOXLIST && var->type != CGI_CHECKBOXLIST_ARRAY)
	{
		GetParam( var->name, s, sizeof( s ) );

		var->match = true;	// Let's not raise hell

		strsafecpy( smatch, s, sizeof(smatch) );	// in case it doesn't get filled in

		if ( optional && smatch[0]==0 /*&& var->type==CGI_STRING*/ )
			var->match = true;
		else
		{
		if ( var->pattern )	// Matching requested?
		{
			regmatch_t match[2];

			if ( regcomp( &compiled, var->pattern->mask, REG_EXTENDED ) != 0 )
			{
				logfmt( logWARNING, "regcomp returned non-0, assuming a match" );
			}
			else
			{
				size_t r = regexec( &compiled, s, 2, match, 0 );

				if ( r==0 )	// match
				{
					int len = match[1].rm_eo-match[1].rm_so;
					if ( len>=sizeof(smatch) )
						len = sizeof(smatch)-1;		// to be safe

					strncpy( smatch, &s[ match[1].rm_so ],  len );
					smatch[ len ] = 0;

				}
				else
					var->match = false;

				regfree( &compiled );
			}
		}

		// Too short or too long?

		if (	( var->minlen>=0 && strlen( smatch )<var->minlen ) ||
		( var->maxlen>=0 && strlen( smatch )>var->maxlen )
		   )
			var->match = false;
		}

		LetStr( var->name, smatch );

	}
	else
		var->match = true;

	if ( ( var->type==CGI_RADIO || var->type==CGI_RADIO_INT || var->type==CGI_RADIO_INT2 ) && smatch[0]==0 )
		var->match = false;


	// If match => store it


	bool *_bool = (bool*)var->target;


	if ( var->match )
	{
		switch ( var->type )
		{
		case CGI_STRING:

			strsafecpy( (char *)var->target, smatch, var->size );
			break;

		case CGI_RADIO:

			for ( i=0; i<var->nradio; i++ )
				if ( strcasecmp( var->radio[i], smatch ) == 0 )
					break;

			if ( i == var->nradio )
				var->match = 0;
			else
			{
				*(unsigned char*)var->target = i;

				snprintf( buf, sizeof(buf), "%schecked", smatch );
				LetStr( buf, "checked" );
			}

			break;

		case CGI_RADIO_INT:

			snprintf( buf, sizeof(buf), "%c%s", var->name[0], smatch );
			LetStr( buf, "checked" );


			char * endptr;
			*(short*)var->target = (short)strtol( smatch, &endptr, 10 );
			var->match = smatch[0]!=0 && *endptr==0;

			break;

               case CGI_RADIO_INT2:

                        snprintf( buf, sizeof(buf), "%.2s%s", var->name, smatch );
                        LetStr( buf, "checked" );


                        *(short*)var->target = (short)strtol( smatch, &endptr, 10 );
                        var->match = smatch[0]!=0 && *endptr==0;

                        break;

		case CGI_SELECT:

			logfmt( FLOG_MAX, "CGI_SELECT" );
			memset( var->target, 0, var->size );
			*(unsigned short*)var->target = atol( smatch );

			if ( var->radio )
				fc = (char)*(*(var->radio+0)+0);
			else
				fc = var->name[0];

			snprintf( buf, sizeof(buf), "%c%s", fc, smatch );
			LetStr( buf, "selected" );

			break;

		case CGI_SELECT_LOOKUP:

			memset( var->target, 0, var->size );

			strsafecpy( (char *)var->target, smatch, var->size );


			if ( var->radio )
				fc = (char)*(*(var->radio+0)+0);
			else
				fc = var->name[0];

			snprintf( buf, sizeof(buf), "%c%s", fc, smatch );
			LetStr( buf, "selected" );

			break;


		case CGI_CHECKBOX:

			// target is a boolean

			*_bool = 0==strcasecmp( smatch, "on" );

			if ( *_bool )
			{
				snprintf( buf, sizeof(buf), "%schecked", var->name );
				LetStr( buf, "checked" );
			}

			break;


		case CGI_CHECKBOXLIST:

			// target is a ULONG 32-bit?

			*(ULONG*)var->target = 0;
			for (i=0; i<var->maxlen && i<sizeof(ULONG)*8; i++)
			{
				snprintf( s, sizeof(s), "%s%d", (char*)var->radio[0], i+1 );
				GetParam( s, buf, sizeof(buf) );

				logfmt( FLOG_MAX, "CHECKBOX_LIST '%s' %s=%s", var->name, s, buf );

				if (buf[0])
				{
//					logfmt( FLOG_MAX, "CGI_CHECKBOXLIST: %d=%s, target=%lu", i+1, buf,
//								*(ULONG*)var->target );
					*(ULONG*)var->target |= (1<<i);
					logfmt( FLOG_MAX, "CGI_CHECKBOXLIST: %d=%s, target=%lu", i+1, buf,
								*(ULONG*)var->target );
					LetStr( s, "checked" );
				}
			}

			break;

		case CGI_CHECKBOXLIST_ARRAY:

			// target is a ULONG 32-bit?

//			*(ULONG*)var->target = 0;
			for (i=0; i<var->maxlen; i++) //&& i<sizeof(ULONG)*8
			{
				snprintf( s, sizeof(s), "%s%d", (char *)var->radio[0], i );
				GetParam( s, buf, sizeof(buf) );
				if (buf[0])
				{
					t[n] = atol( buf ); n++;
//					logfmt( FLOG_MAX, "CGI_CHECKBOXLIST: %d=%s, target=%lu", i+1, buf,
//								*(ULONG*)var->target );
//					*(ULONG*)var->target |= (1<<i);
//					logfmt( FLOG_MAX, "CGI_CHECKBOXLIST: %d=%s, target=%lu", i+1, buf,
//								*(ULONG*)var->target );
					LetStr( s, "checked" );
				}
			}
			t[n] = 0;

			if ( n<var->minlen )	// minimum required number of checked boxes
				var->match = false;

			break;

		case CGI_SELECT_MUL:

			TList *list, *cur;
			GetParamMul( var->name, &list );
			//PrintList( list );

			cur = list;

			for (i=0; cur; i++)
			{
				if ( var->radio )
					if ( *(char*)var->radio==cur->text[0] )
					{
						t[n] = atol( &cur->text[1] ); n++;
					}
					else
					{
						cur = cur->next;
						continue;
					}
				else
				{
					t[n] = atol( cur->text ); n++;
				}

				LetStr( cur->text, "selected" );




				cur = cur->next;
			}
			t[n] = 0;

			if ( n<var->minlen )	// minimum required number of checked boxes
				var->match = false;

			cur = list;

			DestroyList( list );

			break;

		case CGI_LONG:

			// target is a 32-bit long

			*(long*)var->target = strtol( smatch, &endptr, 10 );
			var->match = smatch[0]!=0 && *endptr==0;

			break;

		case CGI_FLOAT:

			*(float*)var->target = strtod( smatch, &endptr );
			var->match = smatch[0]!=0 && *endptr==0;

			break;


                case CGI_AMOUNT:

                        amount = strtod( smatch, &endptr );
                        var->match = smatch[0]!=0 && *endptr==0;;
			*(unsigned long*)var->target = (ULONG)(amount*100.0+0.5);

			break;


		default: ;	// do not let this happen, ok?

		}  // switch


	}

//	else

	if ( !var->match )
	{




		if ( var->pattern /*&& !optional*/ && flag_if_mismatch )
		{
			Flag( var );
		}

//		PrintVars();

	}

	if ( optional && !var->match )
	{
		logfmt( FLOG_MAX, "Resetting '%s'", var->name );
		memset( var->target, 0xFF, var->size );

		if ( var->type== CGI_SELECT )
			logfmt( FLOG_MAX, "%s, smatch=%s", var->name, smatch );

		// Clear the flag if blank
		if ( !smatch[0] )
			Flag( var->name, CGI_FLAG_CLEAR );
	}

	if ( optional && var->type!=CGI_STRING )
		var->match = true;

//	logfmt( FLOG_MAX, "MatchVariable(): %s=%s, match=%s", var->name, smatch, var->match?"true":"false" );
}

void C_CGI_Form::SetVariable( CGI_Variable * var )
{
	char buf[100], s[100];
	int i;

	switch ( var->type )
	{
		case CGI_STRING:

			LetStr( var->name, (char*)var->target );
			break;

		case CGI_RADIO:

			snprintf( buf, sizeof(buf), "%schecked", var->radio[ *(unsigned char *)var->target ] );
			LetStr( buf, "checked" );

			break;

		case CGI_RADIO_INT:

			snprintf( buf, sizeof(buf), "%c%d", var->name[0], *(short *)var->target );
			LetStr( buf, "checked" );

			break;

		case CGI_RADIO_INT2:

			snprintf( buf, sizeof(buf), "%.2s%d", var->name, *(short*)var->target );
                        LetStr( buf, "checked" );

			break;


		case CGI_SELECT:

			snprintf( buf, sizeof(buf), "%c%d", var->name[0], *(unsigned short*)var->target );
			LetStr( buf, "selected" );

			break;

		case CGI_SELECT_LOOKUP:

			snprintf( buf, sizeof(buf), "%c%s",
									var->radio ? (char)*(*(var->radio+0)+0) : var->name[0],

									(char *)var->target );
			LetStr( buf, "selected" );

			break;

		case CGI_CHECKBOX:

			// target is a boolean
			if (var->target)
			{
				snprintf( buf, sizeof(buf), "%schecked", var->name );
				LetStr( buf, "checked" );
			}

			break;

		case CGI_CHECKBOXLIST:

			// target is a ULONG 32-bit?

			for (i=0; i<var->maxlen && i<sizeof(ULONG)*8; i++)
			{
				if (*(ULONG*)var->target & 1<<i)
				{
					snprintf( s, sizeof(s), "%s%d", (char *)var->radio[0], i+1 );
					LetStr( s, "checked" );
				}
			}

			break;

		case CGI_LONG:

			// target is a 32-bit long
			snprintf( s, sizeof(s), "%lu", *(ULONG*)var->target );
			LetStr( var->name, s );

			break;

		case CGI_FLOAT:

			LetStrf( var->name, "%.2f", *(float*)var->target );
			break;

                case CGI_AMOUNT:

                        // target is a 32-bit long
                        snprintf( s, sizeof(s), "%.2f", (*(ULONG*)var->target)/100.0 );
                        LetStr( var->name, s );

                        break;


		default: ;	// do not let this happen, ok?

	}// switch
}


void C_CGI_Form::Email( char * fname, char * from/*=NULL*/ )
{
	char name[100], headers[100];
	snprintf( name, sizeof(name), EMAIL_PREFIX"/%s", fname );

	char * c = FormFile( name );

	logfmt( FLOG_MAX, "Template parsed: %s", name );

//	logfmt( FLOG_MAX, "body=%s", c );

//	Strip 8-bit content

	int len = strlen( c );
	for ( int i=0; i<len; i++ )
		c[i] &= 0x7F;

//	snprintf( headers, sizeof(headers), "X-EmailID: %s\n", fname );


//  --
	bool test = false;

	SMTP2(CfgReadChar("mailsend.server"),
		(from != NULL && strlen(from)>5) ? from : CfgReadChar( "mailsend.from" ),	// From: "support@chiefsoft.com"
		GetVariable("email"),  
		c,
		CfgReadChar( "mailsend.domain" ),
		CfgReadChar( "mailsend.user" ),
		CfgReadChar( "mailsend.pass" ),
		CfgReadChar( "mailsend.name" ));

	free(c);
}

CGIList::CGIList( C_CGI_Form * _form, char * name )

		// Instantiates a CGI_List object
		//	_form points to the parent form object
		//	name - name of the list as used in template

		{
//			logfmt( FLOG_MAX, "CGIList::CGIList()" );
			form = _form; n = 0; listname = strdup(name);
			if (!listname)
				logfmt( FLOG_MAX, "Out of memory" );

			listn = form->nlists;
			form->lists[ listn ] = this;
			form->nlists++;
//			logfmt( FLOG_MAX, "/CGIList::CGIList(), form->lists[%d]='%s'", form->nlists-1, listname );
		};


void CGIList::LetStrf( char * var, const char * format, ... )
{
	char buf[100];
	snprintf( buf, sizeof(buf), "__list_%s_%d_%s", listname, n, var );

	va_list	ap;
	va_start( ap, format );

	form->vLetStrf( buf, format, ap );

	va_end( ap );

}


void CGIList::LetStr( char * var, char * value )
{
	char buf[100];

	snprintf( buf, sizeof(buf), "__list_%s_%d_%s", listname, n, var );
	form->LetStr( buf, value );
}

void CGIList::LetStr( char * var, const char * format, ... )
{
	char buf[100];
	snprintf( buf, sizeof(buf), "__list_%s_%d_%s", listname, n, var );

	char tmp[1000];

	va_list	ap;
	va_start( ap, format );
	vsnprintf( tmp, sizeof(tmp), format, ap );
	va_end( ap );

	form->LetStr( buf, tmp );
}

void CGIList::LetInt16( char * var, const int value )
{
	char buf[100];

	snprintf( buf, sizeof(buf), "__list_%s_%d_%s", listname, n, var );
	form->LetInt16( buf, value );
//	logfmt( FLOG_MAX, "Setting %s=%d", buf, value );
}


char * CGIList::GetVariable( char * var, char * result, int size )
{
	char buf[100];

	snprintf( buf, sizeof(buf), "__list_%s_%d_%s", listname, n, var );
	return form->GetVariable( buf, result, size );
}


void CInputError::Log()
{
	L_Variables * v;
	v = this->var_top;

	if ( !v ) return;

	while ( v )
	{
		logfmt( FLOG_MAX, "%d: %s = %s", v->opts, v->name, v->value );
		v = v->next;
	}

}

void CInputError::Add( bool text, const char * var, const char * explanation, ... )
{
	char *tmp;
	int size;

	va_list	ap;
	va_start( ap, explanation );
	size = vsnprintf( NULL, 0, explanation, ap ) + 1;
	tmp = (char*)malloc( size );
	vsnprintf( tmp, size, explanation, ap );
	va_end( ap );


	L_Variables * v;
	v = FindVariable( var, true );

	if (v->value)
		free( v->value );

    v->value = strdup( tmp );

    v->opts = text;

	free( tmp );
}


char *CInputError::Get( const char * var, bool *text )
{
	L_Variables * v;

	v = FindVariable( var );

	if ( !v )
		return NULL;
	else
	{
		*text = v->opts;
		return v->value;
	}
}

void C_CGI_Form::ClearVariable( CGI_Variable var )
{
	char tmp[200];
	LetStr( var.name, "" );
        snprintf( tmp, sizeof(tmp), "err_%s", var.name );
        LetStr( tmp, "" );
}
