--- libwww/Library/src/HTNews.c 1995/10/15 03:26:13 2.38 +++ libwww/Library/src/HTNews.c 1995/10/27 19:11:40 2.39 @@ -1,8 +1,5 @@ -/* HTNews.c -** NEWS ACCESS -** -** (c) COPYRIGHT MIT 1995. -** Please first read the full copyright statement in the file COPYRIGH. +/* NEWS ACCESS HTNews.c +** =========== ** ** History: ** 26 Sep 90 Written TBL @@ -13,1146 +10,816 @@ ** 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 "tcp.h" -#include "HTUtils.h" /* Coding convention macros */ +#include "HTUtils.h" #include "HTString.h" -#include "HTMLPDTD.h" -#include "HTMLGen.h" #include "HTParse.h" -#include "HTFormat.h" -#include "HTAlert.h" -#include "HTReqMan.h" +#include "HTFWrite.h" +#include "HTWriter.h" +#include "HTConLen.h" #include "HTSocket.h" +#include "HTTCP.h" #include "HTError.h" -#include "HTNews.h" /* Implemented here */ +#include "HTChunk.h" +#include "HTEscape.h" +#include "HTReqMan.h" /* @@@@@@@@@@@@@ */ +#include "HTNetMan.h" /* @@@@@@@@@@@@@ */ +#include "HTNewsRq.h" +#include "HTNews.h" /* Implements */ + +#include "HTMIME.h" + +/* Macros and other defines */ +#ifndef NEWS_PORT +#define NEWS_PORT 119 /* See rfc977 */ +#endif -#define NEWS_PORT 119 /* See rfc977 */ -#define APPEND /* Use append methods */ -#define MAX_CHUNK 40 /* Largest number of articles in one window */ -#define CHUNK_SIZE 20 /* Number of articles for quick display */ +#ifndef NEWS_LIST_FILE +#define NEWS_LIST_FILE ".www_news" /* Name of news list file */ +#endif #ifndef DEFAULT_NEWS_HOST -#define DEFAULT_NEWS_HOST "news" +#define DEFAULT_NEWS_HOST "news" #endif + #ifndef SERVER_FILE -#define SERVER_FILE "/usr/local/lib/rn/server" +#define SERVER_FILE "/usr/local/lib/rn/server" #endif -#define BIG 1024 /* @@@ */ +#define PUTBLOCK(b, l) (*me->target->isa->put_block) (me->target, b, l) +#define ABORT_TARGET (*me->target->isa->abort) (me->target, e) -struct _HTStructured { - CONST HTStructuredClass * isa; - /* ... */ +/* Local context structure used in the HTNet object */ +typedef enum _HTNewsState { + NEWS_BEGIN, + NEWS_SEEK_CACHE, + NEWS_NEED_CONNECTION, + NEWS_NEED_GREETING, + NEWS_NEED_SWITCH, + NEWS_NEED_ARTICLE, +#if 0 + NEWS_NEED_LGRP, +#endif + NEWS_NEED_LIST, + NEWS_NEED_GROUP, + NEWS_NEED_XOVER, + NEWS_NEED_HEAD, + NEWS_NEED_POST, + NEWS_NEED_BODY, + NEWS_ERROR, + NEWS_SUCCESS +} 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 */ +} 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; + HTSocketEOL EOLstate; + BOOL semi_trans; + BOOL junk; + char buffer[MAX_NEWS_LINE+1]; + int buflen; }; +PRIVATE char *HTNewsHost = NULL; +PRIVATE int HTNewsMaxArticles = MAX_NEWS_ARTICLES; -#define NEXT_CHAR HTInputSocket_getCharacter(isoc) -#define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */ -#define GROUP_NAME_LENGTH 256 /* Maximum length of group name */ - - -/* Module-wide variables -*/ -PUBLIC char * HTNewsHost; -PRIVATE struct sockaddr_in soc_address; /* Binary network address */ -PRIVATE SOCKFD s; /* Socket for NewsHost */ -PRIVATE char response_text[LINE_LENGTH+1]; /* Last response */ -PRIVATE HTStructured * target; /* The output sink */ -PRIVATE HTStructuredClass targetClass; /* Copy of fn addresses */ -PRIVATE HTParentAnchor *node_anchor; /* Its anchor */ -PRIVATE int diagnostic; /* level: 0=none 2=source */ - - -#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) -#define END(e) (*targetClass.end_element)(target, e) - -PUBLIC HTInputSocket *isoc; /* @@@ non-reentrant */ - -PUBLIC CONST char * HTGetNewsHost NOARGS -{ - return HTNewsHost; -} - -PUBLIC void HTSetNewsHost ARGS1(CONST char *, value) -{ - StrAllocCopy(HTNewsHost, value); +/* ------------------------------------------------------------------------- */ +/* 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(*(me->buffer))) sscanf(me->buffer, "%d", &news->repcode); + me->buflen = 0; + news->reply = me->buffer+4; + if (PROT_TRACE) fprintf(TDEST, "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; + } + return HT_LOADED; } -/* Initialisation for this module -** ------------------------------ -** -** Except on the NeXT, we pick up the NewsHost name from -** -** 1. Environment variable NNTPSERVER -** 2. File SERVER_FILE -** 3. Compilation time macro DEFAULT_NEWS_HOST -** -** On the NeXT, we pick up the NewsHost name from, in order: -** -** 1. WorldWideWeb default "NewsHost" -** 2. Global default "NewsHost" -** 3. News default "NewsHost" -** 4. Compilation time macro DEFAULT_NEWS_HOST +/* +** Searches for NNTP header line until buffer fills up or a CRLF or LF +** is found */ -PRIVATE BOOL initialized = NO; -PRIVATE BOOL initialize NOARGS +PRIVATE int HTNewsStatus_put_block (HTStream * me, CONST char * b, int l) { - CONST struct hostent *phost; /* Pointer to host - See netdb.h */ - struct sockaddr_in* sin = &soc_address; - - -/* Set up defaults: -*/ - sin->sin_family = AF_INET; /* Family = internet, host order */ - sin->sin_port = htons(NEWS_PORT); /* Default: new port, */ - -/* Get name of Host -*/ -#ifdef NeXTStep - if ((HTNewsHost = NXGetDefaultValue("WorldWideWeb","NewsHost"))==0) - if ((HTNewsHost = NXGetDefaultValue("News","NewsHost")) == 0) - HTNewsHost = DEFAULT_NEWS_HOST; -#else - if (getenv("NNTPSERVER")) { - StrAllocCopy(HTNewsHost, (char *)getenv("NNTPSERVER")); - if (WWWTRACE) fprintf(TDEST, "HTNews: NNTPSERVER defined as `%s'\n", - HTNewsHost); - } else { - char server_name[256]; - FILE* fp = fopen(SERVER_FILE, "r"); - if (fp) { - if (fscanf(fp, "%s", server_name)==1) { - StrAllocCopy(HTNewsHost, server_name); - if (WWWTRACE) fprintf(TDEST, - "HTNews: File %s defines news host as `%s'\n", - SERVER_FILE, HTNewsHost); + while (!me->semi_trans && l-- > 0) { + int status; + 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) { + if (PROT_TRACE) + fprintf(TDEST, "News Status. Line too long - chopped\n"); + me->junk = YES; + if ((status = ScanResponse(me)) != HT_LOADED) return status; } - fclose(fp); } - } - if (!HTNewsHost) HTNewsHost = DEFAULT_NEWS_HOST; -#endif + b++; + } - if (*HTNewsHost>='0' && *HTNewsHost<='9') { /* Numeric node address: */ -#ifdef GUSI - sin->sin_addr = inet_addr((char *)HTNewsHost); /* STR (GUSI) */ -#else - sin->sin_addr.s_addr = inet_addr((char *)HTNewsHost); /* arpa/inet.h */ -#endif - } else { /* Alphanumeric node name: */ - phost=gethostbyname((char*)HTNewsHost); /* See netdb.h */ - if (!phost) { - char message[150]; /* @@@ */ - sprintf(message, - "HTNews: Can't find news host `%s'.\n%s",HTNewsHost, - "Please define your NNTP server"); -/* HTAlert(message); */ - if (PROT_TRACE) - fprintf(TDEST, "HTNews: Can't find news host `%s'.\n",HTNewsHost); - return NO; /* Fail */ + /* + ** 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; } - memcpy(&sin->sin_addr, phost->h_addr, phost->h_length); } - - if (WWWTRACE) fprintf(TDEST, - "HTNews: Parsed address as port %4x, inet %d.%d.%d.%d\n", - (unsigned int)ntohs(sin->sin_port), - (int)*((unsigned char *)(&sin->sin_addr)+0), - (int)*((unsigned char *)(&sin->sin_addr)+1), - (int)*((unsigned char *)(&sin->sin_addr)+2), - (int)*((unsigned char *)(&sin->sin_addr)+3)); - - s = INVSOC; /* Disconnected */ - - return YES; + return HT_LOADED; } - - -/* Send NNTP Command line to remote host & Check Response -** ------------------------------------------------------ -** -** On entry, -** command points to the command to be sent, including CRLF, or is null -** pointer if no command to be sent. -** On exit, -** Negative status indicates transmission error, socket closed. -** Positive status is an NNTP status. -*/ - - -PRIVATE int response ARGS1(CONST char *,command) +PRIVATE int HTNewsStatus_put_character (HTStream * me, char ch) { - int result; - char * p = response_text; - if (command) { - int status; - int length = strlen(command); - if (WWWTRACE) fprintf(TDEST, "NNTP command to be sent: %s", command); -#ifdef NOT_ASCII - { - CONST char * p; - char * q; - char ascii[LINE_LENGTH+1]; - for(p = command, q=ascii; *p; p++, q++) { - *q = TOASCII(*p); - } - status = NETWRITE(s, ascii, length); - } -#else - status = NETWRITE(s, command, length); -#endif - if (status<0){ - if (WWWTRACE) fprintf(TDEST, - "HTNews: Unable to send command. Disconnecting.\n"); - NETCLOSE(s); - HTInputSocket_free(isoc); - s = INVSOC; - return status; - } /* if bad status */ - } /* if command to be sent */ - - for(;;) { - if (((*p++=NEXT_CHAR) == LF) - || (p == &response_text[LINE_LENGTH])) { - *p++=0; /* Terminate the string */ - if (WWWTRACE) fprintf(TDEST, "NNTP Response: %s\n", response_text); - sscanf(response_text, "%d", &result); - if (result >= 411 && result <= 430) { /* no such article/group */ - char * msg = strchr(response_text,' '); - if (!msg) msg = response_text; - PUTS("

News error

\n"); - PUTS(msg); - if (PROT_TRACE) - fprintf(TDEST, "News error.. %s", response_text); - } - return result; - } /* if end of line */ - - if (*(p-1) < 0) { - if (WWWTRACE) fprintf(TDEST, - "HTNews: EOF on read, closing socket %d\n", s); - NETCLOSE(s); /* End of file, close socket */ - HTInputSocket_free(isoc); - return s = INVSOC; /* End of file on response */ - } - } /* Loop over characters */ + return HTNewsStatus_put_block(me, &ch, 1); } - -/* Case insensitive string comparisons -** ----------------------------------- -** -** On entry, -** template must be already un upper case. -** unknown may be in upper or lower or mixed case to match. -*/ -PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,tmplate) +PRIVATE int HTNewsStatus_put_string (HTStream * me, CONST char * str) { - CONST char * u = unknown; - CONST char * t = tmplate; - for (;*u && *t && (TOUPPER(*u)==*t); u++, t++) /* Find mismatch or end */ ; - return (BOOL)(*t==0); /* OK if end of template */ + return HTNewsStatus_put_block(me, str, (int) strlen(str)); } -/* Find Author's name in mail address -** ---------------------------------- -** -** On exit, -** THE EMAIL ADDRESS IS CORRUPTED -** -** For example, returns "Tim Berners-Lee" if given any of -** " Tim Berners-Lee " -** or " tim@w3.org ( Tim Berners-Lee ) " -*/ -PRIVATE char * author_name ARGS1 (char *,email) +PRIVATE int HTNewsStatus_flush (HTStream * me) { - char *s, *e; - - if ((s=strchr(email,'(')) && (e=strchr(email, ')'))) - if (e>s) { - *e=0; /* Chop off everything after the ')' */ - return HTStrip(s+1); /* Remove leading and trailing spaces */ - } - - if ((s=strchr(email,'<')) && (e=strchr(email, '>'))) - if (e>s) { - strcpy(s, e+1); /* Remove <...> */ - return HTStrip(email); /* Remove leading and trailing spaces */ - } - - return HTStrip(email); /* Default to the whole thing */ - + return me->target ? (*me->target->isa->flush)(me->target) : HT_OK; } -/* Start anchor element -** -------------------- -*/ -PRIVATE void start_anchor ARGS1(CONST char *, href) +PRIVATE int HTNewsStatus_free (HTStream * me) { - BOOL present[HTML_A_ATTRIBUTES]; - CONST char* value[HTML_A_ATTRIBUTES]; - - { - int i; - for(i=0; itarget) { + if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK) + return HT_WOULD_BLOCK; } - value[HTML_A_HREF] = href; - (*targetClass.start_element)(target, HTML_A , present, value); - + free(me); + return status; } - -/* Start link element -** -------------------- -*/ -PRIVATE void start_link ARGS2(CONST char *, href, CONST char *, rev) +PRIVATE int HTNewsStatus_abort (HTStream * me, HTError e) { -#ifdef WHEN_WE_HAVE_HTMLPLUS - - BOOL present[HTML_LINK_ATTRIBUTES]; - CONST char* value[HTML_LINK_ATTRIBUTES]; - - { - int i; - for(i=0; itarget) + ABORT_TARGET; + free(me); + if (PROT_TRACE) fprintf(TDEST, "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 +}; - - -/* Paste in an Anchor -** ------------------ -** -** -** On entry, -** HT has a selection of zero length at the end. -** text points to the text to be put into the file, 0 terminated. -** addr points to the hypertext refernce address, -** terminated by white space, comma, NULL or '>' -*/ -PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr) +PUBLIC HTStream *HTNewsStatus_new (HTRequest * request, news_info * news) { - char href[LINE_LENGTH+1]; - - { - CONST char * p; - strcpy(href,"news:"); - for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++); - strncat(href, addr, p-addr); /* Make complete hypertext reference */ - } - - start_anchor(href); - PUTS(text); - END(HTML_A); + HTStream *me = (HTStream *) calloc(1, sizeof(HTStream)); + if (!me) outofmem(__FILE__, "HTNewsStatus_new"); + me->isa = &HTNewsStatusClass; + me->request = request; + me->news = news; + me->EOLstate = EOL_BEGIN; + return me; } +/* ------------------------------------------------------------------------- */ +/* PROTOCOL FUNCTIONS */ +/* ------------------------------------------------------------------------- */ -/* Write list of anchors -** --------------------- -** -** We take a pointer to a list of objects, and write out each, -** generating an anchor for each. -** -** On entry, -** HT has a selection of zero length at the end. -** text points to a comma or space separated list of addresses. -** On exit, -** *text is NOT any more chopped up into substrings. +/* +** Set the max number of articles at the same time. +** Default is MAX_NEWS_ARTICLES */ -PRIVATE void write_anchors ARGS1 (char *,text) -{ - char * start = text; - char * end; - char c; - for (;;) { - for(;*start && (WHITE(*start)); start++); /* Find start */ - if (!*start) return; /* (Done) */ - for(end=start; *end && (*end!=' ') && (*end!=','); end++);/* Find end */ - if (*end) end++; /* Include comma or space but not NULL */ - c = *end; - *end = 0; - write_anchor(start, start); - START(HTML_BR); - *end = c; - start = end; /* Point to next one */ +PUBLIC BOOL HTNews_setMaxArticles (int new_max) +{ + if (new_max > 0) { + HTNewsMaxArticles = new_max; + return YES; } + return NO; } -/* Abort the connection abort_socket -** -------------------- +/* +** Get current max number of articles at the same time. */ -PRIVATE void abort_socket NOARGS -{ - if (WWWTRACE) fprintf(TDEST, - "HTNews: EOF on read, closing socket %d\n", s); - NETCLOSE(s); /* End of file, close socket */ - HTInputSocket_free(isoc); - PUTS("Network Error: connection lost"); - PUTC('\n'); - s = INVSOC; /* End of file on response */ - return; +PUBLIC int HTNews_maxArticles (void) +{ + return HTNewsMaxArticles; } -/* Read in an Article read_article -** ------------------ -** -** -** Note the termination condition of a single dot on a line by itself. -** RFC 977 specifies that the line "folding" of RFC850 is not used, so we -** do not handle it here. -** -** On entry, -** s Global socket number is OK -** HT Global hypertext object is ready for appending text -*/ -PRIVATE void read_article NOARGS -{ - - char line[LINE_LENGTH+1]; - char *references=NULL; /* Hrefs for other articles */ - char *newsgroups=NULL; /* Newsgroups list */ - char *p = line; - BOOL done = NO; - -/* Read in the HEADer of the article: -** -** The header fields are either ignored, or formatted and put into the -** Text. +/* +** Sets the current NEWS server. */ - if (!diagnostic) { - (*targetClass.start_element)(target, HTML_ADDRESS, 0, 0); - while(!done){ - char ch = *p++ = NEXT_CHAR; - if (ch==(char)EOF) { - abort_socket(); /* End of file, close socket */ - return; /* End of file on response */ - } - if ((ch == LF) || (p == &line[LINE_LENGTH])) { - *--p=0; /* Terminate the string */ - if (WWWTRACE) fprintf(TDEST, "H %s\n", line); - - if (line[0]=='.') { - if (line[1]<' ') { /* End of article? */ - done = YES; - break; - } - - } else if (line[0]<' ') { - break; /* End of Header? */ - - } else if (match(line, "SUBJECT:")) { - END(HTML_ADDRESS); - START(HTML_TITLE); /** Uuugh! @@@ */ - PUTS(line+9); - END(HTML_TITLE); - START(HTML_H1); - PUTS(line+8); - END(HTML_H1); - START(HTML_ADDRESS); - - } else if (match(line, "DATE:") - || match(line, "ORGANIZATION:")) { - PUTS(strchr(line,':')+2); - START(HTML_BR); - - } else if(match(line, "FROM:")) { - char * temp=0; - char * href=0; - char *cp1, *cp2; - - /* copy into temporary storage */ - StrAllocCopy(temp, strchr(line,':')+1); - - cp1=temp; - while(isspace(*cp1)) cp1++; - /* remove space and stuff after */ - if((cp2 = strchr(cp1,' ')) != NULL) - *cp2 = '\0'; - - StrAllocCopy(href,"mailto:"); - StrAllocCat(href,cp1); - - start_anchor(href); - PUTS("Reply to "); - PUTS(strchr(line,':')+1); - END(HTML_A); - START(HTML_BR); - - /* put in the owner as a link rel. as well */ - start_link(href, "made"); - - /* free of temp vars */ - free(temp); - free(href); - - } else if (match(line, "NEWSGROUPS:")) { - StrAllocCopy(newsgroups, HTStrip(strchr(line,':')+1)); - - } else if (match(line, "REFERENCES:")) { - StrAllocCopy(references, HTStrip(strchr(line,':')+1)); - - } /* end if match */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - END(HTML_ADDRESS); - - if (newsgroups || references) { - START(HTML_DL); - if (newsgroups) { -#ifdef POSTING - char *href=0; -#endif - - (*targetClass.start_element)(target, HTML_DT , 0, 0); - PUTS("Newsgroups:"); - (*targetClass.start_element)(target, HTML_DD , 0, 0); - write_anchors(newsgroups); - -#ifdef POSTING - /* make posting possible */ - StrAllocCopy(href,"newspost:"); - StrAllocCat(href,newsgroups); - START(HTML_DT); - start_anchor(href); - PUTS("Reply to newsgroup(s)"); - END(HTML_A); -#endif - - free(newsgroups); +PUBLIC BOOL HTNews_setHost (CONST char * newshost) +{ + if (newshost && *newshost) { + StrAllocCopy(HTNewsHost, newshost); + { + char *strptr = HTNewsHost; + while (*strptr) { + *strptr = TOLOWER(*strptr); + strptr++; } - if (references) { - (*targetClass.start_element)(target, HTML_DT , 0, 0); - PUTS("References:"); - (*targetClass.start_element)(target, HTML_DD , 0, 0); - write_anchors(references); - free(references); - } -#ifdef WHEN_WE_HAVE_HTMLPLUS - (*targetClass.end_element)(target, HTML_DLC); -#else - (*targetClass.end_element)(target, HTML_DL); -#endif + /* Remove final dot or paste in domain name */ + if (strchr(HTNewsHost, '.')) { + if (*(HTNewsHost+strlen(HTNewsHost)-1) == '.') + *(HTNewsHost+strlen(HTNewsHost)-1) = '\0'; + } else { + CONST char *domain = HTGetDomainName(); + if (domain) { + StrAllocCat(HTNewsHost, "."); + StrAllocCat(HTNewsHost, domain); + } + } } - PUTS("\n\n\n"); - + if (PROT_TRACE) + fprintf(TDEST, "SetNewsHost. Host name is `%s\'\n", HTNewsHost); + return YES; + } else { + if (PROT_TRACE) + fprintf(TDEST, "SetNewsHost. Bad argument ignored\n"); + return NO; } - -/* Read in the BODY of the Article: -*/ - (*targetClass.start_element)(target, HTML_PRE , 0, 0); - - p = line; - while(!done){ - char ch = *p++ = NEXT_CHAR; - if (ch==(char)EOF) { - abort_socket(); /* End of file, close socket */ - return; /* End of file on response */ - } - if ((ch == LF) || (p == &line[LINE_LENGTH])) { - *p++=0; /* Terminate the string */ - if (WWWTRACE) fprintf(TDEST, "B %s", line); - if (line[0]=='.') { - if (line[1]<' ') { /* End of article? */ - done = YES; - break; - } else { /* Line starts with dot */ - PUTS(&line[1]); /* Ignore first dot */ - } - } else { - -/* Normal lines are scanned for buried references to other articles. -** Unfortunately, it will pick up mail addresses as well! -*/ - char *l = line; - char * p; - while ((p=strchr(l, '<'))) { - char *q = strchr(p,'>'); - char *at = strchr(p, '@'); - if (q && at && at */ - } - PUTS( l); /* Last bit of the line */ - } /* if not dot */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - - (*targetClass.end_element)(target, HTML_PRE); } - -/* Read in a List of Newsgroups -** ---------------------------- -*/ /* -** Note the termination condition of a single dot on a line by itself. -** RFC 977 specifies that the line "folding" of RFC850 is not used, so we -** do not handle it here. -*/ -PRIVATE void read_list NOARGS -{ - - char line[LINE_LENGTH+1]; - char *p; - BOOL done = NO; - -/* Read in the HEADer of the article: +** Except on the NeXT, we pick up the NewsHost name from ** -** The header fields are either ignored, or formatted and put into the -** Text. -*/ - (*targetClass.start_element)(target, HTML_H1 , 0, 0); - PUTS( "Newsgroups"); - (*targetClass.end_element)(target, HTML_PRE); - p = line; - (*targetClass.start_element)(target, HTML_DL, 0, 0); - while(!done){ - char ch = *p++ = NEXT_CHAR; - if (ch==(char)EOF) { - abort_socket(); /* End of file, close socket */ - return; /* End of file on response */ - } - if ((ch == LF) || (p == &line[LINE_LENGTH])) { - *p++=0; /* Terminate the string */ - if (WWWTRACE) fprintf(TDEST, "B %s", line); - (*targetClass.start_element)(target, HTML_DT , 0, 0); - if (line[0]=='.') { - if (line[1]<' ') { /* End of article? */ - done = YES; - break; - } else { /* Line starts with dot */ - PUTS( &line[1]); - } - } else { - -/* Normal lines are scanned for references to newsgroups. -*/ - int i=0; - - /* find whitespace if it exits */ - for(; line[i] != '\0' && !WHITE(line[i]); i++) - ; /* null body */ - - if(line[i] != '\0') { - line[i] = '\0'; - write_anchor(line, line); - (*targetClass.start_element)(target, HTML_DD , 0, 0); - PUTS(&line[i+1]); /* put description */ - } else { - write_anchor(line, line); - } - -#ifdef OLD_CODE - char group[LINE_LENGTH]; - int first, last; - char postable; - if (sscanf(line, "%s %d %d %c", group, &first, &last, &postable)==4) - write_anchor(line, group); - else - PUTS(line); -#endif /*OLD_CODE*/ - - } /* if not dot */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - (*targetClass.end_element)(target, HTML_DL); -} - - -/* Read in a Newsgroup -** ------------------- -** Unfortunately, we have to ask for each article one by one if we -** want more than one field. +** 1. Environment variable NNTPSERVER +** 2. File SERVER_FILE +** 3. Compilation time macro DEFAULT_NEWS_HOST ** +** On the NeXT, we pick up the NewsHost name from, in order: +** +** 1. WorldWideWeb default "NewsHost" +** 2. News default "NewsHost" +** 3. Compilation time macro DEFAULT_NEWS_HOST +** +** Return: HTNewsHost if success else NULL */ -PRIVATE void read_group ARGS3( - CONST char *,groupName, - int,first_required, - int,last_required -) +PUBLIC CONST char *HTNews_host (void) { - char line[LINE_LENGTH+1]; - char author[LINE_LENGTH+1]; - char subject[LINE_LENGTH+1]; - char *p; - BOOL done; - - char buffer[LINE_LENGTH]; - char *reference=0; /* Href for article */ - int art; /* Article number WITHIN GROUP */ - int status, count, first, last; /* Response fields */ - /* count is only an upper limit */ - - sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last); - if(WWWTRACE) - fprintf(TDEST, - "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n", - status, count, first, last, first_required, last_required); - if (last==0) { - PUTS( "\nNo articles in this group.\n"); -#ifdef POSTING - goto add_post; -#endif - return; + if (HTNewsHost) { + if (*HTNewsHost) { + if (PROT_TRACE) + fprintf(TDEST, "GetNewsHost. found as `%s\'\n", HTNewsHost); + return HTNewsHost; + } else + return NULL; /* We couldn't get it the last time */ } - -#define FAST_THRESHOLD 100 /* Above this, read IDs fast */ -#define CHOP_THRESHOLD 50 /* Above this, chop off the rest */ + { + char *newshost = NULL; + char buffer[80]; - if (first_required last)) last_required = last; - - if (last_required<=first_required) { - PUTS( "\nNo articles in this range.\n"); -#ifdef POSTING - goto add_post; -#endif - return; - } +#ifdef NeXTStep + if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0) + if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0) + newshost = DEFAULT_NEWS_HOST; +#else + if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) { + FILE *fp = fopen(SERVER_FILE, "r"); + *(buffer+79) = '\0'; + if (fp) { + if (fgets(buffer, 79, fp)) { + char *end; + newshost = buffer; + while (*newshost == ' ' || *newshost == '\t') + newshost++; + end = newshost; + while (*end && !isspace(*end)) + end++; + *end = '\0'; + } + fclose(fp); + } + } +#endif /* NestStep */ - if (last_required-first_required+1 > MAX_CHUNK) { /* Trim this block */ - first_required = last_required-CHUNK_SIZE+1; + if (!newshost || !*newshost) + newshost = DEFAULT_NEWS_HOST; + if (HTNews_setHost(newshost)) + return HTNewsHost; + StrAllocCopy(HTNewsHost, ""); + return NULL; } - if (WWWTRACE) fprintf (TDEST, " Chunk will be (%d-%d)\n", - first_required, last_required); +} -/* Set window title +/* +** Free Newshostname */ - sprintf(buffer, "Newsgroup %s, Articles %d-%d", - groupName, first_required, last_required); - START(HTML_TITLE); - PUTS(buffer); - END(HTML_TITLE); +PUBLIC void HTFreeNewsHost (void) +{ + FREE(HTNewsHost); +} -/* Link to earlier articles +/* HTNewsCleanup +** ------------- +** This function closes the connection and frees memory. +** Returns YES on OK, else NO */ - if (first_required>first) { - int before; /* Start of one before */ - if (first_required-MAX_CHUNK <= first) before = first; - else before = first_required-CHUNK_SIZE; - sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1); - if (WWWTRACE) fprintf(TDEST, " Block before is %s\n", buffer); - PUTS( " ("); - start_anchor(buffer); - PUTS("Earlier articles"); - END(HTML_A); - PUTS( "...)\n"); - } - - done = NO; - -/*#define USE_XHDR*/ -#ifdef USE_XHDR - if (count>FAST_THRESHOLD) { - sprintf(buffer, - "\nThere are about %d articles currently available in %s, IDs as follows:\n\n", - count, groupName); - PUTS(buffer); - sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF); - status = response(buffer); - if (status==221) { - - p = line; - while(!done){ - char ch = *p++ = NEXT_CHAR; - if (ch==(char)EOF) { - abort_socket(); /* End of file, close socket */ - return; /* End of file on response */ - } - if ((ch == '\n') || (p == &line[LINE_LENGTH])) { - *p++=0; /* Terminate the string */ - if (WWWTRACE) fprintf(TDEST, "X %s", line); - if (line[0]=='.') { - if (line[1]<' ') { /* End of article? */ - done = YES; - break; - } else { /* Line starts with dot */ - /* Ignore strange line */ - } - } else { - - /* Normal lines are scanned for references to articles. - */ - char * space = strchr(line, ' '); - if (space++) - write_anchor(space, space); - } /* if not dot */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - - /* leaving loop with "done" set */ - } /* Good status */ - }; -#endif +PRIVATE int HTNewsCleanup (HTRequest * req, int status) +{ + HTNet *net = req->net; + news_info *news = (news_info *) net->context; -/* Read newsgroup using individual fields: -*/ - if (!done) { - if (first==first_required && last==last_required) - PUTS("\nAll available articles in "); - else PUTS( "\nArticles in "); - PUTS(groupName); - START(HTML_MENU); - for(art=first_required; art<=last_required; art++) { - -/*#define OVERLAP*/ -#ifdef OVERLAP -/* With this code we try to keep the server running flat out by queuing just -** one extra command ahead of time. We assume (1) that the server won't abort -** if it gets input during output, and (2) that TCP buffering is enough for the -** two commands. Both these assumptions seem very reasonable. However, we HAVE -** had a hangup with a loaded server. -*/ - if (art==first_required) { - if (art==last_required) { - sprintf(buffer, "HEAD %d%c%c", art, CR, LF); /* Only one */ - status = response(buffer); - } else { /* First of many */ - sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c", - art, CR, LF, art+1, CR, LF); - status = response(buffer); - } - } else if (art==last_required) { /* Last of many */ - status = response(NULL); - } else { /* Middle of many */ - sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF); - status = response(buffer); - } - -#else /* NOT OVERLAP */ - sprintf(buffer, "HEAD %d%c%c", art, CR, LF); - status = response(buffer); -#endif /* NOT OVERLAP */ - - if (status == 221) { /* Head follows - parse it:*/ - int ch; - p = line; /* Write pointer */ - done = NO; - while(!done){ - if ((ch = HTInputSocket_getCharacter(isoc)) < 0) { - abort_socket(); /* End of file, close socket */ - return; /* End of file on response */ - } - *p++ = (unsigned char) ch; - if ((ch == LF) - || (p == &line[LINE_LENGTH]) ) { - - *--p=0; /* Terminate & chop LF*/ - p = line; /* Restart at beginning */ - if (WWWTRACE) fprintf(TDEST, "G %s\n", line); - switch(line[0]) { - - case '.': - done = (line[1]<' '); /* End of article? */ - break; - - case 'S': - case 's': - if (match(line, "SUBJECT:")) - strcpy(subject, line+9);/* Save subject */ - break; - - case 'M': - case 'm': - if (match(line, "MESSAGE-ID:")) { - char * addr = HTStrip(line+11) +1; /* Chop < */ - addr[strlen(addr)-1]=0; /* Chop > */ - StrAllocCopy(reference, addr); - } - break; - - case 'f': - case 'F': - if (match(line, "FROM:")) { - char * p; - strcpy(author, - author_name(strchr(line,':')+1)); - if (*author) { /* Not always there! */ - p = author + strlen(author) - 1; - if (*p==LF) *p = 0; /* Chop off newline */ - } - } - break; - - } /* end switch on first character */ - } /* if end of line */ - } /* Loop over characters */ - - START(HTML_LI); - sprintf(buffer, "\"%s\" - %s", subject, author); - if (reference) { - write_anchor(buffer, reference); - free(reference); - reference=0; - } else { - PUTS(buffer); - } - - -/* indicate progress! @@@@@@ -*/ - - } /* If good response */ - } /* Loop over article */ - } /* If read headers */ - END(HTML_MENU); - START(HTML_P); - -/* Link to later articles -*/ - if (last_requiredinput_stream) { + if (status == HT_INTERRUPTED) + (*req->input_stream->isa->abort)(req->input_stream, NULL); + else + (*req->input_stream->isa->_free)(req->input_stream); } -#ifdef POSTING - add_post: -#endif - { - char *href=0; - START(HTML_HR); - - StrAllocCopy(href,"newspost:"); - StrAllocCat(href,groupName); - start_anchor(href); - PUTS("Post to "); - PUTS(groupName); - END(HTML_A); - - free(href); - } - - + /* Remove the request object and our own context structure for nntp */ + HTNet_delete(net, status); + FREE(news->name); + HTChunkFree(news->cmd); + FREE(news); + return YES; } - -/* Load by name HTLoadNews -** ============ -*/ -PUBLIC int HTLoadNews ARGS3(SOCKET, soc, HTRequest *, request, SockOps, ops) +PRIVATE int SendCommand (HTRequest *request, news_info *news, + char *token, char *pars) { - char * arg = HTAnchor_physical(request->anchor); - char command[257]; /* The whole command */ - char groupName[GROUP_NAME_LENGTH]; /* Just the group name */ - int status; /* tcp return */ - int retries; /* A count of how hard we have tried */ - BOOL group_wanted; /* Flag: group was asked for, not article */ - BOOL list_wanted; /* Flag: group was asked for, not article */ - int first, last; /* First and last articles asked for */ - - diagnostic = (request->output_format == WWW_SOURCE); /* set global flag */ - + int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2; + HTChunkClear(news->cmd); + HTChunkEnsure(news->cmd, len); + if (pars && *pars) + sprintf(HTChunkData(news->cmd), "%s %s%c%c", token, pars, CR, LF); + else + sprintf(HTChunkData(news->cmd), "%s%c%c", token, CR, LF); + if (PROT_TRACE) fprintf(TDEST, "News Tx..... %s", HTChunkData(news->cmd)); + return (*request->input_stream->isa->put_block) + (request->input_stream, HTChunkData(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. +*/ +PUBLIC int HTLoadNews (SOCKET soc, HTRequest * request, SockOps ops) +{ + int status = HT_ERROR; + HTNet *net = request->net; + HTParentAnchor *anchor = HTRequest_anchor(request); + char *url = HTAnchor_physical(anchor); + news_info *news; + + /* + ** 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 (ops == FD_NONE) { - if (PROT_TRACE) fprintf(TDEST, "News........ Looking for `%s\'\n",arg); -#if 0 + if (PROT_TRACE) + fprintf(TDEST, "NNTP........ Looking for `%s\'\n", url); if ((news = (news_info *) calloc(1, sizeof(news_info))) == NULL) outofmem(__FILE__, "HTLoadNews"); + news->cmd = HTChunkCreate(128); news->state = NEWS_BEGIN; net->context = news; -#endif - } if (ops == FD_CLOSE) { /* Interrupted */ - HTNet_delete(request->net, HT_INTERRUPTED); + } else if (ops == FD_CLOSE) { /* Interrupted */ + if(HTRequest_isPostWeb(request)&&!HTRequest_isMainDestination(request)) + HTNewsCleanup(request, HT_IGNORE); + else + HTNewsCleanup(request, HT_INTERRUPTED); return HT_OK; } else - HTNet_delete(request->net, HT_ERROR); + news = (news_info *) net->context; /* Get existing copy */ + + /* Now start the state machine */ + 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: /* @@@ DO THIS @@@@@@ */ + news->state = NEWS_NEED_CONNECTION; + break; + + case NEWS_NEED_CONNECTION: /* Let's set up a connection */ + if (!strncasecomp(url, "news:", 5)) { + CONST char *newshost = HTNews_host(); + StrAllocCopy(news->name, url+5); + if (newshost) { + char *newshack = NULL; /* Then we can use HTParse :-) */ + StrAllocCopy(newshack, "news://"); + StrAllocCat(newshack, newshost); + status = HTDoConnect(net, (char *) newshack, NEWS_PORT); + free(newshack); + } else + news->state = NEWS_ERROR; + } else if (!strncasecomp(url, "nntp:", 5)) { + news->name = HTParse(url, "", PARSE_PATH); + status = HTDoConnect(net, url, NEWS_PORT); + } else { + if (PROT_TRACE) fprintf(TDEST, "News........ Huh?"); + news->state = NEWS_ERROR; + } + if (status == HT_OK) { + BOOL greeting = NO; + char *s_class = HTDNS_serverClass(net->dns); + if (s_class && strcasecomp(s_class, "nntp")) { + HTErrorAdd(request, ERR_FATAL, NO, HTERR_CLASS, NULL, 0, + "HTLoadNews"); + news->state = NEWS_ERROR; + break; + } + HTDNS_setServerClass(net->dns, "nntp"); + if (HTDNS_socket(net->dns) == INVSOC) { + HTDNS_setSocket(net->dns, net->sockfd); + greeting = YES; + } + if (PROT_TRACE) + fprintf(TDEST, "News........ Connected, socket %d\n", + net->sockfd); - if (!initialized) initialized = initialize(); - if (!initialized) return HT_ERROR; /* FAIL */ - - { - char * p1=arg; + /* Set up stream TO network */ + request->input_stream = HTWriter_new(net, YES); + + /* + ** Set up concurrent read/write if this request isn't the + ** source for a PUT or POST. As source we don't start reading + ** before all destinations are ready. If destination then + ** register the input stream and get ready for read + */ + if (HTRequest_isPostWeb(request)) { + HTEvent_Register(net->sockfd, request, (SockOps) FD_READ, + HTLoadNews, net->priority); + HTRequest_linkDestination(request); + } -/* We will ask for the document, omitting the host name & anchor. -** -** Syntax of address is -** xxx@yyy Article -** Same article -** xxxxx News group (no "@") -** group/n1-n2 Articles n1 to n2 in group -*/ - group_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')==0); - list_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')!=0); - - /* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */ - /* Don't use HTParse because news: access doesn't follow traditional - rules. For instance, if the article reference contains a '#', - the rest of it is lost -- JFG 10/7/92, from a bug report */ - if (!strncasecomp (arg, "news:", 5)) - p1 = arg + 5; /* Skip "news:" prefix */ - HTUnEscape(p1); /* AL May 2, 1994 */ - HTCleanTelnetString(p1); /* Prevent security holes */ - if (list_wanted) { - strcpy(command, "LIST NEWSGROUPS"); - } else if (group_wanted) { - char * slash = strchr(p1, '/'); - strcpy(command, "GROUP "); - first = 0; - last = 0; - if (slash) { - *slash = 0; - strcpy(groupName, p1); - *slash = '/'; - (void) sscanf(slash+1, "%d-%d", &first, &last); + /* Set up stream FROM network and corresponding read buffer */ + net->isoc = HTInputSocket_new(net->sockfd); + net->target = HTNewsStatus_new(request, news); + news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH; + } else if (status == HT_WOULD_BLOCK || status == HT_PERSISTENT) + return HT_OK; + else + news->state = NEWS_ERROR; + break; + + case NEWS_NEED_GREETING: + status = HTSocketRead(request, 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: + /* + ** Find out what to ask the news server. Syntax of address is + ** xxx@yyy Article + ** Same article + ** xxxxx News group (no "@") + */ + if (request->method == METHOD_GET) { + if (strchr(url, '@')) { /* ARTICLE */ + if (*(news->name) != '<') { /* Add '<' and '>' */ + char *newart = (char *) malloc(strlen(news->name)+3); + if (!newart) outofmem(__FILE__, "HTLoadNews"); + sprintf(newart, "<%s>", news->name); + 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 (request->method == METHOD_POST) + news->state = NEWS_NEED_POST; + else { + HTErrorAdd(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; + news->sent = YES; } else { - strcpy(groupName, p1); + status = HTSocketRead(request, 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; } - strcat(command, groupName); - } else { - strcpy(command, "ARTICLE "); - if (strchr(p1, '<')==0) strcat(command,"<"); - strcat(command, p1); - if (strchr(p1, '>')==0) strcat(command,">"); - } + break; - { - char * p = command + strlen(command); - *p++ = CR; /* Macros to be correct on Mac */ - *p++ = LF; - *p++ = 0; - /* strcat(command, "\r\n"); */ /* CR LF, as in rfc 977 */ - } - } /* scope of p1 */ - - if (!*arg) return HT_ERROR; /* Ignore if no name */ +#if 0 + 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 = HTSocketRead(request, 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 - -/* Make a hypertext object with an anchor list. -*/ - node_anchor = request->anchor; - target = HTMLGenerator(request, NULL, WWW_HTML, - request->output_format, request->output_stream); - targetClass = *target->isa; /* Copy routine entry points */ - - -/* Now, let's get a stream setup up from the NewsHost: -*/ - for(retries=0;retries<2; retries++){ - - if (s<0) { - s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address)); - if (status<0){ - NETCLOSE(s); - s = INVSOC; -#ifdef OLD_CODE - char message[256]; - if (WWWTRACE) fprintf(TDEST, "HTNews: Unable to connect to news host.\n"); -/* if (retries<=1) continue; WHY TRY AGAIN ? */ - sprintf(message, -"\nCould not access %s.\n\n (Check default WorldWideWeb NewsHost ?)\n", - HTNewsHost); - return HT_ERROR; -#endif /* OLD_CODE */ - { - char *unescaped = NULL; - StrAllocCopy(unescaped, arg); - HTUnEscape(unescaped); - HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, - (void *) unescaped, - (int) strlen(unescaped), "HTLoadNews"); - free(unescaped); + 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 { - if (WWWTRACE) fprintf(TDEST, "HTNews: Connected to news host %s.\n", - HTNewsHost); - isoc = HTInputSocket_new(s); /* set up buffering */ - if ((response(NULL) / 100) !=2) { - int length = strlen(response_text); - NETCLOSE(s); - HTInputSocket_free(isoc); - s = INVSOC; - HTErrorAdd(request, ERR_FATAL, NO, HTERR_NEWS_SERVER, - (void *) response_text, length < 50 ? - length : 50, "HTLoadNews"); - HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, - (void *) HTNewsHost, - (int) strlen(HTNewsHost), "HTLoadNews"); + status = HTSocketRead(request, net); + if (status == HT_WOULD_BLOCK) return HT_OK; - } -#ifdef OLD_CODE - char message[BIG]; - sprintf(message, - "Can't read news info. News host %.20s responded: %.200s", - HTNewsHost, response_text); - return HT_ERROR; - } -#endif /* OLD_CODE */ + 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; } - } /* If needed opening */ - - /* @@@@@@@@@@@@@@Tell user something's happening */ - - status = response(command); - if (status<0) break; - if (status >= 411 && status <= 430) break; /* no such article/group */ - if ((status/ 100) !=2) { -/* NXRunAlertPanel("News access", response_text, - NULL,NULL,NULL); -*/ - NETCLOSE(s); - HTInputSocket_free(isoc); - s = INVSOC; -/* return HT; -- no:the message might be "Timeout-disconnected" left over */ - continue; /* Try again */ - } - -/* Load a group, article, etc -*/ - - - if (list_wanted) read_list(); - else if (group_wanted) read_group(groupName, first, last); - else read_article(); + break; - (*targetClass._free)(target); - return HT_OK; - - } /* Retry loop */ - - - /* HTAlert("Sorry, could not load requested news.\n"); */ - -/* NXRunAlertPanel(NULL, "Sorry, could not load `%s'.", - NULL,NULL,NULL, arg);No -- message earlier wil have covered it */ + 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 = HTSocketRead(request, 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 (news->total > HTNewsMaxArticles) + news->last = news->first-HTNewsMaxArticles; + news->current = news->first; + news->state = NEWS_NEED_XOVER; + } else + news->state = NEWS_ERROR; + } else + news->state = NEWS_ERROR; + } else + news->state = NEWS_ERROR; + news->sent = NO; + } + break; - (*targetClass._free)(target); /* AL May 2, 1994 */ - return HT_OK; + 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 = HTSocketRead(request, 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 = HTSocketRead(request, 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: + request->input_stream = + HTNewsPost_new(request, HTBuffer_new(request->input_stream, + request, 512)); + + /* Remember to convert to CRLF */ + + news->state = NEWS_NEED_BODY; + break; + + case NEWS_NEED_BODY: + if (ops == FD_WRITE || ops == FD_NONE) { + if (HTRequest_isDestination(request)) { + HTNet *srcnet = request->source->net; + HTEvent_Register(srcnet->sockfd, request->source, + (SockOps) FD_READ, + HTLoadNews, srcnet->priority); + 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 + ops = FD_READ; /* Trick to ensure that we do READ */ + } else if (ops == FD_READ) { + status = HTSocketRead(request, 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: + if (HTRequest_isPostWeb(request)) { + BOOL main = HTRequest_isMainDestination(request); + if (HTRequest_isDestination(request)) { + HTLink *link = + HTAnchor_findLink((HTAnchor *) request->source->anchor, + (HTAnchor *) request->anchor); + HTAnchor_setLinkResult(link, HT_LINK_OK); + } + HTRequest_removeDestination(request); + HTNewsCleanup(request, main ? HT_LOADED : HT_IGNORE); + } else + HTNewsCleanup(request, HT_LOADED); + return HT_OK; + break; + + case NEWS_ERROR: + /* Clean up the other connections or just this one */ + if (HTRequest_isPostWeb(request)) { + BOOL main = HTRequest_isMainDestination(request); + HTRequest_killPostWeb(request); + if (HTRequest_isDestination(request)) { + HTLink *link = + HTAnchor_findLink((HTAnchor *) request->source->anchor, + (HTAnchor *) request->anchor); + HTAnchor_setLinkResult(link, HT_LINK_ERROR); + } + HTRequest_removeDestination(request); + HTNewsCleanup(request, main ? HT_ERROR : HT_IGNORE); + } else + HTNewsCleanup(request, HT_ERROR); + return HT_OK; + break; + } + } /* End of while(1) */ }