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