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

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.40      frystyk    52: #define MAX_NEWS_ARTICLES      100                /* Max number of articles */
                     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;
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.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);
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.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)
2.42      frystyk   308:            TTYPrint(TDEST, "SetNewsHost. Host name is `%s\'\n", HTNewsHost);
2.39      frystyk   309:        return YES;
                    310:     } else {
                    311:        if (PROT_TRACE)
2.42      frystyk   312:            TTYPrint(TDEST, "SetNewsHost. Bad argument ignored\n");
2.39      frystyk   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)
2.42      frystyk   337:                TTYPrint(TDEST, "GetNewsHost. found as `%s\'\n", HTNewsHost);
2.39      frystyk   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);
2.42      frystyk   423:     if (PROT_TRACE) TTYPrint(TDEST, "News Tx..... %s", HTChunkData(news->cmd));
2.39      frystyk   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) 
2.42      frystyk   457:            TTYPrint(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 {
2.42      frystyk   500:                if (PROT_TRACE) TTYPrint(TDEST, "News........ Huh?");
2.39      frystyk   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)
2.42      frystyk   518:                    TTYPrint(TDEST, "News........ Connected, socket %d\n",
2.39      frystyk   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.41      frystyk   770:                status = request->PostCallback ?
                    771:                     request->PostCallback(request, request->input_stream) :
2.39      frystyk   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