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