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

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

Webmaster