/*
 * Handle creation of file to be displayed.
 * Arguments:
 *     user		Is owner name of poster.
 *     text_flag	If true, value argument is a text string, else value
 *			is a URL to be fetched.
 *     value		Value string from form, either text or URL.
 *     show_file.	Used as template for creating new file.
 *     new_file		returns created file name.
 *     content_type	Returns content-type: header, text/plain if text.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unixio.h>
#include <string.h>
#include <ctype.h>
#ifdef VMS
#include <unixio.h>
#include <types.h>
#include <socket.h>
#include <in.h>
#else
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <errno.h>
#include <netdb.h>
#include "cgilib.h"
static int copy_url();

int copy_message ( char *user, int text_flag, char *value, char *showfile,
	char *new_file, char *content_type )
{
    int length, status;
    char *res;
    FILE *mf;
    /*
     * make new file.
     */
    strcpy ( new_file, showfile );
    length = strlen ( showfile );
    strcpy ( &new_file[length], "-XXXXXX" );
    res = mktemp ( new_file );
    mf = fopen ( new_file, "w" );
    if ( !mf ) return 0;
    if ( text_flag ) {
	/*
	 * Write contents to message file.
	 */
	length = fwrite ( value, strlen(value), 1, mf );
	length = fwrite ( "\n", 1, 1, mf );
	strcpy ( content_type, "text/plain" );
    } else {
	status = copy_url ( user, value, mf, content_type );
    }
    fclose ( mf );
 
    return 1;
}
/*
 * See if header line matches input.
 */
static void update_field ( char *hdr, char *field_name, char *value )
{
    int i, j, k, len;
    char *line;
    /*
     * Match header, return if no match.
     */
    for ( i = j = 0; hdr[j]; j++ ) if ( hdr[j] == '\n' ) {
	line = &hdr[i];
	i = j + 1;
	for ( k = 0; line[k] != ':'; k++ ) {
	    if ( line[k] == '\n' ) break;
	    if ( _tolower(line[k]) != field_name[k] ) break;
	    if ( !field_name[k] ) break;
	}
	if ( line[k] == ':' && field_name[k] == '\0' ) {
	    /* trim leading spaces */
	    for ( k++; (line[k] != '\n') && isspace(line[k]); k++ );
	    line = &line[k];
	    for ( len=0; line[len] != '\n'; len++ );
	    /* Trim trailing spaces */
	    while ( len > 0 ) { len--; if ( !isspace(line[len]) ) break; }
	    if ( len > 254 ) len = 254;
	    strncpy ( value, line, len+1 );
	    value[len+1] = '\0';
    printf("Extracted %s: '%s'\n", field_name, value );
	    return;
	}
    }
}

static int copy_url ( char *user,  char *url, FILE *mf, char *content_type )
{
    int status, sd, i, j, timeout, in_hdr, rem_port, http_parse_url();
    int binary_mode, simple_request;
    struct sockaddr local, remote;
    struct hostent *hostinfo;
    char *arg_url, *service, *node, *ident, *arg, *remote_host;
    char url_buf[1024];
    /*
     * Extract arguments from command line.
     */
    arg_url = url;
    binary_mode = 1;
    simple_request = 0;
    /*
     * Parse target.
     */
    status = http_parse_url ( url, url_buf, &service, &remote_host,
		&ident, &arg );
    rem_port = 80;
    for ( j = 0; remote_host[j]; j++ ) if ( remote_host[j] == ':' ) {
	rem_port = atoi ( &remote_host[j+1] );
	remote_host[j] = '\0';
	break;
    }
    if ( !*remote_host ) {
	char *def_port;
	remote_host = cgi_info ( "SERVER_NAME" );
	if ( !remote_host ) remote_host = "localhost";
	def_port = cgi_info ( "SERVER_PORT" );
	if ( def_port ) rem_port = atoi(def_port);
    } else remote_host = &remote_host[1];
    /*
     * Create socket on first available port.
     */
    sd = socket ( AF_INET, SOCK_STREAM, 0 );
    if ( sd < 0 ) {
	    fprintf(stderr,"Error creating socket: %s\n", strerror(errno) );
    }
    local.sa_family = AF_INET;
    for ( j = 0; j < sizeof(local.sa_data); j++ ) local.sa_data[j] = '\0';
    status = bind ( sd, &local, sizeof ( local ) );
    if ( status < 0 ) {
	    fprintf(stderr,"Error binding socket: %s\n", strerror(errno) );
    }
    /*
     * Lookup host.
     */
    hostinfo = gethostbyname ( remote_host );
    if ( !hostinfo ) {
	fprintf(stderr, "Could not find host '%s'\n", remote_host );
	return EXIT_FAILURE;
    }
    remote.sa_family = hostinfo->h_addrtype;
    for ( j = 0; j < hostinfo->h_length; j++ ) {
	    remote.sa_data[j+2] = hostinfo->h_addr_list[0][j];
    }
    remote.sa_data[0] = rem_port >> 8;
    remote.sa_data[1] = (rem_port&255);
    /*
     * Connect to remote host.
     */
    status = connect ( sd, &remote, sizeof(remote) );
    if ( status == 0 ) {
	int length;
	char response[1024];
	/*
	 * Send request followed by newline.
	 */
	in_hdr = !simple_request;
	if ( in_hdr )
	    sprintf(response, "GET %s%s HTTP/1.0\r\n\r\n", ident, arg );
	else
	    sprintf(response, "GET %s%s\r\n", ident, arg );
	status = send ( sd, response, strlen(response), 0 );
	if ( status < 0 )
		fprintf(stderr,"status of write: %d %d\n", status, errno );
	strcpy ( content_type, "Text/plain" );
	/*
	 * Read and echo response.
	 */
	if ( status > 0 ) {
	    int i, j, cr, hdr_state;
	    char ch;
	    FILE *outf, *cur_outf;
	    cr = 0;
	    outf = mf;
	    cur_outf = (in_hdr) ? stdout : outf;
	    hdr_state = 0;
	    while (0 < (length=recv(sd, response, sizeof(response)-1,0))) {
		if ( binary_mode && !in_hdr ) {
		    fwrite ( response, 1, length, cur_outf );
		} else {
		    for ( i = j = 0; i < length; i++ ) {
			ch = response[i];
			if ( response[i] == '\r' ) {
			    if ( cr == 1 ) response[j++] = '\r';
			    cr = 1;
			} else if ( cr == 1 ) {
			    if ( ch != '\n' ) response[j++] = '\r';
			    response[j++] = ch;
			    cr = 0;
			} else {
			    response[j++] = ch;
			    cr = 0;
			}
			if ( in_hdr && (ch == '\n') && !cr ) {
			    if ( hdr_state ) { 
				in_hdr = 0; 
			        response[j] = '\0';
				update_field ( response, "content-type",
					  content_type );
				if ( j > 0 ) fprintf (cur_outf,"%s",response);
				cur_outf = outf;
				if ( binary_mode ) { 
				    i++;
				    if ( i < length ) 
					fwrite (&response[i], 1, length-i,outf);
				    break;
				}
				j = 0;
			    }
			    hdr_state = 1;
			} else if ( !cr ) hdr_state = 0;
		    }
		    response[j] = '\0';
		    if ( !binary_mode ) fwrite(response, j, 1, cur_outf );
		}
	    }
	    if ( outf != stdout ) fprintf(outf,"\n");
	    if (outf != stdout) fclose(outf);
	}
    } else {
	fprintf(stderr, "error connecting to '%s': %d/%d\n", remote_host,
		status, errno );
    }
    close ( sd );
    return EXIT_SUCCESS;
}
/***************************************************************************/
int http_parse_url 
	( char *url, 			/* locator to parse */
	char *info,			/* Scratch area for result pts*/
	char **service,			/* Protocol (e.g. http) indicator */
	char **node,			/* Node name. */
	char **ident,			/* File specification. */
	char **arg )			/* Search argument */
	
{
    int i, state;
    char *last_slash, *p, c, arg_c;
    /*
     * Copy contents of url into info area.
     */
    *service = *node = *ident = *arg = last_slash = "";

    for ( state = i = 0; (info[i] = url[i]) != 0; ) {
	c = info[i];
	switch ( state ) {
	    case 0:
		if ( c == ':' ) {
		    info[i] = '\0';	/* terminate string */
		    *service = info;
		    state = 1;
		}
	    case 1:
		if ( c == '/' ) {
		    *ident = last_slash = &info[i];
		    state = 2;
		}
		break;
	    case 2:
		state = 4;
		if ( c == '/' ) {	/* 2 slashes in a row */
		    *node = *ident;
		    state = 3;
		}
		else if ( (c == '#') || (c == '?') ) {
		    arg_c = c;
		    info[i] = '\0';
		    *arg = &info[i+1];
		    state = 5;
		}
		break;
	    case 3:			/* find end of host spec */
		if ( c == '/' ) {
		    state = 4;
		    *ident = last_slash = &info[i];
		    for ( p = *node; p < *ident; p++ ) p[0] = p[1];
		    info[i-1] = '\0';	/* Terminate host string */
		}
		break;
	    case 4:			/* Find end of filename */
		if ( c == '/' ) last_slash = &info[i];
		else if ( (c == '#') || (c == '?') ) {
		    arg_c = c;
		    info[i] = '\0';
		    *arg = &info[i+1];
		    state = 5;
		}
	    case 5:
		break;
        }
	i++;
    }
    /*
     * Insert arg delimiter back into string.
     */
    if ( **arg ) {
	char tmp;
	for ( p = (*arg); arg_c; p++ ) { tmp = *p; *p = arg_c; arg_c = tmp; }
	*p = '\0';
    }
    return 1;
}
