Annotation of libwww/Library/src/HTNews.c, revision 2.32.6.1

2.26      frystyk     1: /*                                                                    HTNews.c
                      2: **     NEWS ACCESS
                      3: **
2.29      frystyk     4: **     (c) COPYRIGHT MIT 1995.
2.26      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
1.1       timbl       6: **
                      7: ** History:
                      8: **     26 Sep 90       Written TBL
                      9: **     29 Nov 91       Downgraded to C, for portable implementation.
2.19      luotonen   10: **     16 Feb 94  AL   Added Lou Montulli's Lynx & LIST NEWSGROUPS diffs.
                     11: **      2 May 94  AL   Added HTUnEscape() to HTLoadNews(), and
                     12: **                     fixed a possible security hole when the URL contains
                     13: **                     a newline, that could cause multiple commands to be
                     14: **                     sent to an NNTP server.
2.23      duns       15: **      8 Jul 94  FM   Insulate free() from _free structure element.
2.32.6.1! frystyk    16: **     30 Aug 94  HFN  Cleaned up a whole lot and made a state machine
1.1       timbl      17: */
2.27      roeber     18: 
2.32.6.1! frystyk    19: /* Implementation dependent include files */
2.28      frystyk    20: #include "tcp.h"
2.32.6.1! frystyk    21: 
        !            22: /* Library Include files */
        !            23: #include "HTUtils.h"
2.28      frystyk    24: #include "HTML.h"
2.32.6.1! frystyk    25: #include "HTChunk.h"
2.28      frystyk    26: #include "HTParse.h"
                     27: #include "HTFormat.h"
2.32.6.1! frystyk    28: #include "HTTee.h"
        !            29: #include "HTFWriter.h"
        !            30: #include "HTMIME.h"
        !            31: #include "HTTCP.h"
2.28      frystyk    32: #include "HTError.h"
2.32.6.1! frystyk    33: #include "HTArray.h"
        !            34: #include "HTNews.h"                                           /* Implements */
        !            35: 
        !            36: /* Macros and other defines */
        !            37: #ifndef NEWS_PORT
        !            38: #define NEWS_PORT              119                            /* See rfc977 */
        !            39: #endif
1.3       timbl      40: 
2.32.6.1! frystyk    41: #ifndef NEWS_LIST_FILE
        !            42: #define NEWS_LIST_FILE         ".www_news"        /* Name of news list file */
        !            43: #endif
1.1       timbl      44: 
                     45: #ifndef DEFAULT_NEWS_HOST
2.32.6.1! frystyk    46: #define DEFAULT_NEWS_HOST      "news"
1.1       timbl      47: #endif
2.32.6.1! frystyk    48: 
1.1       timbl      49: #ifndef SERVER_FILE
2.32.6.1! frystyk    50: #define SERVER_FILE            "/usr/local/lib/rn/server"
1.1       timbl      51: #endif
                     52: 
2.32.6.1! frystyk    53: #define PUTC(c)                (*target->isa->put_character)   (target, c)
        !            54: #define PUTS(s)                (*target->isa->put_string)      (target, s)
        !            55: #define PUTBLOCK(b, l) (*target->isa->put_block)       (target, b, l)
        !            56: #define START(e)       (*target->isa->start_element)   (target, e, 0, 0)
        !            57: #define END(e)         (*target->isa->end_element)     (target, e)
        !            58: #define FREE_TARGET    (*target->isa->_free)           (target)
        !            59: 
        !            60: /* Type definitions and global variables etc. local to this module */
        !            61: typedef enum _HTNewsState {
        !            62:     NEWS_BEGIN,
        !            63:     NEWS_SEEK_CACHE_LIST,
        !            64:     NEWS_NEED_CONNECTION,
        !            65:     NEWS_NEED_REQUEST,
        !            66:     NEWS_SENT_REQUEST,
        !            67:     NEWS_NEED_DATA,
        !            68:     NEWS_ERROR,
        !            69:     NEWS_GOT_DATA
        !            70: } HTNewsState;
        !            71: 
        !            72: #define MAX_STATUS_LEN         75     /* Max nb of chars to check News Line */
        !            73: 
        !            74: /* This is the local version of the HTNetInfo structure */
        !            75: typedef struct _news_info {
        !            76:     SOCKFD             sockfd;                         /* Socket descripter */
        !            77:     SockA              sock_addr;              /* SockA is defined in tcp.h */
        !            78:     HTInputSocket *    isoc;                                /* Input buffer */
        !            79:     SocAction          action;                 /* Result of the select call */
        !            80:     HTStream *         target;                             /* Target stream */
        !            81:     int                addressCount;        /* Attempts if multi-homed host */
        !            82:     time_t             connecttime;             /* Used on multihomed hosts */
        !            83:     struct _HTRequest *        request;           /* Link back to request structure */
        !            84: 
        !            85:     HTNewsState                state;                    /* State of the connection */
        !            86:     int                        listsent;                /* Number of tries for LIST */
        !            87:     BOOL               nntp;                /* YES if we using a `nntp' URL */
        !            88:     int                        first;            /* First article in the group list */
        !            89:     int                        last;              /* Last article in the group list */
        !            90: } news_info;
        !            91: 
        !            92: /* This version of a stream is used for NEWS LIST conversion to HTML */
        !            93: struct _HTStream {
        !            94:     CONST HTStreamClass *      isa;
        !            95:     HTStructured *             target;
        !            96:     HTRequest *                        request;
        !            97:     news_info *                        news;
        !            98:     HTSocketEOL                        state;
        !            99:     BOOL                       description;       /* YES if LIST NEWSGROUPS */
        !           100:     BOOL                       semi_trans;
        !           101:     int                                status;
        !           102:     char *                     reason;
        !           103:     char                       buffer[MAX_STATUS_LEN+1];
        !           104:     int                                buflen;
        !           105: };
2.8       timbl     106: 
2.32.6.1! frystyk   107: /* We are creating a structured stream */
1.2       timbl     108: struct _HTStructured {
2.32.6.1! frystyk   109:     CONST HTStructuredClass *  isa;
        !           110:     /* ... */
1.2       timbl     111: };
                    112: 
2.32.6.1! frystyk   113: PRIVATE char *HTNewsHost = NULL;
1.1       timbl     114: 
2.32.6.1! frystyk   115: /* ------------------------------------------------------------------------- */
        !           116: /*                            NEWS INPUT STREAM                             */
        !           117: /* ------------------------------------------------------------------------- */
        !           118: PRIVATE int stream_pipe ARGS1(HTStream *, me)
        !           119: {
        !           120:     HTRequest *req = me->request;
        !           121:     if (me->target) {
        !           122:        int status = PUTBLOCK(me->buffer, me->buflen);
        !           123:        if (status == HT_OK)
        !           124:            me->transparent = YES;
        !           125:        return status;
        !           126:     }
        !           127:     
        !           128:     /* Set up the streams */
        !           129:     if (me->status==200) {
        !           130:        HTStream *s;
        !           131:        if (req->output_format == WWW_SOURCE) {
        !           132:            me->target = HTMIMEConvert(req, NULL, WWW_MIME,
        !           133:                                       req->output_format,
        !           134:                                       req->output_stream);
        !           135:        } else {
        !           136:            me->target = HTStreamStack(WWW_MIME, req->output_format,
        !           137:                                       req->output_stream, req, NO);
        !           138:            
        !           139:            /* howcome: test for return value from HTCacheWriter 12/1/95 */
        !           140:            if (req->method==METHOD_GET && HTCache_isEnabled() &&
        !           141:                (s = HTCacheWriter(req, NULL, WWW_MIME, req->output_format,
        !           142:                                   req->output_stream))) {
        !           143:                me->target = HTTee(me->target, s);
        !           144:            }
        !           145:        }
        !           146:     } else {
        !           147:        me->target = HTMIMEConvert(req, NULL, WWW_MIME, req->error_format,
        !           148:                                   req->error_stream);
        !           149:     }
        !           150:     if (!me->target)
        !           151:        me->target = HTBlackHole();                             /* What else */
        !           152:     me->semi_trans = YES;                             /* Look for CRLF.CRLF */
        !           153:     me->state = EOL_BEGIN;                            /* Restart CRLF state */
        !           154:     return HT_OK;
        !           155: }
2.11      timbl     156: 
2.32.6.1! frystyk   157: PRIVATE int HTNewsStatus_put_character ARGS2(HTStream *, me, char, ch)
1.2       timbl     158: {
2.32.6.1! frystyk   159:     return HTNewsStatus_put_block(me, &ch, 1);
1.2       timbl     160: }
1.1       timbl     161: 
2.32.6.1! frystyk   162: PRIVATE int HTNewsStatus_put_string ARGS2(HTStream *, me, CONST char*, s)
1.2       timbl     163: {
2.32.6.1! frystyk   164:     return HTNewsStatus_put_block(me, s, (int) strlen(s));
1.2       timbl     165: }
1.1       timbl     166: 
2.32.6.1! frystyk   167: PRIVATE int HTNewsStatus_flush ARGS1(HTStream *, me)
1.1       timbl     168: {
2.32.6.1! frystyk   169:     return (*me->target->isa->flush)(me->target);
        !           170: }
1.1       timbl     171: 
2.32.6.1! frystyk   172: /*
        !           173: ** Searches for NNTP header line until buffer fills up or a CRLF or LF
        !           174: ** is found
1.1       timbl     175: */
2.32.6.1! frystyk   176: PRIVATE int HTNewsStatus_put_block ARGS3(HTStream *, me,CONST char*, b, int, l)
        !           177: {
        !           178:     while (!me->semi_trans && l-- > 0) {
        !           179:        int status;
        !           180:        if (me->target) {
        !           181:            if ((status = stream_pipe(me)) != HT_OK)
        !           182:                return status;
        !           183:        } else {
        !           184:            if (me->state == EOL_FCR) {
        !           185:                if (*b == LF) {                                /* Line found */
1.1       timbl     186: 
2.32.6.1! frystyk   187:                    /* parse status line */
        !           188:                    char *ptr = me->buffer+5;
        !           189:                    me->version = HTNextField(&ptr);
        !           190:                    me->status = atoi(HTNextField(&ptr));
        !           191:                    me->reason = ptr;
        !           192:                    if ((ptr = strchr(me->reason, '\r')) != NULL)
        !           193:                        *ptr = '\0';
        !           194:                    else if ((ptr = strchr(me->reason, '\n')) != NULL)
        !           195:                        *ptr = '\0';
        !           196:                    if ((status = stream_pipe(me)) != HT_OK)
        !           197:                        return status;
        !           198:                } else {
        !           199:                    me->state = EOL_BEGIN;
        !           200:                }
        !           201:            } else if (*b == CR) {
        !           202:                me->state = EOL_FCR;
        !           203:            } else if (*b == LF) {
        !           204:                if ((status = stream_pipe(me)) != HT_OK)
        !           205:                    return status;
1.1       timbl     206:            }
2.32.6.1! frystyk   207:            b++;
1.1       timbl     208:        }
                    209:     }
                    210: 
2.32.6.1! frystyk   211:     /* In semi transparent mode we look for CRLF.CRLF as end of body */
        !           212:     if (me->target) {                              /* Is the stream set up? */
1.1       timbl     213: 
2.32.6.1! frystyk   214:        /* looking for CFLF.CRLF */
        !           215:        
        !           216: #if 0
        !           217:        while (l-- > 0) {
        !           218:            if (me->EOLstate == EOL_FCR) {
        !           219:                if (*b == LF)                                        /* CRLF */
        !           220:                    me->EOLstate = EOL_FLF;
        !           221:                else if (WHITE(*b)) {                      /* Folding: CR SP */
        !           222:                    me->EOLstate = EOL_BEGIN;
        !           223:            } else {                                             /* New line */
        !           224:                me->EOLstate = EOL_BEGIN;
        !           225:            }
        !           226:        } else if (me->EOLstate == EOL_FLF) {
        !           227:            if (*b == CR)                               /* LF CR or CR LF CR */
        !           228:                me->EOLstate = EOL_SCR;
        !           229:            else if (*b == LF)                              /* End of header */
        !           230:                parseheader(me, me->request, me->request->anchor);
        !           231:            else if (WHITE(*b)) {              /* Folding: LF SP or CR LF SP */
        !           232:                me->EOLstate = EOL_BEGIN;
        !           233:                HTChunkPutc(me->buffer, ' ');
        !           234:            } else {                                            /* New line */
        !           235:                me->EOLstate = EOL_BEGIN;
        !           236:                HTChunkPutc(me->buffer, '\0');
        !           237:                HTChunkPutc(me->buffer, *b);
        !           238:            }
        !           239:        } else if (me->EOLstate == EOL_SCR) {
        !           240:            if (*b==CR || *b==LF)                           /* End of header */
        !           241:                parseheader(me, me->request, me->request->anchor);
        !           242:            else if (WHITE(*b)) {        /* Folding: LF CR SP or CR LF CR SP */
        !           243:                me->EOLstate = EOL_BEGIN;
        !           244:                HTChunkPutc(me->buffer, ' ');
        !           245:            } else {                                            /* New line */
        !           246:                me->EOLstate = EOL_BEGIN;
        !           247:                HTChunkPutc(me->buffer, '\0');
        !           248:                HTChunkPutc(me->buffer, *b);
        !           249:            }
        !           250:        } else if (*b == CR) {
        !           251:            me->EOLstate = EOL_FCR;
        !           252:        } else if (*b == LF) {
        !           253:            me->EOLstate = EOL_FLF;                            /* Line found */
        !           254:        } else
        !           255:            HTChunkPutc(me->buffer, *b);
        !           256:        b++;
        !           257: #endif
1.1       timbl     258: 
2.32.6.1! frystyk   259:        if (l > 0)                                         /* Anything left? */
        !           260:            return PUTBLOCK(b, l);
        !           261:        return HT_OK;
        !           262:     }
        !           263:     return HT_WOULD_BLOCK;
1.1       timbl     264: }
                    265: 
2.32.6.1! frystyk   266: PRIVATE int HTNewsStatus_free ARGS1(HTStream *, me)
        !           267: {
        !           268: }
1.1       timbl     269: 
2.32.6.1! frystyk   270: PRIVATE int HTNewsStatus_abort ARGS2(HTStream *, me, HTError, e)
1.1       timbl     271: {
                    272: }
                    273: 
2.32.6.1! frystyk   274: PRIVATE CONST HTStreamClass HTNewsStatusClass =
        !           275: {              
        !           276:     "NewsStatus",
        !           277:     HTNewsStatus_flush,
        !           278:     HTNewsStatus_free,
        !           279:     HTNewsStatus_abort,
        !           280:     HTNewsStatus_put_character,
        !           281:     HTNewsStatus_put_string,
        !           282:     HTNewsStatus_put_block
        !           283: };
1.1       timbl     284: 
2.32.6.1! frystyk   285: PUBLIC HTStream *HTNewsStatus_new ARGS5(HTRequest *,   request,
        !           286:                                        news_info *,    news)
1.1       timbl     287: {
2.32.6.1! frystyk   288:     HTStream *me = (HTStream *) calloc(1, sizeof(HTStream));
        !           289:     if (!me) outofmem(__FILE__, "HTNewsStatus_new");
        !           290:     me->isa = &HTNewsStatusClass;
        !           291:     me->request = request;
        !           292:     me->news = news;
        !           293:     me->state = EOL_BEGIN;
        !           294:     return me;
1.1       timbl     295: }
                    296: 
2.32.6.1! frystyk   297: /* ------------------------------------------------------------------------- */
        !           298: /*                FUNCTIONS FOR READING ARTICLES AND GROUPS                 */
        !           299: /* ------------------------------------------------------------------------- */
        !           300: 
        !           301: #ifdef 0
1.1       timbl     302: /*     Find Author's name in mail address
                    303: **     ----------------------------------
                    304: **
                    305: ** On exit,
                    306: **     THE EMAIL ADDRESS IS CORRUPTED
                    307: **
                    308: ** For example, returns "Tim Berners-Lee" if given any of
2.32.6.1! frystyk   309: **     " Tim Berners-Lee <tim@online.cern.ch> "
        !           310: **  or " tim@online.cern.ch ( Tim Berners-Lee ) "
1.1       timbl     311: */
                    312: PRIVATE char * author_name ARGS1 (char *,email)
                    313: {
                    314:     char *s, *e;
                    315:     
                    316:     if ((s=strchr(email,'(')) && (e=strchr(email, ')')))
                    317:         if (e>s) {
                    318:            *e=0;                       /* Chop off everything after the ')'  */
                    319:            return HTStrip(s+1);        /* Remove leading and trailing spaces */
                    320:        }
                    321:        
                    322:     if ((s=strchr(email,'<')) && (e=strchr(email, '>')))
                    323:         if (e>s) {
                    324:            strcpy(s, e+1);             /* Remove <...> */
                    325:            return HTStrip(email);      /* Remove leading and trailing spaces */
                    326:        }
                    327:        
                    328:     return HTStrip(email);             /* Default to the whole thing */
                    329: 
                    330: }
                    331: 
1.2       timbl     332: /*     Start anchor element
                    333: **     --------------------
                    334: */
                    335: PRIVATE void start_anchor ARGS1(CONST char *,  href)
                    336: {
                    337:     BOOL               present[HTML_A_ATTRIBUTES];
                    338:     CONST char*                value[HTML_A_ATTRIBUTES];
                    339:     
                    340:     {
                    341:        int i;
                    342:        for(i=0; i<HTML_A_ATTRIBUTES; i++)
                    343:            present[i] = (i==HTML_A_HREF);
                    344:     }
                    345:     value[HTML_A_HREF] = href;
2.32.6.1! frystyk   346:     (*target->isa->start_element)(target, HTML_A , present, value);
1.2       timbl     347: 
                    348: }
1.1       timbl     349: 
2.16      luotonen  350: 
                    351: /*      Start link element
                    352: **      --------------------
                    353: */
                    354: PRIVATE void start_link ARGS2(CONST char *,  href, CONST char *, rev)
                    355: {
                    356: #ifdef WHEN_WE_HAVE_HTMLPLUS
                    357: 
                    358:     BOOL                present[HTML_LINK_ATTRIBUTES];
                    359:     CONST char*         value[HTML_LINK_ATTRIBUTES];
                    360:    
                    361:     {
                    362:         int i;
                    363:         for(i=0; i<HTML_LINK_ATTRIBUTES; i++)
                    364:             present[i] = (i==HTML_LINK_HREF || i==HTML_LINK_REV);
                    365:     }
                    366:     value[HTML_LINK_HREF] = href;
                    367:     value[HTML_LINK_REV]  = rev;
                    368:     (*targetClass.start_element)(target, HTML_LINK , present, value);
                    369: 
                    370: #endif
                    371: }
                    372: 
                    373: 
                    374: 
                    375: 
1.1       timbl     376: /*     Paste in an Anchor
                    377: **     ------------------
                    378: **
                    379: **
                    380: ** On entry,
                    381: **     HT      has a selection of zero length at the end.
                    382: **     text    points to the text to be put into the file, 0 terminated.
                    383: **     addr    points to the hypertext refernce address,
                    384: **             terminated by white space, comma, NULL or '>' 
                    385: */
                    386: PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
                    387: {
                    388:     char href[LINE_LENGTH+1];
                    389:                
                    390:     {
                    391:        CONST char * p;
                    392:        strcpy(href,"news:");
                    393:        for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++);
                    394:         strncat(href, addr, p-addr);   /* Make complete hypertext reference */
                    395:     }
                    396:     
1.2       timbl     397:     start_anchor(href);
                    398:     PUTS(text);
                    399:     END(HTML_A);
1.1       timbl     400: }
                    401: 
                    402: 
                    403: /*     Write list of anchors
                    404: **     ---------------------
                    405: **
                    406: **     We take a pointer to a list of objects, and write out each,
                    407: **     generating an anchor for each.
                    408: **
                    409: ** On entry,
                    410: **     HT      has a selection of zero length at the end.
                    411: **     text    points to a comma or space separated list of addresses.
                    412: ** On exit,
                    413: **     *text   is NOT any more chopped up into substrings.
                    414: */
                    415: PRIVATE void write_anchors ARGS1 (char *,text)
                    416: {
                    417:     char * start = text;
                    418:     char * end;
                    419:     char c;
                    420:     for (;;) {
                    421:         for(;*start && (WHITE(*start)); start++);  /* Find start */
                    422:        if (!*start) return;                    /* (Done) */
                    423:         for(end=start; *end && (*end!=' ') && (*end!=','); end++);/* Find end */
                    424:        if (*end) end++;        /* Include comma or space but not NULL */
                    425:        c = *end;
                    426:        *end = 0;
                    427:        write_anchor(start, start);
2.16      luotonen  428:        START(HTML_BR);
1.1       timbl     429:        *end = c;
                    430:        start = end;                    /* Point to next one */
                    431:     }
                    432: }
                    433: 
                    434: 
                    435: /*     Read in an Article                                      read_article
                    436: **     ------------------
                    437: **
                    438: **
                    439: **     Note the termination condition of a single dot on a line by itself.
                    440: **     RFC 977 specifies that the line "folding" of RFC850 is not used, so we
                    441: **     do not handle it here.
                    442: **
                    443: ** On entry,
                    444: **     s       Global socket number is OK
                    445: **     HT      Global hypertext object is ready for appending text
                    446: */       
                    447: PRIVATE void read_article NOARGS
                    448: {
                    449: 
                    450:     char line[LINE_LENGTH+1];
                    451:     char *references=NULL;                     /* Hrefs for other articles */
                    452:     char *newsgroups=NULL;                     /* Newsgroups list */
                    453:     char *p = line;
                    454:     BOOL done = NO;
                    455:     
                    456: /*     Read in the HEADer of the article:
                    457: **
                    458: **     The header fields are either ignored, or formatted and put into the
                    459: **      Text.
                    460: */
                    461:     if (!diagnostic) {
1.2       timbl     462:         (*targetClass.start_element)(target, HTML_ADDRESS, 0, 0);
1.1       timbl     463:        while(!done){
                    464:            char ch = *p++ = NEXT_CHAR;
                    465:            if (ch==(char)EOF) {
                    466:                abort_socket(); /* End of file, close socket */
                    467:                return;         /* End of file on response */
                    468:            }
1.3       timbl     469:            if ((ch == LF) || (p == &line[LINE_LENGTH])) {
1.1       timbl     470:                *--p=0;                         /* Terminate the string */
2.28      frystyk   471:                if (TRACE) fprintf(TDEST, "H %s\n", line);
1.1       timbl     472: 
                    473:                if (line[0]=='.') {     
                    474:                    if (line[1]<' ') {          /* End of article? */
                    475:                        done = YES;
                    476:                        break;
                    477:                    }
                    478:                
                    479:                } else if (line[0]<' ') {
                    480:                    break;              /* End of Header? */
2.16      luotonen  481: 
2.32.6.1! frystyk   482:                } else if (!strcasecomp(line, "subject:")) {
1.2       timbl     483:                    END(HTML_ADDRESS);
                    484:                    START(HTML_TITLE);                  /** Uuugh! @@@ */
2.16      luotonen  485:                    PUTS(line+9);
                    486:                    END(HTML_TITLE);
                    487:                    START(HTML_H1);
1.2       timbl     488:                    PUTS(line+8);
2.16      luotonen  489:                    END(HTML_H1);
1.2       timbl     490:                    START(HTML_ADDRESS);
2.16      luotonen  491: 
2.32.6.1! frystyk   492:                } else if (!strcasecomp(line, "date:")
        !           493:                        || !strcasecomp(line, "organization:")) {
2.16      luotonen  494:                    PUTS(strchr(line,':')+2);
                    495:                    START(HTML_BR);
                    496: 
2.32.6.1! frystyk   497:                } else if(!strcasecomp(line, "from:")) {
2.16      luotonen  498:                   char * temp=0;
                    499:                   char * href=0;
                    500:                   char *cp1, *cp2;
                    501: 
                    502:                   /* copy into temporary storage */
                    503:                   StrAllocCopy(temp, strchr(line,':')+1);
                    504: 
                    505:                   cp1=temp;
                    506:                   while(isspace(*cp1)) cp1++;
                    507:                   /* remove space and stuff after */
                    508:                   if((cp2 = strchr(cp1,' ')) != NULL)
                    509:                      *cp2 = '\0';
                    510: 
                    511:                   StrAllocCopy(href,"mailto:");
                    512:                   StrAllocCat(href,cp1);
                    513: 
                    514:                   start_anchor(href);
                    515:                   PUTS("Reply to ");
                    516:                   PUTS(strchr(line,':')+1);
                    517:                   END(HTML_A);
                    518:                   START(HTML_BR);
                    519: 
                    520:                   /* put in the owner as a link rel. as well */
                    521:                   start_link(href, "made");
                    522:                
                    523:                   /* free of temp vars */
                    524:                   free(temp);
                    525:                   free(href);
                    526: 
2.32.6.1! frystyk   527:                } else if (!strcasecomp(line, "newsgroups:")) {
1.1       timbl     528:                    StrAllocCopy(newsgroups, HTStrip(strchr(line,':')+1));
                    529:                    
2.32.6.1! frystyk   530:                } else if (!strcasecomp(line, "references:")) {
1.1       timbl     531:                    StrAllocCopy(references, HTStrip(strchr(line,':')+1));
                    532:                    
2.32.6.1! frystyk   533:                } /* end if strcasecomp */
1.1       timbl     534:                p = line;                       /* Restart at beginning */
                    535:            } /* if end of line */
                    536:        } /* Loop over characters */
2.16      luotonen  537:        END(HTML_ADDRESS);
1.1       timbl     538:     
1.2       timbl     539:        if (newsgroups || references) {
2.16      luotonen  540:            START(HTML_DL);
1.2       timbl     541:            if (newsgroups) {
2.16      luotonen  542: #ifdef POSTING
                    543:                char *href=0;
                    544: #endif
                    545: 
1.2       timbl     546:                (*targetClass.start_element)(target, HTML_DT , 0, 0);
                    547:                PUTS("Newsgroups:");
                    548:                (*targetClass.start_element)(target, HTML_DD , 0, 0);
                    549:                write_anchors(newsgroups);
2.16      luotonen  550: 
                    551: #ifdef POSTING
                    552:                /* make posting possible */
                    553:                StrAllocCopy(href,"newspost:");
                    554:                StrAllocCat(href,newsgroups);
                    555:                START(HTML_DT);
                    556:                 start_anchor(href);
                    557:                 PUTS("Reply to newsgroup(s)");
                    558:                 END(HTML_A);
                    559: #endif
                    560: 
1.2       timbl     561:                free(newsgroups);
                    562:            }
                    563:            
                    564:            if (references) {
                    565:                (*targetClass.start_element)(target, HTML_DT , 0, 0);
                    566:                PUTS("References:");
                    567:                (*targetClass.start_element)(target, HTML_DD , 0, 0);
                    568:                write_anchors(references);
                    569:                free(references);
                    570:            }
2.16      luotonen  571: #ifdef WHEN_WE_HAVE_HTMLPLUS
                    572:            (*targetClass.end_element)(target, HTML_DLC);
                    573: #else
2.10      timbl     574:            (*targetClass.end_element)(target, HTML_DL);
2.16      luotonen  575: #endif
1.1       timbl     576:        }
1.2       timbl     577:        PUTS("\n\n\n");
1.1       timbl     578:        
                    579:     }
                    580:     
                    581: /*     Read in the BODY of the Article:
                    582: */
1.2       timbl     583:     (*targetClass.start_element)(target, HTML_PRE , 0, 0);
                    584: 
1.1       timbl     585:     p = line;
                    586:     while(!done){
                    587:        char ch = *p++ = NEXT_CHAR;
                    588:        if (ch==(char)EOF) {
                    589:            abort_socket();     /* End of file, close socket */
                    590:            return;             /* End of file on response */
                    591:        }
1.3       timbl     592:        if ((ch == LF) || (p == &line[LINE_LENGTH])) {
1.1       timbl     593:            *p++=0;                             /* Terminate the string */
2.28      frystyk   594:            if (TRACE) fprintf(TDEST, "B %s", line);
1.1       timbl     595:            if (line[0]=='.') {
                    596:                if (line[1]<' ') {              /* End of article? */
                    597:                    done = YES;
                    598:                    break;
                    599:                } else {                        /* Line starts with dot */
1.2       timbl     600:                    PUTS(&line[1]);     /* Ignore first dot */
1.1       timbl     601:                }
                    602:            } else {
                    603: 
                    604: /*     Normal lines are scanned for buried references to other articles.
                    605: **     Unfortunately, it will pick up mail addresses as well!
                    606: */
                    607:                char *l = line;
                    608:                char * p;
2.14      luotonen  609:                while ((p=strchr(l, '<'))) {
1.1       timbl     610:                    char *q  = strchr(p,'>');
                    611:                    char *at = strchr(p, '@');
                    612:                    if (q && at && at<q) {
                    613:                        char c = q[1];
                    614:                        q[1] = 0;               /* chop up */
                    615:                        *p = 0;
1.2       timbl     616:                        PUTS(l);
1.1       timbl     617:                        *p = '<';               /* again */
                    618:                        *q = 0;
1.2       timbl     619:                        start_anchor(p+1);
1.1       timbl     620:                        *q = '>';               /* again */
1.2       timbl     621:                        PUTS(p);
                    622:                        (*targetClass.end_element)(target, HTML_A);
1.1       timbl     623:                        q[1] = c;               /* again */
                    624:                        l=q+1;
                    625:                    } else break;               /* line has unmatched <> */
                    626:                } 
1.2       timbl     627:                PUTS( l);       /* Last bit of the line */
1.1       timbl     628:            } /* if not dot */
                    629:            p = line;                           /* Restart at beginning */
                    630:        } /* if end of line */
                    631:     } /* Loop over characters */
1.2       timbl     632:     
                    633:     (*targetClass.end_element)(target, HTML_PRE);
1.1       timbl     634: }
                    635: 
                    636: 
                    637: /*     Read in a List of Newsgroups
                    638: **     ----------------------------
                    639: */
                    640: /*
                    641: **     Note the termination condition of a single dot on a line by itself.
                    642: **     RFC 977 specifies that the line "folding" of RFC850 is not used, so we
                    643: **     do not handle it here.
                    644: */        
                    645: PRIVATE void read_list NOARGS
                    646: {
                    647: 
                    648:     char line[LINE_LENGTH+1];
                    649:     char *p;
                    650:     BOOL done = NO;
                    651:     
                    652: /*     Read in the HEADer of the article:
                    653: **
                    654: **     The header fields are either ignored, or formatted and put into the
                    655: **     Text.
                    656: */
1.2       timbl     657:     (*targetClass.start_element)(target, HTML_H1 , 0, 0);
                    658:     PUTS( "Newsgroups");
                    659:     (*targetClass.end_element)(target, HTML_PRE);
1.1       timbl     660:     p = line;
2.16      luotonen  661:     (*targetClass.start_element)(target, HTML_DL, 0, 0);
1.1       timbl     662:     while(!done){
                    663:        char ch = *p++ = NEXT_CHAR;
                    664:        if (ch==(char)EOF) {
                    665:            abort_socket();     /* End of file, close socket */
                    666:            return;             /* End of file on response */
                    667:        }
1.3       timbl     668:        if ((ch == LF) || (p == &line[LINE_LENGTH])) {
1.1       timbl     669:            *p++=0;                             /* Terminate the string */
2.28      frystyk   670:            if (TRACE) fprintf(TDEST, "B %s", line);
2.16      luotonen  671:            (*targetClass.start_element)(target, HTML_DT , 0, 0);
1.1       timbl     672:            if (line[0]=='.') {
                    673:                if (line[1]<' ') {              /* End of article? */
                    674:                    done = YES;
                    675:                    break;
                    676:                } else {                        /* Line starts with dot */
1.2       timbl     677:                    PUTS( &line[1]);
1.1       timbl     678:                }
                    679:            } else {
                    680: 
                    681: /*     Normal lines are scanned for references to newsgroups.
                    682: */
2.16      luotonen  683:                int i=0;
                    684: 
                    685:                /* find whitespace if it exits */
                    686:                for(; line[i] != '\0' && !WHITE(line[i]); i++)
                    687:                    ;  /* null body */
                    688:        
                    689:                if(line[i] != '\0') {
                    690:                    line[i] = '\0';
                    691:                    write_anchor(line, line);
                    692:                    (*targetClass.start_element)(target, HTML_DD , 0, 0);
                    693:                    PUTS(&line[i+1]); /* put description */
                    694:                } else {
                    695:                    write_anchor(line, line);
                    696:                }
                    697: 
                    698: #ifdef OLD_CODE
1.1       timbl     699:                char group[LINE_LENGTH];
                    700:                int first, last;
                    701:                char postable;
                    702:                if (sscanf(line, "%s %d %d %c", group, &first, &last, &postable)==4)
                    703:                    write_anchor(line, group);
                    704:                else
1.2       timbl     705:                    PUTS(line);
2.16      luotonen  706: #endif /*OLD_CODE*/
                    707: 
1.1       timbl     708:            } /* if not dot */
                    709:            p = line;                   /* Restart at beginning */
                    710:        } /* if end of line */
                    711:     } /* Loop over characters */
2.16      luotonen  712:     (*targetClass.end_element)(target, HTML_DL);
1.1       timbl     713: }
                    714: 
                    715: 
                    716: /*     Read in a Newsgroup
                    717: **     -------------------
                    718: **     Unfortunately, we have to ask for each article one by one if we
                    719: **     want more than one field.
                    720: **
                    721: */
                    722: PRIVATE void read_group ARGS3(
                    723:   CONST char *,groupName,
                    724:   int,first_required,
                    725:   int,last_required
                    726: )
                    727: {
                    728:     char line[LINE_LENGTH+1];
                    729:     char author[LINE_LENGTH+1];
                    730:     char subject[LINE_LENGTH+1];
                    731:     char *p;
                    732:     BOOL done;
                    733: 
                    734:     char buffer[LINE_LENGTH];
                    735:     char *reference=0;                 /* Href for article */
                    736:     int art;                           /* Article number WITHIN GROUP */
                    737:     int status, count, first, last;    /* Response fields */
                    738:                                        /* count is only an upper limit */
                    739: 
                    740:     sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
2.17      frystyk   741:     if(TRACE)
2.28      frystyk   742:        fprintf(TDEST, 
2.17      frystyk   743:                "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
                    744:                status, count, first, last, first_required, last_required);
1.1       timbl     745:     if (last==0) {
1.2       timbl     746:         PUTS( "\nNo articles in this group.\n");
2.16      luotonen  747: #ifdef POSTING
                    748:        goto add_post;
                    749: #endif
1.1       timbl     750:        return;
                    751:     }
                    752:     
                    753: #define FAST_THRESHOLD 100     /* Above this, read IDs fast */
                    754: #define CHOP_THRESHOLD 50      /* Above this, chop off the rest */
                    755: 
                    756:     if (first_required<first) first_required = first;          /* clip */
                    757:     if ((last_required==0) || (last_required > last)) last_required = last;
                    758:     
                    759:     if (last_required<=first_required) {
1.2       timbl     760:         PUTS( "\nNo articles in this range.\n");
2.16      luotonen  761: #ifdef POSTING
                    762:        goto add_post;
                    763: #endif
1.1       timbl     764:        return;
                    765:     }
                    766: 
                    767:     if (last_required-first_required+1 > MAX_CHUNK) {  /* Trim this block */
                    768:         first_required = last_required-CHUNK_SIZE+1;
                    769:     }
2.28      frystyk   770:     if (TRACE) fprintf (TDEST, "    Chunk will be (%d-%d)\n",
2.16      luotonen  771:                       first_required, last_required);
1.1       timbl     772: 
1.2       timbl     773: /*     Set window title
                    774: */
                    775:     sprintf(buffer, "Newsgroup %s,  Articles %d-%d",
                    776:                groupName, first_required, last_required);
                    777:     START(HTML_TITLE);
                    778:     PUTS(buffer);
                    779:     END(HTML_TITLE);
                    780: 
1.1       timbl     781: /*     Link to earlier articles
                    782: */
                    783:     if (first_required>first) {
                    784:        int before;                     /* Start of one before */
                    785:        if (first_required-MAX_CHUNK <= first) before = first;
                    786:        else before = first_required-CHUNK_SIZE;
                    787:        sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1);
2.28      frystyk   788:        if (TRACE) fprintf(TDEST, "    Block before is %s\n", buffer);
1.2       timbl     789:        PUTS( " (");
                    790:        start_anchor(buffer);
                    791:        PUTS("Earlier articles");
                    792:        END(HTML_A);
                    793:        PUTS( "...)\n");
1.1       timbl     794:     }
                    795:     
                    796:     done = NO;
                    797: 
                    798: /*#define USE_XHDR*/
                    799: #ifdef USE_XHDR
                    800:     if (count>FAST_THRESHOLD)  {
                    801:         sprintf(buffer,
                    802:        "\nThere are about %d articles currently available in %s, IDs as follows:\n\n",
                    803:                count, groupName); 
1.2       timbl     804:         PUTS(buffer);
1.3       timbl     805:         sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF);
1.1       timbl     806:        status = response(buffer);
                    807:        if (status==221) {
                    808: 
                    809:            p = line;
                    810:            while(!done){
                    811:                char ch = *p++ = NEXT_CHAR;
                    812:                if (ch==(char)EOF) {
                    813:                    abort_socket();     /* End of file, close socket */
                    814:                    return;             /* End of file on response */
                    815:                }
                    816:                if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
                    817:                    *p++=0;                             /* Terminate the string */
2.28      frystyk   818:                    if (TRACE) fprintf(TDEST, "X %s", line);
1.1       timbl     819:                    if (line[0]=='.') {
                    820:                        if (line[1]<' ') {              /* End of article? */
                    821:                            done = YES;
                    822:                            break;
                    823:                        } else {                        /* Line starts with dot */
                    824:                                /* Ignore strange line */
                    825:                        }
                    826:                    } else {
                    827:        
                    828:        /*      Normal lines are scanned for references to articles.
                    829:        */
                    830:                        char * space = strchr(line, ' ');
                    831:                        if (space++)
                    832:                            write_anchor(space, space);
                    833:                    } /* if not dot */
                    834:                    p = line;                   /* Restart at beginning */
                    835:                } /* if end of line */
                    836:            } /* Loop over characters */
                    837: 
                    838:            /* leaving loop with "done" set */
                    839:        } /* Good status */
                    840:     };
                    841: #endif
                    842: 
                    843: /*     Read newsgroup using individual fields:
                    844: */
                    845:     if (!done) {
                    846:         if (first==first_required && last==last_required)
1.2       timbl     847:                PUTS("\nAll available articles in ");
                    848:         else PUTS( "\nArticles in ");
                    849:        PUTS(groupName);
                    850:        START(HTML_MENU);
1.1       timbl     851:        for(art=first_required; art<=last_required; art++) {
                    852:     
                    853: /*#define OVERLAP*/
                    854: #ifdef OVERLAP
                    855: /* With this code we try to keep the server running flat out by queuing just
                    856: ** one extra command ahead of time. We assume (1) that the server won't abort
                    857: ** if it gets input during output, and (2) that TCP buffering is enough for the
                    858: ** two commands. Both these assumptions seem very reasonable. However, we HAVE
                    859: ** had a hangup with a loaded server.
                    860: */
                    861:            if (art==first_required) {
                    862:                if (art==last_required) {
1.3       timbl     863:                        sprintf(buffer, "HEAD %d%c%c", art, CR, LF);    /* Only one */
1.1       timbl     864:                        status = response(buffer);
                    865:                    } else {                                    /* First of many */
1.3       timbl     866:                        sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c",
                    867:                                art, CR, LF, art+1, CR, LF);
1.1       timbl     868:                        status = response(buffer);
                    869:                    }
                    870:            } else if (art==last_required) {                    /* Last of many */
                    871:                    status = response(NULL);
                    872:            } else {                                            /* Middle of many */
1.3       timbl     873:                    sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF);
1.1       timbl     874:                    status = response(buffer);
                    875:            }
                    876:            
                    877: #else  /* NOT OVERLAP */
1.3       timbl     878:            sprintf(buffer, "HEAD %d%c%c", art, CR, LF);
1.1       timbl     879:            status = response(buffer);
                    880: #endif /* NOT OVERLAP */
                    881: 
                    882:            if (status == 221) {        /* Head follows - parse it:*/
2.20      frystyk   883:                int ch;
1.1       timbl     884:                p = line;                               /* Write pointer */
                    885:                done = NO;
                    886:                while(!done){
2.20      frystyk   887:                    if ((ch = HTInputSocket_getCharacter(isoc)) < 0) {
1.1       timbl     888:                        abort_socket(); /* End of file, close socket */
                    889:                        return;         /* End of file on response */
                    890:                    }
2.20      frystyk   891:                    *p++ = (unsigned char) ch;
1.3       timbl     892:                    if ((ch == LF)
1.1       timbl     893:                        || (p == &line[LINE_LENGTH]) ) {
                    894:                    
                    895:                        *--p=0;         /* Terminate  & chop LF*/
                    896:                        p = line;               /* Restart at beginning */
2.28      frystyk   897:                        if (TRACE) fprintf(TDEST, "G %s\n", line);
1.1       timbl     898:                        switch(line[0]) {
                    899:     
                    900:                        case '.':
                    901:                            done = (line[1]<' ');       /* End of article? */
                    902:                            break;
                    903:     
                    904:                        case 'S':
                    905:                        case 's':
2.32.6.1! frystyk   906:                            if (!strcasecomp(line, "subject:"))
1.1       timbl     907:                                strcpy(subject, line+9);/* Save subject */
                    908:                            break;
                    909:     
                    910:                        case 'M':
                    911:                        case 'm':
2.32.6.1! frystyk   912:                            if (!strcasecomp(line, "message-id:")) {
1.1       timbl     913:                                char * addr = HTStrip(line+11) +1; /* Chop < */
                    914:                                addr[strlen(addr)-1]=0;         /* Chop > */
                    915:                                StrAllocCopy(reference, addr);
                    916:                            }
                    917:                            break;
                    918:     
                    919:                        case 'f':
                    920:                        case 'F':
2.32.6.1! frystyk   921:                            if (!strcasecomp(line, "from:")) {
1.1       timbl     922:                                char * p;
                    923:                                strcpy(author,
                    924:                                        author_name(strchr(line,':')+1));
2.17      frystyk   925:                                if (*author) {          /* Not always there! */
                    926:                                    p = author + strlen(author) - 1;
                    927:                                    if (*p==LF) *p = 0; /* Chop off newline */
                    928:                                }
1.1       timbl     929:                            }
                    930:                            break;
                    931:                                    
                    932:                        } /* end switch on first character */
                    933:                    } /* if end of line */
                    934:                } /* Loop over characters */
                    935:     
1.2       timbl     936:                START(HTML_LI);
1.1       timbl     937:                sprintf(buffer, "\"%s\" - %s", subject, author);
                    938:                if (reference) {
                    939:                    write_anchor(buffer, reference);
                    940:                    free(reference);
                    941:                    reference=0;
                    942:                } else {
1.2       timbl     943:                    PUTS(buffer);
1.1       timbl     944:                }
                    945:                
                    946:     
1.2       timbl     947: /*      indicate progress!   @@@@@@
1.1       timbl     948: */
                    949:     
                    950:            } /* If good response */
                    951:        } /* Loop over article */           
                    952:     } /* If read headers */
1.2       timbl     953:     END(HTML_MENU);
                    954:     START(HTML_P);
1.1       timbl     955:     
                    956: /*     Link to later articles
                    957: */
                    958:     if (last_required<last) {
                    959:        int after;                      /* End of article after */
                    960:        after = last_required+CHUNK_SIZE;
                    961:        if (after==last) sprintf(buffer, "news:%s", groupName); /* original group */
                    962:        else sprintf(buffer, "news:%s/%d-%d", groupName, last_required+1, after);
2.28      frystyk   963:        if (TRACE) fprintf(TDEST, "    Block after is %s\n", buffer);
1.2       timbl     964:        PUTS( "(");
                    965:        start_anchor(buffer);
                    966:        PUTS( "Later articles");
                    967:        END(HTML_A);
                    968:        PUTS( "...)\n");
1.1       timbl     969:     }
2.16      luotonen  970: 
2.19      luotonen  971: #ifdef POSTING
                    972:   add_post:
                    973: #endif
2.16      luotonen  974:     {
                    975:        char *href=0;
                    976:        START(HTML_HR);
                    977:        
                    978:        StrAllocCopy(href,"newspost:");
                    979:        StrAllocCat(href,groupName);
                    980:        start_anchor(href);
                    981:        PUTS("Post to ");
                    982:        PUTS(groupName);
                    983:        END(HTML_A);
                    984: 
                    985:        free(href);
                    986:     }
1.1       timbl     987:     
                    988: 
                    989: }
                    990: 
2.32.6.1! frystyk   991: #endif
        !           992: 
        !           993: /* ------------------------------------------------------------------------- */
        !           994: /*                             PROTOCOL FUNCTIONS                           */
        !           995: /* ------------------------------------------------------------------------- */
1.1       timbl     996: 
2.32.6.1! frystyk   997: /*                                                             HTSetNewsHost
        !           998: **     Sets the current NEWS server.
1.1       timbl     999: */
2.32.6.1! frystyk  1000: PUBLIC BOOL HTSetNewsHost ARGS1(char *, newshost)
1.1       timbl    1001: {
2.32.6.1! frystyk  1002:     if (newshost && *newshost) {
        !          1003:        StrAllocCopy(HTNewsHost, newshost);
        !          1004:        {
        !          1005:            char *strptr = HTNewsHost;
        !          1006:            while (*strptr) {
        !          1007:                *strptr = TOLOWER(*strptr);
        !          1008:                strptr++;
        !          1009:            }
1.1       timbl    1010: 
2.32.6.1! frystyk  1011:            /* Remove final dot or paste in domain name */
        !          1012:            if (strchr(HTNewsHost, '.')) {
        !          1013:                if (*(HTNewsHost+strlen(HTNewsHost)-1) == '.')
        !          1014:                    *(HTNewsHost+strlen(HTNewsHost)-1) = '\0';
        !          1015:            } else {
        !          1016:                CONST char *domain = HTGetDomainName();
        !          1017:                if (domain) {
        !          1018:                    StrAllocCat(HTNewsHost, ".");
        !          1019:                    StrAllocCat(HTNewsHost, domain);
        !          1020:                }
        !          1021:            }           
        !          1022:        }
        !          1023:        if (PROT_TRACE)
        !          1024:            fprintf(stderr, "SetNewsHost. Host name is `%s\'\n", HTNewsHost);
        !          1025:        return YES;
        !          1026:     } else {
        !          1027:        if (PROT_TRACE)
        !          1028:            fprintf(stderr, "SetNewsHost. Bad argument ignored\n");
        !          1029:        return NO;
        !          1030:     }
        !          1031: }
1.1       timbl    1032: 
2.32.6.1! frystyk  1033: /*                                                             HTGetNewsHost
        !          1034: **     Except on the NeXT, we pick up the NewsHost name from
1.1       timbl    1035: **
2.32.6.1! frystyk  1036: **     1.      Environment variable NNTPSERVER
        !          1037: **     2.      File SERVER_FILE
        !          1038: **     3.      Compilation time macro DEFAULT_NEWS_HOST
        !          1039: **
        !          1040: **     On the NeXT, we pick up the NewsHost name from, in order:
        !          1041: **
        !          1042: **     1.      WorldWideWeb default "NewsHost"
        !          1043: **     2.      News default "NewsHost"
        !          1044: **     3.      Compilation time macro DEFAULT_NEWS_HOST
        !          1045: **
        !          1046: **     Return: HTNewsHost if success else NULL
        !          1047: */
        !          1048: PUBLIC CONST char *HTGetNewsHost NOARGS
        !          1049: {
        !          1050:     if (HTNewsHost) {
        !          1051:        if (*HTNewsHost) {
        !          1052:            if (PROT_TRACE)
        !          1053:                fprintf(stderr, "GetNewsHost. found as `%s\'\n", HTNewsHost);
        !          1054:            return HTNewsHost;
        !          1055:        } else
        !          1056:            return NULL;                 /* We couldn't get it the last time */
        !          1057:     }
        !          1058:     {
        !          1059:        char *newshost = NULL;
        !          1060:         char buffer[80];
1.1       timbl    1061: 
2.32.6.1! frystyk  1062: #ifdef NeXTStep
        !          1063:        if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0)
        !          1064:            if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0)
        !          1065:                newshost = DEFAULT_NEWS_HOST;
        !          1066: #else
        !          1067:        if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) {
        !          1068:            FILE *fp = fopen(SERVER_FILE, "r");
        !          1069:            *(buffer+79) = '\0';
        !          1070:            if (fp) {
        !          1071:                if (fgets(buffer, 79, fp)) {
        !          1072:                    char *end;
        !          1073:                    newshost = buffer;
        !          1074:                    while (*newshost == ' ' || *newshost == '\t')
        !          1075:                        newshost++;
        !          1076:                    end = newshost;
        !          1077:                    while (*end && !isspace(*end))
        !          1078:                        end++;
        !          1079:                    *end = '\0';
        !          1080:                }
        !          1081:                fclose(fp);
1.1       timbl    1082:            }
                   1083:        }
2.32.6.1! frystyk  1084: #endif /* NestStep */
1.1       timbl    1085: 
2.32.6.1! frystyk  1086:        if (!newshost || !*newshost)
        !          1087:            newshost = DEFAULT_NEWS_HOST;
        !          1088:        if (HTSetNewsHost(newshost))
        !          1089:            return HTNewsHost;
        !          1090:        StrAllocCopy(HTNewsHost, "");
        !          1091:        return NULL;
        !          1092:     }
        !          1093: }
        !          1094: 
        !          1095: 
        !          1096: /*
        !          1097: **     Free Newshostname
        !          1098: */
        !          1099: PUBLIC void HTFreeNewsHost NOARGS
        !          1100: {
        !          1101:     FREE(HTHostName);
        !          1102: }
        !          1103: 
        !          1104: 
        !          1105: /*                                                               HTNewsSendCmd
        !          1106: **     Send the commond the remote server. Connection must be up and the
        !          1107: **     the command terminated by CRLF. This function does never close the
        !          1108: **     connection.
        !          1109: **
        !          1110: **     Return 0 if OK, else -1;
        !          1111: */
        !          1112: PRIVATE int HTNewsSendCmd ARGS1(news_info *, news)
        !          1113: {
        !          1114:     HTChunkPutc(news->transmit, CR);
        !          1115:     HTChunkPutc(news->transmit, LF);
        !          1116:     HTChunkTerminate(news->transmit);
        !          1117:     if (PROT_TRACE)
        !          1118:        fprintf(stderr, "News Tx..... %s", news->transmit->data);
        !          1119: 
        !          1120:     /* Translate into ASCII if necessary */
        !          1121: #ifdef NOT_ASCII
        !          1122:     {
        !          1123:        char * p;
        !          1124:        for(p = news->transmit->data; *p; p++) {
        !          1125:            *p = TOASCII(*p);
1.3       timbl    1126:        }
2.32.6.1! frystyk  1127:     }
        !          1128: #endif
        !          1129:     /* Now, we are ready for sending the request */
        !          1130:     if (NETWRITE(news->sockfd, news->transmit->data,
        !          1131:                 news->transmit->size-1) < 0) {
        !          1132:        if (PROT_TRACE)
        !          1133:            fprintf(stderr, "News Tx..... Error transmitting\n");
        !          1134:        HTErrorSysAdd(news->request, ERR_FATAL, NO, "NETWRITE");
        !          1135:        return -1;
        !          1136:     }
        !          1137:     return 0;
        !          1138: }
1.1       timbl    1139: 
2.32.6.1! frystyk  1140: 
        !          1141: /*                                                           HTNewsGetResponse
        !          1142: **     Gets the response from the news server. Connection must be up and the
        !          1143: **     the command terminated by CRLF. This function does never close the
        !          1144: **     connection.
        !          1145: **
        !          1146: **     Returns the 3-digit return code on OK, else -1
        !          1147: */
        !          1148: PRIVATE int HTNewsGetResponse ARGS1(news_info *, news)
        !          1149: {
        !          1150:     int result;
        !          1151:     char *response = HTInputSocket_getLine(news->isoc);
        !          1152:     if (response) {
        !          1153:        if (!news->receive)
        !          1154:            news->receive = HTChunkCreate(128);
        !          1155:        else
        !          1156:            HTChunkClear(news->receive);            
        !          1157:        HTChunkPuts(news->receive, response);
        !          1158:        if (PROT_TRACE)
        !          1159:            fprintf(stderr, "News Rx..... %s\n", response);
        !          1160:        free(response);
        !          1161:     } else {
        !          1162:        if (PROT_TRACE)
        !          1163:            fprintf(stderr, "News Rx..... No response?\n");
        !          1164:        return -1;
        !          1165:     }
        !          1166:     if (sscanf(news->receive->data, "%d", &result) != 1) {
        !          1167:        if (PROT_TRACE) fprintf(stderr, "News Rx..... no code found\n");
        !          1168:        return -1;
        !          1169:     }
        !          1170:     return result;
        !          1171: }
        !          1172: 
        !          1173: 
        !          1174: /*                                                               HTNewsCleanup
        !          1175: **     Closes the connection and frees memory
        !          1176: **
        !          1177: **     Returns 0 if OK else -1
        !          1178: */
        !          1179: PRIVATE int HTNewsCleanup ARGS1(news_info *, news)
        !          1180: {
        !          1181:     int status = 0;
        !          1182:     if (news->sockfd >=0) {
        !          1183:        if (PROT_TRACE) fprintf(stderr,
        !          1184:                           "News........ Closing socket %d\n", news->sockfd);
        !          1185:        if ((status = NETCLOSE(news->sockfd)) < 0)
        !          1186:            HTErrorSysAdd(news->request, ERR_FATAL, NO, "NETCLOSE");
        !          1187:        news->sockfd = -1;
        !          1188:     }
        !          1189:     if (news->isoc)
        !          1190:        HTInputSocket_free(news->isoc);
        !          1191:     if (news->transmit)
        !          1192:        HTChunkFree(news->transmit);
        !          1193:     if (news->receive)
        !          1194:        HTChunkFree(news->receive);
        !          1195:     news->request->net_info = NULL;         /* Unlink the request structure */
        !          1196:     free(news);
        !          1197:     return status;
        !          1198: }
        !          1199: 
        !          1200: 
        !          1201: /*                                                                 HTNewsParse
        !          1202: **     Parses
        !          1203: **
        !          1204: **     Returns 0 if OK else -1
        !          1205: */
        !          1206: PRIVATE int HTNewsParse ARGS2(char *, filename, char *, url) {
        !          1207: 
        !          1208: 
        !          1209:     return -1;
        !          1210: 
        !          1211: 
        !          1212: }
        !          1213: 
        !          1214: 
        !          1215: /*             Load data object from NNTP Server                    HTLoadNews
        !          1216: **             =================================
        !          1217: **
        !          1218: **     Given a hypertext addres, this routine loads a document
        !          1219: **
        !          1220: ** On Entry,
        !          1221: **     request         The request structure
        !          1222: **
        !          1223: **     returns         HT_ERROR        Error has occured or interrupted
        !          1224: **                     HT_WOULD_BLOCK  if operation would have blocked
        !          1225: **                     HT_LOADED       if 200 OK
        !          1226: **                     HT_NO_DATA      if No Response
        !          1227: **                     HT_RETRY        if Service Unavail.
        !          1228: */
        !          1229: PUBLIC int HTLoadNews ARGS1(HTRequest *, request)
        !          1230: {
        !          1231:     int status = HT_ERROR;
        !          1232:     char *url;                           /* Gets initialized on every entry */
        !          1233:     news_info *news;                       /* Specific protocol information */
        !          1234: 
        !          1235:     if (!request || !request->anchor) {
        !          1236:        if (PROT_TRACE) fprintf(stderr, "HTLoadNews.. Bad argument\n");
        !          1237:        return HT_ERROR;
        !          1238:     }
        !          1239:     url = HTAnchor_physical(request->anchor);
        !          1240: 
        !          1241:     /* Only do the setup first time through */
        !          1242:     if (!request->net_info) {
        !          1243:        if (PROT_TRACE)
        !          1244:            fprintf(stderr, "News........ Looking for `%s\'\n", url);
        !          1245:        if ((news = (news_info *) calloc(1, sizeof(news_info))) == NULL)
        !          1246:            outofmem(__FILE__, "HTLoadNews");
        !          1247:        news->sockfd = -1;
        !          1248:        news->CRLFdotCRLF = YES;            /* We have a dot ending the data */
        !          1249:        news->request = request;
        !          1250:        news->state = NEWS_BEGIN;
        !          1251:        request->net_info = (HTNetInfo *) news;
        !          1252:     } else
        !          1253:        news = (news_info *) request->net_info;         /* Get existing copy */
        !          1254: 
        !          1255:     /* Now start the state machine */
        !          1256:     while (1) {
        !          1257:        switch (news->state) {
        !          1258:          case NEWS_BEGIN:
        !          1259:            news->state = (!strchr(url, '@') && strchr(url, '*')) ?
        !          1260:                NEWS_SEEK_CACHE_LIST : NEWS_NEED_CONNECTION;
        !          1261:            break;
        !          1262: 
        !          1263:          case NEWS_SEEK_CACHE_LIST:
        !          1264: 
        !          1265:            /* We don't do this for the moment */
        !          1266:            news->state = NEWS_NEED_CONNECTION;
        !          1267:            break;
        !          1268: 
        !          1269:          case NEWS_NEED_CONNECTION:            /* Let's set up a connection */
        !          1270:            if (!strncmp(url, "news:", 5)) {
        !          1271:                CONST char *newshost = HTGetNewsHost();
        !          1272:                if (newshost) {
        !          1273:                    char *newshack = NULL;    /* Then we can use HTParse :-) */
        !          1274:                    StrAllocCopy(newshack, "news://");
        !          1275:                    StrAllocCat(newshack, newshost);
        !          1276:                    status = HTDoConnect((HTNetInfo *) news, (char *) newshack,
        !          1277:                                         NEWS_PORT, NULL, NO);
        !          1278:                    free(newshack);
2.21      frystyk  1279:                }
2.32.6.1! frystyk  1280:            } else if (!strncmp(url, "nntp", 5)) {
        !          1281:                news->nntp = YES;
        !          1282:                status = HTDoConnect((HTNetInfo *) news, url, NEWS_PORT,
        !          1283:                                     NULL, NO);
1.1       timbl    1284:            } else {
2.32.6.1! frystyk  1285:                if (PROT_TRACE)
        !          1286:                    fprintf(stderr, "News........ Should never happen");
        !          1287:                news->state = NEWS_ERROR;
1.1       timbl    1288:            }
2.32.6.1! frystyk  1289:            if (!status) {
        !          1290:                if (PROT_TRACE)
        !          1291:                    fprintf(stderr, "News........ Connected, socket %d\n",
        !          1292:                            news->sockfd);
1.1       timbl    1293: 
2.32.6.1! frystyk  1294:                /* Set up stream TO network */
        !          1295:                request->input_stream =
        !          1296:                    HTNewsReq_new(request, HTWriter_new(news->sockfd, YES));
        !          1297: 
        !          1298:                /* Set up stream FROM network and corresponding read buffer */
        !          1299:                news->isoc = HTInputSocket_new(news->sockfd);
        !          1300:                news->target = HTImProxy ?
        !          1301:                    request->output_stream : HTNewsStatus_new(request, news);
        !          1302: 
        !          1303:                /* Now we are ready to ask the server */
        !          1304:                news->state = NEWS_NEED_REQUEST;                    
        !          1305:            } else
        !          1306:                news->state = NEWS_ERROR;
        !          1307:           break;
        !          1308: 
        !          1309:           case NEWS_NEED_REQUEST:
        !          1310:            /*
        !          1311:            **  Syntax of address is
        !          1312:            **          xxx@yyy                 Article
        !          1313:            **          <xxx@yyy>               Same article
        !          1314:            **          xxxxx                   News group (no "@")
        !          1315:            **          group/n1-n2             Articles n1 to n2 in group
        !          1316:            */        
        !          1317:            if (!news->transmit) {
        !          1318:                news->transmit = HTChunkCreate(512);
        !          1319: 
        !          1320:                /* The request generation goes into HTNewsrequest stream */
        !          1321: 
        !          1322:                if (strchr(url, '@')) {              /* Send ARTICLE */
        !          1323:                    char *article = NULL;
        !          1324:                    if (news->nntp)
        !          1325:                        article = HTParse(url, "", PARSE_PATH);
        !          1326:                    else
        !          1327:                        StrAllocCopy(article, url+5);
        !          1328:                    HTChunkPuts(news->transmit, "ARTICLE ");
        !          1329:                    HTUnEscape(article);                   /* AL May 2, 1994 */
        !          1330:                    HTCleanTelnetString(article);
        !          1331:                    if (*article != '<')
        !          1332:                        HTChunkPutc(news->transmit, '<');
        !          1333:                    HTChunkPuts(news->transmit, article);
        !          1334:                    if (*(article+strlen(article)-1) != '>')
        !          1335:                        HTChunkPutc(news->transmit, '>');
        !          1336:                    free(article);
        !          1337:                } else if (strchr(url, '*')) {                  /* Send LIST */
        !          1338:                    if (!news->listsent) {
        !          1339:                        HTChunkPuts(news->transmit, "LIST NEWSGROUPS");
        !          1340:                    } else if (news->listsent == 1) {
        !          1341:                        HTChunkPuts(news->transmit, "LIST");
        !          1342:                    } else
        !          1343:                        news->state = NEWS_ERROR;
        !          1344:                    news->listsent++;
        !          1345:                } else {                                       /* Send GROUP */
        !          1346:                    char *group = NULL;
        !          1347:                    if (news->nntp)
        !          1348:                        group = HTParse(url, "", PARSE_PATH);
        !          1349:                    else
        !          1350:                        StrAllocCopy(group, url+5);
        !          1351:                    
        !          1352:                    /* Scan for first and last numbers of articles */
        !          1353:                    {
        !          1354:                        char *index = strchr(group, '/');
        !          1355:                        if (index) {
        !          1356:                            *index++ = '\0';
        !          1357:                            sscanf(index, "%d-%d",
        !          1358:                                   &news->first, &news->last);
        !          1359:                        }
        !          1360:                    }
        !          1361:                    HTChunkPuts(news->transmit, "GROUP ");
        !          1362:                    HTUnEscape(group);                     /* AL May 2, 1994 */
        !          1363:                    HTCleanTelnetString(group);    /* Prevent security holes */
        !          1364:                    HTChunkPuts(news->transmit, group);
        !          1365:                    free(group);
        !          1366:                }
        !          1367:            }
        !          1368:            news->state = HTNewsSendCmd(news) ? NEWS_ERROR : NEWS_SENT_REQUEST;
        !          1369:            break;
        !          1370:            
        !          1371:          case NEWS_SENT_REQUEST:
        !          1372:            status = HTNewsGetResponse(news);
        !          1373:            if (status == 211 || status == 221) {           /* GROUP OR HEAD */
        !          1374:                news->format = WWW_NEWSGROUP;
        !          1375:                news->state = NEWS_NEED_DATA;
        !          1376:            } else if (status == 220) {                           /* ARTICLE */
        !          1377:                news->format = WWW_MIME;
        !          1378:                news->state = NEWS_NEED_DATA;
        !          1379:            } else if (status == 215) {                   /* LIST NEWSGROUPS */
        !          1380:                news->format = WWW_NEWSLIST;
        !          1381:                news->state = NEWS_NEED_DATA;
        !          1382:            } else if (news->listsent == 1) {                        /* LIST */
        !          1383:                HTChunkFree(news->transmit);
        !          1384:                news->transmit = NULL;
        !          1385:                news->state = NEWS_NEED_REQUEST;
        !          1386:            } else
        !          1387:                news->state = NEWS_ERROR;
        !          1388:            break;
        !          1389: 
        !          1390:          case NEWS_NEED_DATA:
        !          1391:            news->target = HTStreamStack(news->format, request, NO);
        !          1392:            if (!news->target) {
        !          1393:                news->state = NEWS_ERROR;
        !          1394:                break;
        !          1395:            }
1.1       timbl    1396: 
2.32.6.1! frystyk  1397:            /* Push the data down the stream remembering the part of the first
        !          1398:               buffer we just read */
        !          1399:            (*news->target->isa->put_block)(news->target,
        !          1400:                                            news->isoc->input_pointer,
        !          1401:                                            news->isoc->input_limit -
        !          1402:                                            news->isoc->input_pointer);
        !          1403:            HTParseSocket(news->format, news->sockfd, request);
        !          1404:            news->state = NEWS_GOT_DATA;
        !          1405:            break;
        !          1406: 
        !          1407:          case NEWS_GOT_DATA:
        !          1408:            HTNewsCleanup(news);
        !          1409:            return HT_LOADED;
        !          1410:            break;
        !          1411: 
        !          1412:          case NEWS_NO_DATA:
        !          1413:            HTNewsCleanup(news);
        !          1414:            return HT_NO_DATA;
        !          1415:            break;
        !          1416: 
        !          1417:          case NEWS_RETRY:
        !          1418:            HTNewsCleanup(news);
        !          1419:            return HT_RETRY;
        !          1420:            break;
        !          1421: 
        !          1422:          case NEWS_ERROR:
        !          1423:            HTNewsCleanup(news);
        !          1424:            return HT_ERROR;
        !          1425:            break;
        !          1426:        }
        !          1427:     } /* End of while(1) */
1.1       timbl    1428: }
                   1429: 
2.32.6.1! frystyk  1430: 
        !          1431: /* Protocol Descriptor */
2.25      frystyk  1432: GLOBALDEF PUBLIC HTProtocol HTNews = {
2.32.6.1! frystyk  1433:     "news", SOC_NON_BLOCK, HTLoadNews, NULL, NULL
        !          1434: };
        !          1435: GLOBALDEF PUBLIC HTProtocol HTNNTP = {
        !          1436:     "nntp", SOC_NON_BLOCK, HTLoadNews, NULL, NULL
2.25      frystyk  1437: };
2.32.6.1! frystyk  1438: 

Webmaster