Annotation of libwww/Library/src/HTNews.c, revision 1.1
1.1 ! timbl 1: /* NEWS ACCESS HTNews.c
! 2: ** ===========
! 3: **
! 4: ** History:
! 5: ** 26 Sep 90 Written TBL
! 6: ** 29 Nov 91 Downgraded to C, for portable implementation.
! 7: */
! 8:
! 9: #define NEWS_PORT 119 /* See rfc977 */
! 10: #define APPEND /* Use append methods */
! 11: #define MAX_CHUNK 40 /* Largest number of articles in one window */
! 12: #define CHUNK_SIZE 20 /* Number of articles for quick display */
! 13:
! 14: #ifndef DEFAULT_NEWS_HOST
! 15: #define DEFAULT_NEWS_HOST "news"
! 16: #endif
! 17: #ifndef SERVER_FILE
! 18: #define SERVER_FILE "/usr/local/lib/rn/server"
! 19: #endif
! 20:
! 21: #include <ctype.h>
! 22: #include "HTUtils.h" /* Coding convention macros */
! 23: #include "tcp.h"
! 24:
! 25: #include "HTNews.h"
! 26:
! 27: #include "HText.h"
! 28: #include "HTParse.h"
! 29: #include "HTFormat.h"
! 30:
! 31: #ifdef NeXTStep
! 32: #include <appkit/defaults.h>
! 33: #define NEWS_PROGRESS(foo)
! 34: #else
! 35: #define NEWS_PROGRESS(foo) fprintf(stderr, "%s\n", (foo))
! 36: #endif
! 37:
! 38: extern HTStyleSheet * styleSheet;
! 39:
! 40: #define NEXT_CHAR HTGetChararcter()
! 41: #define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */
! 42: #define GROUP_NAME_LENGTH 256 /* Maximum length of group name */
! 43:
! 44:
! 45: /* Module-wide variables
! 46: */
! 47: PRIVATE char * NewsHost;
! 48: PRIVATE struct sockaddr_in soc_address; /* Binary network address */
! 49: PRIVATE int s; /* Socket for NewsHost */
! 50: PRIVATE char response_text[LINE_LENGTH+1]; /* Last response */
! 51: PRIVATE HText * HT; /* the new hypertext */
! 52: PRIVATE HTParentAnchor *node_anchor; /* Its anchor */
! 53: PRIVATE int diagnostic; /* level: 0=none 2=source */
! 54:
! 55: PRIVATE HTStyle *addressStyle; /* For address etc */
! 56: PRIVATE HTStyle *heading1Style; /* For heading level 1 */
! 57: PRIVATE HTStyle *textStyle; /* Text style */
! 58:
! 59:
! 60: /* Initialisation for this module
! 61: ** ------------------------------
! 62: **
! 63: ** Except on the NeXT, we pick up the NewsHost name from
! 64: **
! 65: ** 1. Environment variable NNTPSERVER
! 66: ** 2. File SERVER_FILE
! 67: ** 3. Compilation time macro DEFAULT_NEWS_HOST
! 68: ** 4. Default to "news"
! 69: **
! 70: ** On the NeXT, we pick up the NewsHost name from, in order:
! 71: **
! 72: ** 1. WorldWideWeb default "NewsHost"
! 73: ** 2. Global default "NewsHost"
! 74: ** 3. News default "NewsHost"
! 75: ** 4. Compilation time macro DEFAULT_NEWS_HOST
! 76: ** 5. Default to "news"
! 77: */
! 78: PRIVATE BOOL initialized = NO;
! 79: PRIVATE BOOL initialize NOARGS
! 80: {
! 81: CONST struct hostent *phost; /* Pointer to host - See netdb.h */
! 82: struct sockaddr_in* sin = &soc_address;
! 83:
! 84:
! 85: /* Set up defaults:
! 86: */
! 87: sin->sin_family = AF_INET; /* Family = internet, host order */
! 88: sin->sin_port = htons(NEWS_PORT); /* Default: new port, */
! 89:
! 90: /* Get name of Host
! 91: */
! 92: #ifdef NeXTStep
! 93: if ((NewsHost = NXGetDefaultValue("WorldWideWeb","NewsHost"))==0)
! 94: if ((NewsHost = NXGetDefaultValue("News","NewsHost")) == 0)
! 95: NewsHost = DEFAULT_NEWS_HOST;
! 96: #else
! 97: if (getenv("NNTPSERVER")) {
! 98: StrAllocCopy(NewsHost, (char *)getenv("NNTPSERVER"));
! 99: if (TRACE) fprintf(stderr, "HTNews: NNTPSERVER defined as `%s'\n",
! 100: NewsHost);
! 101: } else {
! 102: char server_name[256];
! 103: FILE* fp = fopen(SERVER_FILE, "r");
! 104: if (fp) {
! 105: if (fscanf(fp, "%s", server_name)==1) {
! 106: StrAllocCopy(NewsHost, server_name);
! 107: if (TRACE) fprintf(stderr,
! 108: "HTNews: File %s defines news host as `%s'\n",
! 109: SERVER_FILE, NewsHost);
! 110: }
! 111: fclose(fp);
! 112: }
! 113: }
! 114: if (!NewsHost) NewsHost = DEFAULT_NEWS_HOST;
! 115: #endif
! 116:
! 117: if (*NewsHost>='0' && *NewsHost<='9') { /* Numeric node address: */
! 118: sin->sin_addr.s_addr = inet_addr((char *)NewsHost); /* See arpa/inet.h */
! 119:
! 120: } else { /* Alphanumeric node name: */
! 121: phost=gethostbyname((char*)NewsHost); /* See netdb.h */
! 122: if (!phost) {
! 123: #ifdef NeXTStep
! 124: NXRunAlertPanel(NULL, "Can't find news host name `%s'.",
! 125: NULL, NULL, NULL, NewsHost);
! 126: #else
! 127: fprintf(stderr,
! 128: "HTNews: Can't find news host `%s'.\n",NewsHost);
! 129: fprintf(stderr,
! 130: " Please see online documentation for instructions to set the news host.\n");
! 131: #endif
! 132: CTRACE(tfp,
! 133: "HTNews: Can't find news host `%s'.\n",NewsHost);
! 134: return NO; /* Fail */
! 135: }
! 136: memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
! 137: }
! 138:
! 139: if (TRACE) fprintf(stderr,
! 140: "HTNews: Parsed address as port %4x, inet %d.%d.%d.%d\n",
! 141: (unsigned int)ntohs(sin->sin_port),
! 142: (int)*((unsigned char *)(&sin->sin_addr)+0),
! 143: (int)*((unsigned char *)(&sin->sin_addr)+1),
! 144: (int)*((unsigned char *)(&sin->sin_addr)+2),
! 145: (int)*((unsigned char *)(&sin->sin_addr)+3));
! 146:
! 147: s = -1; /* Disconnected */
! 148:
! 149: return YES;
! 150: }
! 151:
! 152:
! 153:
! 154: /* Get Styles from stylesheet
! 155: ** --------------------------
! 156: */
! 157: PRIVATE void get_styles NOARGS
! 158: {
! 159: if (!heading1Style) heading1Style = HTStyleNamed(styleSheet, "Heading1");
! 160: if (!addressStyle) addressStyle = HTStyleNamed(styleSheet, "Address");
! 161: if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
! 162: }
! 163:
! 164:
! 165: /* Send NNTP Command line to remote host & Check Response
! 166: ** ------------------------------------------------------
! 167: **
! 168: ** On entry,
! 169: ** command points to the command to be sent, including CRLF, or is null
! 170: ** pointer if no command to be sent.
! 171: ** On exit,
! 172: ** Negative status indicates transmission error, socket closed.
! 173: ** Positive status is an NNTP status.
! 174: */
! 175:
! 176:
! 177: PRIVATE int response ARGS1(CONST char *,command)
! 178: {
! 179: int result;
! 180: char * p = response_text;
! 181: if (command) {
! 182: int status;
! 183: int length = strlen(command);
! 184: if (TRACE) fprintf(stderr, "NNTP command to be sent: %s", command);
! 185: #ifdef NOT_ASCII
! 186: {
! 187: CONST char * p;
! 188: char * q;
! 189: char ascii[LINE_LENGTH+1];
! 190: for(p = command, q=ascii; *p; p++, q++) {
! 191: *q = TOASCII(*p);
! 192: }
! 193: status = NETWRITE(s, ascii, length);
! 194: }
! 195: #else
! 196: status = NETWRITE(s, command, length);
! 197: #endif
! 198: if (status<0){
! 199: if (TRACE) fprintf(stderr,
! 200: "HTNews: Unable to send command. Disconnecting.\n");
! 201: NETCLOSE(s);
! 202: s = -1;
! 203: return status;
! 204: } /* if bad status */
! 205: } /* if command to be sent */
! 206:
! 207: for(;;) {
! 208: if (((*p++=NEXT_CHAR) == '\n') || (p == &response_text[LINE_LENGTH])) {
! 209: *p++=0; /* Terminate the string */
! 210: if (TRACE) fprintf(stderr, "NNTP Response: %s\n", response_text);
! 211: sscanf(response_text, "%d", &result);
! 212: return result;
! 213: } /* if end of line */
! 214:
! 215: if (*(p-1) < 0) {
! 216: if (TRACE) fprintf(stderr,
! 217: "HTNews: EOF on read, closing socket %d\n", s);
! 218: NETCLOSE(s); /* End of file, close socket */
! 219: return s = -1; /* End of file on response */
! 220: }
! 221: } /* Loop over characters */
! 222: }
! 223:
! 224:
! 225: /* Case insensitive string comparisons
! 226: ** -----------------------------------
! 227: **
! 228: ** On entry,
! 229: ** template must be already un upper case.
! 230: ** unknown may be in upper or lower or mixed case to match.
! 231: */
! 232: PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,template)
! 233: {
! 234: CONST char * u = unknown;
! 235: CONST char * t = template;
! 236: for (;*u && *t && (TOUPPER(*u)==*t); u++, t++) /* Find mismatch or end */ ;
! 237: return (BOOL)(*t==0); /* OK if end of template */
! 238: }
! 239:
! 240: /* Find Author's name in mail address
! 241: ** ----------------------------------
! 242: **
! 243: ** On exit,
! 244: ** THE EMAIL ADDRESS IS CORRUPTED
! 245: **
! 246: ** For example, returns "Tim Berners-Lee" if given any of
! 247: ** " Tim Berners-Lee <tim@online.cern.ch> "
! 248: ** or " tim@online.cern.ch ( Tim Berners-Lee ) "
! 249: */
! 250: PRIVATE char * author_name ARGS1 (char *,email)
! 251: {
! 252: char *s, *e;
! 253:
! 254: if ((s=strchr(email,'(')) && (e=strchr(email, ')')))
! 255: if (e>s) {
! 256: *e=0; /* Chop off everything after the ')' */
! 257: return HTStrip(s+1); /* Remove leading and trailing spaces */
! 258: }
! 259:
! 260: if ((s=strchr(email,'<')) && (e=strchr(email, '>')))
! 261: if (e>s) {
! 262: strcpy(s, e+1); /* Remove <...> */
! 263: return HTStrip(email); /* Remove leading and trailing spaces */
! 264: }
! 265:
! 266: return HTStrip(email); /* Default to the whole thing */
! 267:
! 268: }
! 269:
! 270:
! 271: /* Paste in an Anchor
! 272: ** ------------------
! 273: **
! 274: **
! 275: ** On entry,
! 276: ** HT has a selection of zero length at the end.
! 277: ** text points to the text to be put into the file, 0 terminated.
! 278: ** addr points to the hypertext refernce address,
! 279: ** terminated by white space, comma, NULL or '>'
! 280: */
! 281: PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
! 282: {
! 283: char href[LINE_LENGTH+1];
! 284:
! 285: {
! 286: CONST char * p;
! 287: strcpy(href,"news:");
! 288: for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++);
! 289: strncat(href, addr, p-addr); /* Make complete hypertext reference */
! 290: }
! 291:
! 292: HText_beginAnchor(HT,
! 293: HTAnchor_findChildAndLink(node_anchor, "", href, 0));
! 294: HText_appendText(HT, text);
! 295: HText_endAnchor(HT);
! 296: }
! 297:
! 298:
! 299: /* Write list of anchors
! 300: ** ---------------------
! 301: **
! 302: ** We take a pointer to a list of objects, and write out each,
! 303: ** generating an anchor for each.
! 304: **
! 305: ** On entry,
! 306: ** HT has a selection of zero length at the end.
! 307: ** text points to a comma or space separated list of addresses.
! 308: ** On exit,
! 309: ** *text is NOT any more chopped up into substrings.
! 310: */
! 311: PRIVATE void write_anchors ARGS1 (char *,text)
! 312: {
! 313: char * start = text;
! 314: char * end;
! 315: char c;
! 316: for (;;) {
! 317: for(;*start && (WHITE(*start)); start++); /* Find start */
! 318: if (!*start) return; /* (Done) */
! 319: for(end=start; *end && (*end!=' ') && (*end!=','); end++);/* Find end */
! 320: if (*end) end++; /* Include comma or space but not NULL */
! 321: c = *end;
! 322: *end = 0;
! 323: write_anchor(start, start);
! 324: *end = c;
! 325: start = end; /* Point to next one */
! 326: }
! 327: }
! 328:
! 329: /* Abort the connection abort_socket
! 330: ** --------------------
! 331: */
! 332: PRIVATE void abort_socket NOARGS
! 333: {
! 334: if (TRACE) fprintf(stderr,
! 335: "HTNews: EOF on read, closing socket %d\n", s);
! 336: NETCLOSE(s); /* End of file, close socket */
! 337: HText_appendText(HT, "Network Error: connection lost");
! 338: HText_appendParagraph(HT);
! 339: s = -1; /* End of file on response */
! 340: return;
! 341: }
! 342:
! 343: /* Read in an Article read_article
! 344: ** ------------------
! 345: **
! 346: **
! 347: ** Note the termination condition of a single dot on a line by itself.
! 348: ** RFC 977 specifies that the line "folding" of RFC850 is not used, so we
! 349: ** do not handle it here.
! 350: **
! 351: ** On entry,
! 352: ** s Global socket number is OK
! 353: ** HT Global hypertext object is ready for appending text
! 354: */
! 355: PRIVATE void read_article NOARGS
! 356: {
! 357:
! 358: char line[LINE_LENGTH+1];
! 359: char *references=NULL; /* Hrefs for other articles */
! 360: char *newsgroups=NULL; /* Newsgroups list */
! 361: char *p = line;
! 362: BOOL done = NO;
! 363:
! 364: /* Read in the HEADer of the article:
! 365: **
! 366: ** The header fields are either ignored, or formatted and put into the
! 367: ** Text.
! 368: */
! 369: if (!diagnostic) {
! 370: HText_setStyle(HT, addressStyle);
! 371: while(!done){
! 372: char ch = *p++ = NEXT_CHAR;
! 373: if (ch==(char)EOF) {
! 374: abort_socket(); /* End of file, close socket */
! 375: return; /* End of file on response */
! 376: }
! 377: if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
! 378: *--p=0; /* Terminate the string */
! 379: if (TRACE) fprintf(stderr, "H %s\n", line);
! 380:
! 381: if (line[0]=='.') {
! 382: if (line[1]<' ') { /* End of article? */
! 383: done = YES;
! 384: break;
! 385: }
! 386:
! 387: } else if (line[0]<' ') {
! 388: break; /* End of Header? */
! 389: } else if (match(line, "SUBJECT:")) {
! 390: HTAnchor_setTitle(node_anchor, line+8);
! 391: HText_setStyle(HT, heading1Style);
! 392: HText_appendText(HT, line+8);
! 393: HText_setStyle(HT, addressStyle);
! 394: } else if (match(line, "DATE:")
! 395: || match(line, "FROM:")
! 396: || match(line, "ORGANIZATION:")) {
! 397: strcat(line, "\n");
! 398: HText_appendText(HT, strchr(line,':')+1);
! 399: } else if (match(line, "NEWSGROUPS:")) {
! 400: StrAllocCopy(newsgroups, HTStrip(strchr(line,':')+1));
! 401:
! 402: } else if (match(line, "REFERENCES:")) {
! 403: StrAllocCopy(references, HTStrip(strchr(line,':')+1));
! 404:
! 405: } /* end if match */
! 406: p = line; /* Restart at beginning */
! 407: } /* if end of line */
! 408: } /* Loop over characters */
! 409:
! 410: HText_appendCharacter(HT, '\n');
! 411: HText_setStyle(HT, textStyle);
! 412: if (newsgroups) {
! 413: HText_appendText(HT, "\nNewsgroups: ");
! 414: write_anchors(newsgroups);
! 415: free(newsgroups);
! 416: }
! 417:
! 418: if (references) {
! 419: HText_appendText(HT, "\nReferences: ");
! 420: write_anchors(references);
! 421: free(references);
! 422: }
! 423:
! 424: HText_appendText(HT, "\n\n\n");
! 425:
! 426: } else { /* diagnostic */
! 427: HText_setStyle(HT, textStyle);
! 428: }
! 429:
! 430: /* Read in the BODY of the Article:
! 431: */
! 432: p = line;
! 433: while(!done){
! 434: char ch = *p++ = NEXT_CHAR;
! 435: if (ch==(char)EOF) {
! 436: abort_socket(); /* End of file, close socket */
! 437: return; /* End of file on response */
! 438: }
! 439: if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
! 440: *p++=0; /* Terminate the string */
! 441: if (TRACE) fprintf(stderr, "B %s", line);
! 442: if (line[0]=='.') {
! 443: if (line[1]<' ') { /* End of article? */
! 444: done = YES;
! 445: break;
! 446: } else { /* Line starts with dot */
! 447: HText_appendText(HT, &line[1]); /* Ignore first dot */
! 448: }
! 449: } else {
! 450:
! 451: /* Normal lines are scanned for buried references to other articles.
! 452: ** Unfortunately, it will pick up mail addresses as well!
! 453: */
! 454: char *l = line;
! 455: char * p;
! 456: while (p=strchr(l, '<')) {
! 457: char *q = strchr(p,'>');
! 458: char *at = strchr(p, '@');
! 459: if (q && at && at<q) {
! 460: char c = q[1];
! 461: q[1] = 0; /* chop up */
! 462: *p = 0;
! 463: HText_appendText(HT, l);
! 464: *p = '<'; /* again */
! 465: *q = 0;
! 466: HText_beginAnchor(HT,
! 467: HTAnchor_findChildAndLink(
! 468: node_anchor, "", p+1, 0));
! 469: *q = '>'; /* again */
! 470: HText_appendText(HT, p);
! 471: HText_endAnchor(HT);
! 472: q[1] = c; /* again */
! 473: l=q+1;
! 474: } else break; /* line has unmatched <> */
! 475: }
! 476: HText_appendText(HT, l); /* Last bit of the line */
! 477: } /* if not dot */
! 478: p = line; /* Restart at beginning */
! 479: } /* if end of line */
! 480: } /* Loop over characters */
! 481: }
! 482:
! 483:
! 484: /* Read in a List of Newsgroups
! 485: ** ----------------------------
! 486: */
! 487: /*
! 488: ** Note the termination condition of a single dot on a line by itself.
! 489: ** RFC 977 specifies that the line "folding" of RFC850 is not used, so we
! 490: ** do not handle it here.
! 491: */
! 492: PRIVATE void read_list NOARGS
! 493: {
! 494:
! 495: char line[LINE_LENGTH+1];
! 496: char *p;
! 497: BOOL done = NO;
! 498:
! 499: /* Read in the HEADer of the article:
! 500: **
! 501: ** The header fields are either ignored, or formatted and put into the
! 502: ** Text.
! 503: */
! 504: HText_setStyle(HT, heading1Style);
! 505: HText_appendText(HT, "Newsgroups");
! 506: HText_setStyle(HT, textStyle);
! 507: p = line;
! 508: while(!done){
! 509: char ch = *p++ = NEXT_CHAR;
! 510: if (ch==(char)EOF) {
! 511: abort_socket(); /* End of file, close socket */
! 512: return; /* End of file on response */
! 513: }
! 514: if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
! 515: *p++=0; /* Terminate the string */
! 516: if (TRACE) fprintf(stderr, "B %s", line);
! 517: if (line[0]=='.') {
! 518: if (line[1]<' ') { /* End of article? */
! 519: done = YES;
! 520: break;
! 521: } else { /* Line starts with dot */
! 522: HText_appendText(HT, &line[1]);
! 523: }
! 524: } else {
! 525:
! 526: /* Normal lines are scanned for references to newsgroups.
! 527: */
! 528: char group[LINE_LENGTH];
! 529: int first, last;
! 530: char postable;
! 531: if (sscanf(line, "%s %d %d %c", group, &first, &last, &postable)==4)
! 532: write_anchor(line, group);
! 533: else
! 534: HText_appendText(HT, line);
! 535: } /* if not dot */
! 536: p = line; /* Restart at beginning */
! 537: } /* if end of line */
! 538: } /* Loop over characters */
! 539: }
! 540:
! 541:
! 542: /* Read in a Newsgroup
! 543: ** -------------------
! 544: ** Unfortunately, we have to ask for each article one by one if we
! 545: ** want more than one field.
! 546: **
! 547: */
! 548: PRIVATE void read_group ARGS3(
! 549: CONST char *,groupName,
! 550: int,first_required,
! 551: int,last_required
! 552: )
! 553: {
! 554: char line[LINE_LENGTH+1];
! 555: char author[LINE_LENGTH+1];
! 556: char subject[LINE_LENGTH+1];
! 557: char *p;
! 558: BOOL done;
! 559:
! 560: char buffer[LINE_LENGTH];
! 561: char *reference=0; /* Href for article */
! 562: int art; /* Article number WITHIN GROUP */
! 563: int status, count, first, last; /* Response fields */
! 564: /* count is only an upper limit */
! 565:
! 566: sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
! 567: if(TRACE) printf("Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
! 568: status, count, first, last, first_required, last_required);
! 569: if (last==0) {
! 570: HText_appendText(HT, "\nNo articles in this group.\n");
! 571: return;
! 572: }
! 573:
! 574: #define FAST_THRESHOLD 100 /* Above this, read IDs fast */
! 575: #define CHOP_THRESHOLD 50 /* Above this, chop off the rest */
! 576:
! 577: if (first_required<first) first_required = first; /* clip */
! 578: if ((last_required==0) || (last_required > last)) last_required = last;
! 579:
! 580: if (last_required<=first_required) {
! 581: HText_appendText(HT, "\nNo articles in this range.\n");
! 582: return;
! 583: }
! 584:
! 585: if (last_required-first_required+1 > MAX_CHUNK) { /* Trim this block */
! 586: first_required = last_required-CHUNK_SIZE+1;
! 587: }
! 588: if (TRACE) printf (
! 589: " Chunk will be (%d-%d)\n", first_required, last_required);
! 590:
! 591: /* Link to earlier articles
! 592: */
! 593: if (first_required>first) {
! 594: int before; /* Start of one before */
! 595: if (first_required-MAX_CHUNK <= first) before = first;
! 596: else before = first_required-CHUNK_SIZE;
! 597: sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1);
! 598: if (TRACE) fprintf(stderr, " Block before is %s\n", buffer);
! 599: HText_appendText(HT, " (");
! 600: HText_beginAnchor(HT,
! 601: HTAnchor_findChildAndLink(node_anchor, "", buffer, 0));
! 602: HText_appendText(HT, "Earlier articles");
! 603: HText_endAnchor(HT);
! 604: HText_appendText(HT, "...)\n");
! 605: }
! 606:
! 607: done = NO;
! 608:
! 609: /*#define USE_XHDR*/
! 610: #ifdef USE_XHDR
! 611: if (count>FAST_THRESHOLD) {
! 612: sprintf(buffer,
! 613: "\nThere are about %d articles currently available in %s, IDs as follows:\n\n",
! 614: count, groupName);
! 615: HText_appendText(HT, buffer);
! 616: sprintf(buffer, "XHDR Message-ID %d-%d\n", first, last);
! 617: status = response(buffer);
! 618: if (status==221) {
! 619:
! 620: p = line;
! 621: while(!done){
! 622: char ch = *p++ = NEXT_CHAR;
! 623: if (ch==(char)EOF) {
! 624: abort_socket(); /* End of file, close socket */
! 625: return; /* End of file on response */
! 626: }
! 627: if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
! 628: *p++=0; /* Terminate the string */
! 629: if (TRACE) fprintf(stderr, "X %s", line);
! 630: if (line[0]=='.') {
! 631: if (line[1]<' ') { /* End of article? */
! 632: done = YES;
! 633: break;
! 634: } else { /* Line starts with dot */
! 635: /* Ignore strange line */
! 636: }
! 637: } else {
! 638:
! 639: /* Normal lines are scanned for references to articles.
! 640: */
! 641: char * space = strchr(line, ' ');
! 642: if (space++)
! 643: write_anchor(space, space);
! 644: } /* if not dot */
! 645: p = line; /* Restart at beginning */
! 646: } /* if end of line */
! 647: } /* Loop over characters */
! 648:
! 649: /* leaving loop with "done" set */
! 650: } /* Good status */
! 651: };
! 652: #endif
! 653:
! 654: /* Read newsgroup using individual fields:
! 655: */
! 656: if (!done) {
! 657: if (first==first_required && last==last_required)
! 658: HText_appendText(HT, "\nAll available articles in ");
! 659: else HText_appendText(HT, "\nArticles in ");
! 660: HText_appendText(HT, groupName);
! 661: HText_appendText(HT, "\n\n");
! 662: for(art=first_required; art<=last_required; art++) {
! 663:
! 664: /*#define OVERLAP*/
! 665: #ifdef OVERLAP
! 666: /* With this code we try to keep the server running flat out by queuing just
! 667: ** one extra command ahead of time. We assume (1) that the server won't abort
! 668: ** if it gets input during output, and (2) that TCP buffering is enough for the
! 669: ** two commands. Both these assumptions seem very reasonable. However, we HAVE
! 670: ** had a hangup with a loaded server.
! 671: */
! 672: if (art==first_required) {
! 673: if (art==last_required) {
! 674: sprintf(buffer, "HEAD %d\n", art); /* Only one */
! 675: status = response(buffer);
! 676: } else { /* First of many */
! 677: sprintf(buffer, "HEAD %d\nHEAD %d\n", art, art+1);
! 678: status = response(buffer);
! 679: }
! 680: } else if (art==last_required) { /* Last of many */
! 681: status = response(NULL);
! 682: } else { /* Middle of many */
! 683: sprintf(buffer, "HEAD %d\n", art+1);
! 684: status = response(buffer);
! 685: }
! 686:
! 687: #else /* NOT OVERLAP */
! 688: sprintf(buffer, "HEAD %d\n", art);
! 689: status = response(buffer);
! 690: #endif /* NOT OVERLAP */
! 691:
! 692: if (status == 221) { /* Head follows - parse it:*/
! 693:
! 694: p = line; /* Write pointer */
! 695: done = NO;
! 696: while(!done){
! 697: char ch = *p++ = NEXT_CHAR;
! 698: if (ch==(char)EOF) {
! 699: abort_socket(); /* End of file, close socket */
! 700: return; /* End of file on response */
! 701: }
! 702: if ((ch == '\n')
! 703: || (p == &line[LINE_LENGTH]) ) {
! 704:
! 705: *--p=0; /* Terminate & chop LF*/
! 706: p = line; /* Restart at beginning */
! 707: if (TRACE) fprintf(stderr, "G %s\n", line);
! 708: switch(line[0]) {
! 709:
! 710: case '.':
! 711: done = (line[1]<' '); /* End of article? */
! 712: break;
! 713:
! 714: case 'S':
! 715: case 's':
! 716: if (match(line, "SUBJECT:"))
! 717: strcpy(subject, line+9);/* Save subject */
! 718: break;
! 719:
! 720: case 'M':
! 721: case 'm':
! 722: if (match(line, "MESSAGE-ID:")) {
! 723: char * addr = HTStrip(line+11) +1; /* Chop < */
! 724: addr[strlen(addr)-1]=0; /* Chop > */
! 725: StrAllocCopy(reference, addr);
! 726: }
! 727: break;
! 728:
! 729: case 'f':
! 730: case 'F':
! 731: if (match(line, "FROM:")) {
! 732: char * p;
! 733: strcpy(author,
! 734: author_name(strchr(line,':')+1));
! 735: p = author + strlen(author) - 1;
! 736: if (*p=='\n') *p = 0; /* Chop off newline */
! 737: }
! 738: break;
! 739:
! 740: } /* end switch on first character */
! 741: } /* if end of line */
! 742: } /* Loop over characters */
! 743:
! 744: sprintf(buffer, "\"%s\" - %s", subject, author);
! 745: if (reference) {
! 746: write_anchor(buffer, reference);
! 747: free(reference);
! 748: reference=0;
! 749: } else {
! 750: HText_appendText(HT, buffer);
! 751: }
! 752: HText_appendParagraph(HT);
! 753:
! 754:
! 755: /* Change the title bar to indicate progress!
! 756: */
! 757: if (art%10 == 0) {
! 758: sprintf(buffer, "Reading newsgroup %s, Article %d (of %d-%d) ...",
! 759: groupName, art, first, last);
! 760: HTAnchor_setTitle(node_anchor, buffer);
! 761: }
! 762:
! 763: } /* If good response */
! 764: } /* Loop over article */
! 765: } /* If read headers */
! 766:
! 767: /* Link to later articles
! 768: */
! 769: if (last_required<last) {
! 770: int after; /* End of article after */
! 771: after = last_required+CHUNK_SIZE;
! 772: if (after==last) sprintf(buffer, "news:%s", groupName); /* original group */
! 773: else sprintf(buffer, "news:%s/%d-%d", groupName, last_required+1, after);
! 774: if (TRACE) fprintf(stderr, " Block after is %s\n", buffer);
! 775: HText_appendText(HT, "(");
! 776: HText_beginAnchor(HT, HTAnchor_findChildAndLink(
! 777: node_anchor, "", buffer, 0));
! 778: HText_appendText(HT, "Later articles");
! 779: HText_endAnchor(HT);
! 780: HText_appendText(HT, "...)\n");
! 781: }
! 782:
! 783: /* Set window title
! 784: */
! 785: sprintf(buffer, "Newsgroup %s, Articles %d-%d",
! 786: groupName, first_required, last_required);
! 787: HTAnchor_setTitle(node_anchor, buffer);
! 788:
! 789: }
! 790:
! 791:
! 792: /* Load by name HTLoadNews
! 793: ** ============
! 794: */
! 795: PUBLIC int HTLoadNews ARGS3(
! 796: CONST char *,arg,
! 797: HTParentAnchor *,anAnchor,
! 798: int,diag)
! 799: {
! 800: char command[257]; /* The whole command */
! 801: char groupName[GROUP_NAME_LENGTH]; /* Just the group name */
! 802: int status; /* tcp return */
! 803: int retries; /* A count of how hard we have tried */
! 804: BOOL group_wanted; /* Flag: group was asked for, not article */
! 805: BOOL list_wanted; /* Flag: group was asked for, not article */
! 806: int first, last; /* First and last articles asked for */
! 807:
! 808: diagnostic = diag; /* set global flag */
! 809:
! 810: if (TRACE) fprintf(stderr, "HTNews: Looking for %s\n", arg);
! 811: get_styles();
! 812:
! 813: if (!initialized) initialized = initialize();
! 814: if (!initialized) return -1; /* FAIL */
! 815:
! 816: {
! 817: CONST char * p1=arg;
! 818:
! 819: /* We will ask for the document, omitting the host name & anchor.
! 820: **
! 821: ** Syntax of address is
! 822: ** xxx@yyy Article
! 823: ** <xxx@yyy> Same article
! 824: ** xxxxx News group (no "@")
! 825: ** group/n1-n2 Articles n1 to n2 in group
! 826: */
! 827: group_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')==0);
! 828: list_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')!=0);
! 829:
! 830: /* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */
! 831: /* Don't use HTParse because news: access doesn't follow traditional
! 832: rules. For instance, if the article reference contains a '#',
! 833: the rest of it is lost -- JFG 10/7/92, from a bug report */
! 834: if (!strncasecomp (arg, "news:", 5))
! 835: p1 = arg + 5; /* Skip "news:" prefix */
! 836: if (list_wanted) {
! 837: strcpy(command, "LIST ");
! 838: } else if (group_wanted) {
! 839: char * slash = strchr(p1, '/');
! 840: strcpy(command, "GROUP ");
! 841: first = 0;
! 842: last = 0;
! 843: if (slash) {
! 844: *slash = 0;
! 845: strcpy(groupName, p1);
! 846: *slash = '/';
! 847: (void) sscanf(slash+1, "%d-%d", &first, &last);
! 848: } else {
! 849: strcpy(groupName, p1);
! 850: }
! 851: strcat(command, groupName);
! 852: } else {
! 853: strcpy(command, "ARTICLE ");
! 854: if (strchr(p1, '<')==0) strcat(command,"<");
! 855: strcat(command, p1);
! 856: if (strchr(p1, '>')==0) strcat(command,">");
! 857: }
! 858: /* free(p1); * bug fix TBL 5 Aug 92 */
! 859:
! 860: strcat(command, "\r\n"); /* CR LF, as in rfc 977 */
! 861:
! 862: } /* scope of p1 */
! 863:
! 864: if (!*arg) return NO; /* Ignore if no name */
! 865:
! 866:
! 867: /* Make a hypertext object with an anchor list.
! 868: */
! 869: node_anchor = anAnchor;
! 870: HT = HText_new(anAnchor);
! 871: HText_beginAppend(HT);
! 872:
! 873: /* Now, let's get a stream setup up from the NewsHost:
! 874: */
! 875: for(retries=0;retries<2; retries++){
! 876:
! 877: if (s<0) {
! 878: HTAnchor_setTitle(node_anchor, "Connecting to NewsHost ...");/* Tell user */
! 879: NEWS_PROGRESS("Connecting to NewsHost ...");
! 880: s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
! 881: status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
! 882: if (status<0){
! 883: char message[256];
! 884: NETCLOSE(s);
! 885: s = -1;
! 886: if (TRACE) fprintf(stderr, "HTNews: Unable to connect to news host.\n");
! 887: /* if (retries<=1) continue; WHY TRY AGAIN ? */
! 888: #ifdef NeXTStep
! 889: NXRunAlertPanel(NULL,
! 890: "Could not access newshost %s.",
! 891: NULL,NULL,NULL,
! 892: NewsHost);
! 893: #else
! 894: fprintf(stderr, "Could not access newshost %s\n",
! 895: NewsHost);
! 896: #endif
! 897: sprintf(message,
! 898: "\nCould not access %s.\n\n (Check default WorldWideWeb NewsHost ?)\n",
! 899: NewsHost);
! 900: HText_beginAppend(HT);
! 901: HText_appendText(HT, message);
! 902: HText_endAppend(HT);
! 903: return YES;
! 904: } else {
! 905: if (TRACE) fprintf(stderr, "HTNews: Connected to news host %s.\n",
! 906: NewsHost);
! 907: HTInitInput(s); /* set up buffering */
! 908: if ((response(NULL) / 100) !=2) {
! 909: NETCLOSE(s);
! 910: s = -1;
! 911: #ifdef NeXTStep
! 912: NXRunAlertPanel("News access",
! 913: "Could not retrieve information:\n %s.",
! 914: NULL,NULL,NULL,
! 915: response_text);
! 916: #endif
! 917: HTAnchor_setTitle(node_anchor, "News host response");
! 918: HText_beginAppend(HT);
! 919: HText_appendText(HT,
! 920: "Sorry, could not retrieve information: ");
! 921: HText_appendText(HT, response_text);
! 922: HText_endAppend(HT);
! 923: return YES;
! 924: }
! 925: }
! 926: } /* If needed opening */
! 927:
! 928: HTAnchor_setTitle(node_anchor, arg);/* Tell user something's happening */
! 929: status = response(command);
! 930: if (status<0) break;
! 931: if ((status/ 100) !=2) {
! 932: /* NXRunAlertPanel("News access", response_text,
! 933: NULL,NULL,NULL);
! 934: */
! 935: HText_beginAppend(HT);
! 936: HText_appendText(HT, response_text);
! 937: HText_endAppend(HT);
! 938: NETCLOSE(s);
! 939: s = -1;
! 940: /* return HT; -- no:the message might be "Timeout-disconnected" left over */
! 941: continue; /* Try again */
! 942: }
! 943:
! 944: /* Load a group, article, etc
! 945: */
! 946: HText_beginAppend(HT);
! 947:
! 948: if (list_wanted) read_list();
! 949: else if (group_wanted) read_group(groupName, first, last);
! 950: else read_article();
! 951:
! 952: HText_endAppend(HT);
! 953: return YES;
! 954:
! 955: } /* Retry loop */
! 956:
! 957: HText_beginAppend(HT);
! 958: HText_appendText(HT, "Sorry, could not load requested news.\n");
! 959: HText_endAppend(HT);
! 960:
! 961: /* NXRunAlertPanel(NULL, "Sorry, could not load `%s'.",
! 962: NULL,NULL,NULL, arg);No -- message earlier wil have covered it */
! 963:
! 964: return YES;
! 965: }
! 966:
Webmaster