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

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"
2.43      frystyk    30: #include "HTReqMan.h"                          /* @@@ */
                     31: #include "HTNetMan.h"                          /* @@@ */
2.39      frystyk    32: #include "HTNewsRq.h" 
                     33: #include "HTNews.h"                                           /* Implements */
                     34: 
                     35: /* Macros and other defines */
                     36: #ifndef NEWS_PORT
                     37: #define NEWS_PORT              119                            /* See rfc977 */
                     38: #endif
1.3       timbl      39: 
2.39      frystyk    40: #ifndef NEWS_LIST_FILE
                     41: #define NEWS_LIST_FILE         ".www_news"        /* Name of news list file */
                     42: #endif
1.1       timbl      43: 
                     44: #ifndef DEFAULT_NEWS_HOST
2.39      frystyk    45: #define DEFAULT_NEWS_HOST      "news"
1.1       timbl      46: #endif
2.39      frystyk    47: 
1.1       timbl      48: #ifndef SERVER_FILE
2.39      frystyk    49: #define SERVER_FILE            "/usr/local/lib/rn/server"
1.1       timbl      50: #endif
                     51: 
2.45      frystyk    52: #define MAX_NEWS_ARTICLES      0       /* No default max number of articles */
2.40      frystyk    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,
2.45      frystyk    65: #if HT_LISTGROUP
2.39      frystyk    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;
2.45      frystyk   106: PRIVATE int MaxArt = 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;
2.42      frystyk   125:     if (PROT_TRACE) TTYPrint(TDEST, "News Rx..... `%s\'\n", news->reply);
2.39      frystyk   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)
2.42      frystyk   162:                    TTYPrint(TDEST, "News Status. Line too long - chopped\n");
2.39      frystyk   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.50    ! frystyk   221:     HT_FREE(me);
2.39      frystyk   222:     return status;
1.2       timbl     223: }
1.1       timbl     224: 
2.44      frystyk   225: PRIVATE int HTNewsStatus_abort (HTStream * me, HTList * e)
2.16      luotonen  226: {
2.39      frystyk   227:     if (me->target)
                    228:         ABORT_TARGET;
2.50    ! frystyk   229:     HT_FREE(me);
2.42      frystyk   230:     if (PROT_TRACE) TTYPrint(TDEST, "NewsStatus.. ABORTING...\n");
2.39      frystyk   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.50    ! frystyk   247:     HTStream *me;
        !           248:     if ((me = (HTStream  *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
        !           249:         HT_OUTOFMEM("HTNewsStatus_new");
2.39      frystyk   250:     me->isa = &HTNewsStatusClass;
                    251:     me->request = request;
                    252:     me->news = news;
                    253:     me->EOLstate = EOL_BEGIN;
                    254:     return me;
1.1       timbl     255: }
                    256: 
2.39      frystyk   257: /* ------------------------------------------------------------------------- */
                    258: /*                             PROTOCOL FUNCTIONS                           */
                    259: /* ------------------------------------------------------------------------- */
1.1       timbl     260: 
2.39      frystyk   261: /*
                    262: **     Set the max number of articles at the same time.
                    263: **     Default is MAX_NEWS_ARTICLES
1.1       timbl     264: */
2.39      frystyk   265: PUBLIC BOOL HTNews_setMaxArticles (int new_max)
                    266: { 
2.45      frystyk   267:     if (new_max >= 0) {
                    268:        MaxArt = new_max;
2.39      frystyk   269:        return YES;
1.1       timbl     270:     }
2.39      frystyk   271:     return NO;
1.1       timbl     272: }
                    273: 
2.39      frystyk   274: /*
                    275: **     Get current max number of articles at the same time.
1.1       timbl     276: */
2.39      frystyk   277: PUBLIC int HTNews_maxArticles (void)
                    278: { 
2.45      frystyk   279:     return MaxArt;
1.1       timbl     280: }
                    281: 
2.39      frystyk   282: /*
                    283: **     Sets the current NEWS server.
                    284: */
                    285: PUBLIC BOOL HTNews_setHost (CONST char * newshost)
1.1       timbl     286: {
2.39      frystyk   287:     if (newshost && *newshost) {
                    288:        StrAllocCopy(HTNewsHost, newshost);
                    289:        {
                    290:            char *strptr = HTNewsHost;
                    291:            while (*strptr) {
                    292:                *strptr = TOLOWER(*strptr);
                    293:                strptr++;
1.2       timbl     294:            }
                    295:            
2.39      frystyk   296:            /* Remove final dot or paste in domain name */
                    297:            if (strchr(HTNewsHost, '.')) {
                    298:                if (*(HTNewsHost+strlen(HTNewsHost)-1) == '.')
                    299:                    *(HTNewsHost+strlen(HTNewsHost)-1) = '\0';
                    300:            } else {
                    301:                CONST char *domain = HTGetDomainName();
                    302:                if (domain) {
                    303:                    StrAllocCat(HTNewsHost, ".");
                    304:                    StrAllocCat(HTNewsHost, domain);
                    305:                }
                    306:            }           
1.1       timbl     307:        }
2.39      frystyk   308:        if (PROT_TRACE)
2.42      frystyk   309:            TTYPrint(TDEST, "SetNewsHost. Host name is `%s\'\n", HTNewsHost);
2.39      frystyk   310:        return YES;
                    311:     } else {
                    312:        if (PROT_TRACE)
2.42      frystyk   313:            TTYPrint(TDEST, "SetNewsHost. Bad argument ignored\n");
2.39      frystyk   314:        return NO;
1.1       timbl     315:     }
                    316: }
                    317: 
                    318: /*
2.39      frystyk   319: **     Except on the NeXT, we pick up the NewsHost name from
                    320: **
                    321: **     1.      Environment variable NNTPSERVER
                    322: **     2.      File SERVER_FILE
                    323: **     3.      Compilation time macro DEFAULT_NEWS_HOST
                    324: **
                    325: **     On the NeXT, we pick up the NewsHost name from, in order:
1.1       timbl     326: **
2.39      frystyk   327: **     1.      WorldWideWeb default "NewsHost"
                    328: **     2.      News default "NewsHost"
                    329: **     3.      Compilation time macro DEFAULT_NEWS_HOST
1.1       timbl     330: **
2.39      frystyk   331: **     Return: HTNewsHost if success else NULL
1.1       timbl     332: */
2.39      frystyk   333: PUBLIC CONST char *HTNews_host (void)
1.1       timbl     334: {
2.39      frystyk   335:     if (HTNewsHost) {
                    336:        if (*HTNewsHost) {
                    337:            if (PROT_TRACE)
2.42      frystyk   338:                TTYPrint(TDEST, "GetNewsHost. found as `%s\'\n", HTNewsHost);
2.39      frystyk   339:            return HTNewsHost;
                    340:        } else
                    341:            return NULL;                 /* We couldn't get it the last time */
1.1       timbl     342:     }
2.39      frystyk   343:     {
                    344:        char *newshost = NULL;
                    345:         char buffer[80];
1.1       timbl     346: 
2.39      frystyk   347: #ifdef NeXTStep
                    348:        if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0)
                    349:            if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0)
                    350:                newshost = DEFAULT_NEWS_HOST;
                    351: #else
                    352:        if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) {
                    353:            FILE *fp = fopen(SERVER_FILE, "r");
                    354:            *(buffer+79) = '\0';
                    355:            if (fp) {
                    356:                if (fgets(buffer, 79, fp)) {
                    357:                    char *end;
                    358:                    newshost = buffer;
                    359:                    while (*newshost == ' ' || *newshost == '\t')
                    360:                        newshost++;
                    361:                    end = newshost;
                    362:                    while (*end && !isspace(*end))
                    363:                        end++;
                    364:                    *end = '\0';
                    365:                }
                    366:                fclose(fp);
                    367:            }
                    368:        }
                    369: #endif /* NestStep */
1.1       timbl     370: 
2.39      frystyk   371:        if (!newshost || !*newshost)
                    372:            newshost = DEFAULT_NEWS_HOST;
                    373:        if (HTNews_setHost(newshost))
                    374:            return HTNewsHost;
                    375:        StrAllocCopy(HTNewsHost, "");
                    376:        return NULL;
1.1       timbl     377:     }
2.39      frystyk   378: }
1.1       timbl     379: 
2.39      frystyk   380: /*
                    381: **     Free Newshostname
1.2       timbl     382: */
2.39      frystyk   383: PUBLIC void HTFreeNewsHost (void)
                    384: {
2.50    ! frystyk   385:     HT_FREE(HTNewsHost);
2.39      frystyk   386: }
1.2       timbl     387: 
2.39      frystyk   388: /*     HTNewsCleanup
                    389: **     -------------
                    390: **      This function closes the connection and frees memory.
                    391: **      Returns YES on OK, else NO
1.1       timbl     392: */
2.39      frystyk   393: PRIVATE int HTNewsCleanup (HTRequest * req, int status)
                    394: {
                    395:     HTNet *net = req->net;
                    396:     news_info *news = (news_info *) net->context;
1.1       timbl     397: 
2.39      frystyk   398:     /* Free stream with data TO network */
                    399:     if (!HTRequest_isDestination(req) && req->input_stream) {
                    400:        if (status == HT_INTERRUPTED)
                    401:            (*req->input_stream->isa->abort)(req->input_stream, NULL);
                    402:        else
                    403:            (*req->input_stream->isa->_free)(req->input_stream);
1.1       timbl     404:     }
2.16      luotonen  405: 
2.39      frystyk   406:     /* Remove the request object and our own context structure for nntp */
                    407:     HTNet_delete(net, status);
2.49      frystyk   408:     if (news) {
2.50    ! frystyk   409:        HT_FREE(news->name);
2.49      frystyk   410:        HTChunk_delete(news->cmd);
2.50    ! frystyk   411:        HT_FREE(news);
2.49      frystyk   412:     }
2.39      frystyk   413:     return YES;
1.1       timbl     414: }
                    415: 
2.39      frystyk   416: PRIVATE int SendCommand (HTRequest *request, news_info *news,
                    417:                         char *token, char *pars)
1.1       timbl     418: {
2.39      frystyk   419:     int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2;
2.48      frystyk   420:     HTChunk_clear(news->cmd);
                    421:     HTChunk_ensure(news->cmd, len);
2.39      frystyk   422:     if (pars && *pars)
2.48      frystyk   423:        sprintf(HTChunk_data(news->cmd), "%s %s%c%c", token, pars, CR, LF);
2.39      frystyk   424:     else
2.48      frystyk   425:        sprintf(HTChunk_data(news->cmd), "%s%c%c", token, CR, LF);
                    426:     if (PROT_TRACE) TTYPrint(TDEST, "News Tx..... %s", HTChunk_data(news->cmd));
2.39      frystyk   427:     return (*request->input_stream->isa->put_block)
2.48      frystyk   428:        (request->input_stream, HTChunk_data(news->cmd), len);
2.39      frystyk   429: }
                    430: 
                    431: /*             Load data object from NNTP Server                    HTLoadNews
                    432: **             =================================
                    433: **
                    434: **     Given a hypertext addres, this routine loads a document
                    435: **
                    436: ** On Entry,
                    437: **     request         The request structure
                    438: **
                    439: **     returns         HT_ERROR        Error has occured or interrupted
                    440: **                     HT_WOULD_BLOCK  if operation would have blocked
                    441: **                     HT_LOADED       if 200 OK
                    442: **                     HT_NO_DATA      if No Response
                    443: **                     HT_RETRY        if Service Unavail.
                    444: */
                    445: PUBLIC int HTLoadNews (SOCKET soc, HTRequest * request, SockOps ops)
                    446: {
                    447:     int status = HT_ERROR;
                    448:     HTNet *net = request->net;
                    449:     HTParentAnchor *anchor = HTRequest_anchor(request);
                    450:     char *url = HTAnchor_physical(anchor);
                    451:     news_info *news;
                    452:     
                    453:     /*
                    454:     ** Initiate a new nntp structure and bind to request structure
                    455:     ** This is actually state NNTP_BEGIN, but it can't be in the state
                    456:     ** machine as we need the structure first.
                    457:     */
2.36      frystyk   458:     if (ops == FD_NONE) {
2.39      frystyk   459:        if (PROT_TRACE) 
2.42      frystyk   460:            TTYPrint(TDEST, "NNTP........ Looking for `%s\'\n", url);
2.50    ! frystyk   461:        if ((news = (news_info *) HT_CALLOC(1, sizeof(news_info))) == NULL)
        !           462:            HT_OUTOFMEM("HTLoadNews");
2.48      frystyk   463:        news->cmd = HTChunk_new(128);
2.36      frystyk   464:        news->state = NEWS_BEGIN;
                    465:        net->context = news;
2.39      frystyk   466:     } else if (ops == FD_CLOSE) {                            /* Interrupted */
                    467:        if(HTRequest_isPostWeb(request)&&!HTRequest_isMainDestination(request))
                    468:            HTNewsCleanup(request, HT_IGNORE);
                    469:        else
                    470:            HTNewsCleanup(request, HT_INTERRUPTED);
2.36      frystyk   471:        return HT_OK;
                    472:     } else
2.39      frystyk   473:        news = (news_info *) net->context;              /* Get existing copy */
                    474:      
                    475:     /* Now start the state machine */
                    476:     while (1) {
                    477:         switch (news->state) {
                    478:           case NEWS_BEGIN:
                    479:            news->state = (!strchr(url, '@') && strchr(url, '*')) ?
                    480:                NEWS_SEEK_CACHE : NEWS_NEED_CONNECTION;
                    481:            break;
                    482: 
                    483:           case NEWS_SEEK_CACHE:                               /* @@@ DO THIS @@@@@@ */
                    484:            news->state = NEWS_NEED_CONNECTION;
                    485:            break;
                    486: 
                    487:          case NEWS_NEED_CONNECTION:            /* Let's set up a connection */
                    488:            if (!strncasecomp(url, "news:", 5)) {
                    489:                CONST char *newshost = HTNews_host();
                    490:                StrAllocCopy(news->name, url+5);
                    491:                if (newshost) {
                    492:                    char *newshack = NULL;    /* Then we can use HTParse :-) */
                    493:                    StrAllocCopy(newshack, "news://");
                    494:                    StrAllocCat(newshack, newshost);
                    495:                    status = HTDoConnect(net, (char *) newshack, NEWS_PORT);
2.50    ! frystyk   496:                    HT_FREE(newshack);
2.39      frystyk   497:                } else
                    498:                    news->state = NEWS_ERROR;
                    499:            } else if (!strncasecomp(url, "nntp:", 5)) {
                    500:                news->name = HTParse(url, "", PARSE_PATH);
                    501:                status = HTDoConnect(net, url, NEWS_PORT);
                    502:            } else {
2.42      frystyk   503:                if (PROT_TRACE) TTYPrint(TDEST, "News........ Huh?");
2.39      frystyk   504:                news->state = NEWS_ERROR;
                    505:             }
                    506:             if (status == HT_OK) {
                    507:                BOOL greeting = NO;
                    508:                char *s_class = HTDNS_serverClass(net->dns);
                    509:                if (s_class && strcasecomp(s_class, "nntp")) {
2.46      frystyk   510:                    HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS,
                    511:                                       NULL, 0, "HTLoadNews");
2.39      frystyk   512:                    news->state = NEWS_ERROR;
                    513:                    break;
                    514:                }
                    515:                HTDNS_setServerClass(net->dns, "nntp");
                    516:                if (HTDNS_socket(net->dns) == INVSOC) {
                    517:                    HTDNS_setSocket(net->dns, net->sockfd);
                    518:                    greeting = YES;
                    519:                }
                    520:                if (PROT_TRACE)
2.42      frystyk   521:                    TTYPrint(TDEST, "News........ Connected, socket %d\n",
2.39      frystyk   522:                            net->sockfd);
2.36      frystyk   523: 
2.39      frystyk   524:                /* Set up stream TO network */
                    525:                request->input_stream = HTWriter_new(net, YES);
                    526:              
                    527:                /*
                    528:                ** Set up concurrent read/write if this request isn't the
                    529:                ** source for a PUT or POST. As source we don't start reading
                    530:                ** before all destinations are ready. If destination then
                    531:                ** register the input stream and get ready for read
                    532:                */
                    533:                if (HTRequest_isPostWeb(request)) {
                    534:                    HTEvent_Register(net->sockfd, request, (SockOps) FD_READ,
                    535:                                     HTLoadNews, net->priority);
                    536:                    HTRequest_linkDestination(request);
                    537:                }
                    538: 
                    539:                /* Set up stream FROM network and corresponding read buffer */
                    540:                net->isoc = HTInputSocket_new(net->sockfd);
                    541:                net->target = HTNewsStatus_new(request, news);
                    542:                news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH;
                    543:            } else if (status == HT_WOULD_BLOCK || status == HT_PERSISTENT)
                    544:                return HT_OK;
                    545:            else
                    546:                news->state = NEWS_ERROR;
                    547:            break;
                    548: 
                    549:          case NEWS_NEED_GREETING:
                    550:            status = HTSocketRead(request, net);
                    551:            if (status == HT_WOULD_BLOCK)
                    552:                return HT_OK;
                    553:            else if (status == HT_LOADED) {
                    554:                if (news->repcode/100 == 2)
                    555:                    news->state = NEWS_NEED_SWITCH;
                    556:                else
                    557:                    news->state = NEWS_ERROR;
                    558:            } else
                    559:                news->state = NEWS_ERROR;
                    560:            break;
                    561: 
                    562:          case NEWS_NEED_SWITCH:
                    563:            /*
                    564:            ** Find out what to ask the news server. Syntax of address is
                    565:            **  xxx@yyy         Article
                    566:            **  <xxx@yyy>       Same article
                    567:            **  xxxxx           News group (no "@")
                    568:            */
                    569:            if (request->method == METHOD_GET) {
                    570:                if (strchr(url, '@')) {                           /* ARTICLE */
                    571:                    if (*(news->name) != '<') {           /* Add '<' and '>' */
2.50    ! frystyk   572:                        char *newart;
        !           573:                        if ((newart = (char  *) HT_MALLOC(strlen(news->name)+3)) == NULL)
        !           574:                            HT_OUTOFMEM("HTLoadNews");
2.39      frystyk   575:                        sprintf(newart, "<%s>", news->name);
2.50    ! frystyk   576:                        HT_FREE(news->name);
2.39      frystyk   577:                        news->name = newart;
                    578:                    }
                    579:                    news->state = NEWS_NEED_ARTICLE;
                    580:                } else if (strchr(url, '*'))
                    581:                    news->state = NEWS_NEED_LIST;
                    582:                else
                    583:                    news->state = NEWS_NEED_GROUP;
                    584:            } else if (request->method == METHOD_POST)
                    585:                news->state = NEWS_NEED_POST;
                    586:            else {
2.46      frystyk   587:                HTRequest_addError(request, ERR_FATAL, NO,
                    588:                                   HTERR_NOT_IMPLEMENTED,NULL, 0,"HTLoadNews");
2.39      frystyk   589:                news->state = NEWS_ERROR;
                    590:            }
                    591:            HTUnEscape(news->name);
                    592:            HTCleanTelnetString(news->name);
                    593:            break;
                    594: 
                    595:          case NEWS_NEED_ARTICLE:
                    596:            if (!news->sent) {
                    597:                status = SendCommand(request, news, "ARTICLE", news->name);
                    598:                if (status == HT_WOULD_BLOCK)
                    599:                    return HT_OK;
                    600:                else if (status == HT_ERROR)
                    601:                    news->state = NEWS_ERROR;
                    602:                news->format = WWW_MIME;
                    603:                news->sent = YES;
                    604:            } else {
                    605:                status = HTSocketRead(request, net);
                    606:                if (status == HT_WOULD_BLOCK)
                    607:                    return HT_OK;
                    608:                else if (status == HT_OK)
                    609:                    news->state = NEWS_NEED_BODY;
                    610:                else if (status == HT_LOADED) {
                    611:                    news->state = (news->repcode/100 == 2) ?
                    612:                        NEWS_SUCCESS : NEWS_ERROR;
                    613:                } else
                    614:                    news->state = NEWS_ERROR;
                    615:                news->sent = NO;
                    616:            }
                    617:            break;
                    618: 
2.45      frystyk   619: #if HT_LISTGROUP
2.39      frystyk   620:          case NEWS_NEED_LGRP:
                    621:            if (!news->sent) {
                    622:                status = SendCommand(request, news, "LIST", "NEWSGROUPS");
                    623:                if (status == HT_WOULD_BLOCK)
                    624:                    return HT_OK;
                    625:                else if (status == HT_ERROR)
                    626:                    news->state = NEWS_ERROR;
                    627:                news->format = WWW_NNTP_LIST;
                    628:                news->sent = YES;
                    629:            } else {
                    630:                status = HTSocketRead(request, net);
                    631:                if (status == HT_WOULD_BLOCK)
                    632:                    return HT_OK;
                    633:                else if (status == HT_OK)
                    634:                    news->state = NEWS_NEED_BODY;
                    635:                else if (status == HT_LOADED) {
                    636:                    news->state = (news->repcode/100 == 2) ?
                    637:                        NEWS_SUCCESS : NEWS_NEED_LIST;
                    638:                } else
                    639:                    news->state = NEWS_ERROR;
                    640:                news->sent = NO;
                    641:            }
                    642:            break;
2.45      frystyk   643: #endif /* HT_LISTGROUP */
2.39      frystyk   644: 
                    645:          case NEWS_NEED_LIST:
                    646:            if (!news->sent) {
                    647:                status = SendCommand(request, news, "LIST", NULL);
                    648:                if (status == HT_WOULD_BLOCK)
                    649:                    return HT_OK;
                    650:                else if (status == HT_ERROR)
                    651:                    news->state = NEWS_ERROR;
                    652:                news->format = WWW_NNTP_LIST;
                    653:                news->sent = YES;
                    654:            } else {
                    655:                status = HTSocketRead(request, net);
                    656:                if (status == HT_WOULD_BLOCK)
                    657:                    return HT_OK;
                    658:                else if (status == HT_OK)
                    659:                    news->state = NEWS_NEED_BODY;
                    660:                else if (status == HT_LOADED) {
                    661:                    news->state = (news->repcode/100 == 2) ?
                    662:                        NEWS_SUCCESS : NEWS_ERROR;
                    663:                } else
                    664:                    news->state = NEWS_ERROR;
                    665:                news->sent = NO;
                    666:            }
                    667:            break;
1.1       timbl     668: 
2.39      frystyk   669:          case NEWS_NEED_GROUP:
                    670:            if (!news->sent) {
                    671:                status = SendCommand(request, news, "GROUP", news->name);
                    672:                if (status == HT_WOULD_BLOCK)
                    673:                    return HT_OK;
                    674:                else if (status == HT_ERROR)
                    675:                    news->state = NEWS_ERROR;
                    676:                news->sent = YES;
1.1       timbl     677:            } else {
2.39      frystyk   678:                status = HTSocketRead(request, net);
                    679:                if (status == HT_WOULD_BLOCK)
                    680:                    return HT_OK;
                    681:                else if (status == HT_LOADED) {
                    682:                    if (news->repcode/100 == 2) {
                    683:                        if (sscanf(news->reply, "%d%d%d", &news->total,
                    684:                                   &news->first, &news->last) == 3) {
2.45      frystyk   685:                            if (MaxArt && news->total>MaxArt)
                    686:                                news->last = news->first-MaxArt;
2.39      frystyk   687:                            news->current = news->first;
                    688:                            news->state = NEWS_NEED_XOVER;
                    689:                        } else
                    690:                            news->state = NEWS_ERROR;
                    691:                    } else
                    692:                        news->state = NEWS_ERROR;
                    693:                } else
                    694:                    news->state = NEWS_ERROR;
                    695:                news->sent = NO;
1.1       timbl     696:            }
2.39      frystyk   697:            break;
1.1       timbl     698: 
2.39      frystyk   699:          case NEWS_NEED_XOVER:
                    700:            if (!news->sent) {
                    701:                char buf[20];
                    702:                sprintf(buf, "%d-%d", news->first, news->last);
                    703:                status = SendCommand(request, news, "XOVER", buf);
                    704:                if (status == HT_WOULD_BLOCK)
                    705:                    return HT_OK;
                    706:                else if (status == HT_ERROR)
                    707:                    news->state = NEWS_ERROR;
                    708:                news->format = WWW_NNTP_OVER;
                    709:                news->sent = YES;
                    710:            } else {
                    711:                status = HTSocketRead(request, net);
                    712:                if (status == HT_WOULD_BLOCK)
                    713:                    return HT_OK;
                    714:                else if (status == HT_OK)
                    715:                    news->state = NEWS_NEED_BODY;
                    716:                else if (status == HT_LOADED) {
                    717:                    if (news->repcode/100 == 2)
                    718:                        news->state = NEWS_SUCCESS;
                    719:                    else {
                    720:                        news->format = WWW_NNTP_HEAD;
                    721:                        news->state = NEWS_NEED_HEAD;
                    722:                    }
                    723:                } else
                    724:                    news->state = NEWS_ERROR;
                    725:                news->sent = NO;
                    726:            }
                    727:            break;
1.1       timbl     728: 
2.39      frystyk   729:          case NEWS_NEED_HEAD:
                    730:            if (!news->sent) {
                    731:                char buf[10];
                    732:                sprintf(buf, "%d", news->current++);
                    733:                status = SendCommand(request, news, "HEAD", buf);
                    734:                if (status == HT_WOULD_BLOCK)
2.36      frystyk   735:                    return HT_OK;
2.39      frystyk   736:                else if (status == HT_ERROR)
                    737:                    news->state = NEWS_ERROR;
                    738:                news->sent = YES;
1.1       timbl     739:            } else {
2.39      frystyk   740:                status = HTSocketRead(request, net);
                    741:                if (status == HT_WOULD_BLOCK)
                    742:                    return HT_OK;
                    743:                else if (status == HT_LOADED) {
                    744:                    if (news->repcode/100 == 2) {
                    745:                        if (news->current > news->last)
                    746:                            news->state = NEWS_SUCCESS;
                    747:                    } else
                    748:                        news->state = NEWS_ERROR;
                    749:                } else
                    750:                    news->state = NEWS_ERROR;
                    751:                news->sent = NO;
                    752:            }
                    753:            break;
                    754: 
                    755:          case NEWS_NEED_POST:
                    756:            request->input_stream =
                    757:                HTNewsPost_new(request, HTBuffer_new(request->input_stream,
                    758:                                                     request, 512));
                    759: 
                    760:            /* Remember to convert to CRLF */
                    761: 
                    762:            news->state = NEWS_NEED_BODY;
                    763:            break;
                    764: 
                    765:           case NEWS_NEED_BODY:
                    766:             if (ops == FD_WRITE || ops == FD_NONE) {
                    767:                if (HTRequest_isDestination(request)) {
                    768:                    HTNet *srcnet = request->source->net;
                    769:                    HTEvent_Register(srcnet->sockfd, request->source,
                    770:                                     (SockOps) FD_READ,
                    771:                                     HTLoadNews, srcnet->priority);
2.36      frystyk   772:                    return HT_OK;
2.21      frystyk   773:                }
2.41      frystyk   774:                status = request->PostCallback ?
                    775:                     request->PostCallback(request, request->input_stream) :
2.39      frystyk   776:                        (*request->input_stream->isa->flush)(request->input_stream);
                    777:                if (status == HT_WOULD_BLOCK)
                    778:                     return HT_OK;
                    779:                 else   
                    780:                     ops = FD_READ;       /* Trick to ensure that we do READ */
                    781:            } else if (ops == FD_READ) {
                    782:                 status = HTSocketRead(request, net);
                    783:                if (status == HT_WOULD_BLOCK)
                    784:                    return HT_OK;
                    785:                 else if (status == HT_LOADED)
                    786:                    news->state = NEWS_SUCCESS;
                    787:                else
                    788:                    news->state = NEWS_ERROR;
                    789:            } else {
                    790:                news->state = NEWS_ERROR;
1.1       timbl     791:            }
2.39      frystyk   792:            break;
                    793:                
                    794:          case NEWS_SUCCESS:
                    795:            if (HTRequest_isPostWeb(request)) {
                    796:                BOOL main = HTRequest_isMainDestination(request);
                    797:                if (HTRequest_isDestination(request)) {
                    798:                    HTLink *link =
                    799:                        HTAnchor_findLink((HTAnchor *) request->source->anchor,
                    800:                                          (HTAnchor *) request->anchor);
2.47      frystyk   801:                    HTLink_setResult(link, HT_LINK_OK);
2.39      frystyk   802:                }
                    803:                HTRequest_removeDestination(request);
                    804:                HTNewsCleanup(request, main ? HT_LOADED : HT_IGNORE);
                    805:            } else
                    806:                HTNewsCleanup(request, HT_LOADED);
                    807:            return HT_OK;
                    808:            break;
                    809:            
                    810:          case NEWS_ERROR:
                    811:            /* Clean up the other connections or just this one */
                    812:            if (HTRequest_isPostWeb(request)) {
                    813:                BOOL main = HTRequest_isMainDestination(request);
                    814:                HTRequest_killPostWeb(request);
                    815:                if (HTRequest_isDestination(request)) {
                    816:                    HTLink *link =
                    817:                        HTAnchor_findLink((HTAnchor *) request->source->anchor,
                    818:                                          (HTAnchor *) request->anchor);
2.47      frystyk   819:                    HTLink_setResult(link, HT_LINK_ERROR);
2.39      frystyk   820:                }
                    821:                HTRequest_removeDestination(request);
                    822:                HTNewsCleanup(request, main ? HT_ERROR : HT_IGNORE);
                    823:            } else
                    824:                HTNewsCleanup(request, HT_ERROR);
                    825:            return HT_OK;
                    826:            break;
1.1       timbl     827:        }
2.39      frystyk   828:     } /* End of while(1) */
1.1       timbl     829: }

Webmaster