Annotation of libwww/Library/src/HTNews.c, revision 2.32.6.1
2.26 frystyk 1: /* HTNews.c
2: ** NEWS ACCESS
3: **
2.29 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.26 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
1.1 timbl 6: **
7: ** History:
8: ** 26 Sep 90 Written TBL
9: ** 29 Nov 91 Downgraded to C, for portable implementation.
2.19 luotonen 10: ** 16 Feb 94 AL Added Lou Montulli's Lynx & LIST NEWSGROUPS diffs.
11: ** 2 May 94 AL Added HTUnEscape() to HTLoadNews(), and
12: ** fixed a possible security hole when the URL contains
13: ** a newline, that could cause multiple commands to be
14: ** sent to an NNTP server.
2.23 duns 15: ** 8 Jul 94 FM Insulate free() from _free structure element.
2.32.6.1! frystyk 16: ** 30 Aug 94 HFN Cleaned up a whole lot and made a state machine
1.1 timbl 17: */
2.27 roeber 18:
2.32.6.1! frystyk 19: /* Implementation dependent include files */
2.28 frystyk 20: #include "tcp.h"
2.32.6.1! frystyk 21:
! 22: /* Library Include files */
! 23: #include "HTUtils.h"
2.28 frystyk 24: #include "HTML.h"
2.32.6.1! frystyk 25: #include "HTChunk.h"
2.28 frystyk 26: #include "HTParse.h"
27: #include "HTFormat.h"
2.32.6.1! frystyk 28: #include "HTTee.h"
! 29: #include "HTFWriter.h"
! 30: #include "HTMIME.h"
! 31: #include "HTTCP.h"
2.28 frystyk 32: #include "HTError.h"
2.32.6.1! frystyk 33: #include "HTArray.h"
! 34: #include "HTNews.h" /* Implements */
! 35:
! 36: /* Macros and other defines */
! 37: #ifndef NEWS_PORT
! 38: #define NEWS_PORT 119 /* See rfc977 */
! 39: #endif
1.3 timbl 40:
2.32.6.1! frystyk 41: #ifndef NEWS_LIST_FILE
! 42: #define NEWS_LIST_FILE ".www_news" /* Name of news list file */
! 43: #endif
1.1 timbl 44:
45: #ifndef DEFAULT_NEWS_HOST
2.32.6.1! frystyk 46: #define DEFAULT_NEWS_HOST "news"
1.1 timbl 47: #endif
2.32.6.1! frystyk 48:
1.1 timbl 49: #ifndef SERVER_FILE
2.32.6.1! frystyk 50: #define SERVER_FILE "/usr/local/lib/rn/server"
1.1 timbl 51: #endif
52:
2.32.6.1! frystyk 53: #define PUTC(c) (*target->isa->put_character) (target, c)
! 54: #define PUTS(s) (*target->isa->put_string) (target, s)
! 55: #define PUTBLOCK(b, l) (*target->isa->put_block) (target, b, l)
! 56: #define START(e) (*target->isa->start_element) (target, e, 0, 0)
! 57: #define END(e) (*target->isa->end_element) (target, e)
! 58: #define FREE_TARGET (*target->isa->_free) (target)
! 59:
! 60: /* Type definitions and global variables etc. local to this module */
! 61: typedef enum _HTNewsState {
! 62: NEWS_BEGIN,
! 63: NEWS_SEEK_CACHE_LIST,
! 64: NEWS_NEED_CONNECTION,
! 65: NEWS_NEED_REQUEST,
! 66: NEWS_SENT_REQUEST,
! 67: NEWS_NEED_DATA,
! 68: NEWS_ERROR,
! 69: NEWS_GOT_DATA
! 70: } HTNewsState;
! 71:
! 72: #define MAX_STATUS_LEN 75 /* Max nb of chars to check News Line */
! 73:
! 74: /* This is the local version of the HTNetInfo structure */
! 75: typedef struct _news_info {
! 76: SOCKFD sockfd; /* Socket descripter */
! 77: SockA sock_addr; /* SockA is defined in tcp.h */
! 78: HTInputSocket * isoc; /* Input buffer */
! 79: SocAction action; /* Result of the select call */
! 80: HTStream * target; /* Target stream */
! 81: int addressCount; /* Attempts if multi-homed host */
! 82: time_t connecttime; /* Used on multihomed hosts */
! 83: struct _HTRequest * request; /* Link back to request structure */
! 84:
! 85: HTNewsState state; /* State of the connection */
! 86: int listsent; /* Number of tries for LIST */
! 87: BOOL nntp; /* YES if we using a `nntp' URL */
! 88: int first; /* First article in the group list */
! 89: int last; /* Last article in the group list */
! 90: } news_info;
! 91:
! 92: /* This version of a stream is used for NEWS LIST conversion to HTML */
! 93: struct _HTStream {
! 94: CONST HTStreamClass * isa;
! 95: HTStructured * target;
! 96: HTRequest * request;
! 97: news_info * news;
! 98: HTSocketEOL state;
! 99: BOOL description; /* YES if LIST NEWSGROUPS */
! 100: BOOL semi_trans;
! 101: int status;
! 102: char * reason;
! 103: char buffer[MAX_STATUS_LEN+1];
! 104: int buflen;
! 105: };
2.8 timbl 106:
2.32.6.1! frystyk 107: /* We are creating a structured stream */
1.2 timbl 108: struct _HTStructured {
2.32.6.1! frystyk 109: CONST HTStructuredClass * isa;
! 110: /* ... */
1.2 timbl 111: };
112:
2.32.6.1! frystyk 113: PRIVATE char *HTNewsHost = NULL;
1.1 timbl 114:
2.32.6.1! frystyk 115: /* ------------------------------------------------------------------------- */
! 116: /* NEWS INPUT STREAM */
! 117: /* ------------------------------------------------------------------------- */
! 118: PRIVATE int stream_pipe ARGS1(HTStream *, me)
! 119: {
! 120: HTRequest *req = me->request;
! 121: if (me->target) {
! 122: int status = PUTBLOCK(me->buffer, me->buflen);
! 123: if (status == HT_OK)
! 124: me->transparent = YES;
! 125: return status;
! 126: }
! 127:
! 128: /* Set up the streams */
! 129: if (me->status==200) {
! 130: HTStream *s;
! 131: if (req->output_format == WWW_SOURCE) {
! 132: me->target = HTMIMEConvert(req, NULL, WWW_MIME,
! 133: req->output_format,
! 134: req->output_stream);
! 135: } else {
! 136: me->target = HTStreamStack(WWW_MIME, req->output_format,
! 137: req->output_stream, req, NO);
! 138:
! 139: /* howcome: test for return value from HTCacheWriter 12/1/95 */
! 140: if (req->method==METHOD_GET && HTCache_isEnabled() &&
! 141: (s = HTCacheWriter(req, NULL, WWW_MIME, req->output_format,
! 142: req->output_stream))) {
! 143: me->target = HTTee(me->target, s);
! 144: }
! 145: }
! 146: } else {
! 147: me->target = HTMIMEConvert(req, NULL, WWW_MIME, req->error_format,
! 148: req->error_stream);
! 149: }
! 150: if (!me->target)
! 151: me->target = HTBlackHole(); /* What else */
! 152: me->semi_trans = YES; /* Look for CRLF.CRLF */
! 153: me->state = EOL_BEGIN; /* Restart CRLF state */
! 154: return HT_OK;
! 155: }
2.11 timbl 156:
2.32.6.1! frystyk 157: PRIVATE int HTNewsStatus_put_character ARGS2(HTStream *, me, char, ch)
1.2 timbl 158: {
2.32.6.1! frystyk 159: return HTNewsStatus_put_block(me, &ch, 1);
1.2 timbl 160: }
1.1 timbl 161:
2.32.6.1! frystyk 162: PRIVATE int HTNewsStatus_put_string ARGS2(HTStream *, me, CONST char*, s)
1.2 timbl 163: {
2.32.6.1! frystyk 164: return HTNewsStatus_put_block(me, s, (int) strlen(s));
1.2 timbl 165: }
1.1 timbl 166:
2.32.6.1! frystyk 167: PRIVATE int HTNewsStatus_flush ARGS1(HTStream *, me)
1.1 timbl 168: {
2.32.6.1! frystyk 169: return (*me->target->isa->flush)(me->target);
! 170: }
1.1 timbl 171:
2.32.6.1! frystyk 172: /*
! 173: ** Searches for NNTP header line until buffer fills up or a CRLF or LF
! 174: ** is found
1.1 timbl 175: */
2.32.6.1! frystyk 176: PRIVATE int HTNewsStatus_put_block ARGS3(HTStream *, me,CONST char*, b, int, l)
! 177: {
! 178: while (!me->semi_trans && l-- > 0) {
! 179: int status;
! 180: if (me->target) {
! 181: if ((status = stream_pipe(me)) != HT_OK)
! 182: return status;
! 183: } else {
! 184: if (me->state == EOL_FCR) {
! 185: if (*b == LF) { /* Line found */
1.1 timbl 186:
2.32.6.1! frystyk 187: /* parse status line */
! 188: char *ptr = me->buffer+5;
! 189: me->version = HTNextField(&ptr);
! 190: me->status = atoi(HTNextField(&ptr));
! 191: me->reason = ptr;
! 192: if ((ptr = strchr(me->reason, '\r')) != NULL)
! 193: *ptr = '\0';
! 194: else if ((ptr = strchr(me->reason, '\n')) != NULL)
! 195: *ptr = '\0';
! 196: if ((status = stream_pipe(me)) != HT_OK)
! 197: return status;
! 198: } else {
! 199: me->state = EOL_BEGIN;
! 200: }
! 201: } else if (*b == CR) {
! 202: me->state = EOL_FCR;
! 203: } else if (*b == LF) {
! 204: if ((status = stream_pipe(me)) != HT_OK)
! 205: return status;
1.1 timbl 206: }
2.32.6.1! frystyk 207: b++;
1.1 timbl 208: }
209: }
210:
2.32.6.1! frystyk 211: /* In semi transparent mode we look for CRLF.CRLF as end of body */
! 212: if (me->target) { /* Is the stream set up? */
1.1 timbl 213:
2.32.6.1! frystyk 214: /* looking for CFLF.CRLF */
! 215:
! 216: #if 0
! 217: while (l-- > 0) {
! 218: if (me->EOLstate == EOL_FCR) {
! 219: if (*b == LF) /* CRLF */
! 220: me->EOLstate = EOL_FLF;
! 221: else if (WHITE(*b)) { /* Folding: CR SP */
! 222: me->EOLstate = EOL_BEGIN;
! 223: } else { /* New line */
! 224: me->EOLstate = EOL_BEGIN;
! 225: }
! 226: } else if (me->EOLstate == EOL_FLF) {
! 227: if (*b == CR) /* LF CR or CR LF CR */
! 228: me->EOLstate = EOL_SCR;
! 229: else if (*b == LF) /* End of header */
! 230: parseheader(me, me->request, me->request->anchor);
! 231: else if (WHITE(*b)) { /* Folding: LF SP or CR LF SP */
! 232: me->EOLstate = EOL_BEGIN;
! 233: HTChunkPutc(me->buffer, ' ');
! 234: } else { /* New line */
! 235: me->EOLstate = EOL_BEGIN;
! 236: HTChunkPutc(me->buffer, '\0');
! 237: HTChunkPutc(me->buffer, *b);
! 238: }
! 239: } else if (me->EOLstate == EOL_SCR) {
! 240: if (*b==CR || *b==LF) /* End of header */
! 241: parseheader(me, me->request, me->request->anchor);
! 242: else if (WHITE(*b)) { /* Folding: LF CR SP or CR LF CR SP */
! 243: me->EOLstate = EOL_BEGIN;
! 244: HTChunkPutc(me->buffer, ' ');
! 245: } else { /* New line */
! 246: me->EOLstate = EOL_BEGIN;
! 247: HTChunkPutc(me->buffer, '\0');
! 248: HTChunkPutc(me->buffer, *b);
! 249: }
! 250: } else if (*b == CR) {
! 251: me->EOLstate = EOL_FCR;
! 252: } else if (*b == LF) {
! 253: me->EOLstate = EOL_FLF; /* Line found */
! 254: } else
! 255: HTChunkPutc(me->buffer, *b);
! 256: b++;
! 257: #endif
1.1 timbl 258:
2.32.6.1! frystyk 259: if (l > 0) /* Anything left? */
! 260: return PUTBLOCK(b, l);
! 261: return HT_OK;
! 262: }
! 263: return HT_WOULD_BLOCK;
1.1 timbl 264: }
265:
2.32.6.1! frystyk 266: PRIVATE int HTNewsStatus_free ARGS1(HTStream *, me)
! 267: {
! 268: }
1.1 timbl 269:
2.32.6.1! frystyk 270: PRIVATE int HTNewsStatus_abort ARGS2(HTStream *, me, HTError, e)
1.1 timbl 271: {
272: }
273:
2.32.6.1! frystyk 274: PRIVATE CONST HTStreamClass HTNewsStatusClass =
! 275: {
! 276: "NewsStatus",
! 277: HTNewsStatus_flush,
! 278: HTNewsStatus_free,
! 279: HTNewsStatus_abort,
! 280: HTNewsStatus_put_character,
! 281: HTNewsStatus_put_string,
! 282: HTNewsStatus_put_block
! 283: };
1.1 timbl 284:
2.32.6.1! frystyk 285: PUBLIC HTStream *HTNewsStatus_new ARGS5(HTRequest *, request,
! 286: news_info *, news)
1.1 timbl 287: {
2.32.6.1! frystyk 288: HTStream *me = (HTStream *) calloc(1, sizeof(HTStream));
! 289: if (!me) outofmem(__FILE__, "HTNewsStatus_new");
! 290: me->isa = &HTNewsStatusClass;
! 291: me->request = request;
! 292: me->news = news;
! 293: me->state = EOL_BEGIN;
! 294: return me;
1.1 timbl 295: }
296:
2.32.6.1! frystyk 297: /* ------------------------------------------------------------------------- */
! 298: /* FUNCTIONS FOR READING ARTICLES AND GROUPS */
! 299: /* ------------------------------------------------------------------------- */
! 300:
! 301: #ifdef 0
1.1 timbl 302: /* Find Author's name in mail address
303: ** ----------------------------------
304: **
305: ** On exit,
306: ** THE EMAIL ADDRESS IS CORRUPTED
307: **
308: ** For example, returns "Tim Berners-Lee" if given any of
2.32.6.1! frystyk 309: ** " Tim Berners-Lee <tim@online.cern.ch> "
! 310: ** or " tim@online.cern.ch ( Tim Berners-Lee ) "
1.1 timbl 311: */
312: PRIVATE char * author_name ARGS1 (char *,email)
313: {
314: char *s, *e;
315:
316: if ((s=strchr(email,'(')) && (e=strchr(email, ')')))
317: if (e>s) {
318: *e=0; /* Chop off everything after the ')' */
319: return HTStrip(s+1); /* Remove leading and trailing spaces */
320: }
321:
322: if ((s=strchr(email,'<')) && (e=strchr(email, '>')))
323: if (e>s) {
324: strcpy(s, e+1); /* Remove <...> */
325: return HTStrip(email); /* Remove leading and trailing spaces */
326: }
327:
328: return HTStrip(email); /* Default to the whole thing */
329:
330: }
331:
1.2 timbl 332: /* Start anchor element
333: ** --------------------
334: */
335: PRIVATE void start_anchor ARGS1(CONST char *, href)
336: {
337: BOOL present[HTML_A_ATTRIBUTES];
338: CONST char* value[HTML_A_ATTRIBUTES];
339:
340: {
341: int i;
342: for(i=0; i<HTML_A_ATTRIBUTES; i++)
343: present[i] = (i==HTML_A_HREF);
344: }
345: value[HTML_A_HREF] = href;
2.32.6.1! frystyk 346: (*target->isa->start_element)(target, HTML_A , present, value);
1.2 timbl 347:
348: }
1.1 timbl 349:
2.16 luotonen 350:
351: /* Start link element
352: ** --------------------
353: */
354: PRIVATE void start_link ARGS2(CONST char *, href, CONST char *, rev)
355: {
356: #ifdef WHEN_WE_HAVE_HTMLPLUS
357:
358: BOOL present[HTML_LINK_ATTRIBUTES];
359: CONST char* value[HTML_LINK_ATTRIBUTES];
360:
361: {
362: int i;
363: for(i=0; i<HTML_LINK_ATTRIBUTES; i++)
364: present[i] = (i==HTML_LINK_HREF || i==HTML_LINK_REV);
365: }
366: value[HTML_LINK_HREF] = href;
367: value[HTML_LINK_REV] = rev;
368: (*targetClass.start_element)(target, HTML_LINK , present, value);
369:
370: #endif
371: }
372:
373:
374:
375:
1.1 timbl 376: /* Paste in an Anchor
377: ** ------------------
378: **
379: **
380: ** On entry,
381: ** HT has a selection of zero length at the end.
382: ** text points to the text to be put into the file, 0 terminated.
383: ** addr points to the hypertext refernce address,
384: ** terminated by white space, comma, NULL or '>'
385: */
386: PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
387: {
388: char href[LINE_LENGTH+1];
389:
390: {
391: CONST char * p;
392: strcpy(href,"news:");
393: for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++);
394: strncat(href, addr, p-addr); /* Make complete hypertext reference */
395: }
396:
1.2 timbl 397: start_anchor(href);
398: PUTS(text);
399: END(HTML_A);
1.1 timbl 400: }
401:
402:
403: /* Write list of anchors
404: ** ---------------------
405: **
406: ** We take a pointer to a list of objects, and write out each,
407: ** generating an anchor for each.
408: **
409: ** On entry,
410: ** HT has a selection of zero length at the end.
411: ** text points to a comma or space separated list of addresses.
412: ** On exit,
413: ** *text is NOT any more chopped up into substrings.
414: */
415: PRIVATE void write_anchors ARGS1 (char *,text)
416: {
417: char * start = text;
418: char * end;
419: char c;
420: for (;;) {
421: for(;*start && (WHITE(*start)); start++); /* Find start */
422: if (!*start) return; /* (Done) */
423: for(end=start; *end && (*end!=' ') && (*end!=','); end++);/* Find end */
424: if (*end) end++; /* Include comma or space but not NULL */
425: c = *end;
426: *end = 0;
427: write_anchor(start, start);
2.16 luotonen 428: START(HTML_BR);
1.1 timbl 429: *end = c;
430: start = end; /* Point to next one */
431: }
432: }
433:
434:
435: /* Read in an Article read_article
436: ** ------------------
437: **
438: **
439: ** Note the termination condition of a single dot on a line by itself.
440: ** RFC 977 specifies that the line "folding" of RFC850 is not used, so we
441: ** do not handle it here.
442: **
443: ** On entry,
444: ** s Global socket number is OK
445: ** HT Global hypertext object is ready for appending text
446: */
447: PRIVATE void read_article NOARGS
448: {
449:
450: char line[LINE_LENGTH+1];
451: char *references=NULL; /* Hrefs for other articles */
452: char *newsgroups=NULL; /* Newsgroups list */
453: char *p = line;
454: BOOL done = NO;
455:
456: /* Read in the HEADer of the article:
457: **
458: ** The header fields are either ignored, or formatted and put into the
459: ** Text.
460: */
461: if (!diagnostic) {
1.2 timbl 462: (*targetClass.start_element)(target, HTML_ADDRESS, 0, 0);
1.1 timbl 463: while(!done){
464: char ch = *p++ = NEXT_CHAR;
465: if (ch==(char)EOF) {
466: abort_socket(); /* End of file, close socket */
467: return; /* End of file on response */
468: }
1.3 timbl 469: if ((ch == LF) || (p == &line[LINE_LENGTH])) {
1.1 timbl 470: *--p=0; /* Terminate the string */
2.28 frystyk 471: if (TRACE) fprintf(TDEST, "H %s\n", line);
1.1 timbl 472:
473: if (line[0]=='.') {
474: if (line[1]<' ') { /* End of article? */
475: done = YES;
476: break;
477: }
478:
479: } else if (line[0]<' ') {
480: break; /* End of Header? */
2.16 luotonen 481:
2.32.6.1! frystyk 482: } else if (!strcasecomp(line, "subject:")) {
1.2 timbl 483: END(HTML_ADDRESS);
484: START(HTML_TITLE); /** Uuugh! @@@ */
2.16 luotonen 485: PUTS(line+9);
486: END(HTML_TITLE);
487: START(HTML_H1);
1.2 timbl 488: PUTS(line+8);
2.16 luotonen 489: END(HTML_H1);
1.2 timbl 490: START(HTML_ADDRESS);
2.16 luotonen 491:
2.32.6.1! frystyk 492: } else if (!strcasecomp(line, "date:")
! 493: || !strcasecomp(line, "organization:")) {
2.16 luotonen 494: PUTS(strchr(line,':')+2);
495: START(HTML_BR);
496:
2.32.6.1! frystyk 497: } else if(!strcasecomp(line, "from:")) {
2.16 luotonen 498: char * temp=0;
499: char * href=0;
500: char *cp1, *cp2;
501:
502: /* copy into temporary storage */
503: StrAllocCopy(temp, strchr(line,':')+1);
504:
505: cp1=temp;
506: while(isspace(*cp1)) cp1++;
507: /* remove space and stuff after */
508: if((cp2 = strchr(cp1,' ')) != NULL)
509: *cp2 = '\0';
510:
511: StrAllocCopy(href,"mailto:");
512: StrAllocCat(href,cp1);
513:
514: start_anchor(href);
515: PUTS("Reply to ");
516: PUTS(strchr(line,':')+1);
517: END(HTML_A);
518: START(HTML_BR);
519:
520: /* put in the owner as a link rel. as well */
521: start_link(href, "made");
522:
523: /* free of temp vars */
524: free(temp);
525: free(href);
526:
2.32.6.1! frystyk 527: } else if (!strcasecomp(line, "newsgroups:")) {
1.1 timbl 528: StrAllocCopy(newsgroups, HTStrip(strchr(line,':')+1));
529:
2.32.6.1! frystyk 530: } else if (!strcasecomp(line, "references:")) {
1.1 timbl 531: StrAllocCopy(references, HTStrip(strchr(line,':')+1));
532:
2.32.6.1! frystyk 533: } /* end if strcasecomp */
1.1 timbl 534: p = line; /* Restart at beginning */
535: } /* if end of line */
536: } /* Loop over characters */
2.16 luotonen 537: END(HTML_ADDRESS);
1.1 timbl 538:
1.2 timbl 539: if (newsgroups || references) {
2.16 luotonen 540: START(HTML_DL);
1.2 timbl 541: if (newsgroups) {
2.16 luotonen 542: #ifdef POSTING
543: char *href=0;
544: #endif
545:
1.2 timbl 546: (*targetClass.start_element)(target, HTML_DT , 0, 0);
547: PUTS("Newsgroups:");
548: (*targetClass.start_element)(target, HTML_DD , 0, 0);
549: write_anchors(newsgroups);
2.16 luotonen 550:
551: #ifdef POSTING
552: /* make posting possible */
553: StrAllocCopy(href,"newspost:");
554: StrAllocCat(href,newsgroups);
555: START(HTML_DT);
556: start_anchor(href);
557: PUTS("Reply to newsgroup(s)");
558: END(HTML_A);
559: #endif
560:
1.2 timbl 561: free(newsgroups);
562: }
563:
564: if (references) {
565: (*targetClass.start_element)(target, HTML_DT , 0, 0);
566: PUTS("References:");
567: (*targetClass.start_element)(target, HTML_DD , 0, 0);
568: write_anchors(references);
569: free(references);
570: }
2.16 luotonen 571: #ifdef WHEN_WE_HAVE_HTMLPLUS
572: (*targetClass.end_element)(target, HTML_DLC);
573: #else
2.10 timbl 574: (*targetClass.end_element)(target, HTML_DL);
2.16 luotonen 575: #endif
1.1 timbl 576: }
1.2 timbl 577: PUTS("\n\n\n");
1.1 timbl 578:
579: }
580:
581: /* Read in the BODY of the Article:
582: */
1.2 timbl 583: (*targetClass.start_element)(target, HTML_PRE , 0, 0);
584:
1.1 timbl 585: p = line;
586: while(!done){
587: char ch = *p++ = NEXT_CHAR;
588: if (ch==(char)EOF) {
589: abort_socket(); /* End of file, close socket */
590: return; /* End of file on response */
591: }
1.3 timbl 592: if ((ch == LF) || (p == &line[LINE_LENGTH])) {
1.1 timbl 593: *p++=0; /* Terminate the string */
2.28 frystyk 594: if (TRACE) fprintf(TDEST, "B %s", line);
1.1 timbl 595: if (line[0]=='.') {
596: if (line[1]<' ') { /* End of article? */
597: done = YES;
598: break;
599: } else { /* Line starts with dot */
1.2 timbl 600: PUTS(&line[1]); /* Ignore first dot */
1.1 timbl 601: }
602: } else {
603:
604: /* Normal lines are scanned for buried references to other articles.
605: ** Unfortunately, it will pick up mail addresses as well!
606: */
607: char *l = line;
608: char * p;
2.14 luotonen 609: while ((p=strchr(l, '<'))) {
1.1 timbl 610: char *q = strchr(p,'>');
611: char *at = strchr(p, '@');
612: if (q && at && at<q) {
613: char c = q[1];
614: q[1] = 0; /* chop up */
615: *p = 0;
1.2 timbl 616: PUTS(l);
1.1 timbl 617: *p = '<'; /* again */
618: *q = 0;
1.2 timbl 619: start_anchor(p+1);
1.1 timbl 620: *q = '>'; /* again */
1.2 timbl 621: PUTS(p);
622: (*targetClass.end_element)(target, HTML_A);
1.1 timbl 623: q[1] = c; /* again */
624: l=q+1;
625: } else break; /* line has unmatched <> */
626: }
1.2 timbl 627: PUTS( l); /* Last bit of the line */
1.1 timbl 628: } /* if not dot */
629: p = line; /* Restart at beginning */
630: } /* if end of line */
631: } /* Loop over characters */
1.2 timbl 632:
633: (*targetClass.end_element)(target, HTML_PRE);
1.1 timbl 634: }
635:
636:
637: /* Read in a List of Newsgroups
638: ** ----------------------------
639: */
640: /*
641: ** Note the termination condition of a single dot on a line by itself.
642: ** RFC 977 specifies that the line "folding" of RFC850 is not used, so we
643: ** do not handle it here.
644: */
645: PRIVATE void read_list NOARGS
646: {
647:
648: char line[LINE_LENGTH+1];
649: char *p;
650: BOOL done = NO;
651:
652: /* Read in the HEADer of the article:
653: **
654: ** The header fields are either ignored, or formatted and put into the
655: ** Text.
656: */
1.2 timbl 657: (*targetClass.start_element)(target, HTML_H1 , 0, 0);
658: PUTS( "Newsgroups");
659: (*targetClass.end_element)(target, HTML_PRE);
1.1 timbl 660: p = line;
2.16 luotonen 661: (*targetClass.start_element)(target, HTML_DL, 0, 0);
1.1 timbl 662: while(!done){
663: char ch = *p++ = NEXT_CHAR;
664: if (ch==(char)EOF) {
665: abort_socket(); /* End of file, close socket */
666: return; /* End of file on response */
667: }
1.3 timbl 668: if ((ch == LF) || (p == &line[LINE_LENGTH])) {
1.1 timbl 669: *p++=0; /* Terminate the string */
2.28 frystyk 670: if (TRACE) fprintf(TDEST, "B %s", line);
2.16 luotonen 671: (*targetClass.start_element)(target, HTML_DT , 0, 0);
1.1 timbl 672: if (line[0]=='.') {
673: if (line[1]<' ') { /* End of article? */
674: done = YES;
675: break;
676: } else { /* Line starts with dot */
1.2 timbl 677: PUTS( &line[1]);
1.1 timbl 678: }
679: } else {
680:
681: /* Normal lines are scanned for references to newsgroups.
682: */
2.16 luotonen 683: int i=0;
684:
685: /* find whitespace if it exits */
686: for(; line[i] != '\0' && !WHITE(line[i]); i++)
687: ; /* null body */
688:
689: if(line[i] != '\0') {
690: line[i] = '\0';
691: write_anchor(line, line);
692: (*targetClass.start_element)(target, HTML_DD , 0, 0);
693: PUTS(&line[i+1]); /* put description */
694: } else {
695: write_anchor(line, line);
696: }
697:
698: #ifdef OLD_CODE
1.1 timbl 699: char group[LINE_LENGTH];
700: int first, last;
701: char postable;
702: if (sscanf(line, "%s %d %d %c", group, &first, &last, &postable)==4)
703: write_anchor(line, group);
704: else
1.2 timbl 705: PUTS(line);
2.16 luotonen 706: #endif /*OLD_CODE*/
707:
1.1 timbl 708: } /* if not dot */
709: p = line; /* Restart at beginning */
710: } /* if end of line */
711: } /* Loop over characters */
2.16 luotonen 712: (*targetClass.end_element)(target, HTML_DL);
1.1 timbl 713: }
714:
715:
716: /* Read in a Newsgroup
717: ** -------------------
718: ** Unfortunately, we have to ask for each article one by one if we
719: ** want more than one field.
720: **
721: */
722: PRIVATE void read_group ARGS3(
723: CONST char *,groupName,
724: int,first_required,
725: int,last_required
726: )
727: {
728: char line[LINE_LENGTH+1];
729: char author[LINE_LENGTH+1];
730: char subject[LINE_LENGTH+1];
731: char *p;
732: BOOL done;
733:
734: char buffer[LINE_LENGTH];
735: char *reference=0; /* Href for article */
736: int art; /* Article number WITHIN GROUP */
737: int status, count, first, last; /* Response fields */
738: /* count is only an upper limit */
739:
740: sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
2.17 frystyk 741: if(TRACE)
2.28 frystyk 742: fprintf(TDEST,
2.17 frystyk 743: "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
744: status, count, first, last, first_required, last_required);
1.1 timbl 745: if (last==0) {
1.2 timbl 746: PUTS( "\nNo articles in this group.\n");
2.16 luotonen 747: #ifdef POSTING
748: goto add_post;
749: #endif
1.1 timbl 750: return;
751: }
752:
753: #define FAST_THRESHOLD 100 /* Above this, read IDs fast */
754: #define CHOP_THRESHOLD 50 /* Above this, chop off the rest */
755:
756: if (first_required<first) first_required = first; /* clip */
757: if ((last_required==0) || (last_required > last)) last_required = last;
758:
759: if (last_required<=first_required) {
1.2 timbl 760: PUTS( "\nNo articles in this range.\n");
2.16 luotonen 761: #ifdef POSTING
762: goto add_post;
763: #endif
1.1 timbl 764: return;
765: }
766:
767: if (last_required-first_required+1 > MAX_CHUNK) { /* Trim this block */
768: first_required = last_required-CHUNK_SIZE+1;
769: }
2.28 frystyk 770: if (TRACE) fprintf (TDEST, " Chunk will be (%d-%d)\n",
2.16 luotonen 771: first_required, last_required);
1.1 timbl 772:
1.2 timbl 773: /* Set window title
774: */
775: sprintf(buffer, "Newsgroup %s, Articles %d-%d",
776: groupName, first_required, last_required);
777: START(HTML_TITLE);
778: PUTS(buffer);
779: END(HTML_TITLE);
780:
1.1 timbl 781: /* Link to earlier articles
782: */
783: if (first_required>first) {
784: int before; /* Start of one before */
785: if (first_required-MAX_CHUNK <= first) before = first;
786: else before = first_required-CHUNK_SIZE;
787: sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1);
2.28 frystyk 788: if (TRACE) fprintf(TDEST, " Block before is %s\n", buffer);
1.2 timbl 789: PUTS( " (");
790: start_anchor(buffer);
791: PUTS("Earlier articles");
792: END(HTML_A);
793: PUTS( "...)\n");
1.1 timbl 794: }
795:
796: done = NO;
797:
798: /*#define USE_XHDR*/
799: #ifdef USE_XHDR
800: if (count>FAST_THRESHOLD) {
801: sprintf(buffer,
802: "\nThere are about %d articles currently available in %s, IDs as follows:\n\n",
803: count, groupName);
1.2 timbl 804: PUTS(buffer);
1.3 timbl 805: sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF);
1.1 timbl 806: status = response(buffer);
807: if (status==221) {
808:
809: p = line;
810: while(!done){
811: char ch = *p++ = NEXT_CHAR;
812: if (ch==(char)EOF) {
813: abort_socket(); /* End of file, close socket */
814: return; /* End of file on response */
815: }
816: if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
817: *p++=0; /* Terminate the string */
2.28 frystyk 818: if (TRACE) fprintf(TDEST, "X %s", line);
1.1 timbl 819: if (line[0]=='.') {
820: if (line[1]<' ') { /* End of article? */
821: done = YES;
822: break;
823: } else { /* Line starts with dot */
824: /* Ignore strange line */
825: }
826: } else {
827:
828: /* Normal lines are scanned for references to articles.
829: */
830: char * space = strchr(line, ' ');
831: if (space++)
832: write_anchor(space, space);
833: } /* if not dot */
834: p = line; /* Restart at beginning */
835: } /* if end of line */
836: } /* Loop over characters */
837:
838: /* leaving loop with "done" set */
839: } /* Good status */
840: };
841: #endif
842:
843: /* Read newsgroup using individual fields:
844: */
845: if (!done) {
846: if (first==first_required && last==last_required)
1.2 timbl 847: PUTS("\nAll available articles in ");
848: else PUTS( "\nArticles in ");
849: PUTS(groupName);
850: START(HTML_MENU);
1.1 timbl 851: for(art=first_required; art<=last_required; art++) {
852:
853: /*#define OVERLAP*/
854: #ifdef OVERLAP
855: /* With this code we try to keep the server running flat out by queuing just
856: ** one extra command ahead of time. We assume (1) that the server won't abort
857: ** if it gets input during output, and (2) that TCP buffering is enough for the
858: ** two commands. Both these assumptions seem very reasonable. However, we HAVE
859: ** had a hangup with a loaded server.
860: */
861: if (art==first_required) {
862: if (art==last_required) {
1.3 timbl 863: sprintf(buffer, "HEAD %d%c%c", art, CR, LF); /* Only one */
1.1 timbl 864: status = response(buffer);
865: } else { /* First of many */
1.3 timbl 866: sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c",
867: art, CR, LF, art+1, CR, LF);
1.1 timbl 868: status = response(buffer);
869: }
870: } else if (art==last_required) { /* Last of many */
871: status = response(NULL);
872: } else { /* Middle of many */
1.3 timbl 873: sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF);
1.1 timbl 874: status = response(buffer);
875: }
876:
877: #else /* NOT OVERLAP */
1.3 timbl 878: sprintf(buffer, "HEAD %d%c%c", art, CR, LF);
1.1 timbl 879: status = response(buffer);
880: #endif /* NOT OVERLAP */
881:
882: if (status == 221) { /* Head follows - parse it:*/
2.20 frystyk 883: int ch;
1.1 timbl 884: p = line; /* Write pointer */
885: done = NO;
886: while(!done){
2.20 frystyk 887: if ((ch = HTInputSocket_getCharacter(isoc)) < 0) {
1.1 timbl 888: abort_socket(); /* End of file, close socket */
889: return; /* End of file on response */
890: }
2.20 frystyk 891: *p++ = (unsigned char) ch;
1.3 timbl 892: if ((ch == LF)
1.1 timbl 893: || (p == &line[LINE_LENGTH]) ) {
894:
895: *--p=0; /* Terminate & chop LF*/
896: p = line; /* Restart at beginning */
2.28 frystyk 897: if (TRACE) fprintf(TDEST, "G %s\n", line);
1.1 timbl 898: switch(line[0]) {
899:
900: case '.':
901: done = (line[1]<' '); /* End of article? */
902: break;
903:
904: case 'S':
905: case 's':
2.32.6.1! frystyk 906: if (!strcasecomp(line, "subject:"))
1.1 timbl 907: strcpy(subject, line+9);/* Save subject */
908: break;
909:
910: case 'M':
911: case 'm':
2.32.6.1! frystyk 912: if (!strcasecomp(line, "message-id:")) {
1.1 timbl 913: char * addr = HTStrip(line+11) +1; /* Chop < */
914: addr[strlen(addr)-1]=0; /* Chop > */
915: StrAllocCopy(reference, addr);
916: }
917: break;
918:
919: case 'f':
920: case 'F':
2.32.6.1! frystyk 921: if (!strcasecomp(line, "from:")) {
1.1 timbl 922: char * p;
923: strcpy(author,
924: author_name(strchr(line,':')+1));
2.17 frystyk 925: if (*author) { /* Not always there! */
926: p = author + strlen(author) - 1;
927: if (*p==LF) *p = 0; /* Chop off newline */
928: }
1.1 timbl 929: }
930: break;
931:
932: } /* end switch on first character */
933: } /* if end of line */
934: } /* Loop over characters */
935:
1.2 timbl 936: START(HTML_LI);
1.1 timbl 937: sprintf(buffer, "\"%s\" - %s", subject, author);
938: if (reference) {
939: write_anchor(buffer, reference);
940: free(reference);
941: reference=0;
942: } else {
1.2 timbl 943: PUTS(buffer);
1.1 timbl 944: }
945:
946:
1.2 timbl 947: /* indicate progress! @@@@@@
1.1 timbl 948: */
949:
950: } /* If good response */
951: } /* Loop over article */
952: } /* If read headers */
1.2 timbl 953: END(HTML_MENU);
954: START(HTML_P);
1.1 timbl 955:
956: /* Link to later articles
957: */
958: if (last_required<last) {
959: int after; /* End of article after */
960: after = last_required+CHUNK_SIZE;
961: if (after==last) sprintf(buffer, "news:%s", groupName); /* original group */
962: else sprintf(buffer, "news:%s/%d-%d", groupName, last_required+1, after);
2.28 frystyk 963: if (TRACE) fprintf(TDEST, " Block after is %s\n", buffer);
1.2 timbl 964: PUTS( "(");
965: start_anchor(buffer);
966: PUTS( "Later articles");
967: END(HTML_A);
968: PUTS( "...)\n");
1.1 timbl 969: }
2.16 luotonen 970:
2.19 luotonen 971: #ifdef POSTING
972: add_post:
973: #endif
2.16 luotonen 974: {
975: char *href=0;
976: START(HTML_HR);
977:
978: StrAllocCopy(href,"newspost:");
979: StrAllocCat(href,groupName);
980: start_anchor(href);
981: PUTS("Post to ");
982: PUTS(groupName);
983: END(HTML_A);
984:
985: free(href);
986: }
1.1 timbl 987:
988:
989: }
990:
2.32.6.1! frystyk 991: #endif
! 992:
! 993: /* ------------------------------------------------------------------------- */
! 994: /* PROTOCOL FUNCTIONS */
! 995: /* ------------------------------------------------------------------------- */
1.1 timbl 996:
2.32.6.1! frystyk 997: /* HTSetNewsHost
! 998: ** Sets the current NEWS server.
1.1 timbl 999: */
2.32.6.1! frystyk 1000: PUBLIC BOOL HTSetNewsHost ARGS1(char *, newshost)
1.1 timbl 1001: {
2.32.6.1! frystyk 1002: if (newshost && *newshost) {
! 1003: StrAllocCopy(HTNewsHost, newshost);
! 1004: {
! 1005: char *strptr = HTNewsHost;
! 1006: while (*strptr) {
! 1007: *strptr = TOLOWER(*strptr);
! 1008: strptr++;
! 1009: }
1.1 timbl 1010:
2.32.6.1! frystyk 1011: /* Remove final dot or paste in domain name */
! 1012: if (strchr(HTNewsHost, '.')) {
! 1013: if (*(HTNewsHost+strlen(HTNewsHost)-1) == '.')
! 1014: *(HTNewsHost+strlen(HTNewsHost)-1) = '\0';
! 1015: } else {
! 1016: CONST char *domain = HTGetDomainName();
! 1017: if (domain) {
! 1018: StrAllocCat(HTNewsHost, ".");
! 1019: StrAllocCat(HTNewsHost, domain);
! 1020: }
! 1021: }
! 1022: }
! 1023: if (PROT_TRACE)
! 1024: fprintf(stderr, "SetNewsHost. Host name is `%s\'\n", HTNewsHost);
! 1025: return YES;
! 1026: } else {
! 1027: if (PROT_TRACE)
! 1028: fprintf(stderr, "SetNewsHost. Bad argument ignored\n");
! 1029: return NO;
! 1030: }
! 1031: }
1.1 timbl 1032:
2.32.6.1! frystyk 1033: /* HTGetNewsHost
! 1034: ** Except on the NeXT, we pick up the NewsHost name from
1.1 timbl 1035: **
2.32.6.1! frystyk 1036: ** 1. Environment variable NNTPSERVER
! 1037: ** 2. File SERVER_FILE
! 1038: ** 3. Compilation time macro DEFAULT_NEWS_HOST
! 1039: **
! 1040: ** On the NeXT, we pick up the NewsHost name from, in order:
! 1041: **
! 1042: ** 1. WorldWideWeb default "NewsHost"
! 1043: ** 2. News default "NewsHost"
! 1044: ** 3. Compilation time macro DEFAULT_NEWS_HOST
! 1045: **
! 1046: ** Return: HTNewsHost if success else NULL
! 1047: */
! 1048: PUBLIC CONST char *HTGetNewsHost NOARGS
! 1049: {
! 1050: if (HTNewsHost) {
! 1051: if (*HTNewsHost) {
! 1052: if (PROT_TRACE)
! 1053: fprintf(stderr, "GetNewsHost. found as `%s\'\n", HTNewsHost);
! 1054: return HTNewsHost;
! 1055: } else
! 1056: return NULL; /* We couldn't get it the last time */
! 1057: }
! 1058: {
! 1059: char *newshost = NULL;
! 1060: char buffer[80];
1.1 timbl 1061:
2.32.6.1! frystyk 1062: #ifdef NeXTStep
! 1063: if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0)
! 1064: if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0)
! 1065: newshost = DEFAULT_NEWS_HOST;
! 1066: #else
! 1067: if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) {
! 1068: FILE *fp = fopen(SERVER_FILE, "r");
! 1069: *(buffer+79) = '\0';
! 1070: if (fp) {
! 1071: if (fgets(buffer, 79, fp)) {
! 1072: char *end;
! 1073: newshost = buffer;
! 1074: while (*newshost == ' ' || *newshost == '\t')
! 1075: newshost++;
! 1076: end = newshost;
! 1077: while (*end && !isspace(*end))
! 1078: end++;
! 1079: *end = '\0';
! 1080: }
! 1081: fclose(fp);
1.1 timbl 1082: }
1083: }
2.32.6.1! frystyk 1084: #endif /* NestStep */
1.1 timbl 1085:
2.32.6.1! frystyk 1086: if (!newshost || !*newshost)
! 1087: newshost = DEFAULT_NEWS_HOST;
! 1088: if (HTSetNewsHost(newshost))
! 1089: return HTNewsHost;
! 1090: StrAllocCopy(HTNewsHost, "");
! 1091: return NULL;
! 1092: }
! 1093: }
! 1094:
! 1095:
! 1096: /*
! 1097: ** Free Newshostname
! 1098: */
! 1099: PUBLIC void HTFreeNewsHost NOARGS
! 1100: {
! 1101: FREE(HTHostName);
! 1102: }
! 1103:
! 1104:
! 1105: /* HTNewsSendCmd
! 1106: ** Send the commond the remote server. Connection must be up and the
! 1107: ** the command terminated by CRLF. This function does never close the
! 1108: ** connection.
! 1109: **
! 1110: ** Return 0 if OK, else -1;
! 1111: */
! 1112: PRIVATE int HTNewsSendCmd ARGS1(news_info *, news)
! 1113: {
! 1114: HTChunkPutc(news->transmit, CR);
! 1115: HTChunkPutc(news->transmit, LF);
! 1116: HTChunkTerminate(news->transmit);
! 1117: if (PROT_TRACE)
! 1118: fprintf(stderr, "News Tx..... %s", news->transmit->data);
! 1119:
! 1120: /* Translate into ASCII if necessary */
! 1121: #ifdef NOT_ASCII
! 1122: {
! 1123: char * p;
! 1124: for(p = news->transmit->data; *p; p++) {
! 1125: *p = TOASCII(*p);
1.3 timbl 1126: }
2.32.6.1! frystyk 1127: }
! 1128: #endif
! 1129: /* Now, we are ready for sending the request */
! 1130: if (NETWRITE(news->sockfd, news->transmit->data,
! 1131: news->transmit->size-1) < 0) {
! 1132: if (PROT_TRACE)
! 1133: fprintf(stderr, "News Tx..... Error transmitting\n");
! 1134: HTErrorSysAdd(news->request, ERR_FATAL, NO, "NETWRITE");
! 1135: return -1;
! 1136: }
! 1137: return 0;
! 1138: }
1.1 timbl 1139:
2.32.6.1! frystyk 1140:
! 1141: /* HTNewsGetResponse
! 1142: ** Gets the response from the news server. Connection must be up and the
! 1143: ** the command terminated by CRLF. This function does never close the
! 1144: ** connection.
! 1145: **
! 1146: ** Returns the 3-digit return code on OK, else -1
! 1147: */
! 1148: PRIVATE int HTNewsGetResponse ARGS1(news_info *, news)
! 1149: {
! 1150: int result;
! 1151: char *response = HTInputSocket_getLine(news->isoc);
! 1152: if (response) {
! 1153: if (!news->receive)
! 1154: news->receive = HTChunkCreate(128);
! 1155: else
! 1156: HTChunkClear(news->receive);
! 1157: HTChunkPuts(news->receive, response);
! 1158: if (PROT_TRACE)
! 1159: fprintf(stderr, "News Rx..... %s\n", response);
! 1160: free(response);
! 1161: } else {
! 1162: if (PROT_TRACE)
! 1163: fprintf(stderr, "News Rx..... No response?\n");
! 1164: return -1;
! 1165: }
! 1166: if (sscanf(news->receive->data, "%d", &result) != 1) {
! 1167: if (PROT_TRACE) fprintf(stderr, "News Rx..... no code found\n");
! 1168: return -1;
! 1169: }
! 1170: return result;
! 1171: }
! 1172:
! 1173:
! 1174: /* HTNewsCleanup
! 1175: ** Closes the connection and frees memory
! 1176: **
! 1177: ** Returns 0 if OK else -1
! 1178: */
! 1179: PRIVATE int HTNewsCleanup ARGS1(news_info *, news)
! 1180: {
! 1181: int status = 0;
! 1182: if (news->sockfd >=0) {
! 1183: if (PROT_TRACE) fprintf(stderr,
! 1184: "News........ Closing socket %d\n", news->sockfd);
! 1185: if ((status = NETCLOSE(news->sockfd)) < 0)
! 1186: HTErrorSysAdd(news->request, ERR_FATAL, NO, "NETCLOSE");
! 1187: news->sockfd = -1;
! 1188: }
! 1189: if (news->isoc)
! 1190: HTInputSocket_free(news->isoc);
! 1191: if (news->transmit)
! 1192: HTChunkFree(news->transmit);
! 1193: if (news->receive)
! 1194: HTChunkFree(news->receive);
! 1195: news->request->net_info = NULL; /* Unlink the request structure */
! 1196: free(news);
! 1197: return status;
! 1198: }
! 1199:
! 1200:
! 1201: /* HTNewsParse
! 1202: ** Parses
! 1203: **
! 1204: ** Returns 0 if OK else -1
! 1205: */
! 1206: PRIVATE int HTNewsParse ARGS2(char *, filename, char *, url) {
! 1207:
! 1208:
! 1209: return -1;
! 1210:
! 1211:
! 1212: }
! 1213:
! 1214:
! 1215: /* Load data object from NNTP Server HTLoadNews
! 1216: ** =================================
! 1217: **
! 1218: ** Given a hypertext addres, this routine loads a document
! 1219: **
! 1220: ** On Entry,
! 1221: ** request The request structure
! 1222: **
! 1223: ** returns HT_ERROR Error has occured or interrupted
! 1224: ** HT_WOULD_BLOCK if operation would have blocked
! 1225: ** HT_LOADED if 200 OK
! 1226: ** HT_NO_DATA if No Response
! 1227: ** HT_RETRY if Service Unavail.
! 1228: */
! 1229: PUBLIC int HTLoadNews ARGS1(HTRequest *, request)
! 1230: {
! 1231: int status = HT_ERROR;
! 1232: char *url; /* Gets initialized on every entry */
! 1233: news_info *news; /* Specific protocol information */
! 1234:
! 1235: if (!request || !request->anchor) {
! 1236: if (PROT_TRACE) fprintf(stderr, "HTLoadNews.. Bad argument\n");
! 1237: return HT_ERROR;
! 1238: }
! 1239: url = HTAnchor_physical(request->anchor);
! 1240:
! 1241: /* Only do the setup first time through */
! 1242: if (!request->net_info) {
! 1243: if (PROT_TRACE)
! 1244: fprintf(stderr, "News........ Looking for `%s\'\n", url);
! 1245: if ((news = (news_info *) calloc(1, sizeof(news_info))) == NULL)
! 1246: outofmem(__FILE__, "HTLoadNews");
! 1247: news->sockfd = -1;
! 1248: news->CRLFdotCRLF = YES; /* We have a dot ending the data */
! 1249: news->request = request;
! 1250: news->state = NEWS_BEGIN;
! 1251: request->net_info = (HTNetInfo *) news;
! 1252: } else
! 1253: news = (news_info *) request->net_info; /* Get existing copy */
! 1254:
! 1255: /* Now start the state machine */
! 1256: while (1) {
! 1257: switch (news->state) {
! 1258: case NEWS_BEGIN:
! 1259: news->state = (!strchr(url, '@') && strchr(url, '*')) ?
! 1260: NEWS_SEEK_CACHE_LIST : NEWS_NEED_CONNECTION;
! 1261: break;
! 1262:
! 1263: case NEWS_SEEK_CACHE_LIST:
! 1264:
! 1265: /* We don't do this for the moment */
! 1266: news->state = NEWS_NEED_CONNECTION;
! 1267: break;
! 1268:
! 1269: case NEWS_NEED_CONNECTION: /* Let's set up a connection */
! 1270: if (!strncmp(url, "news:", 5)) {
! 1271: CONST char *newshost = HTGetNewsHost();
! 1272: if (newshost) {
! 1273: char *newshack = NULL; /* Then we can use HTParse :-) */
! 1274: StrAllocCopy(newshack, "news://");
! 1275: StrAllocCat(newshack, newshost);
! 1276: status = HTDoConnect((HTNetInfo *) news, (char *) newshack,
! 1277: NEWS_PORT, NULL, NO);
! 1278: free(newshack);
2.21 frystyk 1279: }
2.32.6.1! frystyk 1280: } else if (!strncmp(url, "nntp", 5)) {
! 1281: news->nntp = YES;
! 1282: status = HTDoConnect((HTNetInfo *) news, url, NEWS_PORT,
! 1283: NULL, NO);
1.1 timbl 1284: } else {
2.32.6.1! frystyk 1285: if (PROT_TRACE)
! 1286: fprintf(stderr, "News........ Should never happen");
! 1287: news->state = NEWS_ERROR;
1.1 timbl 1288: }
2.32.6.1! frystyk 1289: if (!status) {
! 1290: if (PROT_TRACE)
! 1291: fprintf(stderr, "News........ Connected, socket %d\n",
! 1292: news->sockfd);
1.1 timbl 1293:
2.32.6.1! frystyk 1294: /* Set up stream TO network */
! 1295: request->input_stream =
! 1296: HTNewsReq_new(request, HTWriter_new(news->sockfd, YES));
! 1297:
! 1298: /* Set up stream FROM network and corresponding read buffer */
! 1299: news->isoc = HTInputSocket_new(news->sockfd);
! 1300: news->target = HTImProxy ?
! 1301: request->output_stream : HTNewsStatus_new(request, news);
! 1302:
! 1303: /* Now we are ready to ask the server */
! 1304: news->state = NEWS_NEED_REQUEST;
! 1305: } else
! 1306: news->state = NEWS_ERROR;
! 1307: break;
! 1308:
! 1309: case NEWS_NEED_REQUEST:
! 1310: /*
! 1311: ** Syntax of address is
! 1312: ** xxx@yyy Article
! 1313: ** <xxx@yyy> Same article
! 1314: ** xxxxx News group (no "@")
! 1315: ** group/n1-n2 Articles n1 to n2 in group
! 1316: */
! 1317: if (!news->transmit) {
! 1318: news->transmit = HTChunkCreate(512);
! 1319:
! 1320: /* The request generation goes into HTNewsrequest stream */
! 1321:
! 1322: if (strchr(url, '@')) { /* Send ARTICLE */
! 1323: char *article = NULL;
! 1324: if (news->nntp)
! 1325: article = HTParse(url, "", PARSE_PATH);
! 1326: else
! 1327: StrAllocCopy(article, url+5);
! 1328: HTChunkPuts(news->transmit, "ARTICLE ");
! 1329: HTUnEscape(article); /* AL May 2, 1994 */
! 1330: HTCleanTelnetString(article);
! 1331: if (*article != '<')
! 1332: HTChunkPutc(news->transmit, '<');
! 1333: HTChunkPuts(news->transmit, article);
! 1334: if (*(article+strlen(article)-1) != '>')
! 1335: HTChunkPutc(news->transmit, '>');
! 1336: free(article);
! 1337: } else if (strchr(url, '*')) { /* Send LIST */
! 1338: if (!news->listsent) {
! 1339: HTChunkPuts(news->transmit, "LIST NEWSGROUPS");
! 1340: } else if (news->listsent == 1) {
! 1341: HTChunkPuts(news->transmit, "LIST");
! 1342: } else
! 1343: news->state = NEWS_ERROR;
! 1344: news->listsent++;
! 1345: } else { /* Send GROUP */
! 1346: char *group = NULL;
! 1347: if (news->nntp)
! 1348: group = HTParse(url, "", PARSE_PATH);
! 1349: else
! 1350: StrAllocCopy(group, url+5);
! 1351:
! 1352: /* Scan for first and last numbers of articles */
! 1353: {
! 1354: char *index = strchr(group, '/');
! 1355: if (index) {
! 1356: *index++ = '\0';
! 1357: sscanf(index, "%d-%d",
! 1358: &news->first, &news->last);
! 1359: }
! 1360: }
! 1361: HTChunkPuts(news->transmit, "GROUP ");
! 1362: HTUnEscape(group); /* AL May 2, 1994 */
! 1363: HTCleanTelnetString(group); /* Prevent security holes */
! 1364: HTChunkPuts(news->transmit, group);
! 1365: free(group);
! 1366: }
! 1367: }
! 1368: news->state = HTNewsSendCmd(news) ? NEWS_ERROR : NEWS_SENT_REQUEST;
! 1369: break;
! 1370:
! 1371: case NEWS_SENT_REQUEST:
! 1372: status = HTNewsGetResponse(news);
! 1373: if (status == 211 || status == 221) { /* GROUP OR HEAD */
! 1374: news->format = WWW_NEWSGROUP;
! 1375: news->state = NEWS_NEED_DATA;
! 1376: } else if (status == 220) { /* ARTICLE */
! 1377: news->format = WWW_MIME;
! 1378: news->state = NEWS_NEED_DATA;
! 1379: } else if (status == 215) { /* LIST NEWSGROUPS */
! 1380: news->format = WWW_NEWSLIST;
! 1381: news->state = NEWS_NEED_DATA;
! 1382: } else if (news->listsent == 1) { /* LIST */
! 1383: HTChunkFree(news->transmit);
! 1384: news->transmit = NULL;
! 1385: news->state = NEWS_NEED_REQUEST;
! 1386: } else
! 1387: news->state = NEWS_ERROR;
! 1388: break;
! 1389:
! 1390: case NEWS_NEED_DATA:
! 1391: news->target = HTStreamStack(news->format, request, NO);
! 1392: if (!news->target) {
! 1393: news->state = NEWS_ERROR;
! 1394: break;
! 1395: }
1.1 timbl 1396:
2.32.6.1! frystyk 1397: /* Push the data down the stream remembering the part of the first
! 1398: buffer we just read */
! 1399: (*news->target->isa->put_block)(news->target,
! 1400: news->isoc->input_pointer,
! 1401: news->isoc->input_limit -
! 1402: news->isoc->input_pointer);
! 1403: HTParseSocket(news->format, news->sockfd, request);
! 1404: news->state = NEWS_GOT_DATA;
! 1405: break;
! 1406:
! 1407: case NEWS_GOT_DATA:
! 1408: HTNewsCleanup(news);
! 1409: return HT_LOADED;
! 1410: break;
! 1411:
! 1412: case NEWS_NO_DATA:
! 1413: HTNewsCleanup(news);
! 1414: return HT_NO_DATA;
! 1415: break;
! 1416:
! 1417: case NEWS_RETRY:
! 1418: HTNewsCleanup(news);
! 1419: return HT_RETRY;
! 1420: break;
! 1421:
! 1422: case NEWS_ERROR:
! 1423: HTNewsCleanup(news);
! 1424: return HT_ERROR;
! 1425: break;
! 1426: }
! 1427: } /* End of while(1) */
1.1 timbl 1428: }
1429:
2.32.6.1! frystyk 1430:
! 1431: /* Protocol Descriptor */
2.25 frystyk 1432: GLOBALDEF PUBLIC HTProtocol HTNews = {
2.32.6.1! frystyk 1433: "news", SOC_NON_BLOCK, HTLoadNews, NULL, NULL
! 1434: };
! 1435: GLOBALDEF PUBLIC HTProtocol HTNNTP = {
! 1436: "nntp", SOC_NON_BLOCK, HTLoadNews, NULL, NULL
2.25 frystyk 1437: };
2.32.6.1! frystyk 1438:
Webmaster