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