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

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

Webmaster