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

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

Webmaster