Annotation of libwww/Library/src/HTNews.c, revision 1.1.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