home *** CD-ROM | disk | FTP | other *** search
- /* NEWS ACCESS HTNews.c
- ** ===========
- **
- ** History:
- ** 26 Sep 90 Written TBL
- ** 29 Nov 91 Downgraded to C, for portable implementation.
- */
-
- #include "HTUtils.h" /* Coding convention macros */
- #include "tcp.h"
-
- /* Implements:
- */
- #include "HTNews.h"
-
- #define CR FROMASCII('\015') /* Must be converted to ^M for transmission */
- #define LF FROMASCII('\012') /* Must be converted to ^J for transmission */
-
- #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 30 /* Number of articles for quick display */
-
- #ifndef DEFAULT_NEWS_HOST
- #define DEFAULT_NEWS_HOST "news"
- #endif
- #ifndef SERVER_FILE
- #define SERVER_FILE "/usr/local/lib/rn/server"
- #endif
-
- #include <ctype.h>
-
- #include "HTML.h"
- #include "HTParse.h"
- #include "HTFormat.h"
- #include "HTAlert.h"
-
- #include "LYLeaks.h"
-
- #define BIG 1024 /* @@@ */
-
- struct _HTStructured {
- CONST HTStructuredClass * isa;
- /* ... */
- };
-
- #define NEWS_PROGRESS(foo) HTProgress(foo)
-
-
- #define NEXT_CHAR HTGetCharacter()
- #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 int s; /* Socket for NewsHost */
- PRIVATE char response_text[LINE_LENGTH+1]; /* Last response */
- /* PRIVATE HText * HT; */ /* the new hypertext */
- 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 CONST char * HTGetNewsHost NOARGS
- {
- return HTNewsHost;
- }
-
- PUBLIC void HTSetNewsHost ARGS1(CONST char *, value)
- {
- StrAllocCopy(HTNewsHost, value);
- }
-
- /* 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
- ** 4. Default to "news"
- **
- ** 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
- ** 5. Default to "news"
- */
- PRIVATE BOOL initialized = NO;
- PRIVATE BOOL initialize NOARGS
- {
- CONST struct hostent *phost; /* Pointer to host - See netdb.h */
-
- /* 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 (TRACE) fprintf(stderr, "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 (TRACE) fprintf(stderr,
- "HTNews: File %s defines news host as `%s'\n",
- SERVER_FILE, HTNewsHost);
- }
- fclose(fp);
- }
- }
- if (!HTNewsHost) HTNewsHost = DEFAULT_NEWS_HOST;
- #endif
-
- s = -1; /* Disconnected */
-
- return YES;
- }
-
-
-
- /* 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)
- {
- int result;
- char * p = response_text;
- if (command) {
- int status;
- int length = strlen(command);
- if (TRACE) fprintf(stderr, "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, (char *)command, length);
- #endif
- if (status<0){
- if (TRACE) fprintf(stderr,
- "HTNews: Unable to send command. Disconnecting.\n");
- NETCLOSE(s);
- s = -1;
- 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 (TRACE) fprintf(stderr, "NNTP Response: %s\n", response_text);
- sscanf(response_text, "%d", &result);
- return result;
- } /* if end of line */
-
- if (*(p-1) < 0) {
- if (TRACE) fprintf(stderr,
- "HTNews: EOF on read, closing socket %d\n", s);
- NETCLOSE(s); /* End of file, close socket */
- return s = -1; /* End of file on response */
- }
- } /* Loop over characters */
- }
-
-
- /* 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 *,template)
- {
- CONST char * u = unknown;
- CONST char * t = template;
- for (;*u && *t && (TOUPPER(*u)==*t); u++, t++) /* Find mismatch or end */ ;
- return (BOOL)(*t==0); /* OK if end of template */
- }
-
- /* 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 <tim@online.cern.ch> "
- ** or " tim@online.cern.ch ( Tim Berners-Lee ) "
- */
- PRIVATE char * author_name ARGS1 (char *,email)
- {
- 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 */
-
- }
- /* Find Author's mail address
- ** --------------------------
- **
- ** On exit,
- ** THE EMAIL ADDRESS IS CORRUPTED
- **
- ** For example, returns "montulli@spaced.out.galaxy.net" if given any of
- ** " Lou Montulli <montulli@spaced.out.galaxy.net> "
- ** or " montulli@spacedout.galaxy.net ( Lou "The Stud" Montulli ) "
- */
- PRIVATE char * author_address ARGS1(char *,email)
- {
- char *s, *e;
-
- if(TRACE)
- fprintf(stderr,"Trying to find address in: %s\n",email);
-
- if ((s=strchr(email,'<')))
- {
- if((e=strchr(email, '>')))
- *e = 0; /* Remove > */
- return HTStrip(s+1); /* Remove leading and trailing spaces */
- }
-
- if ((s=strchr(email,'(')) && (e=strchr(email, ')')))
- if (e>s)
- {
- *s=0; /* Chop off everything after the ')' */
- return HTStrip(email); /* Remove leading and trailing spaces */
- }
-
- /* Default to the first word */
- s = email;
- while(isspace(*s)) s++; /* find first non-space */
- e = s;
- while(!isspace(*e) && *e != '\0') e++; /* find next space or end */
- *e=0; /* terminate space */
-
- return(s);
- }
-
-
- /* Start anchor element
- ** --------------------
- */
- PRIVATE void start_anchor ARGS1(CONST char *, href)
- {
- BOOL present[HTML_A_ATTRIBUTES];
- CONST char* value[HTML_A_ATTRIBUTES];
-
- {
- int i;
- for(i=0; i<HTML_A_ATTRIBUTES; i++)
- present[i] = (i==HTML_A_HREF);
- }
- ((CONST char **)value)[HTML_A_HREF] = href;
- (*targetClass.start_element)(target, HTML_A , present,(CONST char **)value);
-
- }
-
- /* Start link element
- ** --------------------
- */
- PRIVATE void start_link ARGS2(CONST char *, href, CONST char *, rev)
- {
- BOOL present[HTML_LINK_ATTRIBUTES];
- CONST char* value[HTML_LINK_ATTRIBUTES];
-
- {
- int i;
- for(i=0; i<HTML_LINK_ATTRIBUTES; i++)
- present[i] = (i==HTML_LINK_HREF || i==HTML_LINK_REV);
- }
- ((CONST char **)value)[HTML_LINK_HREF] = href;
- ((CONST char **)value)[HTML_LINK_REV] = rev;
- (*targetClass.start_element)(target, HTML_LINK , present,
- (CONST char **)value);
-
- }
-
-
- /* 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)
- {
- 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);
- }
-
-
- /* 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.
- */
- 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;
- if (*start == '<')
- write_anchor(start, start+1);
- else
- write_anchor(start, start);
- START(HTML_BR);
- *end = c;
- start = end; /* Point to next one */
- }
- }
-
- /* Abort the connection abort_socket
- ** --------------------
- */
- PRIVATE void abort_socket NOARGS
- {
- if (TRACE) fprintf(stderr,
- "HTNews: EOF on read, closing socket %d\n", s);
- NETCLOSE(s); /* End of file, close socket */
- PUTS("Network Error: connection lost");
- PUTC('\n');
- s = -1; /* End of file on response */
- return;
- }
-
- /* 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 *subject=NULL; /* Subject string */
- char *from=NULL; /* From string */
- char *replyto=NULL; /* Reply-to string */
- char *date=NULL; /* Date string */
- char *organization=NULL; /* Organization string */
- 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.
- */
- if (!diagnostic) {
- char * href=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 (TRACE) fprintf(stderr, "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:")) {
- StrAllocCopy(subject, HTStrip(strchr(line,':')+1));
-
- } else if (match(line, "DATE:")) {
- StrAllocCopy(date, HTStrip(strchr(line,':')+1));
-
- } else if (match(line, "ORGANIZATION:")) {
- StrAllocCopy(organization, HTStrip(strchr(line,':')+1));
-
- } else if(match(line, "FROM:")) {
- StrAllocCopy(from, HTStrip(strchr(line,':')+1));
-
- } else if(match(line, "REPLY-TO:")) {
- StrAllocCopy(replyto, HTStrip(strchr(line,':')+1));
-
- } 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 */
-
- START(HTML_HEAD);
- PUTS("\n");
- START(HTML_TITLE);
- if (subject && *subject != '\0')
- PUTS(subject);
- else
- PUTS("No Subject");
- END(HTML_TITLE);
- PUTS("\n");
- /* put in the owner as a link rel. */
- if (from || replyto) {
- char *temp=NULL;
- StrAllocCopy(temp, replyto ? replyto : from);
- StrAllocCopy(href,"mailto:");
- StrAllocCat(href,author_address(temp));
- start_link(href, "made");
- PUTS("\n");
- free(temp);
- }
- END(HTML_HEAD);
- PUTS("\n");
-
- START(HTML_H1);
- if (subject && *subject != '\0')
- PUTS(subject);
- else
- PUTS("No Subject");
- END(HTML_H1);
- PUTS("\n");
-
- if (subject)
- free(subject);
-
- START(HTML_DLC);
- PUTS("\n");
-
- if (from || replyto) {
- START(HTML_DT);
- START(HTML_B);
- PUTS("From:");
- END(HTML_B);
- PUTS(" ");
- if (from)
- PUTS(from);
- else
- PUTS(from);
- PUTS("\n");
-
- if (!replyto)
- StrAllocCopy(replyto, from);
- START(HTML_DT);
- START(HTML_B);
- PUTS("Reply to:");
- END(HTML_B);
- PUTS(" ");
- start_anchor(href);
- if (*replyto != '<')
- PUTS(author_name(replyto));
- else
- PUTS(author_address(replyto));
- END(HTML_A);
- START(HTML_BR);
- PUTS("\n");
-
- if (from)
- free(from);
- if (replyto)
- free(replyto);
- }
-
- if (date) {
- START(HTML_DT);
- START(HTML_B);
- PUTS("Date:");
- END(HTML_B);
- PUTS(" ");
- PUTS(date);
- PUTS("\n");
- free(date);
- }
-
- if (organization) {
- START(HTML_DT);
- START(HTML_B);
- PUTS("Organization:");
- END(HTML_B);
- PUTS(" ");
- PUTS(organization);
- PUTS("\n");
- free(organization);
- }
-
- if (newsgroups) {
- /* make posting possible */
- StrAllocCopy(href,"newsreply:");
- StrAllocCat(href,newsgroups);
-
- START(HTML_DT);
- START(HTML_B);
- PUTS("Newsgroups:");
- END(HTML_B);
- PUTS("\n");
- START(HTML_DD);
- write_anchors(newsgroups);
- PUTS("\n");
-
- START(HTML_DT);
- START(HTML_B);
- PUTS("Reply to:");
- END(HTML_B);
- PUTS(" ");
- start_anchor(href);
- PUTS("newsgroup(s)");
- END(HTML_A);
- PUTS("\n");
- free(newsgroups);
- }
-
- if (references) {
- START(HTML_DT);
- START(HTML_B);
- PUTS("References:");
- END(HTML_B);
- PUTS("\n");
- START(HTML_DD);
- write_anchors(references);
- PUTS("\n");
- free(references);
- }
-
- END(HTML_DLC);
- PUTS("\n");
- if (href)
- free(href);
- }
-
- /* Read in the BODY of the Article:
- */
- START(HTML_PRE);
- PUTS("\n");
-
- 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 (TRACE) fprintf(stderr, "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<q) {
- char c = q[1];
- q[1] = 0; /* chop up */
- *p = 0;
- PUTS(l);
- *p = '<'; /* again */
- *q = 0;
- start_anchor(p+1);
- *q = '>'; /* again */
- PUTS(p);
- END(HTML_A);
- q[1] = c; /* again */
- l=q+1;
- } else break; /* line has unmatched <> */
- }
- PUTS( l); /* Last bit of the line */
- } /* if not dot */
- p = line; /* Restart at beginning */
- } /* if end of line */
- } /* Loop over characters */
-
- END(HTML_PRE);
- PUTS("\n");
- }
-
-
- /* 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:
- **
- ** The header fields are either ignored, or formatted and put into the
- ** Text.
- */
- START(HTML_HEAD);
- PUTS("\n");
- START(HTML_TITLE);
- PUTS("Newsgroups");
- END(HTML_TITLE);
- PUTS("\n");
- END(HTML_HEAD);
- PUTS("\n");
- START(HTML_H1);
- PUTS( "Newsgroups");
- END(HTML_H1);
- PUTS("\n");
- p = line;
- START(HTML_DLC);
- 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 (TRACE) fprintf(stderr, "B %s", line);
- START(HTML_DT);
- 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);
- START(HTML_DD);
- PUTS(&line[i+1]); /* put description */
- } else {
- write_anchor(line, line);
- }
- } /* if not dot */
- p = line; /* Restart at beginning */
- } /* if end of line */
- } /* Loop over characters */
- END(HTML_DLC);
- PUTS("\n");
- }
-
-
- /* Read in a Newsgroup
- ** -------------------
- ** Unfortunately, we have to ask for each article one by one if we
- ** want more than one field.
- **
- */
- PRIVATE void read_group ARGS3(
- CONST char *,groupName,
- int,first_required,
- int,last_required
- )
- {
- 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 */
-
- START(HTML_HEAD);
- PUTS("\n");
- START(HTML_TITLE);
- PUTS("Newsgroup ");
- PUTS(groupName);
- END(HTML_TITLE);
- PUTS("\n");
- END(HTML_HEAD);
- PUTS("\n");
-
- sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
- if(TRACE) fprintf(stderr,
- "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");
- goto add_post;
- }
-
- #define FAST_THRESHOLD 100 /* Above this, read IDs fast */
- #define CHOP_THRESHOLD 50 /* Above this, chop off the rest */
-
- if (first_required<first) first_required = first; /* clip */
- if ((last_required==0) || (last_required > last)) last_required = last;
-
- if (last_required<first_required) {
- PUTS( "\nNo articles in this range.\n");
- goto add_post;
- }
-
- if (last_required-first_required+1 > MAX_CHUNK) { /* Trim this block */
- first_required = last_required-CHUNK_SIZE+1;
- }
- if (TRACE) fprintf(stderr, " Chunk will be (%d-%d)\n",
- first_required, last_required);
-
- /* Set window title
- */
- sprintf(buffer, "%s, Articles %d-%d",
- groupName, first_required, last_required);
- START(HTML_H1);
- PUTS(buffer);
- END(HTML_H1);
- PUTS("\n");
-
- /* Link to earlier articles
- */
- 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 (TRACE) fprintf(stderr, " Block before is %s\n", buffer);
- PUTS( "(");
- start_anchor(buffer);
- PUTS("Earlier articles");
- END(HTML_A);
- PUTS("...)\n");
- START(HTML_P);
- 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 (TRACE) fprintf(stderr, "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
-
- /* Read newsgroup using individual fields:
- */
- if (!done) {
- START(HTML_B);
- if (first==first_required && last==last_required)
- PUTS("All available articles in ");
- else PUTS( "Articles in ");
- PUTS(groupName);
- END(HTML_B);
- 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:*/
-
- p = line; /* Write pointer */
- done = NO;
- 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 & chop LF*/
- p = line; /* Restart at beginning */
- if (TRACE) fprintf(stderr, "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));
- 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);
-
- /* Link to later articles
- */
- if (last_required<last) {
- int after; /* End of article after */
- after = last_required+CHUNK_SIZE;
- if (after==last) sprintf(buffer, "news:%s", groupName); /* original group */
- else sprintf(buffer, "news:%s/%d-%d", groupName, last_required+1, after);
- if (TRACE) fprintf(stderr, " Block after is %s\n", buffer);
- PUTS("(");
- start_anchor(buffer);
- PUTS( "Later articles");
- END(HTML_A);
- PUTS( "...)\n");
- }
-
- add_post:
- {
- 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);
- }
- PUTS("\n");
- }
-
-
- /* Load by name HTLoadNews
- ** ============
- */
- PUBLIC int HTLoadNews ARGS4(
- CONST char *, arg,
- HTParentAnchor *, anAnchor,
- HTFormat, format_out,
- HTStream*, stream)
- {
- 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 = (format_out == WWW_SOURCE); /* set global flag */
-
- if (TRACE) fprintf(stderr, "HTNews: Looking for %s\n", arg);
-
- if (!initialized) initialized = initialize();
- if (!initialized) return -1; /* FAIL */
-
- {
- CONST char * p1=arg;
-
- /* We will ask for the document, omitting the host name & anchor.
- **
- ** Syntax of address is
- ** xxx@yyy Article
- ** <xxx@yyy> 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 */
- 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);
- } else {
- strcpy(groupName, p1);
- }
- strcat(command, groupName);
- } else {
- strcpy(command, "ARTICLE ");
- if (strchr(p1, '<')==0) strcat(command,"<");
- strcat(command, p1);
- if (strchr(p1, '>')==0) strcat(command,">");
- }
-
- {
- 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 NO; /* Ignore if no name */
-
-
- /* Make a hypertext object with an anchor list.
- */
- node_anchor = anAnchor;
- target = HTML_new(anAnchor, format_out, 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) {
- /* CONNECTING to news host */
- char url[1024];
- sprintf (url, "lose://%s/", HTNewsHost);
- if (TRACE)
- fprintf (stderr, "News: doing HTDoConnect on '%s'\n", url);
-
- _HTProgress("Connecting to NewsHost ...");
-
- status = HTDoConnect (url, "NNTP", NEWS_PORT, &s);
- if (status == HT_INTERRUPTED)
- {
- /* Interrupt cleanly. */
- fprintf (stderr,
- "News: Interrupted on connect; recovering cleanly.\n");
- _HTProgress ("Connection interrupted.");
-
- (*targetClass._abort)(target, NULL);
-
- return HT_INTERRUPTED;
- }
- if (status<0){
- char message[256];
- NETCLOSE(s);
- s = -1;
- if (TRACE) fprintf(stderr, "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 HTLoadError(stream, 500, message);
- } else {
- if (TRACE) fprintf(stderr, "HTNews: Connected to news host %s.\n",
- HTNewsHost);
- HTInitInput(s); /* set up buffering */
- if ((response(NULL) / 100) !=2) {
- char message[BIG];
- NETCLOSE(s);
- s = -1;
- sprintf(message,
- "Can't read news info. News host %.20s responded: %.200s",
- HTNewsHost, response_text);
- return HTLoadError(stream, 500, message);
- }
- }
- } /* If needed opening */
-
- /* Ensure reader mode */
- {
- char buffer[20];
- sprintf(buffer, "mode reader%c%c", CR, LF);
- status = response(buffer);
- }
-
- /* @@@@@@@@@@@@@@Tell user something's happening */
-
- status = response(command);
- if (status<0) break;
- if ((status/ 100) !=2) {
- _HTProgress(response_text);
- /* NXRunAlertPanel("News access", response_text,
- NULL,NULL,NULL);
- */
- NETCLOSE(s);
- s = -1;
- /* 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();
-
- (*targetClass._free)(target);
- return HT_LOADED;
-
- } /* Retry loop */
-
-
- /* HTAlert("Sorry, could not load requested news."); */
-
- /* NXRunAlertPanel(NULL, "Sorry, could not load `%s'.",
- NULL,NULL,NULL, arg);No -- message earlier wil have covered it */
-
- return HT_LOADED;
- }
-
- GLOBALDEF PUBLIC HTProtocol HTNews = { "news", HTLoadNews, NULL };
-