Annotation of libwww/Library/src/HTNews.c, revision 2.58
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.52 frystyk 18: #include "sysdep.h"
2.54 frystyk 19: #include "WWWUtil.h"
20: #include "WWWCore.h"
2.55 frystyk 21: #include "WWWStream.h"
2.54 frystyk 22: #include "HTTCP.h"
2.43 frystyk 23: #include "HTReqMan.h" /* @@@ */
24: #include "HTNetMan.h" /* @@@ */
2.39 frystyk 25: #include "HTNewsRq.h"
26: #include "HTNews.h" /* Implements */
27:
28: /* Macros and other defines */
29: #ifndef NEWS_PORT
30: #define NEWS_PORT 119 /* See rfc977 */
31: #endif
1.3 timbl 32:
2.39 frystyk 33: #ifndef NEWS_LIST_FILE
34: #define NEWS_LIST_FILE ".www_news" /* Name of news list file */
35: #endif
1.1 timbl 36:
2.45 frystyk 37: #define MAX_NEWS_ARTICLES 0 /* No default max number of articles */
2.40 frystyk 38:
2.39 frystyk 39: #define PUTBLOCK(b, l) (*me->target->isa->put_block) (me->target, b, l)
40: #define ABORT_TARGET (*me->target->isa->abort) (me->target, e)
2.8 timbl 41:
2.39 frystyk 42: /* Local context structure used in the HTNet object */
43: typedef enum _HTNewsState {
44: NEWS_BEGIN,
45: NEWS_SEEK_CACHE,
46: NEWS_NEED_CONNECTION,
47: NEWS_NEED_GREETING,
48: NEWS_NEED_SWITCH,
49: NEWS_NEED_ARTICLE,
2.45 frystyk 50: #if HT_LISTGROUP
2.39 frystyk 51: NEWS_NEED_LGRP,
52: #endif
53: NEWS_NEED_LIST,
54: NEWS_NEED_GROUP,
55: NEWS_NEED_XOVER,
56: NEWS_NEED_HEAD,
57: NEWS_NEED_POST,
58: NEWS_NEED_BODY,
59: NEWS_ERROR,
60: NEWS_SUCCESS
61: } HTNewsState;
62:
63: typedef struct _news_info {
64: HTChunk * cmd;
65: int repcode;
66: char * reply;
67: HTNewsState state; /* State of the connection */
68: HTFormat format;
69: char * name; /* Name of article or newsgroup */
70: BOOL sent; /* Read or write command */
71: int first; /* First article in the group list */
72: int last; /* Last article in the group list */
73: int total; /* Estimated number of articles */
74: int current; /* To count # of HEADS sent */
75: } news_info;
76:
77: /* This version of a stream is used for NEWS LIST conversion to HTML */
78: struct _HTStream {
2.52 frystyk 79: const HTStreamClass * isa;
2.39 frystyk 80: HTStream * target;
81: HTRequest * request;
82: news_info * news;
2.53 frystyk 83: HTEOLState EOLstate;
2.39 frystyk 84: BOOL semi_trans;
85: BOOL junk;
86: char buffer[MAX_NEWS_LINE+1];
87: int buflen;
1.2 timbl 88: };
89:
2.54 frystyk 90: struct _HTInputStream {
91: const HTInputStreamClass * isa;
92: };
93:
2.45 frystyk 94: PRIVATE int MaxArt = MAX_NEWS_ARTICLES;
1.1 timbl 95:
2.39 frystyk 96: /* ------------------------------------------------------------------------- */
97: /* NEWS INPUT STREAM */
98: /* ------------------------------------------------------------------------- */
99:
100: /* ScanResponse
101: ** ------------
102: ** Analyzes the response from the NNTP server.
103: ** We only expect one line response codes.
104: ** Returns HT_LOADED if OK, HT_ERROR if error
105: */
106: PRIVATE int ScanResponse (HTStream * me)
107: {
108: news_info *news = me->news;
109: *(me->buffer+me->buflen) = '\0';
110: if (isdigit(*(me->buffer))) sscanf(me->buffer, "%d", &news->repcode);
111: me->buflen = 0;
112: news->reply = me->buffer+4;
2.51 eric 113: if (PROT_TRACE) HTTrace("News Rx..... `%s\'\n", news->reply);
2.39 frystyk 114:
115: /* If 2xx code and we expect data then go into semi-transparent mode */
116: if (me->news->format && news->repcode/100 == 2) {
117: HTRequest *req = me->request;
118: me->target = HTStreamStack(me->news->format, req->output_format,
119: req->output_stream, req, NO);
120: me->semi_trans = YES;
121: if (!me->target) return HT_ERROR;
122: }
123: return HT_LOADED;
1.2 timbl 124: }
1.1 timbl 125:
2.39 frystyk 126: /*
127: ** Searches for NNTP header line until buffer fills up or a CRLF or LF
128: ** is found
1.1 timbl 129: */
2.52 frystyk 130: PRIVATE int HTNewsStatus_put_block (HTStream * me, const char * b, int l)
1.1 timbl 131: {
2.39 frystyk 132: while (!me->semi_trans && l-- > 0) {
133: int status;
134: if (me->EOLstate == EOL_FCR) {
135: if (*b == LF) {
136: if (me->junk) me->junk = NO;
137: me->EOLstate = EOL_BEGIN;
138: if ((status = ScanResponse(me)) != HT_LOADED) return status;
139: }
140: } else if (*b == CR) {
141: me->EOLstate = EOL_FCR;
142: } else if (*b == LF) {
143: if (me->junk) me->junk = NO;
144: me->EOLstate = EOL_BEGIN;
145: if ((status = ScanResponse(me)) != HT_LOADED) return status;
146: } else {
147: *(me->buffer+me->buflen++) = *b;
148: if (me->buflen >= MAX_NEWS_LINE) {
149: if (PROT_TRACE)
2.51 eric 150: HTTrace("News Status. Line too long - chopped\n");
2.39 frystyk 151: me->junk = YES;
152: if ((status = ScanResponse(me)) != HT_LOADED) return status;
1.1 timbl 153: }
154: }
2.39 frystyk 155: b++;
156: }
1.1 timbl 157:
2.39 frystyk 158: /*
159: ** Now see if we have parts of the body to put down the stream pipe.
160: ** At this point we are looking for CRLF.CRLF. We are guaranteed a stream
161: */
162: if (l > 0) {
163: int rest = l;
2.52 frystyk 164: const char *ptr = b;
2.39 frystyk 165: while (rest-- > 0) {
166: if (*ptr == CR) {
167: me->EOLstate = me->EOLstate==EOL_DOT ? EOL_SCR : EOL_FCR;
168: } else if (*ptr == '.') {
169: me->EOLstate = me->EOLstate==EOL_FLF ? EOL_DOT : EOL_BEGIN;
170: } else if (*ptr == LF) {
171: me->EOLstate = me->EOLstate>EOL_DOT ? EOL_SLF : EOL_FLF;
172: } else
173: me->EOLstate = EOL_BEGIN;
174: ptr++;
175: }
176: if (me->EOLstate == EOL_SLF) {
177: int status = PUTBLOCK(b, l-5);
178: return status != HT_OK ? status : HT_LOADED;
179: } else {
180: int status = PUTBLOCK(b, l);
181: return status;
1.1 timbl 182: }
183: }
2.39 frystyk 184: return HT_LOADED;
1.1 timbl 185: }
186:
2.39 frystyk 187: PRIVATE int HTNewsStatus_put_character (HTStream * me, char ch)
1.1 timbl 188: {
2.39 frystyk 189: return HTNewsStatus_put_block(me, &ch, 1);
1.1 timbl 190: }
191:
2.52 frystyk 192: PRIVATE int HTNewsStatus_put_string (HTStream * me, const char * str)
1.1 timbl 193: {
2.39 frystyk 194: return HTNewsStatus_put_block(me, str, (int) strlen(str));
1.1 timbl 195: }
196:
2.39 frystyk 197: PRIVATE int HTNewsStatus_flush (HTStream * me)
1.1 timbl 198: {
2.39 frystyk 199: return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
1.1 timbl 200: }
201:
2.39 frystyk 202: PRIVATE int HTNewsStatus_free (HTStream * me)
1.2 timbl 203: {
2.39 frystyk 204: int status = HT_OK;
205: if (me->target) {
206: if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
207: return HT_WOULD_BLOCK;
1.2 timbl 208: }
2.50 frystyk 209: HT_FREE(me);
2.39 frystyk 210: return status;
1.2 timbl 211: }
1.1 timbl 212:
2.44 frystyk 213: PRIVATE int HTNewsStatus_abort (HTStream * me, HTList * e)
2.16 luotonen 214: {
2.39 frystyk 215: if (me->target)
216: ABORT_TARGET;
2.50 frystyk 217: HT_FREE(me);
2.51 eric 218: if (PROT_TRACE) HTTrace("NewsStatus.. ABORTING...\n");
2.39 frystyk 219: return HT_ERROR;
2.16 luotonen 220: }
221:
2.52 frystyk 222: PRIVATE const HTStreamClass HTNewsStatusClass =
2.39 frystyk 223: {
224: "NewsStatus",
225: HTNewsStatus_flush,
226: HTNewsStatus_free,
227: HTNewsStatus_abort,
228: HTNewsStatus_put_character,
229: HTNewsStatus_put_string,
230: HTNewsStatus_put_block
231: };
2.16 luotonen 232:
2.39 frystyk 233: PUBLIC HTStream *HTNewsStatus_new (HTRequest * request, news_info * news)
1.1 timbl 234: {
2.50 frystyk 235: HTStream *me;
236: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
237: HT_OUTOFMEM("HTNewsStatus_new");
2.39 frystyk 238: me->isa = &HTNewsStatusClass;
239: me->request = request;
240: me->news = news;
241: me->EOLstate = EOL_BEGIN;
242: return me;
1.1 timbl 243: }
244:
2.39 frystyk 245: /* ------------------------------------------------------------------------- */
246: /* PROTOCOL FUNCTIONS */
247: /* ------------------------------------------------------------------------- */
1.1 timbl 248:
2.39 frystyk 249: /*
250: ** Set the max number of articles at the same time.
251: ** Default is MAX_NEWS_ARTICLES
1.1 timbl 252: */
2.39 frystyk 253: PUBLIC BOOL HTNews_setMaxArticles (int new_max)
254: {
2.45 frystyk 255: if (new_max >= 0) {
256: MaxArt = new_max;
2.39 frystyk 257: return YES;
1.1 timbl 258: }
2.39 frystyk 259: return NO;
1.1 timbl 260: }
261:
2.39 frystyk 262: /*
263: ** Get current max number of articles at the same time.
1.1 timbl 264: */
2.39 frystyk 265: PUBLIC int HTNews_maxArticles (void)
266: {
2.45 frystyk 267: return MaxArt;
1.1 timbl 268: }
269:
2.39 frystyk 270: /* HTNewsCleanup
271: ** -------------
272: ** This function closes the connection and frees memory.
273: ** Returns YES on OK, else NO
1.1 timbl 274: */
2.39 frystyk 275: PRIVATE int HTNewsCleanup (HTRequest * req, int status)
276: {
277: HTNet *net = req->net;
278: news_info *news = (news_info *) net->context;
1.1 timbl 279:
2.39 frystyk 280: /* Free stream with data TO network */
281: if (!HTRequest_isDestination(req) && req->input_stream) {
282: if (status == HT_INTERRUPTED)
283: (*req->input_stream->isa->abort)(req->input_stream, NULL);
284: else
285: (*req->input_stream->isa->_free)(req->input_stream);
1.1 timbl 286: }
2.16 luotonen 287:
2.39 frystyk 288: /* Remove the request object and our own context structure for nntp */
289: HTNet_delete(net, status);
2.49 frystyk 290: if (news) {
2.50 frystyk 291: HT_FREE(news->name);
2.49 frystyk 292: HTChunk_delete(news->cmd);
2.50 frystyk 293: HT_FREE(news);
2.49 frystyk 294: }
2.39 frystyk 295: return YES;
1.1 timbl 296: }
297:
2.39 frystyk 298: PRIVATE int SendCommand (HTRequest *request, news_info *news,
299: char *token, char *pars)
1.1 timbl 300: {
2.39 frystyk 301: int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2;
2.48 frystyk 302: HTChunk_clear(news->cmd);
303: HTChunk_ensure(news->cmd, len);
2.39 frystyk 304: if (pars && *pars)
2.48 frystyk 305: sprintf(HTChunk_data(news->cmd), "%s %s%c%c", token, pars, CR, LF);
2.39 frystyk 306: else
2.48 frystyk 307: sprintf(HTChunk_data(news->cmd), "%s%c%c", token, CR, LF);
2.51 eric 308: if (PROT_TRACE) HTTrace("News Tx..... %s", HTChunk_data(news->cmd));
2.39 frystyk 309: return (*request->input_stream->isa->put_block)
2.48 frystyk 310: (request->input_stream, HTChunk_data(news->cmd), len);
2.39 frystyk 311: }
312:
313: /* Load data object from NNTP Server HTLoadNews
314: ** =================================
315: **
316: ** Given a hypertext addres, this routine loads a document
317: **
318: ** On Entry,
319: ** request The request structure
320: **
321: ** returns HT_ERROR Error has occured or interrupted
322: ** HT_WOULD_BLOCK if operation would have blocked
323: ** HT_LOADED if 200 OK
324: ** HT_NO_DATA if No Response
325: ** HT_RETRY if Service Unavail.
326: */
327: PUBLIC int HTLoadNews (SOCKET soc, HTRequest * request, SockOps ops)
328: {
329: int status = HT_ERROR;
330: HTNet *net = request->net;
331: HTParentAnchor *anchor = HTRequest_anchor(request);
332: char *url = HTAnchor_physical(anchor);
333: news_info *news;
334:
335: /*
336: ** Initiate a new nntp structure and bind to request structure
337: ** This is actually state NNTP_BEGIN, but it can't be in the state
338: ** machine as we need the structure first.
339: */
2.36 frystyk 340: if (ops == FD_NONE) {
2.39 frystyk 341: if (PROT_TRACE)
2.51 eric 342: HTTrace("NNTP........ Looking for `%s\'\n", url);
2.50 frystyk 343: if ((news = (news_info *) HT_CALLOC(1, sizeof(news_info))) == NULL)
344: HT_OUTOFMEM("HTLoadNews");
2.48 frystyk 345: news->cmd = HTChunk_new(128);
2.36 frystyk 346: news->state = NEWS_BEGIN;
347: net->context = news;
2.39 frystyk 348: } else if (ops == FD_CLOSE) { /* Interrupted */
349: if(HTRequest_isPostWeb(request)&&!HTRequest_isMainDestination(request))
350: HTNewsCleanup(request, HT_IGNORE);
351: else
352: HTNewsCleanup(request, HT_INTERRUPTED);
2.36 frystyk 353: return HT_OK;
354: } else
2.39 frystyk 355: news = (news_info *) net->context; /* Get existing copy */
356:
357: /* Now start the state machine */
358: while (1) {
359: switch (news->state) {
360: case NEWS_BEGIN:
361: news->state = (!strchr(url, '@') && strchr(url, '*')) ?
362: NEWS_SEEK_CACHE : NEWS_NEED_CONNECTION;
363: break;
364:
365: case NEWS_SEEK_CACHE: /* @@@ DO THIS @@@@@@ */
366: news->state = NEWS_NEED_CONNECTION;
367: break;
368:
369: case NEWS_NEED_CONNECTION: /* Let's set up a connection */
370: if (!strncasecomp(url, "news:", 5)) {
2.57 frystyk 371: HTUserProfile * up = HTRequest_userProfile(request);
372: char * newshost = HTUserProfile_news(up);
2.39 frystyk 373: StrAllocCopy(news->name, url+5);
374: if (newshost) {
375: char *newshack = NULL; /* Then we can use HTParse :-) */
376: StrAllocCopy(newshack, "news://");
377: StrAllocCat(newshack, newshost);
378: status = HTDoConnect(net, (char *) newshack, NEWS_PORT);
2.50 frystyk 379: HT_FREE(newshack);
2.39 frystyk 380: } else
381: news->state = NEWS_ERROR;
382: } else if (!strncasecomp(url, "nntp:", 5)) {
383: news->name = HTParse(url, "", PARSE_PATH);
384: status = HTDoConnect(net, url, NEWS_PORT);
385: } else {
2.51 eric 386: if (PROT_TRACE) HTTrace("News........ Huh?");
2.39 frystyk 387: news->state = NEWS_ERROR;
388: }
389: if (status == HT_OK) {
390: BOOL greeting = NO;
2.54 frystyk 391:
392: /* Set up the persistent connection */
2.53 frystyk 393: if (!HTNet_persistent(net)) {
394: HTNet_setPersistent(net, YES);
2.39 frystyk 395: greeting = YES;
396: }
2.36 frystyk 397:
2.39 frystyk 398: /*
2.54 frystyk 399: ** Check the protocol class to see if we have connected to a
400: ** the right class of server, in this case HTTP.
401: */
402: {
403: HTHost * host = HTNet_host(net);
404: char * s_class = HTHost_class(host);
405: if (s_class && strcasecomp(s_class, "nntp")) {
406: HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS,
407: NULL, 0, "HTLoadNews");
408: news->state = NEWS_ERROR;
409: break;
410: }
411: HTHost_setClass(host, "nntp");
412: }
413:
414: /*
415: ** Create the stream pipe FROM the channel to the application.
416: ** The target for the input stream pipe is set up using the
417: ** stream stack.
418: */
419: HTNet_getInput(net, HTNewsStatus_new(request, news), NULL, 0);
2.58 ! frystyk 420: HTRequest_setOutputConnected(request, YES);
2.54 frystyk 421:
422: /*
423: ** Create the stream pipe TO the channel from the application
424: ** and hook it up to the request object
425: */
426: {
427: HTOutputStream * output = HTNet_getOutput(net, NULL, 0);
428: HTRequest_setInputStream(request, (HTStream *) output);
429: }
430:
431: /*
2.39 frystyk 432: ** Set up concurrent read/write if this request isn't the
433: ** source for a PUT or POST. As source we don't start reading
434: ** before all destinations are ready. If destination then
435: ** register the input stream and get ready for read
436: */
437: if (HTRequest_isPostWeb(request)) {
2.56 eric 438: HTEvent_register(net->sockfd, request, (SockOps) FD_READ,
2.39 frystyk 439: HTLoadNews, net->priority);
440: HTRequest_linkDestination(request);
441: }
2.54 frystyk 442: news->state = greeting ? NEWS_NEED_GREETING : NEWS_NEED_SWITCH;
2.39 frystyk 443:
444: } else if (status == HT_WOULD_BLOCK || status == HT_PERSISTENT)
445: return HT_OK;
446: else
447: news->state = NEWS_ERROR;
448: break;
449:
450: case NEWS_NEED_GREETING:
2.54 frystyk 451: status = (*net->input->isa->read)(net->input);
2.39 frystyk 452: if (status == HT_WOULD_BLOCK)
453: return HT_OK;
454: else if (status == HT_LOADED) {
455: if (news->repcode/100 == 2)
456: news->state = NEWS_NEED_SWITCH;
457: else
458: news->state = NEWS_ERROR;
459: } else
460: news->state = NEWS_ERROR;
461: break;
462:
463: case NEWS_NEED_SWITCH:
464: /*
465: ** Find out what to ask the news server. Syntax of address is
466: ** xxx@yyy Article
467: ** <xxx@yyy> Same article
468: ** xxxxx News group (no "@")
469: */
470: if (request->method == METHOD_GET) {
471: if (strchr(url, '@')) { /* ARTICLE */
472: if (*(news->name) != '<') { /* Add '<' and '>' */
2.50 frystyk 473: char *newart;
474: if ((newart = (char *) HT_MALLOC(strlen(news->name)+3)) == NULL)
475: HT_OUTOFMEM("HTLoadNews");
2.39 frystyk 476: sprintf(newart, "<%s>", news->name);
2.50 frystyk 477: HT_FREE(news->name);
2.39 frystyk 478: news->name = newart;
479: }
480: news->state = NEWS_NEED_ARTICLE;
481: } else if (strchr(url, '*'))
482: news->state = NEWS_NEED_LIST;
483: else
484: news->state = NEWS_NEED_GROUP;
485: } else if (request->method == METHOD_POST)
486: news->state = NEWS_NEED_POST;
487: else {
2.46 frystyk 488: HTRequest_addError(request, ERR_FATAL, NO,
489: HTERR_NOT_IMPLEMENTED,NULL, 0,"HTLoadNews");
2.39 frystyk 490: news->state = NEWS_ERROR;
491: }
492: HTUnEscape(news->name);
493: HTCleanTelnetString(news->name);
494: break;
495:
496: case NEWS_NEED_ARTICLE:
497: if (!news->sent) {
498: status = SendCommand(request, news, "ARTICLE", news->name);
499: if (status == HT_WOULD_BLOCK)
500: return HT_OK;
501: else if (status == HT_ERROR)
502: news->state = NEWS_ERROR;
503: news->format = WWW_MIME;
504: news->sent = YES;
505: } else {
2.54 frystyk 506: status = (*net->input->isa->read)(net->input);
2.39 frystyk 507: if (status == HT_WOULD_BLOCK)
508: return HT_OK;
509: else if (status == HT_OK)
510: news->state = NEWS_NEED_BODY;
511: else if (status == HT_LOADED) {
512: news->state = (news->repcode/100 == 2) ?
513: NEWS_SUCCESS : NEWS_ERROR;
514: } else
515: news->state = NEWS_ERROR;
516: news->sent = NO;
517: }
518: break;
519:
2.45 frystyk 520: #if HT_LISTGROUP
2.39 frystyk 521: case NEWS_NEED_LGRP:
522: if (!news->sent) {
523: status = SendCommand(request, news, "LIST", "NEWSGROUPS");
524: if (status == HT_WOULD_BLOCK)
525: return HT_OK;
526: else if (status == HT_ERROR)
527: news->state = NEWS_ERROR;
528: news->format = WWW_NNTP_LIST;
529: news->sent = YES;
530: } else {
2.54 frystyk 531: status = (*net->input->isa->read)(net->input);
2.39 frystyk 532: if (status == HT_WOULD_BLOCK)
533: return HT_OK;
534: else if (status == HT_OK)
535: news->state = NEWS_NEED_BODY;
536: else if (status == HT_LOADED) {
537: news->state = (news->repcode/100 == 2) ?
538: NEWS_SUCCESS : NEWS_NEED_LIST;
539: } else
540: news->state = NEWS_ERROR;
541: news->sent = NO;
542: }
543: break;
2.45 frystyk 544: #endif /* HT_LISTGROUP */
2.39 frystyk 545:
546: case NEWS_NEED_LIST:
547: if (!news->sent) {
548: status = SendCommand(request, news, "LIST", NULL);
549: if (status == HT_WOULD_BLOCK)
550: return HT_OK;
551: else if (status == HT_ERROR)
552: news->state = NEWS_ERROR;
553: news->format = WWW_NNTP_LIST;
554: news->sent = YES;
555: } else {
2.54 frystyk 556: status = (*net->input->isa->read)(net->input);
2.39 frystyk 557: if (status == HT_WOULD_BLOCK)
558: return HT_OK;
559: else if (status == HT_OK)
560: news->state = NEWS_NEED_BODY;
561: else if (status == HT_LOADED) {
562: news->state = (news->repcode/100 == 2) ?
563: NEWS_SUCCESS : NEWS_ERROR;
564: } else
565: news->state = NEWS_ERROR;
566: news->sent = NO;
567: }
568: break;
1.1 timbl 569:
2.39 frystyk 570: case NEWS_NEED_GROUP:
571: if (!news->sent) {
572: status = SendCommand(request, news, "GROUP", news->name);
573: if (status == HT_WOULD_BLOCK)
574: return HT_OK;
575: else if (status == HT_ERROR)
576: news->state = NEWS_ERROR;
577: news->sent = YES;
1.1 timbl 578: } else {
2.54 frystyk 579: status = (*net->input->isa->read)(net->input);
2.39 frystyk 580: if (status == HT_WOULD_BLOCK)
581: return HT_OK;
582: else if (status == HT_LOADED) {
583: if (news->repcode/100 == 2) {
584: if (sscanf(news->reply, "%d%d%d", &news->total,
585: &news->first, &news->last) == 3) {
2.45 frystyk 586: if (MaxArt && news->total>MaxArt)
587: news->last = news->first-MaxArt;
2.39 frystyk 588: news->current = news->first;
589: news->state = NEWS_NEED_XOVER;
590: } else
591: news->state = NEWS_ERROR;
592: } else
593: news->state = NEWS_ERROR;
594: } else
595: news->state = NEWS_ERROR;
596: news->sent = NO;
1.1 timbl 597: }
2.39 frystyk 598: break;
1.1 timbl 599:
2.39 frystyk 600: case NEWS_NEED_XOVER:
601: if (!news->sent) {
602: char buf[20];
603: sprintf(buf, "%d-%d", news->first, news->last);
604: status = SendCommand(request, news, "XOVER", buf);
605: if (status == HT_WOULD_BLOCK)
606: return HT_OK;
607: else if (status == HT_ERROR)
608: news->state = NEWS_ERROR;
609: news->format = WWW_NNTP_OVER;
610: news->sent = YES;
611: } else {
2.54 frystyk 612: status = (*net->input->isa->read)(net->input);
2.39 frystyk 613: if (status == HT_WOULD_BLOCK)
614: return HT_OK;
615: else if (status == HT_OK)
616: news->state = NEWS_NEED_BODY;
617: else if (status == HT_LOADED) {
618: if (news->repcode/100 == 2)
619: news->state = NEWS_SUCCESS;
620: else {
621: news->format = WWW_NNTP_HEAD;
622: news->state = NEWS_NEED_HEAD;
623: }
624: } else
625: news->state = NEWS_ERROR;
626: news->sent = NO;
627: }
628: break;
1.1 timbl 629:
2.39 frystyk 630: case NEWS_NEED_HEAD:
631: if (!news->sent) {
632: char buf[10];
633: sprintf(buf, "%d", news->current++);
634: status = SendCommand(request, news, "HEAD", buf);
635: if (status == HT_WOULD_BLOCK)
2.36 frystyk 636: return HT_OK;
2.39 frystyk 637: else if (status == HT_ERROR)
638: news->state = NEWS_ERROR;
639: news->sent = YES;
1.1 timbl 640: } else {
2.54 frystyk 641: status = (*net->input->isa->read)(net->input);
2.39 frystyk 642: if (status == HT_WOULD_BLOCK)
643: return HT_OK;
644: else if (status == HT_LOADED) {
645: if (news->repcode/100 == 2) {
646: if (news->current > news->last)
647: news->state = NEWS_SUCCESS;
648: } else
649: news->state = NEWS_ERROR;
650: } else
651: news->state = NEWS_ERROR;
652: news->sent = NO;
653: }
654: break;
655:
656: case NEWS_NEED_POST:
2.54 frystyk 657: {
658: HTStream * oldinput = HTRequest_inputStream(request);
659: HTStream * newinput =
660: HTNewsPost_new(request, HTBuffer_new(oldinput, request,512));
661: HTRequest_setInputStream(request, newinput);
662:
663: /* Remember to convert to CRLF */
2.39 frystyk 664:
2.54 frystyk 665: }
2.39 frystyk 666: news->state = NEWS_NEED_BODY;
667: break;
668:
669: case NEWS_NEED_BODY:
670: if (ops == FD_WRITE || ops == FD_NONE) {
671: if (HTRequest_isDestination(request)) {
672: HTNet *srcnet = request->source->net;
2.56 eric 673: HTEvent_register(srcnet->sockfd, request->source,
2.39 frystyk 674: (SockOps) FD_READ,
675: HTLoadNews, srcnet->priority);
2.36 frystyk 676: return HT_OK;
2.21 frystyk 677: }
2.41 frystyk 678: status = request->PostCallback ?
679: request->PostCallback(request, request->input_stream) :
2.39 frystyk 680: (*request->input_stream->isa->flush)(request->input_stream);
681: if (status == HT_WOULD_BLOCK)
682: return HT_OK;
683: else
684: ops = FD_READ; /* Trick to ensure that we do READ */
685: } else if (ops == FD_READ) {
2.54 frystyk 686: status = (*net->input->isa->read)(net->input);
2.39 frystyk 687: if (status == HT_WOULD_BLOCK)
688: return HT_OK;
689: else if (status == HT_LOADED)
690: news->state = NEWS_SUCCESS;
691: else
692: news->state = NEWS_ERROR;
693: } else {
694: news->state = NEWS_ERROR;
1.1 timbl 695: }
2.39 frystyk 696: break;
697:
698: case NEWS_SUCCESS:
699: if (HTRequest_isPostWeb(request)) {
700: BOOL main = HTRequest_isMainDestination(request);
701: if (HTRequest_isDestination(request)) {
702: HTLink *link =
2.58 ! frystyk 703: HTLink_find((HTAnchor *) request->source->anchor,
2.39 frystyk 704: (HTAnchor *) request->anchor);
2.47 frystyk 705: HTLink_setResult(link, HT_LINK_OK);
2.39 frystyk 706: }
707: HTRequest_removeDestination(request);
708: HTNewsCleanup(request, main ? HT_LOADED : HT_IGNORE);
709: } else
710: HTNewsCleanup(request, HT_LOADED);
711: return HT_OK;
712: break;
713:
714: case NEWS_ERROR:
715: /* Clean up the other connections or just this one */
716: if (HTRequest_isPostWeb(request)) {
717: BOOL main = HTRequest_isMainDestination(request);
718: HTRequest_killPostWeb(request);
719: if (HTRequest_isDestination(request)) {
720: HTLink *link =
2.58 ! frystyk 721: HTLink_find((HTAnchor *) request->source->anchor,
2.39 frystyk 722: (HTAnchor *) request->anchor);
2.47 frystyk 723: HTLink_setResult(link, HT_LINK_ERROR);
2.39 frystyk 724: }
725: HTRequest_removeDestination(request);
726: HTNewsCleanup(request, main ? HT_ERROR : HT_IGNORE);
727: } else
728: HTNewsCleanup(request, HT_ERROR);
729: return HT_OK;
730: break;
1.1 timbl 731: }
2.39 frystyk 732: } /* End of while(1) */
1.1 timbl 733: }
Webmaster