/*
 * Test client for fastcgi.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef VMS
#ifndef __GNUC__
#include <unixio.h>
#include <types.h>
#include <ucx$inetdef.h>
#else
#define caddr_t caddr_t_gcc
#define u_char u_char_gcc
#define u_long u_long_gcc
#define u_short u_short_gcc
#endif
#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>
struct fcgi_message_header {
	/* 
         * FASTCGI record structure.  Note that version byte is declared
         * as an array to facilitate continutation while reading header.
         */
	unsigned char version[1];	/* FASTCGI version (1) */
	unsigned char type;		/* Record type: */
	unsigned char requestid_msb;	/* High byte of request ID */
	unsigned char requestid_lsb;
	unsigned char contentlength_msb;  /* High byte of data length */
	unsigned char contentlength_lsb;
	unsigned char paddinglength;	/* # of pad bytes that follow data */
        unsigned char reserved;
};

static int send_message ( int sd, int msgtype, int id,
	int msg_size, char *message )
{
    struct fcgi_message_header hdr;
    int i, total_length, segment, chunk;

    hdr.version[0] = 1;
    hdr.type = msgtype;
    hdr.requestid_msb = id >> 8;
    hdr.requestid_lsb = id & 255;
    hdr.contentlength_msb = msg_size >> 8;
    hdr.contentlength_lsb = msg_size & 255;
    hdr.paddinglength = hdr.reserved  = 0;

    for ( i = 0; i < 8; i += segment ) {
	segment = write ( sd, &hdr.version[i], 8-i );
	if ( segment <= 0 ) { printf("send error: %d\n", segment); return 0;}
    }
    for ( i = 0; i < msg_size; i += segment ) {
	chunk = msg_size-i;
	segment = write ( sd, &message[i], chunk > 4096 ? 4096 : chunk );
	if ( segment <= 0 ) { printf("send error: %d\n", segment); return 0;}
    }
    return 1;
}

static int recv_message ( int sd, int *msgtype, int *id, 
	int *msgsize, char **message )
{
    struct fcgi_message_header hdr;
    char *buf;
    int i, total_length, segment;
    for ( i = 0; i < 8; i += segment ) {
        segment = read ( sd, &hdr.version[i], 8-i );
	if ( segment <= 0 ) return 0;
    }
    *msgtype = hdr.type;
    *id = (hdr.requestid_msb<<8) | hdr.requestid_lsb;
    *msgsize = (hdr.contentlength_msb<<8) | hdr.contentlength_lsb;
    total_length = *msgsize + hdr.paddinglength;

    *message = buf = malloc ( total_length );
    for ( i = 0; i < total_length; i += segment ) {
        segment = read ( sd, &buf[i], total_length-i );
	if ( segment <= 0 ) { printf("recv error: %d\n", segment ); return 0; }
    }
    return 1;
}
/************************************************************************/
int main ( int argc, char **argv )
{
    int mgr_port, status, sd, i, j, timeout, rem_port, CLI$GET_VALUE();
    int cd, msgtype, msgsize, id; unsigned int ra_len, pstream, istream;
    int optval, optsize;
    char *target;
    struct sockaddr local, remote;
    struct hostent *hostinfo;
    char remote_host[256];
    static char getvalues_msg[] = { 14, 1, 'F', 'C', 'G', 'I', '_', 'M',
	'A', 'X', '_', 'C', 'O', 'N', 'N', 'S', '1', 13, 1, 'F', 'C', 'G', 'I',
	'_', 'M', 'A', 'X', '_', 'R', 'E', 'Q', 'S', '1', 15, 1, 'F', 'C', 'G',
	'I', '_', 'M', 'P', 'X', 'S', '_', 'C', 'O', 'N', 'N', 'S', '0'
    };
    /*
     * Extract arguments from command line.
     */
    if ( argc < 2 ) {
	fprintf(stderr,"Missing arguments, usage:\n");
	fprintf(stderr,"   $ testfcgi listen-port\n");
	exit ( 1 );
    }
    mgr_port = atoi ( argv[1] );
    if ( mgr_port < 0 ) {
	fprintf(stderr,"Invalid port number\n" );
	exit ( 1 );
    }
    /*
     * Create socket and bind to port.
     */
    sd = socket ( AF_INET, SOCK_STREAM, 0 );
    if ( sd < 0 ) {
	    fprintf(stderr,"Error creating socket: %s\n", strerror(errno) );
    }
    optval = 1;
    optsize = sizeof(int);
    status = setsockopt ( sd, SOL_SOCKET, UCX$C_SO_SHARE, (char *) &optval,
		optsize);
    printf("Status of setsockopt reuseaddr: %d\n", status);

    local.sa_family = AF_INET;
    for ( j = 0; j < sizeof(local.sa_data); j++ ) local.sa_data[j] = '\0';
    local.sa_data[0] = mgr_port >> 8;
    local.sa_data[1] = (mgr_port&255);
    status = bind ( sd, &local, sizeof ( local ) );
    if ( status < 0 ) {
	fprintf(stderr,"Error binding socket: %s\n", strerror(errno) );
    }
    /*
     * Listen on port and accept connects.
     */
    status = listen ( sd, 5 );
    fprintf(stderr,"Status of listen: %d\n", status );

    for ( ra_len = sizeof(remote); 
	-1 < (cd = accept(sd, &remote, &ra_len)); ra_len = sizeof(remote) ) {
        printf("Got connection\n" );
	status = recv_message ( cd, &msgtype, &id, &msgsize, &target );
	printf("Recv status: %d, type: %d, id: %d, size: %d\n",
		status, msgtype, id, msgsize );

	if ( msgtype == 9 && id == 0 ) {
	    free ( target );
	    status = recv_message ( cd, &msgtype, &id, &msgsize, &target );
	    printf("2nd recv status: %d, type: %d, id: %d, size: %d\n",
		status, msgtype, id, msgsize );
	    status = send_message ( cd, 10, 0, sizeof(getvalues_msg),
			getvalues_msg );
	    printf ( "Send status: %d (%d)\n", status, sizeof(getvalues_msg) );
	}
	free ( target );

	for ( pstream = istream = 1; (pstream | istream) && (status&1); ) {
	    status = recv_message ( cd, &msgtype, &id, &msgsize, &target );
	    printf("Recv status: %d, type: %d, id: %d, size: %d\n",
		status, msgtype, id, msgsize );
	    if ( msgtype == 4 && msgsize == 0 ) pstream = 0;
	    if ( msgtype == 5 && msgsize == 0 ) istream = 0;
	    if ( (status&1) ) free ( target );
	}
	status = send_message ( cd, 6, id, 47,
	    "Content-type: text/plain\r\n\r\nGot here didn't I?\n" );

	if ( (status&1) == 1 ) {
	    char end_request[8] = { 0, 0, 0, 1, 0, 0, 0, 0 };
	    /* END_REQUEST message*/
	    status = send_message ( cd, 3, id, 8, end_request );
	}
	
	close ( cd );
    }
    return 1;
}
