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