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

2.39      frystyk     1: /*                     NEWS ACCESS                             HTNews.c
                      2: **                     ===========
1.1       timbl       3: **
                      4: ** History:
                      5: **     26 Sep 90       Written TBL
                      6: **     29 Nov 91       Downgraded to C, for portable implementation.
2.19      luotonen    7: **     16 Feb 94  AL   Added Lou Montulli's Lynx & LIST NEWSGROUPS diffs.
                      8: **      2 May 94  AL   Added HTUnEscape() to HTLoadNews(), and
                      9: **                     fixed a possible security hole when the URL contains
                     10: **                     a newline, that could cause multiple commands to be
                     11: **                     sent to an NNTP server.
2.23      duns       12: **      8 Jul 94  FM   Insulate free() from _free structure element.
2.39      frystyk    13: **     30 Aug 95  FTLO Added POST functionality and updated state machine
                     14: **     30 Aug 95  HFN  Cleaned up a whole lot and made a state machine
1.1       timbl      15: */
2.27      roeber     16: 
2.39      frystyk    17: /* Library Include files */
2.52      frystyk    18: #include "sysdep.h"
2.39      frystyk    19: #include "HTUtils.h"
2.28      frystyk    20: #include "HTString.h"
                     21: #include "HTParse.h"
2.39      frystyk    22: #include "HTFWrite.h"
                     23: #include "HTWriter.h"
                     24: #include "HTConLen.h"
2.30      frystyk    25: #include "HTSocket.h"
2.39      frystyk    26: #include "HTTCP.h"
2.28      frystyk    27: #include "HTError.h"
2.39      frystyk    28: #include "HTChunk.h"
                     29: #include "HTEscape.h"
2.43      frystyk    30: #include "HTReqMan.h"                          /* @@@ */
                     31: #include "HTNetMan.h"                          /* @@@ */
2.39      frystyk    32: #include "HTNewsRq.h" 
                     33: #include "HTNews.h"                                           /* Implements */
                     34: 
                     35: /* Macros and other defines */
                     36: #ifndef NEWS_PORT
                     37: #define NEWS_PORT              119                            /* See rfc977 */
                     38: #endif
1.3       timbl      39: 
2.39      frystyk    40: #ifndef NEWS_LIST_FILE
                     41: #define NEWS_LIST_FILE         ".www_news"        /* Name of news list file */
                     42: #endif
1.1       timbl      43: 
                     44: #ifndef DEFAULT_NEWS_HOST
2.39      frystyk    45: #define DEFAULT_NEWS_HOST      "news"
1.1       timbl      46: #endif
2.39      frystyk    47: 
1.1       timbl      48: #ifndef SERVER_FILE
2.39      frystyk    49: #define SERVER_FILE            "/usr/local/lib/rn/server"
1.1       timbl      50: #endif
                     51: 
2.45      frystyk    52: #define MAX_NEWS_ARTICLES      0       /* No default max number of articles */
2.40      frystyk    53: 
2.39      frystyk    54: #define PUTBLOCK(b, l) (*me->target->isa->put_block)        (me->target, b, l)
                     55: #define ABORT_TARGET    (*me->target->isa->abort)            (me->target, e)
2.8       timbl      56: 
2.39      frystyk    57: /* Local context structure used in the HTNet object */
                     58: typedef enum _HTNewsState {
                     59:     NEWS_BEGIN,
                     60:     NEWS_SEEK_CACHE,
                     61:     NEWS_NEED_CONNECTION,
                     62:     NEWS_NEED_GREETING,
                     63:     NEWS_NEED_SWITCH,
                     64:     NEWS_NEED_ARTICLE,
2.45      frystyk    65: #if HT_LISTGROUP
2.39      frystyk    66:     NEWS_NEED_LGRP,
                     67: #endif
                     68:     NEWS_NEED_LIST,
                     69:     NEWS_NEED_GROUP,
                     70:     NEWS_NEED_XOVER,
                     71:     NEWS_NEED_HEAD,
                     72:     NEWS_NEED_POST,
                     73:     NEWS_NEED_BODY,
                     74:     NEWS_ERROR,
                     75:     NEWS_SUCCESS
                     76: } HTNewsState;
                     77: 
                     78: typedef struct _news_info {
                     79:     HTChunk *          cmd;
                     80:     int                        repcode;
                     81:     char *             reply;
                     82:     HTNewsState         state;                    /* State of the connection */
                     83:     HTFormat           format;
                     84:     char *             name;                /* Name of article or newsgroup */
                     85:     BOOL               sent;                       /* Read or write command */
                     86:     int                 first;            /* First article in the group list */
                     87:     int                 last;              /* Last article in the group list */
                     88:     int                        total;               /* Estimated number of articles */
                     89:     int                 current;                 /* To count # of HEADS sent */
                     90: } news_info;
                     91: 
                     92: /* This version of a stream is used for NEWS LIST conversion to HTML */
                     93: struct _HTStream {
2.52      frystyk    94:     const HTStreamClass *      isa;
2.39      frystyk    95:     HTStream *                 target;
                     96:     HTRequest *                        request;
                     97:     news_info *                        news;
2.53    ! frystyk    98:     HTEOLState                 EOLstate;
2.39      frystyk    99:     BOOL                       semi_trans;
                    100:     BOOL                       junk;
                    101:     char                       buffer[MAX_NEWS_LINE+1];
                    102:     int                                buflen;
1.2       timbl     103: };
                    104: 
2.39      frystyk   105: PRIVATE char *HTNewsHost = NULL;
2.45      frystyk   106: PRIVATE int MaxArt = MAX_NEWS_ARTICLES;
1.1       timbl     107: 
2.39      frystyk   108: /* ------------------------------------------------------------------------- */
                    109: /*                            NEWS INPUT STREAM                             */
                    110: /* ------------------------------------------------------------------------- */
                    111: 
                    112: /*     ScanResponse
                    113: **     ------------
                    114: **     Analyzes the response from the NNTP server.
                    115: **     We only expect one line response codes.
                    116: **     Returns HT_LOADED if OK, HT_ERROR if error
                    117: */
                    118: PRIVATE int ScanResponse (HTStream * me)
                    119: {
                    120:     news_info *news = me->news;
                    121:     *(me->buffer+me->buflen) = '\0';
                    122:     if (isdigit(*(me->buffer))) sscanf(me->buffer, "%d", &news->repcode);
                    123:     me->buflen = 0;
                    124:     news->reply = me->buffer+4;
2.51      eric      125:     if (PROT_TRACE) HTTrace("News Rx..... `%s\'\n", news->reply);
2.39      frystyk   126: 
                    127:     /* If 2xx code and we expect data then go into semi-transparent mode */
                    128:     if (me->news->format && news->repcode/100 == 2) {
                    129:        HTRequest *req = me->request;
                    130:        me->target = HTStreamStack(me->news->format, req->output_format,
                    131:                                   req->output_stream, req, NO);
                    132:        me->semi_trans = YES;
                    133:        if (!me->target) return HT_ERROR;
                    134:     }
                    135:     return HT_LOADED;
1.2       timbl     136: }
1.1       timbl     137: 
2.39      frystyk   138: /*
                    139: **     Searches for NNTP header line until buffer fills up or a CRLF or LF
                    140: **     is found
1.1       timbl     141: */
2.52      frystyk   142: PRIVATE int HTNewsStatus_put_block (HTStream * me, const char * b, int l)
1.1       timbl     143: {
2.39      frystyk   144:     while (!me->semi_trans && l-- > 0) {
                    145:        int status;
                    146:        if (me->EOLstate == EOL_FCR) {
                    147:            if (*b == LF) {
                    148:                if (me->junk) me->junk = NO;
                    149:                me->EOLstate = EOL_BEGIN;
                    150:                if ((status = ScanResponse(me)) != HT_LOADED) return status;
                    151:            }
                    152:        } else if (*b == CR) {
                    153:            me->EOLstate = EOL_FCR;
                    154:        } else if (*b == LF) {
                    155:            if (me->junk) me->junk = NO;
                    156:            me->EOLstate = EOL_BEGIN;
                    157:            if ((status = ScanResponse(me)) != HT_LOADED) return status;
                    158:        } else {
                    159:            *(me->buffer+me->buflen++) = *b;
                    160:            if (me->buflen >= MAX_NEWS_LINE) {
                    161:                if (PROT_TRACE)
2.51      eric      162:                    HTTrace("News Status. Line too long - chopped\n");
2.39      frystyk   163:                me->junk = YES;
                    164:                if ((status = ScanResponse(me)) != HT_LOADED) return status;
1.1       timbl     165:            }
                    166:        }
2.39      frystyk   167:        b++;
                    168:     }  
1.1       timbl     169: 
2.39      frystyk   170:     /*
                    171:     ** Now see if we have parts of the body to put down the stream pipe.
                    172:     ** At this point we are looking for CRLF.CRLF. We are guaranteed a stream
                    173:     */
                    174:     if (l > 0) {
                    175:        int rest = l;
2.52      frystyk   176:        const char *ptr = b;
2.39      frystyk   177:        while (rest-- > 0) {
                    178:            if (*ptr == CR) {
                    179:                me->EOLstate = me->EOLstate==EOL_DOT ? EOL_SCR : EOL_FCR;
                    180:            } else if (*ptr == '.') {
                    181:                me->EOLstate = me->EOLstate==EOL_FLF ? EOL_DOT : EOL_BEGIN;
                    182:            } else if (*ptr == LF) {
                    183:                me->EOLstate = me->EOLstate>EOL_DOT ? EOL_SLF : EOL_FLF;
                    184:            } else
                    185:                me->EOLstate = EOL_BEGIN;
                    186:            ptr++;
                    187:        }
                    188:        if (me->EOLstate == EOL_SLF) {
                    189:            int status = PUTBLOCK(b, l-5);
                    190:            return status != HT_OK ? status : HT_LOADED;
                    191:        } else {
                    192:            int status = PUTBLOCK(b, l);
                    193:            return status;
1.1       timbl     194:        }
                    195:     }
2.39      frystyk   196:     return HT_LOADED;
1.1       timbl     197: }
                    198: 
2.39      frystyk   199: PRIVATE int HTNewsStatus_put_character (HTStream * me, char ch)
1.1       timbl     200: {
2.39      frystyk   201:     return HTNewsStatus_put_block(me, &ch, 1);
1.1       timbl     202: }
                    203: 
2.52      frystyk   204: PRIVATE int HTNewsStatus_put_string (HTStream * me, const char * str)
1.1       timbl     205: {
2.39      frystyk   206:     return HTNewsStatus_put_block(me, str, (int) strlen(str));
1.1       timbl     207: }
                    208: 
2.39      frystyk   209: PRIVATE int HTNewsStatus_flush (HTStream * me)
1.1       timbl     210: {
2.39      frystyk   211:     return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
1.1       timbl     212: }
                    213: 
2.39      frystyk   214: PRIVATE int HTNewsStatus_free (HTStream * me)
1.2       timbl     215: {
2.39      frystyk   216:     int status = HT_OK;
                    217:     if (me->target) {
                    218:        if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
                    219:            return HT_WOULD_BLOCK;
1.2       timbl     220:     }
2.50      frystyk   221:     HT_FREE(me);
2.39      frystyk   222:     return status;
1.2       timbl     223: }
1.1       timbl     224: 
2.44      frystyk   225: PRIVATE int HTNewsStatus_abort (HTStream * me, HTList * e)
2.16      luotonen  226: {
2.39      frystyk   227:     if (me->target)
                    228:         ABORT_TARGET;
2.50      frystyk   229:     HT_FREE(me);
2.51      eric      230:     if (PROT_TRACE) HTTrace("NewsStatus.. ABORTING...\n");
2.39      frystyk   231:     return HT_ERROR;
2.16      luotonen  232: }
                    233: 
2.52      frystyk   234: PRIVATE const HTStreamClass HTNewsStatusClass =
2.39      frystyk   235: {              
                    236:     "NewsStatus",
                    237:     HTNewsStatus_flush,
                    238:     HTNewsStatus_free,
                    239:     HTNewsStatus_abort,
                    240:     HTNewsStatus_put_character,
                    241:     HTNewsStatus_put_string,
                    242:     HTNewsStatus_put_block
                    243: };
2.16      luotonen  244: 
2.39      frystyk   245: PUBLIC HTStream *HTNewsStatus_new (HTRequest * request, news_info * news)
1.1       timbl     246: {
2.50      frystyk   247:     HTStream *me;
                    248:     if ((me = (HTStream  *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
                    249:         HT_OUTOFMEM("HTNewsStatus_new");
2.39      frystyk   250:     me->isa = &HTNewsStatusClass;
                    251:     me->request = request;
                    252:     me->news = news;
                    253:     me->EOLstate = EOL_BEGIN;
                    254:     return me;
1.1       timbl     255: }
                    256: 
2.39      frystyk   257: /* ------------------------------------------------------------------------- */
                    258: /*                             PROTOCOL FUNCTIONS                           */
                    259: /* ------------------------------------------------------------------------- */
1.1       timbl     260: 
2.39      frystyk   261: /*
                    262: **     Set the max number of articles at the same time.
                    263: **     Default is MAX_NEWS_ARTICLES
1.1       timbl     264: */
2.39      frystyk   265: PUBLIC BOOL HTNews_setMaxArticles (int new_max)
                    266: { 
2.45      frystyk   267:     if (new_max >= 0) {
                    268:        MaxArt = new_max;
2.39      frystyk   269:        return YES;
1.1       timbl     270:     }
2.39      frystyk   271:     return NO;
1.1       timbl     272: }
                    273: 
2.39      frystyk   274: /*
                    275: **     Get current max number of articles at the same time.
1.1       timbl     276: */
2.39      frystyk   277: PUBLIC int HTNews_maxArticles (void)
                    278: { 
2.45      frystyk   279:     return MaxArt;
1.1       timbl     280: }
                    281: 
2.39      frystyk   282: /*
                    283: **     Sets the current NEWS server.
                    284: */
2.52      frystyk   285: PUBLIC BOOL HTNews_setHost (const char * newshost)
1.1       timbl     286: {
2.39      frystyk   287:     if (newshost && *newshost) {
                    288:        StrAllocCopy(HTNewsHost, newshost);
                    289:        {
                    290:            char *strptr = HTNewsHost;
                    291:            while (*strptr) {
                    292:                *strptr = TOLOWER(*strptr);
                    293:                strptr++;
1.2       timbl     294:            }
                    295:            
2.39      frystyk   296:            /* Remove final dot or paste in domain name */
                    297:            if (strchr(HTNewsHost, '.')) {
                    298:                if (*(HTNewsHost+strlen(HTNewsHost)-1) == '.')
                    299:                    *(HTNewsHost+strlen(HTNewsHost)-1) = '\0';
                    300:            } else {
2.52      frystyk   301:                const char *domain = HTGetDomainName();
2.39      frystyk   302:                if (domain) {
                    303:                    StrAllocCat(HTNewsHost, ".");
                    304:                    StrAllocCat(HTNewsHost, domain);
                    305:                }
                    306:            }           
1.1       timbl     307:        }
2.39      frystyk   308:        if (PROT_TRACE)
2.51      eric      309:            HTTrace("SetNewsHost. Host name is `%s\'\n", HTNewsHost);
2.39      frystyk   310:        return YES;
                    311:     } else {
                    312:        if (PROT_TRACE)
2.51      eric      313:            HTTrace("SetNewsHost. Bad argument ignored\n");
2.39      frystyk   314:        return NO;
1.1       timbl     315:     }
                    316: }
                    317: 
                    318: /*
2.39      frystyk   319: **     Except on the NeXT, we pick up the NewsHost name from
                    320: **
                    321: **     1.      Environment variable NNTPSERVER
                    322: **     2.      File SERVER_FILE
                    323: **     3.      Compilation time macro DEFAULT_NEWS_HOST
                    324: **
                    325: **     On the NeXT, we pick up the NewsHost name from, in order:
1.1       timbl     326: **
2.39      frystyk   327: **     1.      WorldWideWeb default "NewsHost"
                    328: **     2.      News default "NewsHost"
                    329: **     3.      Compilation time macro DEFAULT_NEWS_HOST
1.1       timbl     330: **
2.39      frystyk   331: **     Return: HTNewsHost if success else NULL
1.1       timbl     332: */
2.52      frystyk   333: PUBLIC const char *HTNews_host (void)
1.1       timbl     334: {
2.39      frystyk   335:     if (HTNewsHost) {
                    336:        if (*HTNewsHost) {
                    337:            if (PROT_TRACE)
2.51      eric      338:                HTTrace("GetNewsHost. found as `%s\'\n", HTNewsHost);
2.39      frystyk   339:            return HTNewsHost;
                    340:        } else
                    341:            return NULL;                 /* We couldn't get it the last time */
1.1       timbl     342:     }
2.39      frystyk   343:     {
                    344:        char *newshost = NULL;
                    345:         char buffer[80];
1.1       timbl     346: 
2.39      frystyk   347: #ifdef NeXTStep
                    348:        if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0)
                    349:            if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0)
                    350:                newshost = DEFAULT_NEWS_HOST;
                    351: #else
                    352:        if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) {
                    353:            FILE *fp = fopen(SERVER_FILE, "r");
                    354:            *(buffer+79) = '\0';
                    355:            if (fp) {
                    356:                if (fgets(buffer, 79, fp)) {
                    357:                    char *end;
                    358:                    newshost = buffer;
                    359:                    while (*newshost == ' ' || *newshost == '\t')
                    360:                        newshost++;
                    361:                    end = newshost;
                    362:                    while (*end && !isspace(*end))
                    363:                        end++;
                    364:                    *end = '\0';
                    365:                }
                    366:                fclose(fp);
                    367:            }
                    368:        }
                    369: #endif /* NestStep */
1.1       timbl     370: 
2.39      frystyk   371:        if (!newshost || !*newshost)
                    372:            newshost = DEFAULT_NEWS_HOST;
                    373:        if (HTNews_setHost(newshost))
                    374:            return HTNewsHost;
                    375:        StrAllocCopy(HTNewsHost, "");
                    376:        return NULL;
1.1       timbl     377:     }
2.39      frystyk   378: }
1.1       timbl     379: 
2.39      frystyk   380: /*
                    381: **     Free Newshostname
1.2       timbl     382: */
2.39      frystyk   383: PUBLIC void HTFreeNewsHost (void)
                    384: {
2.50      frystyk   385:     HT_FREE(HTNewsHost);
2.39      frystyk   386: }
1.2       timbl     387: 
2.39      frystyk   388: /*     HTNewsCleanup
                    389: **     -------------
                    390: **      This function closes the connection and frees memory.
                    391: **      Returns YES on OK, else NO
1.1       timbl     392: */
2.39      frystyk   393: PRIVATE int HTNewsCleanup (HTRequest * req, int status)
                    394: {
                    395:     HTNet *net = req->net;
                    396:     news_info *news = (news_info *) net->context;
1.1       timbl     397: 
2.39      frystyk   398:     /* Free stream with data TO network */
                    399:     if (!HTRequest_isDestination(req) && req->input_stream) {
                    400:        if (status == HT_INTERRUPTED)
                    401:            (*req->input_stream->isa->abort)(req->input_stream, NULL);
                    402:        else
                    403:            (*req->input_stream->isa->_free)(req->input_stream);
1.1       timbl     404:     }
2.16      luotonen  405: 
2.39      frystyk   406:     /* Remove the request object and our own context structure for nntp */
                    407:     HTNet_delete(net, status);
2.49      frystyk   408:     if (news) {
2.50      frystyk   409:        HT_FREE(news->name);
2.49      frystyk   410:        HTChunk_delete(news->cmd);
2.50      frystyk   411:        HT_FREE(news);
2.49      frystyk   412:     }
2.39      frystyk   413:     return YES;
1.1       timbl     414: }
                    415: 
2.39      frystyk   416: PRIVATE int SendCommand (HTRequest *request, news_info *news,
                    417:                         char *token, char *pars)
1.1       timbl     418: {
2.39      frystyk   419:     int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2;
2.48      frystyk   420:     HTChunk_clear(news->cmd);
                    421:     HTChunk_ensure(news->cmd, len);
2.39      frystyk   422:     if (pars && *pars)
2.48      frystyk   423:        sprintf(HTChunk_data(news->cmd), "%s %s%c%c", token, pars, CR, LF);
2.39      frystyk   424:     else
2.48      frystyk   425:        sprintf(HTChunk_data(news->cmd), "%s%c%c", token, CR, LF);
2.51      eric      426:     if (PROT_TRACE) HTTrace("News Tx..... %s", HTChunk_data(news->cmd));
2.39      frystyk   427:     return (*request->input_stream->isa->put_block)
2.48      frystyk   428:        (request->input_stream, HTChunk_data(news->cmd), len);
2.39      frystyk   429: }
                    430: 
                    431: /*             Load data object from NNTP Server                    HTLoadNews
                    432: **             =================================
                    433: **
                    434: **     Given a hypertext addres, this routine loads a document
                    435: **
                    436: ** On Entry,
                    437: **     request         The request structure
                    438: **
                    439: **     returns         HT_ERROR        Error has occured or interrupted
                    440: **                     HT_WOULD_BLOCK  if operation would have blocked
                    441: **                     HT_LOADED       if 200 OK
                    442: **                     HT_NO_DATA      if No Response
                    443: **                     HT_RETRY        if Service Unavail.
                    444: */
                    445: PUBLIC int HTLoadNews (SOCKET soc, HTRequest * request, SockOps ops)
                    446: {
                    447:     int status = HT_ERROR;
                    448:     HTNet *net = request->net;
                    449:     HTParentAnchor *anchor = HTRequest_anchor(request);
                    450:     char *url = HTAnchor_physical(anchor);
                    451:     news_info *news;
                    452:     
                    453:     /*
                    454:     ** Initiate a new nntp structure and bind to request structure
                    455:     ** This is actually state NNTP_BEGIN, but it can't be in the state
                    456:     ** machine as we need the structure first.
                    457:     */
2.36      frystyk   458:     if (ops == FD_NONE) {
2.39      frystyk   459:        if (PROT_TRACE) 
2.51      eric      460:            HTTrace("NNTP........ Looking for `%s\'\n", url);
2.50      frystyk   461:        if ((news = (news_info *) HT_CALLOC(1, sizeof(news_info))) == NULL)
                    462:            HT_OUTOFMEM("HTLoadNews");
2.48      frystyk   463:        news->cmd = HTChunk_new(128);
2.36      frystyk   464:        news->state = NEWS_BEGIN;
                    465:        net->context = news;
2.39      frystyk   466:     } else if (ops == FD_CLOSE) {                            /* Interrupted */
                    467:        if(HTRequest_isPostWeb(request)&&!HTRequest_isMainDestination(request))
                    468:            HTNewsCleanup(request, HT_IGNORE);
                    469:        else
                    470:            HTNewsCleanup(request, HT_INTERRUPTED);
2.36      frystyk   471:        return HT_OK;
                    472:     } else
2.39      frystyk   473:        news = (news_info *) net->context;              /* Get existing copy */
                    474:      
                    475:     /* Now start the state machine */
                    476:     while (1) {
                    477:         switch (news->state) {
                    478:           case NEWS_BEGIN:
                    479:            news->state = (!strchr(url, '@') && strchr(url, '*')) ?
                    480:                NEWS_SEEK_CACHE : NEWS_NEED_CONNECTION;
                    481:            break;
                    482: 
                    483:           case NEWS_SEEK_CACHE:                               /* @@@ DO THIS @@@@@@ */
                    484:            news->state = NEWS_NEED_CONNECTION;
                    485:            break;
                    486: 
                    487:          case NEWS_NEED_CONNECTION:            /* Let's set up a connection */
                    488:            if (!strncasecomp(url, "news:", 5)) {
2.52      frystyk   489:                const char *newshost = HTNews_host();
2.39      frystyk   490:                StrAllocCopy(news->name, url+5);
                    491:                if (newshost) {
                    492:                    char *newshack = NULL;    /* Then we can use HTParse :-) */
                    493:                    StrAllocCopy(newshack, "news://");
                    494:                    StrAllocCat(newshack, newshost);
                    495:                    status = HTDoConnect(net, (char *) newshack, NEWS_PORT);
2.50      frystyk   496:                    HT_FREE(newshack);
2.39      frystyk   497:                } else
                    498:                    news->state = NEWS_ERROR;
                    499:            } else if (!strncasecomp(url, "nntp:", 5)) {
                    500:                news->name = HTParse(url, "", PARSE_PATH);
                    501:                status = HTDoConnect(net, url, NEWS_PORT);
                    502:            } else {
2.51      eric      503:                if (PROT_TRACE) HTTrace("News........ Huh?");
2.39      frystyk   504:                news->state = NEWS_ERROR;
                    505:             }
                    506:             if (status == HT_OK) {
                    507:                BOOL greeting = NO;
                    508:                char *s_class = HTDNS_serverClass(net->dns);
                    509:                if (s_class && strcasecomp(s_class, "nntp")) {
2.46      frystyk   510:                    HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS,
                    511:                                       NULL, 0, "HTLoadNews");
2.39      frystyk   512:                    news->state = NEWS_ERROR;
                    513:                    break;
                    514:                }
                    515:                HTDNS_setServerClass(net->dns, "nntp");
2.53    ! frystyk   516:                if (!HTNet_persistent(net)) {
        !           517:                    HTNet_setPersistent(net, YES);
2.39      frystyk   518:                    greeting = YES;
                    519:                }
                    520:                if (PROT_TRACE)
2.51      eric      521:                    HTTrace("News........ Connected, socket %d\n",
2.39      frystyk   522:                            net->sockfd);
2.36      frystyk   523: 
2.39      frystyk   524:                /* Set up stream TO network */
                    525:                request->input_stream = HTWriter_new(net, YES);
                    526:              
                    527:                /*
                    528:                ** Set up concurrent read/write if this request isn't the
                    529:                ** source for a PUT or POST. As source we don't start reading
                    530:                ** before all destinations are ready. If destination then
                    531:                ** register the input stream and get ready for read
                    532:                */
                    533:                if (HTRequest_isPostWeb(request)) {
                    534:                    HTEvent_Register(net->sockfd, request, (SockOps) FD_READ,
                    535:                                     HTLoadNews, net->priority);
                    536:                    HTRequest_linkDestination(request);
                    537:                }
                    538: 
                    539:                /* Set up stream FROM network and corresponding read buffer */
                    540:                net->target = HTNewsStatus_new(request, news);
                    541:                news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH;
                    542:            } else if (status == HT_WOULD_BLOCK || status == HT_PERSISTENT)
                    543:                return HT_OK;
                    544:            else
                    545:                news->state = NEWS_ERROR;
                    546:            break;
                    547: 
                    548:          case NEWS_NEED_GREETING:
2.53    ! frystyk   549:            status = HTChannel_readSocket(request, net);
2.39      frystyk   550:            if (status == HT_WOULD_BLOCK)
                    551:                return HT_OK;
                    552:            else if (status == HT_LOADED) {
                    553:                if (news->repcode/100 == 2)
                    554:                    news->state = NEWS_NEED_SWITCH;
                    555:                else
                    556:                    news->state = NEWS_ERROR;
                    557:            } else
                    558:                news->state = NEWS_ERROR;
                    559:            break;
                    560: 
                    561:          case NEWS_NEED_SWITCH:
                    562:            /*
                    563:            ** Find out what to ask the news server. Syntax of address is
                    564:            **  xxx@yyy         Article
                    565:            **  <xxx@yyy>       Same article
                    566:            **  xxxxx           News group (no "@")
                    567:            */
                    568:            if (request->method == METHOD_GET) {
                    569:                if (strchr(url, '@')) {                           /* ARTICLE */
                    570:                    if (*(news->name) != '<') {           /* Add '<' and '>' */
2.50      frystyk   571:                        char *newart;
                    572:                        if ((newart = (char  *) HT_MALLOC(strlen(news->name)+3)) == NULL)
                    573:                            HT_OUTOFMEM("HTLoadNews");
2.39      frystyk   574:                        sprintf(newart, "<%s>", news->name);
2.50      frystyk   575:                        HT_FREE(news->name);
2.39      frystyk   576:                        news->name = newart;
                    577:                    }
                    578:                    news->state = NEWS_NEED_ARTICLE;
                    579:                } else if (strchr(url, '*'))
                    580:                    news->state = NEWS_NEED_LIST;
                    581:                else
                    582:                    news->state = NEWS_NEED_GROUP;
                    583:            } else if (request->method == METHOD_POST)
                    584:                news->state = NEWS_NEED_POST;
                    585:            else {
2.46      frystyk   586:                HTRequest_addError(request, ERR_FATAL, NO,
                    587:                                   HTERR_NOT_IMPLEMENTED,NULL, 0,"HTLoadNews");
2.39      frystyk   588:                news->state = NEWS_ERROR;
                    589:            }
                    590:            HTUnEscape(news->name);
                    591:            HTCleanTelnetString(news->name);
                    592:            break;
                    593: 
                    594:          case NEWS_NEED_ARTICLE:
                    595:            if (!news->sent) {
                    596:                status = SendCommand(request, news, "ARTICLE", news->name);
                    597:                if (status == HT_WOULD_BLOCK)
                    598:                    return HT_OK;
                    599:                else if (status == HT_ERROR)
                    600:                    news->state = NEWS_ERROR;
                    601:                news->format = WWW_MIME;
                    602:                news->sent = YES;
                    603:            } else {
2.53    ! frystyk   604:                status = HTChannel_readSocket(request, net);
2.39      frystyk   605:                if (status == HT_WOULD_BLOCK)
                    606:                    return HT_OK;
                    607:                else if (status == HT_OK)
                    608:                    news->state = NEWS_NEED_BODY;
                    609:                else if (status == HT_LOADED) {
                    610:                    news->state = (news->repcode/100 == 2) ?
                    611:                        NEWS_SUCCESS : NEWS_ERROR;
                    612:                } else
                    613:                    news->state = NEWS_ERROR;
                    614:                news->sent = NO;
                    615:            }
                    616:            break;
                    617: 
2.45      frystyk   618: #if HT_LISTGROUP
2.39      frystyk   619:          case NEWS_NEED_LGRP:
                    620:            if (!news->sent) {
                    621:                status = SendCommand(request, news, "LIST", "NEWSGROUPS");
                    622:                if (status == HT_WOULD_BLOCK)
                    623:                    return HT_OK;
                    624:                else if (status == HT_ERROR)
                    625:                    news->state = NEWS_ERROR;
                    626:                news->format = WWW_NNTP_LIST;
                    627:                news->sent = YES;
                    628:            } else {
2.53    ! frystyk   629:                status = HTChannel_readSocket(request, net);
2.39      frystyk   630:                if (status == HT_WOULD_BLOCK)
                    631:                    return HT_OK;
                    632:                else if (status == HT_OK)
                    633:                    news->state = NEWS_NEED_BODY;
                    634:                else if (status == HT_LOADED) {
                    635:                    news->state = (news->repcode/100 == 2) ?
                    636:                        NEWS_SUCCESS : NEWS_NEED_LIST;
                    637:                } else
                    638:                    news->state = NEWS_ERROR;
                    639:                news->sent = NO;
                    640:            }
                    641:            break;
2.45      frystyk   642: #endif /* HT_LISTGROUP */
2.39      frystyk   643: 
                    644:          case NEWS_NEED_LIST:
                    645:            if (!news->sent) {
                    646:                status = SendCommand(request, news, "LIST", NULL);
                    647:                if (status == HT_WOULD_BLOCK)
                    648:                    return HT_OK;
                    649:                else if (status == HT_ERROR)
                    650:                    news->state = NEWS_ERROR;
                    651:                news->format = WWW_NNTP_LIST;
                    652:                news->sent = YES;
                    653:            } else {
2.53    ! frystyk   654:                status = HTChannel_readSocket(request, net);
2.39      frystyk   655:                if (status == HT_WOULD_BLOCK)
                    656:                    return HT_OK;
                    657:                else if (status == HT_OK)
                    658:                    news->state = NEWS_NEED_BODY;
                    659:                else if (status == HT_LOADED) {
                    660:                    news->state = (news->repcode/100 == 2) ?
                    661:                        NEWS_SUCCESS : NEWS_ERROR;
                    662:                } else
                    663:                    news->state = NEWS_ERROR;
                    664:                news->sent = NO;
                    665:            }
                    666:            break;
1.1       timbl     667: 
2.39      frystyk   668:          case NEWS_NEED_GROUP:
                    669:            if (!news->sent) {
                    670:                status = SendCommand(request, news, "GROUP", news->name);
                    671:                if (status == HT_WOULD_BLOCK)
                    672:                    return HT_OK;
                    673:                else if (status == HT_ERROR)
                    674:                    news->state = NEWS_ERROR;
                    675:                news->sent = YES;
1.1       timbl     676:            } else {
2.53    ! frystyk   677:                status = HTChannel_readSocket(request, net);
2.39      frystyk   678:                if (status == HT_WOULD_BLOCK)
                    679:                    return HT_OK;
                    680:                else if (status == HT_LOADED) {
                    681:                    if (news->repcode/100 == 2) {
                    682:                        if (sscanf(news->reply, "%d%d%d", &news->total,
                    683:                                   &news->first, &news->last) == 3) {
2.45      frystyk   684:                            if (MaxArt && news->total>MaxArt)
                    685:                                news->last = news->first-MaxArt;
2.39      frystyk   686:                            news->current = news->first;
                    687:                            news->state = NEWS_NEED_XOVER;
                    688:                        } else
                    689:                            news->state = NEWS_ERROR;
                    690:                    } else
                    691:                        news->state = NEWS_ERROR;
                    692:                } else
                    693:                    news->state = NEWS_ERROR;
                    694:                news->sent = NO;
1.1       timbl     695:            }
2.39      frystyk   696:            break;
1.1       timbl     697: 
2.39      frystyk   698:          case NEWS_NEED_XOVER:
                    699:            if (!news->sent) {
                    700:                char buf[20];
                    701:                sprintf(buf, "%d-%d", news->first, news->last);
                    702:                status = SendCommand(request, news, "XOVER", buf);
                    703:                if (status == HT_WOULD_BLOCK)
                    704:                    return HT_OK;
                    705:                else if (status == HT_ERROR)
                    706:                    news->state = NEWS_ERROR;
                    707:                news->format = WWW_NNTP_OVER;
                    708:                news->sent = YES;
                    709:            } else {
2.53    ! frystyk   710:                status = HTChannel_readSocket(request, net);
2.39      frystyk   711:                if (status == HT_WOULD_BLOCK)
                    712:                    return HT_OK;
                    713:                else if (status == HT_OK)
                    714:                    news->state = NEWS_NEED_BODY;
                    715:                else if (status == HT_LOADED) {
                    716:                    if (news->repcode/100 == 2)
                    717:                        news->state = NEWS_SUCCESS;
                    718:                    else {
                    719:                        news->format = WWW_NNTP_HEAD;
                    720:                        news->state = NEWS_NEED_HEAD;
                    721:                    }
                    722:                } else
                    723:                    news->state = NEWS_ERROR;
                    724:                news->sent = NO;
                    725:            }
                    726:            break;
1.1       timbl     727: 
2.39      frystyk   728:          case NEWS_NEED_HEAD:
                    729:            if (!news->sent) {
                    730:                char buf[10];
                    731:                sprintf(buf, "%d", news->current++);
                    732:                status = SendCommand(request, news, "HEAD", buf);
                    733:                if (status == HT_WOULD_BLOCK)
2.36      frystyk   734:                    return HT_OK;
2.39      frystyk   735:                else if (status == HT_ERROR)
                    736:                    news->state = NEWS_ERROR;
                    737:                news->sent = YES;
1.1       timbl     738:            } else {
2.53    ! frystyk   739:                status = HTChannel_readSocket(request, net);
2.39      frystyk   740:                if (status == HT_WOULD_BLOCK)
                    741:                    return HT_OK;
                    742:                else if (status == HT_LOADED) {
                    743:                    if (news->repcode/100 == 2) {
                    744:                        if (news->current > news->last)
                    745:                            news->state = NEWS_SUCCESS;
                    746:                    } else
                    747:                        news->state = NEWS_ERROR;
                    748:                } else
                    749:                    news->state = NEWS_ERROR;
                    750:                news->sent = NO;
                    751:            }
                    752:            break;
                    753: 
                    754:          case NEWS_NEED_POST:
                    755:            request->input_stream =
                    756:                HTNewsPost_new(request, HTBuffer_new(request->input_stream,
                    757:                                                     request, 512));
                    758: 
                    759:            /* Remember to convert to CRLF */
                    760: 
                    761:            news->state = NEWS_NEED_BODY;
                    762:            break;
                    763: 
                    764:           case NEWS_NEED_BODY:
                    765:             if (ops == FD_WRITE || ops == FD_NONE) {
                    766:                if (HTRequest_isDestination(request)) {
                    767:                    HTNet *srcnet = request->source->net;
                    768:                    HTEvent_Register(srcnet->sockfd, request->source,
                    769:                                     (SockOps) FD_READ,
                    770:                                     HTLoadNews, srcnet->priority);
2.36      frystyk   771:                    return HT_OK;
2.21      frystyk   772:                }
2.41      frystyk   773:                status = request->PostCallback ?
                    774:                     request->PostCallback(request, request->input_stream) :
2.39      frystyk   775:                        (*request->input_stream->isa->flush)(request->input_stream);
                    776:                if (status == HT_WOULD_BLOCK)
                    777:                     return HT_OK;
                    778:                 else   
                    779:                     ops = FD_READ;       /* Trick to ensure that we do READ */
                    780:            } else if (ops == FD_READ) {
2.53    ! frystyk   781:                 status = HTChannel_readSocket(request, net);
2.39      frystyk   782:                if (status == HT_WOULD_BLOCK)
                    783:                    return HT_OK;
                    784:                 else if (status == HT_LOADED)
                    785:                    news->state = NEWS_SUCCESS;
                    786:                else
                    787:                    news->state = NEWS_ERROR;
                    788:            } else {
                    789:                news->state = NEWS_ERROR;
1.1       timbl     790:            }
2.39      frystyk   791:            break;
                    792:                
                    793:          case NEWS_SUCCESS:
                    794:            if (HTRequest_isPostWeb(request)) {
                    795:                BOOL main = HTRequest_isMainDestination(request);
                    796:                if (HTRequest_isDestination(request)) {
                    797:                    HTLink *link =
                    798:                        HTAnchor_findLink((HTAnchor *) request->source->anchor,
                    799:                                          (HTAnchor *) request->anchor);
2.47      frystyk   800:                    HTLink_setResult(link, HT_LINK_OK);
2.39      frystyk   801:                }
                    802:                HTRequest_removeDestination(request);
                    803:                HTNewsCleanup(request, main ? HT_LOADED : HT_IGNORE);
                    804:            } else
                    805:                HTNewsCleanup(request, HT_LOADED);
                    806:            return HT_OK;
                    807:            break;
                    808:            
                    809:          case NEWS_ERROR:
                    810:            /* Clean up the other connections or just this one */
                    811:            if (HTRequest_isPostWeb(request)) {
                    812:                BOOL main = HTRequest_isMainDestination(request);
                    813:                HTRequest_killPostWeb(request);
                    814:                if (HTRequest_isDestination(request)) {
                    815:                    HTLink *link =
                    816:                        HTAnchor_findLink((HTAnchor *) request->source->anchor,
                    817:                                          (HTAnchor *) request->anchor);
2.47      frystyk   818:                    HTLink_setResult(link, HT_LINK_ERROR);
2.39      frystyk   819:                }
                    820:                HTRequest_removeDestination(request);
                    821:                HTNewsCleanup(request, main ? HT_ERROR : HT_IGNORE);
                    822:            } else
                    823:                HTNewsCleanup(request, HT_ERROR);
                    824:            return HT_OK;
                    825:            break;
1.1       timbl     826:        }
2.39      frystyk   827:     } /* End of while(1) */
1.1       timbl     828: }

Webmaster