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