/*
 * This program is run by the WWWEXEC scriptserver to do pre-processing of
 * html files, dynamically inserting files or other generated data.
 *
 * To specify the preprocessing you must give the file a distinct file type
 * (e.g. htmlx) and add the following to the configuration file:
 *
 *	suffix .htmlx text/x-server-parsed-html
 *	presentation text/x-server-parsed-html html_preproc
 *
 * Script_execute sends the following prolog:
 *
 *	html_preproc method url protocol
 *
 *      prolog[0]		Sub-function.
 *	prolog[1]		Method specified in request (e.g. GET).
 *	prolog[2]		Protocol.
 *	prolog[3]		Ident portion of requested URL, after translation by
 *
 * None of the resulting file contents is returned until the entire file
 * has been processed.  This is required for 2 reasons:
 *
 *   1. The HTTP status, which is the first part of the response, is not known
 *       until processing is complete.
 *
 *   2.  Processing of the file may require using additional server functions,
 *       such as <DNETXLATE>, which become unavailable once the HTTP response
 *       <DNETRAW> is started.
 *
 * Author:	David Jones
 * Date:	11-MAY-1996	Convert to MST.
 * Revsied:	16-JAN-1997
 * Revised:	30-APR-1997	Replace use of auto variable for context key
 *				with block allocated from heap.
 * Revised:	26-AUG-1997	Add last-modified header.
 * Revised:	8-DEC-1997	Add recursion counter to per-thread context.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>		/* variable argument list */
#include <string.h>
#include <errno.h>

#include "pthread_1c_np.h"
#include "mst_share.h"
#include "tutil.h"
#include "tmemory.h"
#include "file_access.h"
#include "file_cache.h"
#define isspace(a) ((a == ' ') || (a=='\t'))
#include "access_db.h"

static int send_http_header ( char *stsline, char *content );
static int parse_directive (
	char *path, char **targ, int *outlen, char **outbuf );
static int parse_tag ( char *tag, int maxlen, int *taglen, char **targ );
static int preproc_main ( int argc, char **argv );

static pthread_key_t private_context;
static pthread_mutex_t private_ctx_lock;
struct private_ctx {		/* per-thread context structure */
    struct private_ctx *next;	/* Next context block in chain */
    void *link;			/* MST link handle */
    char **prolog;		/* scriptserver prolog */
    int tag_verify;		/* If true, include directives in output */
    int cgimode_active;		/* if true, write </DNETCGI> on exit */
    int accessesKnown;		/* state of connection access counter file */
    int Accesses;		/* Number of accesses for current file */
    int version;		/* Version spec. */
    int have_lock;		/* true iff currently own DB lock */
    unsigned int last_mod;	/* last modification date on file */
    int recur_level;		/* recursion count for nested includes */
    int cache_allowed;		/* If true, may use file cache */
    char id2_info[256];		/* response from <DNETID2> query */
};
static struct private_ctx *ctx_pool;
static IFILE *count_db = (IFILE *) 0;
#define POOL_STATIC_SIZE 31
#define POOL_EXPAND_SIZE 16
static struct private_ctx initial_pool[POOL_STATIC_SIZE];

static void rundown_private ( struct private_ctx *ctx )
{
    int length;
    /*
     * Make sure we don't lock file.
     */
    if ( ctx->have_lock ) {
	irelease_db();
	ctx->have_lock = 0;
    }
    /*
     * Return context block to lookaside list.
     */
    pthread_mutex_lock ( &private_ctx_lock );
    ctx->next = ctx_pool;
    ctx_pool = ctx;
    pthread_mutex_unlock ( &private_ctx_lock );
}
/***************************************************************************/
/* Every dynamically loaded service must have an INIT routine, which is
 * called once during the processing of the Service rule to initialize
 * static structures needed by the MST.  By default the init routine
 * name is the start routine + the string _init, and 'init=name' clause
 * on the service rule will override the default.
 *
 * Arguments:
 *    mst_linkage vector  Structure containing addresses of essential
 *			items wee need from main program, such as
 *			address of putlog() routine.
 *
 *    char *info	Administrator provided argument (info=).
 *
 *    char *errmsg	Error text for log file when error status returned.
 */
int preproc_init ( mst_linkage vector, char *info, char *errmsg )
{
    int status, i;
   /*
    * Setup MST interface callbacks.
    */
   if ( (1&mstshr_init ( vector, info, errmsg )) == 0 ) return 0;
   /*
    * Initialize other components used by this module: tmemory, per-thread
    * context pool and file_access.
    * Errors create message in errmsg.
    */
    INITIALIZE_MUTEX(&private_ctx_lock);
    SET_MUTEX_NAME(&private_ctx_lock,"OSU preproc context")
    CREATE_KEY(&private_context, (pthread_destructor_t) rundown_private );
    SET_KEY_NAME(private_context,"OSU preproc rundown")
    ctx_pool = initial_pool;
    for ( i = 0; i < POOL_STATIC_SIZE-1; i++ ) 
	initial_pool[i].next = &initial_pool[i+1];
    initial_pool[i].next = (struct private_ctx *) 0;
    status = (tf_initialize("") == 0) ? 1 : 0;
    if ( status == 1 ) {
	status = tm_initialize();	/* per-thread heaps */
    }
    /*
     * Return success status.
     */
    if ( status == 1 ) {
        tu_strcpy ( errmsg, "HTML pre-processor sucessfully initialized" );
     } else {
	tu_strcpy ( errmsg, "HTML pre-processor init error: " );
	tu_strint ( status, &errmsg[20] );
     }
     return 1;
}
/************************************************************************/
/* Define some utility routines.
 */
static char *my_strchr ( char *str, char c )
{
    for ( ; *str != c; str++ ) if ( !*str ) return (char *) 0;
    return str;
}
static char *my_strrchr ( char *str, char c )
{
    char *result;
    if ( c == '\0' ) return &str[tu_strlen(str)];

    for ( result = (char *) 0; *str; str++ ) if ( *str == c ) result = str;
    return result;
}
static int put_string ( mst_link_t link, char *fmt, char *arg )
{
   int status, length, i, j;
   for ( i = 0; fmt[i]; i++ ) if ( fmt[i] == '%' ) if ( fmt[i+1] == 's' ) {
	if ( i > 0 ) {
	    status = mst_write ( link, fmt, i, &length );
	    if ( 0 == (status&1) ) return status;
	}
	j = tu_strlen ( arg );
	if ( j > 0 ) {
	    status = mst_write ( link, arg, j, &length );
	    if ( 0 == (status&1) ) return status;
	}
	if ( fmt[i+2] ) {
	    status = mst_write ( link, &fmt[i+2], tu_strlen(&fmt[i+2]), 
			&length );
	    if ( 0 == (status&1) ) return status;
	}
	return 1;
   }
   if ( i > 0 ) {
       status = mst_write ( link, fmt, i, &length );
   } else status = 1;
   return 1;
}

int link_query ( mst_link_t link, char *tag, char *buffer, int bufsize,
	int *reslen )
{
    int status, length;
    status = mst_write ( link, tag, tu_strlen(tag), &length );
    if ( (status&1) == 0 ) return status;
    status = mst_read ( link, buffer, bufsize, reslen );
    return status;
}

struct private_ctx *init_private_context ( mst_link_t link, char **prolog ) 
{
    struct private_ctx *ctx;
    int status, id2_len;
    /*
     * Allocate from pool.
     */
    pthread_mutex_lock ( &private_ctx_lock );
    ctx = ctx_pool;
    if ( !ctx ) {
	/* Expand pool */
	int i;
	LOCK_C_RTL
	ctx = (struct private_ctx *) malloc ( sizeof(struct
		private_ctx)*POOL_EXPAND_SIZE );
	UNLOCK_C_RTL
	if ( !ctx ) { pthread_mutex_unlock ( &private_ctx_lock ); return ctx; }
	for ( i = 0; i < POOL_EXPAND_SIZE-1; i++ ) ctx[i].next = &ctx[i+1];
	ctx[POOL_EXPAND_SIZE-1].next = (struct private_ctx *) 0;
    }
    ctx_pool = ctx->next;
    pthread_mutex_unlock ( &private_ctx_lock );

    ctx->next = (struct private_ctx *) 0;
    ctx->link = link;
    ctx->prolog = prolog;
    ctx->tag_verify = 0;
    ctx->cgimode_active = 0;
    ctx->accessesKnown = 0;
    ctx->have_lock = 0;
    ctx->last_mod = 0;
    ctx->recur_level = 0;
    /*
     * query server for information and parse out local port.  Parse failures
     * leave cache_allowed set to false.
     */
    ctx->cache_allowed = 0;
    id2_len = 0;
    status = link_query ( link, "<DNETID2>", ctx->id2_info,
	sizeof(ctx->id2_info)-1, &id2_len );
    if ( status&1 ) {
	char *port_str;
	int server_port, digit;
	port_str = tu_strstr ( ctx->id2_info, " " );
	if ( port_str ) port_str = tu_strstr ( port_str+1, " " );
	if ( port_str ) port_str++;	/* skip space */
	for ( server_port = 0; *port_str; ) {
	    digit = *port_str++ - '0';
	    if ( digit < 0 || digit > 9 ) break;
	    server_port = (server_port*10) + digit;
	    if ( server_port > 65536 ) break;
	    if ( *port_str == ' ' ) {
		ctx->cache_allowed = tfc_test_port_number ( server_port ) ? 0 : 1;
		break;
	    }
	}

    }
    ctx->id2_info[id2_len] = '\0';
    pthread_setspecific ( private_context, ctx );
    return ctx;
}
/***************************************************************************/
/* Main routine.
 */
int preproc ( mst_link_t link, char *service, char *info, int ndx, int avail )
{
    void *hfile;
    int status, is_directive, i, j, k, length, s_size, used, sc, sg_size;
    int argc;
    char *source, *targ[11];
    char *prolog[4], *argv[5];
    struct private_ctx *ctx;		/* per-thread context structure */
    char errmsg[256], plbuf[2048];
    /*
     * Read prolog sent by server and convert to wwwexec style arglist.
     */
    if ( http_log_level > 6 )  tlog_putlog ( 7,
	"Preprocessor thread started, ndx: !SL avail !SL!/", ndx, avail );
    for ( i = 0; i < 4; i++ ) {
	prolog[i] = &plbuf[256*i];
        status = mst_read ( link, prolog[i], 255, &length );
	if ( (status&1) == 0 ) return status;
	prolog[i][length] = '\0';		/* terminate string */
    }
    argv[0] = "HTML_PREPROCESSOR";
    argv[1] = prolog[1];		/* Method */
    argv[2] = prolog[3];		/* translated path */
    argv[3] = prolog[2][0] ? "HTTP/1.0 " : "";	/* protocol version */
    /*
     * Setup per-thread context.
     */
    ctx = init_private_context(link, prolog);
    if ( !ctx ) return 0;
    /*
     * Call 'common' main routine.
     */
    status = preproc_main ( 4, argv );
    if ( http_log_level > 6 )
        tlog_putlog(7,"Preprocessor final status: !SL, cgi active: !SL!/", 
	status, ctx->cgimode_active );
    /*
     * do clean close of CGIMODE if abort occured.
     */
    if ( ctx->cgimode_active ) {
	ctx->cgimode_active = 0;
	mst_write ( ctx->link, "</DNETCGI>", 10, &length );
    }
    return status;
}
/***************************************************************************/
/* Substitute routine for net_link_printf
 */
static int preproc_printf ( char *fmt, ... )
{
    va_list alist;
    int i,j, status, length, reslen, i_arg;
    char *c_arg;
    struct private_ctx *ctx;
    char numbuf[16];

    GET_SPECIFIC ( private_context, ctx )
    /*
     * Setup pointer to variable argument list.
     */
    va_start ( alist, fmt );
    status = 1;
    for ( i = j = 0; fmt[i]; i++ ) {
	if ( fmt[i] == '%' ) {
	    if ( j < i ) {
		status = mst_write ( ctx->link,  &fmt[j], i-j, &reslen );
		if ( (status&1) == 0 ) return status;
		j = i+2;
	    }
	    i++;
	    if ( fmt[i] == '\0' ) break;
	    switch ( fmt[i] ) {
		case 's':
		    c_arg = va_arg ( alist, char * );
		    length = c_arg ? tu_strlen(c_arg) : 0;
		    if ( length > 0 ) {
			status = mst_write (ctx->link, c_arg, length, &reslen);
			if ( (status&1) == 0 ) return status;
		    }
		    break;
		case 'd':
		    i_arg = va_arg ( alist, int );
		    tu_strint ( i_arg, numbuf );
		    status = mst_write ( ctx->link, numbuf,
			tu_strlen(numbuf), &reslen );
		    if ( (status&1) == 0 ) return status;

		    break;
		default:
		break;
	    }
	}
    }
    if ( j < i ) {
	status = mst_write ( ctx->link,  &fmt[j], i-j, &reslen );
    }
    return status;
}
/***************************************************************************/
/* Substitute routine for net_link_query, using mst communication.
 */
static int preproc_link_query 
	( char *tag, char *buffer, int size, int *length )
{
    struct private_ctx *ctx;
    int reslen, status;

    GET_SPECIFIC ( private_context, ctx )
    status = mst_write ( ctx->link, tag, tu_strlen(tag), &reslen );
    if ( (status&1) == 1 ) 
	status = mst_read ( ctx->link, buffer, size, length );
    return status;
}
/***************************************************************************/
/*
 * Define macros so operations are transparent.
 */
#ifdef malloc
#undef malloc
#endif
#ifdef free
#undef free
#endif
#ifdef realloc
#undef realloc
#endif
#ifdef calloc
#undef calloc
#endif
#define malloc tm_malloc
#define free tm_free
#define realloc tm_realloc
#define calloc tm_calloc

#define strchr my_strchr
#define strrchr my_strrchr
#ifndef tu_strncpy
#define strncpy tu_strncpy
#endif
#ifndef tu_strncmp
#define strncmp tu_strncmp
#endif
#ifndef tu_strlen
#define strlen tu_strlen
#endif
#ifndef tu_strcpy
#define strcpy tu_strcpy
#endif

#define net_link_printf preproc_printf
#define net_link_query preproc_link_query

#define IS_MST
#include "../script_code/html_preproc.c"
