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

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

Webmaster