/*
 * This module provides access to the form-parsing routines in
 * hpss_parse_form.c from a CGILIB-based script programs.
 *
 * If the environment variable CGILIB_FORM_DEBUG is defined, a form_debug.tmp
 * file will be created.
 *
 * cgi_parse_form_data ( char errmsg[256] )
 * cgi_form_value ( )
 *
 * Author:	David Jones
 * Date:	30-MAR-2001
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "cgilib.h"
#include "cgilib_form.h"		/* verify prototypes */

int hpss_parse_form_content();

struct field_def
{
    struct field_def *next;
    char *value;
    int vallen, namlen;
    char name[1];
};
static struct field_def *flist = (struct field_def *) 0;
static int callback_errors;
/*
 * Internal routine to add entry to list of fields parsed from form.
 * New memory for key storage is allocated, but the value string is
 * not.
 */
static int add_field ( int *context,  char *key, int keylen,
    char *value, int vallen )
{
    struct field_def *def;
    /*
     * Allocate structure and insert in list.
     */
    def = malloc ( sizeof(struct field_def) + keylen );
    if ( !def ) { callback_errors++; return 1; }
    def->value = value;
    def->vallen = vallen;
    def->namlen = keylen;
    memcpy ( def->name, key, keylen );
    def->name[keylen] = '\0';
    def->next = flist;
    flist = def;
    /*
     * Use return value of 3 to signal that we want auxially symbols
     * created for content-type and filename.  We can only do this because
     * we are making separate copies of the key strings.
     */
    return 3;
}
/****************************************************************************/
/*
 * Primary routine called to convert the form content into a symbol table.
 */
int cgi_parse_form_data ( char *fail_reason )
{
    char *cl_str, *content, *ctype, content_type[256];
    size_t content_length, i, count;
    int status, context;
    FILE *dump;
    /*
     * Determine the source and encoding type of the form data, either
     * the request content of the query string.
     */
    dump = (FILE *) 0;
    if ( getenv ( "CGILIB_FORM_DEBUG" ) ) dump = fopen ( 
	"form_debug.tmp", "w" );
    cl_str = cgi_info ( "CONTENT_LENGTH" );
    if ( dump ) fprintf ( dump, "content length: %s\n", cl_str ? cl_str : "" );
    if ( cl_str ) content_length = atoi ( cl_str );
    else content_length = 0;
    content = (char *) 0;

    ctype = cgi_info ( "CONTENT_TYPE" );
    if ( dump ) fprintf ( dump, "ctype: %s\n", ctype ?ctype : "" );
    if ( ctype ) {
	/* Make local copy of content type, upcased and trimmed at semi-colon
	 */
	int pre_semicolon;
	for ( i = 0, pre_semicolon = 1; i < 254; i++ ) {
	    if ( !ctype[i] ) break;
	    if ( ctype[i] == ';' ) pre_semicolon = 0;
	    content_type[i] = (pre_semicolon) ? _toupper ( ctype[i] ) : ctype[i];
	}
	content_type[i] = '\0';
    } else {
	strcpy ( content_type, "" );
    }

    if ( content_length == 0 ) {
	/* See if query string present and method is get. */
	char *qstring, *method;
	qstring = cgi_info ( "QUERY_STRING" );
	method = cgi_info ( "REQUEST_METHOD" );
	if ( method ) {
	    /* Hide query string if method not GET */
	    if ( strcmp ( method, "GET" ) != 0 ) qstring = (char *) 0;
	}

	if ( qstring ) {
	    /*
	     * Fake the content.
	     */
	    strcpy ( content_type, "APPLICATION/X-WWW-FORM-URLENCODED" );
	    content_length = strlen ( qstring );
	    content = malloc ( content_length + 1 );
	    if ( !content ) {
	        strcpy ( fail_reason, 
			"Failed to allocate memory to store content" );
	        return 0;
	    }
	}
    }
    /*
     * Allocate memory and load all content from request.
     */
    if ( !content && (content_length > 0) ) {
	content = malloc ( content_length + 1 );
	if ( !content ) {
	    strcpy ( fail_reason, "Failed to allocate memory to store content" );
	    return 0;
	}
	for ( i = 0; i < content_length; i += count ) {
            count = cgi_read ( &content[i], content_length-i );
	    if ( count < 1 ) {
	        strcpy ( fail_reason, "Error reading content" );
	        return 0;
	    }
            if ( count == 0 ) {
	        strcpy ( fail_reason, "Unexpected end of file" );
	        return 0;
	    }
	}
    } else if ( !content ) {
	strcpy ( fail_reason, "No content to parse" );
	return 0;
    }
    fail_reason[0] = '\0';
    /*
     * Initialize symbol table used to save the parsed form fields as
     * key/value pairs.
     */
    callback_errors = 0;
    /*
     * Call common parse routine, callback will load parsed key/value pairs
     * into our symbol table.
     */
    context = 0;
    status = hpss_parse_form_content ( &context, content_type, content,
	content_length, add_field );
    if ( dump ) fprintf ( dump, "Status of HPSS parse: %d\n", status );
    if ( (status&1) == 0 ) {
	sprintf ( fail_reason, "hpss parse returned %d cl=%d '%s'\n", status,
		 content_length, content_type );
    } else {
	/*
	 * for convenience, zero terminate all the value strings.
	 */
	struct field_def *def;
	for ( def = flist; def; def = def->next ) {
	    if ( def->value ) if ( def->value[def->vallen] )
		def->value[def->vallen] = '\0';

	    if ( dump ) fprintf ( dump, "def: '%s' = '%s'\n",
		def->name, def->value );
	}
    }

    return status;
}
/**************************************************************************/
/*
 */
int cgi_form_field_lookup ( char *key, char **value, int *vallen )
{
    int keylen;
    struct field_def *def;
    keylen = strlen ( key );
    for ( def = flist; def; def = def->next ) {
	/*
	 * see if this field matches the caller-specified key.
	 */
	if ( keylen == def->namlen ) {
	    if ( strncmp ( key, def->name, keylen ) == 0 ) {
		/* Found a match, return pointer to it. */
		*value = def->value;
		*vallen = def->vallen;
		return 1;
	    }
	}
    }
    /* Finishing loop means failure. */
    return 0;
}
