File:  [Public] / libwww / Library / src / HTNews.c
Revision 2.67: download - view: text, annotated - select for diffs
Wed Jul 7 15:43:28 1999 UTC (24 years, 11 months ago) by frystyk
Branches: MAIN
CVS tags: Release-5-3-1, HEAD, Amaya-6-3, Amaya-6-1, Amaya-5-2, Amaya-4-3-2, Amaya-4-3-1, Amaya-4-3, Amaya-4-1-2, Amaya-4-1-0, Amaya-4-0-0, Amaya-3-2-1, Amaya-3-2
Committing a set of patches that fixed the problem with listening on
sockets. The implementation is still client specific - it doesn't
handle generic incoming connections. However, it works for FTP and the
simple listen sample application. A future architecture should support
incoming connections as well as outgoing connections. For now, the
mget tool also works when downloading multiple files from an FTP
server using both PASV and PORT.

/*			NEWS ACCESS				HTNews.c
**			===========
**
** History:
**	26 Sep 90	Written TBL
**	29 Nov 91	Downgraded to C, for portable implementation.
**	16 Feb 94  AL	Added Lou Montulli's Lynx & LIST NEWSGROUPS diffs.
**	 2 May 94  AL	Added HTUnEscape() to HTLoadNews(), and
**			fixed a possible security hole when the URL contains
**			a newline, that could cause multiple commands to be
**			sent to an NNTP server.
**	 8 Jul 94  FM	Insulate free() from _free structure element.
**	30 Aug 95  FTLO Added POST functionality and updated state machine
**	30 Aug 95  HFN	Cleaned up a whole lot and made a state machine
*/

/* Library Include files */
#include "wwwsys.h"
#include "WWWUtil.h"
#include "WWWCore.h"
#include "WWWStream.h"
#include "WWWTrans.h"
#include "HTReqMan.h"				/* @@@ */
#include "HTNewsRq.h"
#include "HTNewsLs.h"
#include "HTNews.h"					       /* Implements */

/* Macros and other defines */
#ifndef NEWS_LIST_FILE
#define NEWS_LIST_FILE		".www_news"	   /* Name of news list file */
#endif

#define MAX_NEWS_ARTICLES	0 	/* No default max number of articles */

#define PUTBLOCK(b, l)	(*me->target->isa->put_block)	     (me->target, b, l)
#define ABORT_TARGET    (*me->target->isa->abort)            (me->target, e)

/* Local context structure used in the HTNet object */
typedef enum _HTNewsState {
    NEWS_ERROR		= -3,
    NEWS_SUCCESS	= -2,
    NEWS_NO_DATA	= -1,
    NEWS_BEGIN		= 0,
    NEWS_SEEK_CACHE,
    NEWS_NEED_CONNECTION,
    NEWS_NEED_GREETING,
    NEWS_NEED_SWITCH,
    NEWS_NEED_ARTICLE,
#if HT_LISTGROUP
    NEWS_NEED_LGRP,
#endif
    NEWS_NEED_LIST,
    NEWS_NEED_GROUP,
    NEWS_NEED_XOVER,
    NEWS_NEED_HEAD,
    NEWS_NEED_POST,
    NEWS_NEED_BODY
} HTNewsState;

typedef struct _news_info {
    HTChunk *		cmd;
    int			repcode;
    char *		reply;
    HTNewsState         state;                    /* State of the connection */
    HTFormat		format;
    char *		name;		     /* Name of article or newsgroup */
    BOOL		sent;			    /* Read or write command */
    int                 first;            /* First article in the group list */
    int                 last;              /* Last article in the group list */
    int			total;		     /* Estimated number of articles */
    int                 current;                 /* To count # of HEADS sent */
    HTNet *		net;
} news_info;

/* This version of a stream is used for NEWS LIST conversion to HTML */
struct _HTStream {
    const HTStreamClass *	isa;
    HTStream *			target;
    HTRequest *			request;
    news_info *			news;
    HTEOLState			EOLstate;
    BOOL			semi_trans;
    BOOL			junk;
    char 			buffer[MAX_NEWS_LINE+1];
    int				buflen;
    HTHost *			host;
};

struct _HTInputStream {
    const HTInputStreamClass *	isa;
};

PRIVATE int MaxArt = MAX_NEWS_ARTICLES;

/* ------------------------------------------------------------------------- */
/*			       NEWS INPUT STREAM			     */
/* ------------------------------------------------------------------------- */

/*	ScanResponse
**	------------
**	Analyzes the response from the NNTP server.
**	We only expect one line response codes.
**	Returns HT_LOADED if OK, HT_ERROR if error
*/
PRIVATE int ScanResponse (HTStream * me)
{
    news_info *news = me->news;
    *(me->buffer+me->buflen) = '\0';
    if (isdigit((int) *(me->buffer))) sscanf(me->buffer, "%d", &news->repcode);
    me->buflen = 0;
    news->reply = me->buffer+4;
    HTTRACE(PROT_TRACE, "News Rx..... `%s\'\n" _ news->reply);

    /* If 2xx code and we expect data then go into semi-transparent mode */
    if (me->news->format && news->repcode/100 == 2) {
	HTRequest *req = me->request;
	me->target = HTStreamStack(me->news->format, req->output_format,
				   req->output_stream, req, NO);
	me->semi_trans = YES;
	if (!me->target) return HT_ERROR;
    } else if (news->repcode/100 == 4) {
	HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NOT_FOUND,
			   news->reply, strlen(news->reply), "ScanResponse");
    }
    return HT_LOADED;
}

/*
**	Searches for NNTP header line until buffer fills up or a CRLF or LF
**	is found
*/
PRIVATE int HTNewsStatus_put_block (HTStream * me, const char * b, int l)
{
    int status;
    HTHost_setConsumed(me->host, l);
    while (!me->semi_trans && l-- > 0) {
	if (me->EOLstate == EOL_FCR) {
	    if (*b == LF) {
		if (me->junk) me->junk = NO;
		me->EOLstate = EOL_BEGIN;
		if ((status = ScanResponse(me)) != HT_LOADED) return status;
	    }
	} else if (*b == CR) {
	    me->EOLstate = EOL_FCR;
	} else if (*b == LF) {
	    if (me->junk) me->junk = NO;
	    me->EOLstate = EOL_BEGIN;
	    if ((status = ScanResponse(me)) != HT_LOADED) return status;
	} else {
	    *(me->buffer+me->buflen++) = *b;
	    if (me->buflen >= MAX_NEWS_LINE) {
		HTTRACE(PROT_TRACE, "News Status. Line too long - chopped\n");
		me->junk = YES;
		if ((status = ScanResponse(me)) != HT_LOADED) return status;
	    }
	}
	b++;
    }	

    /*
    ** Now see if we have parts of the body to put down the stream pipe.
    ** At this point we are looking for CRLF.CRLF. We are guaranteed a stream
    */
    if (l > 0) {
	int rest = l;
	const char *ptr = b;
	while (rest-- > 0) {
	    if (*ptr == CR) {
		me->EOLstate = me->EOLstate==EOL_DOT ? EOL_SCR : EOL_FCR;
	    } else if (*ptr == '.') {
		me->EOLstate = me->EOLstate==EOL_FLF ? EOL_DOT : EOL_BEGIN;
	    } else if (*ptr == LF) {
		me->EOLstate = me->EOLstate>EOL_DOT ? EOL_SLF : EOL_FLF;
	    } else
		me->EOLstate = EOL_BEGIN;
	    ptr++;
	}
	if (me->EOLstate == EOL_SLF) {
	    int status = PUTBLOCK(b, l-5);
	    return status != HT_OK ? status : HT_LOADED;
	} else {
	    int status = PUTBLOCK(b, l);
	    return status;
	}
    }
    return HT_LOADED;
}

PRIVATE int HTNewsStatus_put_character (HTStream * me, char ch)
{
    return HTNewsStatus_put_block(me, &ch, 1);
}

PRIVATE int HTNewsStatus_put_string (HTStream * me, const char * str)
{
    return HTNewsStatus_put_block(me, str, (int) strlen(str));
}

PRIVATE int HTNewsStatus_flush (HTStream * me)
{
    return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
}

PRIVATE int HTNewsStatus_free (HTStream * me)
{
    int status = HT_OK;
    if (me->target) {
	if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
	    return HT_WOULD_BLOCK;
    }
    HT_FREE(me);
    return status;
}

PRIVATE int HTNewsStatus_abort (HTStream * me, HTList * e)
{
    if (me->target)
        ABORT_TARGET;
    HT_FREE(me);
    HTTRACE(PROT_TRACE, "NewsStatus.. ABORTING...\n");
    return HT_ERROR;
}

PRIVATE const HTStreamClass HTNewsStatusClass =
{		
    "NewsStatus",
    HTNewsStatus_flush,
    HTNewsStatus_free,
    HTNewsStatus_abort,
    HTNewsStatus_put_character,
    HTNewsStatus_put_string,
    HTNewsStatus_put_block
};

PRIVATE HTStream * HTNewsStatus_new (HTRequest * request, news_info * news,
				     HTHost * host)
{
    HTStream *me;
    if ((me = (HTStream  *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
        HT_OUTOFMEM("HTNewsStatus_new");
    me->isa = &HTNewsStatusClass;
    me->request = request;
    me->news = news;
    me->EOLstate = EOL_BEGIN;
    me->host = host;
    return me;
}

/* ------------------------------------------------------------------------- */
/*				PROTOCOL FUNCTIONS			     */
/* ------------------------------------------------------------------------- */

/*
**	Set the max number of articles at the same time.
**	Default is MAX_NEWS_ARTICLES
*/
PUBLIC BOOL HTNews_setMaxArticles (int new_max)
{ 
    if (new_max >= 0) {
	MaxArt = new_max;
	return YES;
    }
    return NO;
}

/*
**	Get current max number of articles at the same time.
*/
PUBLIC int HTNews_maxArticles (void)
{ 
    return MaxArt;
}

/*	HTNewsCleanup
**	-------------
**      This function closes the connection and frees memory.
**      Returns YES on OK, else NO
*/
PRIVATE int HTNewsCleanup (HTRequest * req, int status)
{
    HTNet * net = HTRequest_net(req);
    news_info *news = (news_info *) HTNet_context(net);
    HTStream * input = HTRequest_inputStream(req);

    /* Free stream with data TO network */
    if (!HTRequest_isDestination(req))
	HTRequest_removeDestination(req);
    else if (input) {
	if (status == HT_INTERRUPTED)
	    (*input->isa->abort)(input, NULL);
	else
	    (*input->isa->_free)(input);
	HTRequest_setInputStream(req, NULL);
    }

    /* Remove the request object and our own context structure for nntp */
    HTNet_delete(net, status);
    if (news) {
	HT_FREE(news->name);
	HTChunk_delete(news->cmd);
	HT_FREE(news);
    }
    return YES;
}

PRIVATE int SendCommand (HTRequest *request, news_info *news,
			 char *token, char *pars)
{
    HTStream * input = HTRequest_inputStream(request);
    int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2;
    HTChunk_clear(news->cmd);
    HTChunk_ensure(news->cmd, len);
    if (pars && *pars)
	sprintf(HTChunk_data(news->cmd), "%s %s%c%c", token, pars, CR, LF);
    else
	sprintf(HTChunk_data(news->cmd), "%s%c%c", token, CR, LF);
    HTTRACE(PROT_TRACE, "News Tx..... %s" _ HTChunk_data(news->cmd));
    return (*input->isa->put_block)(input, HTChunk_data(news->cmd), len);
}

/*		Load data object from NNTP Server		     HTLoadNews
**		=================================
**
**	Given a hypertext addres, this routine loads a document
**
** On Entry,
**	request		The request structure
**
**	returns		HT_ERROR	Error has occured or interrupted
**			HT_WOULD_BLOCK  if operation would have blocked
**			HT_LOADED	if 200 OK
**			HT_NO_DATA	if No Response
**			HT_RETRY	if Service Unavail.
*/
PRIVATE int NewsEvent (SOCKET soc, void * pVoid, HTEventType type);

PUBLIC int HTLoadNews (SOCKET soc, HTRequest * request)
{
    news_info *news;
    HTParentAnchor *anchor = HTRequest_anchor(request);
    HTNet * net = HTRequest_net(request);
    char * url = HTAnchor_physical(anchor);

    HTTRACE(PROT_TRACE, "NNTP........ Looking for `%s\'\n" _ url);
    if ((news = (news_info *) HT_CALLOC(1, sizeof(news_info))) == NULL)
	HT_OUTOFMEM("HTLoadNews");
    news->cmd = HTChunk_new(128);
    news->state = NEWS_BEGIN;
    news->net = net;
    HTNet_setContext(net, news);
    HTNet_setEventCallback(net, NewsEvent);
    HTNet_setEventParam(net, news);  /* callbacks get http* */

    return NewsEvent(soc, news, HTEvent_BEGIN);		/* get it started - ops is ignored */
}

PRIVATE int NewsEvent (SOCKET soc, void * pVoid, HTEventType type)
{
    news_info *news = (news_info *)pVoid;
    int status = HT_ERROR;
    HTNet * net = news->net;
    HTRequest * request = HTNet_request(net);
    HTParentAnchor * anchor = HTRequest_anchor(request);
    char * url = HTAnchor_physical(anchor);
    HTHost * host = HTNet_host(net);
    
    /*
    ** Initiate a new nntp structure and bind to request structure
    ** This is actually state NNTP_BEGIN, but it can't be in the state
    ** machine as we need the structure first.
    */
    if (type == HTEvent_CLOSE) {			      /* Interrupted */
	HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
			   NULL, 0, "HTLoadHTTP");
	HTNewsCleanup(request, HT_INTERRUPTED);
	return HT_OK;
    } else
	news = (news_info *) HTNet_context(net);		/* Get existing copy */

    /* Now jump into the machine. We know the state from the previous run */
    while (1) {
        switch (news->state) {
          case NEWS_BEGIN:
	    news->state = (!strchr(url, '@') && strchr(url, '*')) ?
		NEWS_SEEK_CACHE : NEWS_NEED_CONNECTION;
	    break;

	case NEWS_SEEK_CACHE:
	    if (HTNewsCache_before(request, NULL, 0) == HT_LOADED)
		news->state = NEWS_SUCCESS;
	    else
		news->state = NEWS_NEED_CONNECTION;
	    break;

	  case NEWS_NEED_CONNECTION: 		/* Let's set up a connection */
	    if (!strncasecomp(url, "news:", 5)) {
		HTUserProfile * up = HTRequest_userProfile(request);
		char * newshost = HTUserProfile_news(up);
		StrAllocCopy(news->name, url+5);
		if (newshost) {
		    char *newshack = NULL;    /* Then we can use HTParse :-) */
		    StrAllocCopy(newshack, "news://");
		    StrAllocCat(newshack, newshost);
		    status = HTHost_connect(host, net, (char *) newshack);
		    host = HTNet_host(net);
		    HT_FREE(newshack);
		} else
		    news->state = NEWS_ERROR;
	    } else if (!strncasecomp(url, "nntp:", 5)) {
		news->name = HTParse(url, "", PARSE_PATH);
		status = HTHost_connect(host, net, url);
		host = HTNet_host(net);
	    } else {
		HTTRACE(PROT_TRACE, "News........ Huh?");
		news->state = NEWS_ERROR;
            }
            if (status == HT_OK) {
		BOOL greeting = NO;

		/* Set up the persistent connection */
		if (!HTNet_persistent(net)) {
		    HTNet_setPersistent(net, YES, HT_TP_SINGLE);
		    greeting = YES;
		}

		/*
		** Check the protocol class to see if we have connected to a
		** the right class of server, in this case HTTP.
		*/
		{
		    HTHost * host = HTNet_host(net);
		    char * s_class = HTHost_class(host);
		    if (s_class && strcasecomp(s_class, "nntp")) {
			HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS,
					   NULL, 0, "HTLoadNews");
			news->state = NEWS_ERROR;
			break;
		    }
		    HTHost_setClass(host, "nntp");
		}

		/* 
		** Create the stream pipe FROM the channel to the application.
		** The target for the input stream pipe is set up using the
		** stream stack.
		*/
		{
		    HTStream * rstream = HTNewsStatus_new(request, news, host);
		    HTNet_setReadStream(net, rstream);
		    HTRequest_setOutputConnected(request, YES);
		}

		/*
		** Create the stream pipe TO the channel from the application
		** and hook it up to the request object
		*/
		{
		    HTOutputStream * output = HTNet_getOutput(net, NULL, 0);
		    HTRequest_setInputStream(request, (HTStream *) output);
		}

		news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH;

	    } else if (status == HT_WOULD_BLOCK || status == HT_PENDING)
		return HT_OK;
	    else
		news->state = NEWS_ERROR;
	    break;

	  case NEWS_NEED_GREETING:
	    status = HTHost_read(HTNet_host(net), net);
	    if (status == HT_WOULD_BLOCK)
		return HT_OK;
	    else if (status == HT_LOADED) {
		if (news->repcode/100 == 2)
		    news->state = NEWS_NEED_SWITCH;
		else
		    news->state = NEWS_ERROR;
	    } else
		news->state = NEWS_ERROR;
	    break;

	  case NEWS_NEED_SWITCH:
	  {
	      HTMethod method = HTRequest_method(request);
	      /*
	      ** Find out what to ask the news server. Syntax of address is
	      **	xxx@yyy		Article
	      **	<xxx@yyy>	Same article
	      **	xxxxx		News group (no "@")
	      */
	      if (method == METHOD_GET) {
		  if (strchr(url, '@')) {				  /* ARTICLE */
		      if (*(news->name) != '<') {		  /* Add '<' and '>' */
			  char *newart;
			  if ((newart = (char  *) HT_MALLOC(strlen(news->name)+3)) == NULL)
			      HT_OUTOFMEM("HTLoadNews");
			  sprintf(newart, "<%s>", news->name);
			  HT_FREE(news->name);
			  news->name = newart;
		      }
		      news->state = NEWS_NEED_ARTICLE;
		  } else if (strchr(url, '*'))
		      news->state = NEWS_NEED_LIST;
		  else
		      news->state = NEWS_NEED_GROUP;
	      } else if (method == METHOD_POST)
		  news->state = NEWS_NEED_POST;
	      else {
		  HTRequest_addError(request, ERR_FATAL, NO,
				     HTERR_NOT_IMPLEMENTED,NULL, 0,"HTLoadNews");
		  news->state = NEWS_ERROR;
	      }
	      HTUnEscape(news->name);
	      HTCleanTelnetString(news->name);
	  }
	  break;

	  case NEWS_NEED_ARTICLE:
	    if (!news->sent) {
		status = SendCommand(request, news, "ARTICLE", news->name);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_ERROR)
		    news->state = NEWS_ERROR;
		news->format = WWW_MIME;

		/*
		** Set the default content type to plain text as news servers
		** almost never send any useful information about the length
		** of the body or the type - the success of MIME!
		*/
		HTAnchor_setFormat(anchor, WWW_PLAINTEXT);
		news->sent = YES;
	    } else {
		status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_OK)
		    news->state = NEWS_NEED_BODY;
		else if (status == HT_LOADED) {
		    news->state = (news->repcode/100 == 2) ?
			NEWS_SUCCESS : NEWS_ERROR;
		} else
		    news->state = NEWS_ERROR;
		news->sent = NO;
	    }
	    break;

#if HT_LISTGROUP
	  case NEWS_NEED_LGRP:
	    if (!news->sent) {
		status = SendCommand(request, news, "LIST", "NEWSGROUPS");
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_ERROR)
		    news->state = NEWS_ERROR;
 		news->format = WWW_NNTP_LIST;
		news->sent = YES;
	    } else {
		status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_OK)
		    news->state = NEWS_NEED_BODY;
		else if (status == HT_LOADED) {
		    news->state = (news->repcode/100 == 2) ?
			NEWS_SUCCESS : NEWS_NEED_LIST;
		} else
		    news->state = NEWS_ERROR;
		news->sent = NO;
	    }
	    break;
#endif /* HT_LISTGROUP */

	  case NEWS_NEED_LIST:
	    if (!news->sent) {
		status = SendCommand(request, news, "LIST", NULL);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_ERROR)
		    news->state = NEWS_ERROR;
		news->format = WWW_NNTP_LIST;
		news->sent = YES;
	    } else {
		status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_OK)
		    news->state = NEWS_NEED_BODY;
		else if (status == HT_LOADED) {
		    news->state = (news->repcode/100 == 2) ?
			NEWS_SUCCESS : NEWS_ERROR;
		} else
		    news->state = NEWS_ERROR;
		news->sent = NO;
	    }
	    break;

	  case NEWS_NEED_GROUP:
	    if (!news->sent) {
		status = SendCommand(request, news, "GROUP", news->name);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_ERROR)
		    news->state = NEWS_ERROR;
		news->sent = YES;
	    } else {
		status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_LOADED) {
		    if (news->repcode/100 == 2) {
			if (sscanf(news->reply, "%d%d%d", &news->total,
				   &news->first, &news->last) == 3) {
			    if (MaxArt && news->total>MaxArt)
				news->last = news->first-MaxArt;
			    news->current = news->first;

			    /* If no content in this group */
			    if (news->first == news->last) {
				HTRequest_addError(request, ERR_FATAL, NO,
						   HTERR_NO_CONTENT,
						   NULL, 0, "HTLoadNews");
				news->state = NEWS_NO_DATA;
				break;
			    }
			    news->state = NEWS_NEED_XOVER;
			} else
			    news->state = NEWS_ERROR;
		    } else
			news->state = NEWS_ERROR;
		} else
		    news->state = NEWS_ERROR;
		news->sent = NO;
	    }
	    break;

	  case NEWS_NEED_XOVER:
	    if (!news->sent) {
		char buf[20];
		sprintf(buf, "%d-%d", news->first, news->last);
		status = SendCommand(request, news, "XOVER", buf);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_ERROR)
		    news->state = NEWS_ERROR;
		news->format = WWW_NNTP_OVER;
		news->sent = YES;
	    } else {
		status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_OK)
		    news->state = NEWS_NEED_BODY;
		else if (status == HT_LOADED) {
		    if (news->repcode/100 == 2)
			news->state = NEWS_SUCCESS;
		    else {
			news->format = WWW_NNTP_HEAD;
			news->state = NEWS_NEED_HEAD;
		    }
		} else
		    news->state = NEWS_ERROR;
		news->sent = NO;
	    }
	    break;

	  case NEWS_NEED_HEAD:
	    if (!news->sent) {
		char buf[10];
		sprintf(buf, "%d", news->current++);
		status = SendCommand(request, news, "HEAD", buf);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_ERROR)
		    news->state = NEWS_ERROR;
		news->sent = YES;
	    } else {
		status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
		else if (status == HT_LOADED) {
		    if (news->repcode/100 == 2) {
			if (news->current > news->last)
			    news->state = NEWS_SUCCESS;
		    } else
			news->state = NEWS_ERROR;
		} else
		    news->state = NEWS_ERROR;
		news->sent = NO;
	    }
	    break;

	  case NEWS_NEED_POST:
	  {
	      HTStream * oldinput = HTRequest_inputStream(request);
	      HTStream * newinput =
		  HTNewsPost_new(request, HTBuffer_new(oldinput, request,512));
	      HTRequest_setInputStream(request, newinput);
	      
	      /* Remember to convert to CRLF */

	  }
	  news->state = NEWS_NEED_BODY;
	  break;

          case NEWS_NEED_BODY:
            if (type == HTEvent_WRITE || type == HTEvent_BEGIN) {
		if (HTRequest_isDestination(request)) {
		    HTRequest * source = HTRequest_source(request);
		    HTNet * srcnet = HTRequest_net(source);
		    if (srcnet) {
			HTHost_register(HTNet_host(srcnet), srcnet, HTEvent_READ);
			HTHost_unregister(HTNet_host(srcnet), srcnet, HTEvent_WRITE);
		    }
		    return HT_OK;
		}

		/*
		**  Should we use the input stream directly or call the post
		**  callback function to send data down to the network?
		*/
		{
		    HTStream * input = HTRequest_inputStream(request);
		    HTPostCallback * pcbf = HTRequest_postCallback(request);
		    if (pcbf) {
			status = pcbf(request, input);
			if (status == HT_PAUSE || status == HT_LOADED)
			    type = HTEvent_READ;
		    } else {
			status = (*input->isa->flush)(input);
			type = HTEvent_READ;
		    }
		    if (status == HT_WOULD_BLOCK) return HT_OK;
		}
		status = request->PostCallback ?
                    request->PostCallback(request, request->input_stream) :
			(*request->input_stream->isa->flush)(request->input_stream);
 		if (status == HT_WOULD_BLOCK)
                    return HT_OK;
                else 	
                    type = HTEvent_READ;	  /* Trick to ensure that we do READ */
	    } else if (type == HTEvent_READ) {
                status = HTHost_read(HTNet_host(net), net);
		if (status == HT_WOULD_BLOCK)
		    return HT_OK;
                else if (status == HT_LOADED)
		    news->state = NEWS_SUCCESS;
		else
		    news->state = NEWS_ERROR;
	    } else {
		news->state = NEWS_ERROR;
	    }
	    break;
		
	  case NEWS_SUCCESS:
	    HTNewsCleanup(request, HT_LOADED);
	    return HT_OK;
	    break;

	case NEWS_NO_DATA:
	    HTNewsCleanup(request, HT_NO_DATA);
	    return HT_OK;
	    break;

	  case NEWS_ERROR:
	    HTNewsCleanup(request, HT_NOT_FOUND);
	    return HT_OK;
	    break;
	}
    } /* End of while(1) */
}

Webmaster