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

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

Webmaster