/* CGIOPS.C - cgi-related script directives
 * Copyright 1998-2002 Stephen C. Grubb  (quisp.sourceforge.net)
 * This code is covered under the GNU General Public License (GPL);
 * see the file ./Copyright for details. */

/* Results are written to outfp (usually this will be stdout for CGI). */

/* Unlike sinterp(), this module can never have more than one active instance per process,
	so static storage can be maintained here */

#include <ctype.h>
#include "cgic.h"
#include "tdhkit.h"
#include "quispcgi.h"

extern int oplist(), cgitolist(), TDH_get_var_i(), TDH_sqltabdef(), TDH_function_set(), GL_setmaxlen(), GL_addmember();
extern int ploticus_begin(), ploticus_init(), ploticus_arg(), ploticus_execscript(), ploticus_execline(), ploticus_end();
extern int unlink();

#define COMMALIST 'c'
#define MULTIROW 'm'
#define OPLIST 0
#define PLAINLIST 1
#define LIST_SENTRY "!-list-!"

static int inoplist = 0;
static char textareabuf[MAXTEXTAREA];
#ifdef ENABLE_PL
static int inplot = 0;
static char plscriptfile[MAXPATH] = "";
static int plprefab = 0;
static int plerr = 0;
#endif


/* --------------------- */
/* CGIOPS                */
int
cgiops( outfp, buf, ss )
FILE *outfp;
char *buf;
struct sinterpstate *ss;
{
char tok[DATAMAXLEN+1];
char tok2[DATAMAXLEN+1];
char varname[40];
int ix;
int stat;
int i, len;
char *fnames[MAXITEMS];
int nitems;
char table[MAXPATH];
char filename[MAXPATH];


ix = 0;
strcpy( tok, GL_getok( buf, &ix ) );


if( inoplist ) {
	/* send incoming lines to buildoplist() */
	stat = oplist( outfp, buf, ss );
	if( stat == SINTERP_END_BUT_PRINT ) {
		fprintf( outfp, "%s", buf );
		inoplist = 0;
		}
	if( strcmp( tok, "#endlist" )==0 ) inoplist = 0; /* for display_only mode */
	return( SINTERP_END );
	}

#ifdef ENABLE_PL
if( inplot ) {
	if( ss->writefp != NULL ) fprintf( outfp, "%s", buf );
		
	else if( strncmp( tok, "#endgraph", 9 )==0 ) {
		stat = ploticus_end();
		TDH_errprog( "quisp" ); /* restore */
		inplot = 0;
		if( !plerr && stat != 0 ) { sprintf( buf, "error %d in ploticus_end\n", stat ); return( 100 ); }
		}
	else if( strncmp( tok, "#arg", 4 )==0 ) {
		strcpy( tok, GL_getok( buf, &ix ));
		if( strcmp( tok, "-prefab" )==0 ) {
			strcpy( plscriptfile, GL_getok( buf, &ix ) );
			plprefab = 1;
			}
		else if( strcmp( tok, "-f" )==0 ) strcpy( plscriptfile, GL_getok( buf, &ix ) );
		else if( tok[0] != '-' ) strcpy( plscriptfile, tok );
		else	{
			int found, blen;
			for( i = 0, found = 0; tok[i] != '\0'; i++ ) {
				if( tok[i] == '=' ) { found = 1; break; }
				}
			if( found ) {
				blen = strlen( buf );
				if( buf[ blen-1 ] == '\n' ) buf[ blen-1 ] = '\0';
				ploticus_arg( buf, "" ); /* var value pair - just pass whole thing */
				}
			else	{
				/* ordinary -arg */
				strcpy( tok2, GL_getok( buf, &ix ));
				ploticus_arg( tok, tok2 );
				}
			}
 	 	}
	else	{
		if( inplot == 1 ) { 
			stat = ploticus_begin(); 
			if( !plerr && stat != 0 ) { plerr = 1; sprintf( buf, "error %d in ploticus_begin\n", stat ); return( 100 ); }
			inplot = 2; 
			}
		if( plscriptfile[0] != '\0' ) stat = ploticus_execscript( plscriptfile, plprefab );
		else stat = ploticus_execline( buf );
		if( !plerr && stat != 0 ) { plerr = 1; sprintf( buf, "error %d in ploticus script\n", stat ); return( 100 ); }
		}
	return( SINTERP_END );
	}
#endif


/* if doing shell or sql result lines, output now.. don't look for embedded cgi ops */
if( (ss->doingshellresult || ss->doingsqlresult )) {  /* scg 5/12/04 */
	fprintf( outfp, "%s", buf );
	return( SINTERP_END );
	}


/* all non-op lines can now print and be done.. */
if( tok[0] != '#' ) {
	/* preliminary header stuff.. */
        if( MRpreliminary && strnicmp( buf, "Set-Cookie:", 11 ) != 0 ) { /* other header tags may be listed here in future */
                if( MRcontenthtml ) fprintf( outfp, "\n<html>\n" ); /* the all-important blank line */
                MRpreliminary = 0;  /* done with preliminaries.. jump into normal script processing.. */
		TDH_errmode( "cgi" );
                }
	fprintf( outfp, "%s", buf );
	return( SINTERP_END );
	}



if( GL_smember( tok, "#cgivar #cgilistvar #cgimultivar #cgitextvar" )) { 
	char typ;
	typ = tok[4];
	for( i = 0; buf[i] != '\0'; i++ ) if( buf[i] == ',' ) buf[i] = ' ';
	while( 1 ) { /* for each variable listed.. */
		strcpy( varname, GL_getok( buf, &ix ) );
		if( varname[0] == '\0' ) break;
		stat = TDH_getvar( varname, tok );
		if( stat == 0 ) continue; 				  /* var already exists, leave alone.. */
		if( typ == 'l' ) {
			cgiFormStringNoNewlines( varname, tok, DATAMAXLEN ); 

			/* check for list sentry.. if found it's a previously converted commalist.. don't use cgitolist() */
			if( strncmp( tok, LIST_SENTRY, 8 )==0 ) strcpy( tok, &tok[8] ); 

			else cgitolist( varname, tok );		  /* commalist */
			}
		else if( typ == 'm' || typ == 't' ) strcpy( tok, "" );    /* multi & text.. just reserve.. */
		else 	{
			cgiFormStringNoNewlines( varname, tok, DATAMAXLEN ); /* ordinary var */
			if( strncmp( tok, LIST_SENTRY, 8 )==0 ) strcpy( tok, &tok[8] );  /* remove list sentry if present.. */
			}
		TDH_setvar( varname, tok );
		if( typ == 'l' ) {
			int ivar;
			/* remember that this variable was converted.. */
			ivar = TDH_get_var_i();
			/* printf( "(var %d was a list)<br>\n", ivar ); */
			MRconvflag[ ivar ] = 'L';
			}
		}
	return( SINTERP_END );
	}


if( GL_smember( tok, "#sqlcgivars" ) ) { 	
	int overwrite = 0;
	strcpy( table, GL_getok( buf, &ix ) );			/* 1st arg is tablename */
	strcpy( tok, GL_getok( buf, &ix ) );			/* "overwrite" option */
	if( strcmp( tok, "overwrite" )== 0 ) overwrite = 1;
	else if( tok[0] != '\0' ) err( 2838, "sqlcgivars: invalid option", tok );
	TDH_altfmap( 1 );
	stat = TDH_sqltabdef( table, fnames, &nitems );  /* caution - fnames points to info w/ limited lifespan */
	TDH_altfmap( 0 );
	if( stat != 0 ) return( err( stat, "sqlcgivars: no such table", table ));
	for( i = 0; i < nitems; i++ ) {
		stat = TDH_getvar( fnames[i], tok );
		if( !overwrite && stat == 0 ) continue;
		else cgiFormStringNoNewlines( fnames[i], tok, DATAMAXLEN ); 
		TDH_setvar( fnames[i], tok );
		}
	return( SINTERP_END );
	}

if( strcmp( tok, "#cookie" )==0 ) {  /* cookies actually set in mrcgi.c .. here, just 
						make sure var is set to "" if no cookie */
	for( i = 0, len = strlen( buf ); i < len; i++ ) if( buf[i] == ',' ) buf[i] = ' ';
	while( 1 ) { /* for each variable listed.. */
		strcpy( varname, GL_getok( buf, &ix ) );
		if( varname[0] == '\0' ) break;
		if( TDH_getvar( varname, tok ) != 0 ) TDH_setvar( varname, "" );
		}
	return( SINTERP_END ); 
	}


if( strncmp( tok, "#optionlist", 11 )==0 ) {  
	inoplist = 1;
	oplist( outfp, buf, ss ); /* first line */
	return( SINTERP_END );
	}

if( strcmp( tok, "#showtext" )==0 ) { 	/* #showtext name [addbr] [evalvars] 
					   name can be either a cgi var (submitted textarea) 
						OR a filename relative to ./textchunks */
	FILE *chunkfp;
	int addbr, evalvars, cgivar, wordpos, eof, jj;
	char c;

	strcpy( tok, GL_getok( buf, &ix ));
	if( tok[0] == '\0' ) return( err( 2850, "showtext: not enough parameters", "" ));

	cgivar = 1;

	/* see if it looks like a pathname.. */
	for( jj = 0; tok[jj] != '\0'; jj++ ) if( tok[jj] == '/' ) cgivar = 0;
	if( tok[jj-1] == '/' ) return( SINTERP_END ); /* likely a directory.. bail  - scg 4/13/04 */

	addbr = evalvars = 0;
	while( 1 ) {
		strcpy( tok2, GL_getok( buf, &ix ));
		if( tok2[0] == '\0' ) break;
		else if( strcmp( tok2, "addbr")==0 ) addbr = 1;
		else if( strcmp( tok2, "evalvars")==0 ) evalvars = 1;
		}
	if( cgivar ) {
		stat = cgiFormString( tok, textareabuf, MAXTEXTAREA );
		if( textareabuf[0] == '\0' ) cgivar = 0;
		}
	else	{	/* try textchunk file.. */
		sprintf( filename, "%s/textchunks/%s", MRprojdir, tok );
		chunkfp = fopen( filename, "r" );
		if( chunkfp == NULL ) return( SINTERP_END );
		cgivar = 0;
		}
	/* now go thru and glob into words, doing necessary processing.. we may be going thru cgi var bytes or file bytes */
	for( i = 0, wordpos = 0, eof = 0; ; i++ ) {
		if( cgivar ) { c = textareabuf[i]; if( c == '\0' ) eof = 1; }
		else { c = getc( chunkfp ); if( c == EOF ) eof = 1; }
		if( isspace( (int) c ) || eof ) {
			if( wordpos > 0 ) {
				tok[ wordpos ] = '\0';
				if( evalvars && tok[0] == '@' ) { TDH_getvar( &tok[1], tok2 ); strcpy( tok, tok2 ); }
				fprintf( outfp, "%s ", tok );
				wordpos = 0;
				}
			if( c == '\n' ) fprintf( outfp, "%s\n", (addbr)?"<br>":"" );
			if( eof ) break;
			}
		else tok[wordpos++] = c;
		}
	if( !cgivar ) fclose( chunkfp );

	return( SINTERP_END );
	}

if( strcmp( tok, "#savetext" )==0 ) {  /* savetext textareaname filename  - filename is relative to ./textchunks */
	FILE *chunkfp;
	TDH_function_set( "textsaved", 0 ); /* so status is accessible via $textsaved() function */
	strcpy( tok, GL_getok( buf, &ix ));
	strcpy( tok2, GL_getok( buf, &ix ));
	if( tok[0] == '\0' ) return( err( 2850, "savetext: not enough parameters", "" ));
	sprintf( filename, "%s/textchunks/%s", MRprojdir, tok2 );
	stat = cgiFormString( tok, textareabuf, MAXTEXTAREA );
	if( sscanf( textareabuf, "%s", tok2 ) < 1 ) {
		unlink( filename ); /* now empty; remove it */
		}
	else 	{
		chunkfp = fopen( filename, "w" );
		if( chunkfp == NULL ) return( err( 2840, "savetext: can't open textchunk file", filename ));
		fprintf( chunkfp, "%s", textareabuf );
		fclose( chunkfp );
		TDH_function_set( "textsaved", 1 ); /* so status is accessible via $textsaved() function */
		}
	return( SINTERP_END );
	}

if( strcmp( tok, "#pass" )==0 ) {
	for( i = 0, len = strlen( buf ); i < len;  i++ ) if( buf[i] == ',' ) buf[i] = ' ';
	while( 1 ) {
		strcpy( varname, GL_getok( buf, &ix ));
		if( varname[0] == '\0' ) break;
		/* TDH_getvalue( tok, varname, TDH_dat, TDH_recid ); */
		TDH_getvar( varname, tok ); /* use var instead of value .. why not? 6/25/03 */
		if( MRconvflag[ TDH_get_var_i() ] == 'L' ) 
			fprintf( outfp, "<input type=hidden name=%s value=\"%s%s\">\n", varname, LIST_SENTRY, tok );
		else fprintf( outfp, "<input type=hidden name=%s value=\"%s\">\n", varname, tok );
		}
	return( SINTERP_END );
	}

if( strcmp( tok, "#formtarget" )==0 ) {
	fprintf( outfp, "<input type=hidden name=\"%s\" value=\"%s\">", MRdoctag, GL_getok( buf, &ix ));
	return( SINTERP_END );
	}

if( strcmp( tok, "#allresponses" )==0 ) {
	/* #allresponses  resultvarname  namepat  valuepat  */
	/* if namepat and valuepat are omitted, all names are eligible we will be looking for "on" .. */
	/* parse QUERY_STRING and make a list of the names of all user variables having a certain value.. */
	int j, len, slen;
	char *uri, *getenv();
	char namepat[128], name[40], valuepat[128];
	strcpy( varname, GL_getok( buf, &ix ));
	strcpy( namepat, GL_getok( buf, &ix ));
	if( namepat[0] == '\0' ) strcpy( namepat, "*" );
	strcpy( valuepat, GL_getok( buf, &ix ));
	if( valuepat[0] == '\0' ) strcpy( valuepat, "on" );
	if( cgiPostContent != NULL ) uri = cgiPostContent; /* from cgic */
	else uri = getenv( "QUERY_STRING" );
	if( uri == NULL ) return( SINTERP_END ); /* should never happen */
	j = 0;
	GL_setmaxlen( DATAMAXLEN ); /* set max length for getchunk().. */
	tok2[0] = '\0';  /* s will hold result list */
	slen = 0;
	while( 1 ) {
		GL_getchunk( tok, uri, &j, "&" ); /* get arg */
		if( tok[0] == '\0' ) break;

		for( i = 0; tok[i] != '\0'; i++ ) if( tok[i] == '=' ) break; /* find '=' */

		if( strcmp( namepat, "*" ) != 0 ) { strncpy( name, tok, i-1 ); name[i-1] = '\0'; }
		if( GL_slmember( &tok[i+1], valuepat ) && ( strcmp( namepat, "*" )==0 || GL_slmember( name, namepat ))) {
			tok[i] = '\0';
			len = strlen( tok );
			if( slen + len >= (DATAMAXLEN-2) ) {
				err( 8402, "warning: #allresponses list truncated", "" );
				break;
				}
			GL_addmember( tok, tok2 );
			slen += (len + 1);
			}
		}
	TDH_setvalue( varname, tok2, TDH_dat, TDH_recid );
	GL_setmaxlen( 0 ); /* no max */
	return( SINTERP_END );
	}


#ifdef ENABLE_PL
if( strncmp( tok, "#graph", 6 )==0 ) {
	/* #graphic outformat outfile 
	   #arg -prefab prefabname 
	   #arg
	   pl script code
	   #endgraphic
	 */
	
	char outformat[80], outfile[MAXPATH];
	ix = 0;
	GL_getok( buf, &ix );
	strcpy( outformat, GL_getok( buf, &ix ));
	strcpy( outfile, GL_getok( buf, &ix ));
	if( outfile[0] == '\0' ) return( err( 5279, "usage: #graphic outformat outfilename", "" ));
	stat = ploticus_init( outformat, outfile );
	if( !plerr && stat != 0 ) { plerr = 1; sprintf( buf, "error %d in ploticus_init", stat ); return( 100 ); } 
	
	inplot = 1;
	plprefab = 0;
	strcpy( plscriptfile, "" );
	return( SINTERP_END );
	}
#endif


/* any other #ops will print here */
fprintf( outfp, "%s", buf );
return( SINTERP_END );
}


/* ============================================= */
/* CGITOLIST - take a cgi multi-select response and convert it to a comma-delimited list */
int
cgitolist( fieldname, result )
char *fieldname;
char *result;
{
char **responses;
int addtolist;
int stat, j, rlen, tlen;
char tok[DATAMAXLEN+1];

strcpy( result, "" );
rlen = 0;

stat = cgiFormStringMultiple( fieldname, &responses);
if ( stat != cgiFormNotFound) {

	/* loop thru responses; concatenate as many as possible into a 
	   comma-delimited list.  If there is too much text to fit into the variable, 
	   the last member of the list will be "((MORE))" */
	j = 0;
	addtolist = 1;
	while (responses[j]) {

		sscanf( responses[j], "%s", tok );
		tlen = strlen( tok );

		if( rlen + tlen > (DATAMAXLEN-10) ) {
			strcpy( &result[ rlen ], ",((MORE))" );
			addtolist = 0;
			}
		else if( addtolist ) {
			if( j > 0 ) result[rlen++] = ',';
			strcpy( &result[rlen], tok );
			rlen += tlen;
			}
		j++;
		}
	cgiStringArrayFree(responses); /* free cgic memory */
	result[rlen] = '\0';
	}
return( 0 );
}

