/*			CHARGEN ACCESS				HTChargen.c
**			==============
** Authors:
**  FM  Foteos Macrides
**
** History:
**	26 Sep 97   First version.  Supports the URL format chargen://host/
**		    where any username field and path are ignored, and the
**		    port must be defaulted or 19.
*/

#include "HTUtils.h"
#include "tcp.h"
#include "HTAlert.h"
#include "HTML.h"
#include "HTParse.h"
#include "HTFormat.h"
#include "HTTCP.h"
#include "HTString.h"
#include "HTChargen.h"
#include <signal.h>

#include "LYLeaks.h"

/* #define TRACE 1 */

#define CHARGEN_PORT 19
#define BUF_SIZE 4096

#define FREE(x) if (x) {free(x); x = NULL;}

#define PUTC(c) (*targetClass.put_character)(target, c)
#define PUTS(s) (*targetClass.put_string)(target, s)
#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
#define END(e) (*targetClass.end_element)(target, e, 0)
#define FREE_TARGET (*targetClass._free)(target)
#define NEXT_CHAR HTGetCharacter() 


/*	Module-wide variables
*/
struct _HTStructured {
	CONST HTStructuredClass * isa;		/* For chargen streams */
	/* ... */
};

PRIVATE HTStructured * target;			/* The output sink */
PRIVATE HTStructuredClass targetClass;		/* Routine entry points */


/*		Load by name					HTLoadChargen
**		============
*/
PUBLIC int HTLoadChargen ARGS4(
	CONST char *,		arg,
	HTParentAnchor *,	anAnchor,
	HTFormat,		format_out,
	HTStream*,		stream)
{
    char *p1, *host;			/* Fields extracted from URL */
    char *colon, *at_sign;		/* Fields extracted from URL */
    int port;				/* Port number from URL */
    int s = -1;				/* Socket for ChargenHost */
    int status;				/* tcp return */
    unsigned int nbytes;		/* tcp read size */
    char buf[BUF_SIZE], line[80], *str;	/* Buffers */
    int len, i, j, k;			/* Indexes */

    if (TRACE) {
        fprintf(stderr, "HTChargen: Looking for %s\n", (arg ? arg : "NULL"));
    }
  
    if (!(arg && *arg)) {
        HTAlert("Could not load data.");
	return HT_NOT_LOADED;			/* Ignore if no name */
    }
  
    /*
    **  Set up the host field.
    */
    p1 = HTParse(arg, "", PARSE_HOST);
    if ((at_sign = strrchr(p1, '@')) != NULL) {
	host = (at_sign +1);
    } else {
	host = p1;
    }

    /*
    **  Ignore if no host.
    */
    if (*host == '\0') {
        HTAlert("Could not load data (no host in chargen URL)");
	FREE(p1);
	return HT_NOT_LOADED;
    }

    /*
    ** Ignore if wrong port.
    */
    if ((colon = strchr(host, ':')) != NULL) {
        *colon++ = '\0';
	port = atoi(colon);
	if (port != 19) {
	    HTAlert("Invalid port number - will only use port 19!");
	    FREE(p1);
	    return HT_NOT_LOADED;
	}
    }

    /*
    **  Load the string for making a connection.
    */
    str = (char *)calloc(1, (strlen(host) + 10));
    if (str == NULL)
        outofmem(__FILE__, "HTLoadChargen");
    sprintf(str, "lose://%s/", host);
    
    /*
    **  Now, let's get a stream setup up from the chargen host.
    */
    if (TRACE) {
        fprintf(stderr, "HTLoadChargen: doing HTDoConnect on '%s'\n", str);
    }
    status = HTDoConnect(str, "chargen", CHARGEN_PORT, &s);
    if (TRACE)
        fprintf(stderr,
		"HTLoadChargen: Done DoConnect; status %d\n", status);
    if (status == HT_INTERRUPTED) {
        /*
	**  Interrupt cleanly.
	*/
	if (TRACE) {
	    fprintf(stderr,
	      "HTLoadChargen: Interrupted on connect; recovering cleanly.\n");
	}
	HTProgress ("Connection interrupted.");
	FREE(str);
	FREE(p1);
	return HT_NOT_LOADED;
    }
    if (status < 0) {
        NETCLOSE(s);
	if (TRACE) {
	    fprintf(stderr,
		    "HTLoadChargen: Unable to connect to chargen host.\n");
	}
        HTAlert("Could not access chargen host.");
	FREE(str);
	FREE(p1);
	return HT_NOT_LOADED;	/* FAIL */
    }
    if (TRACE) {
        fprintf(stderr,
		"HTLoadChargen: Connected to chargen host '%s'.\n", str);
    }
    FREE(str);

    /*
    **  Accumulate 55 lines of characters into the read buffer.
    */
    len = 0;
    nbytes = 4070;
    sleep(1);
    status = NETREAD(s, buf, nbytes);
    if (TRACE) {
	fprintf(stderr, "HTLoadChargen: Read %d\n", status);
    }
    if (status <= 0) {
	if (status == HT_INTERRUPTED) {
	    if (TRACE) {
		fprintf(stderr, "HTLoadChargen: Interrupted first read.\n");
	    }
	    _HTProgress ("Connection interrupted.");
	} else {
	    if (TRACE) {
		fprintf(stderr,
  "HTLoadChargen: Hit unexpected network read error; aborting; status %d.\n",
			status);
	    }
	    HTAlert("Unexpected network read error; connection aborted.");
	}
	NETCLOSE(s);
	FREE(p1);
	return HT_NOT_LOADED;
    }
    len += status;
    if (len < 4070) {
	nbytes -= len;
	sleep(1);
	while ((status = NETREAD(s, &buf[len], nbytes)) > 0) {
	    if (TRACE) {
		fprintf(stderr, "HTLoadChargen: Read %d\n", status);
	    }
	    len += status;
	    if (len < 4070) {
		nbytes = (4070 - len);
		sleep(1);
	    } else {
		break;
	    }
	}
	if (status <= 0) {
	    if (status == HT_INTERRUPTED) {
		if (TRACE) {
		    fprintf(stderr,
			    "HTLoadChargen: Interrupted read.\n");
		}
		_HTProgress ("Connection interrupted.");
	    } else {
		if (TRACE) {
		    fprintf(stderr,
  "HTLoadChargen: Hit unexpected network read error; aborting; status %d.\n",
			    status);
		}
		HTAlert("Unexpected network read error; connection aborted.");
	    }
        }
    }
    NETCLOSE(s);
    buf[len] = '\0';

    /*
    **  Make a hypertext object.
    */
    target = HTML_new(anAnchor, format_out, stream);
    targetClass = *target->isa;		/* Copy routine entry points */

    /*
    **  Create the results report.
    */
    if (TRACE)
	fprintf(stderr, "HTLoadChargen: Reading chargen information.\n");
    START(HTML_HTML);
    PUTC('\n');
    START(HTML_HEAD);
    PUTC('\n');
    START(HTML_TITLE);
    PUTS("Chargen server on ");
    PUTS(host);
    END(HTML_TITLE);
    PUTC('\n');
    END(HTML_HEAD);
    PUTC('\n');
    START(HTML_BODY);
    PUTC('\n');
    START(HTML_H1);
    PUTS("Chargen server on ");
    START(HTML_EM);
    PUTS(host);
    FREE(p1);
    END(HTML_EM);
    END(HTML_H1);
    PUTC('\n');
    START(HTML_PRE);
    if (len < 73) {
	k = (len - 1);
	for (i = 0, j = 0; i < len; i++) {
	    if (buf[i] != CR && buf[i] != LF) {
		line[j] = buf[i];
		j++;
	    }
	    if (i == k) {
		line[j] = '\n';
		j++;
		line[j] = '\0';
		PUTS(line);
	    }
	}
    } else {
	line[72] = '\n';
	line[73] = '\0';
	for (i = 0, j = 0; i < len; i++) {
	    if (buf[i] != CR && buf[i] != LF) {
		line[j] = buf[i];
		j++;
		if (j == 72) {
		    PUTS(line);
		    j = 0;
		}
	    }
	}
    }
    END(HTML_PRE);
    PUTC('\n');
    END(HTML_BODY);
    PUTC('\n');
    END(HTML_HTML);
    PUTC('\n');
    FREE_TARGET;

    return HT_LOADED;
}

#ifdef GLOBALDEF_IS_MACRO
#define _HTCHARGEN_C_1_INIT { "chargen", HTLoadChargen, NULL }
GLOBALDEF (HTProtocol, HTChargen, _HTChargen_C_1_INIT);
#else
GLOBALDEF PUBLIC HTProtocol HTChargen = { "chargen", HTLoadChargen, NULL };
#endif /* GLOBALDEF_IS_MACRO */
