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