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