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