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

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

Webmaster