Annotation of libwww/Library/src/HTGopher.c, revision 2.26
1.1 timbl 1: /* GOPHER ACCESS HTGopher.c
2: ** =============
3: **
4: ** History:
5: ** 26 Sep 90 Adapted from other accesses (News, HTTP) TBL
6: ** 29 Nov 91 Downgraded to C, for portable implementation.
2.17 frystyk 7: ** 28 Apr 94 target no more global and icons implemented
8: ** HF, frystyk@dxcern.cern.ch
2.19 luotonen 9: ** 2 May 94 Fixed possible security hole when the URL contains
10: ** a newline, that could cause multiple commands to be
11: ** sent to a Gopher server. AL, luotonen@www.cern.ch
2.20 frystyk 12: ** 12 May 94 Checked and made ready for multi-threads, Frystyk
2.21 frystyk 13: **
14: ** NOTE:
15: ** When parsing a gopher menu, we can't use the default HTParseSocket
16: ** but we will hav eto take care of the stram ourselves :-(
17: **
1.1 timbl 18: */
19:
2.20 frystyk 20: /* Implementation dependent include files */
1.1 timbl 21: #include "tcp.h"
22:
2.20 frystyk 23: /* Library include files */
24: #include "HTParse.h"
25: #include "HTUtils.h"
26: #include "HTTCP.h"
2.17 frystyk 27: #include "HTIcons.h"
2.20 frystyk 28: #include "HTAccess.h"
1.1 timbl 29: #include "HTFormat.h"
2.20 frystyk 30: #include "HTError.h"
31: #include "HTFile.h"
1.2 timbl 32: #include "HTML.h"
2.20 frystyk 33: #include "HTMLPDTD.h"
34: #include "HTDirBrw.h"
35: #include "HTGopher.h" /* Implemented here */
36:
37: /* Macros and other defines */
38: #ifndef GOPHER_PORT
39: #define GOPHER_PORT 70 /* See protocol spec */
40: #endif
1.2 timbl 41:
2.20 frystyk 42: /* Hypertext object building machinery */
2.17 frystyk 43: #define PUTC(c) (*target->isa->put_character)(target, c)
44: #define PUTS(s) (*target->isa->put_string)(target, s)
45: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
46: #define END(e) (*target->isa->end_element)(target, e)
47: #define FREE_TARGET (*target->isa->free)(target)
2.20 frystyk 48:
49: /* Type definitions and global variables etc. local to this module */
50: typedef enum _HTGopherType {
51: GOPHER_TEXT = '0',
52: GOPHER_MENU = '1',
53: GOPHER_CSO = '2',
54: GOPHER_ERROR = '3',
55: GOPHER_MACBINHEX = '4',
56: GOPHER_PCBINHEX = '5',
57: GOPHER_UUENCODED = '6',
58: GOPHER_INDEX = '7',
59: GOPHER_TELNET = '8',
60: GOPHER_BINARY = '9',
61: GOPHER_GIF = 'g',
62: GOPHER_HTML = 'h', /* HTML */
63: GOPHER_SOUND = 's',
64: GOPHER_WWW = 'w', /* W3 address */
65: GOPHER_IMAGE = 'I',
66: GOPHER_TN3270 = 'T',
67: GOPHER_DUPLICATE = '+',
68: GOPHER_PLUS_IMAGE = ':', /* Addition from Gopher Plus */
69: GOPHER_PLUS_MOVIE = ';',
70: GOPHER_PLUS_SOUND = '<'
71: } HTGopherType;
72:
1.2 timbl 73: struct _HTStructured {
74: CONST HTStructuredClass * isa;
75: /* ... */
76: };
77:
2.26 ! frystyk 78: /* This is the local definition of HTRequest->net_info */
2.20 frystyk 79: typedef struct _gopher_info {
2.26 ! frystyk 80: int sockfd; /* Socket number for communication */
! 81: HTInputSocket * isoc; /* Input buffer */
! 82: HTRequest * request; /* Request structure */
2.20 frystyk 83: HTGopherType type; /* Gopher item type */
84: } gopher_info;
1.1 timbl 85:
2.20 frystyk 86: /* ------------------------------------------------------------------------- */
1.1 timbl 87:
2.20 frystyk 88: /* get_gopher_icon
1.1 timbl 89: **
2.20 frystyk 90: ** This function finds an appopriate icon for the item in the gopher
91: ** list. Actually it is only a shell build upon HTGetIcon().
2.17 frystyk 92: **
93: */
94: PRIVATE HTIconNode *get_gopher_icon ARGS2(CONST char *, filename,
95: int, gopher_type)
96: {
97: HTFormat content_type = NULL;
98: HTAtom *content_encoding = NULL;
99:
100: if (gopher_type == GOPHER_MENU)
101: return icon_dir ? icon_dir : icon_unknown;
102:
103: switch(gopher_type) {
104: case GOPHER_TEXT:
105: content_type = HTAtom_for("text/void");
106: break;
2.20 frystyk 107: case GOPHER_IMAGE:
108: case GOPHER_PLUS_IMAGE:
2.17 frystyk 109: case GOPHER_GIF:
110: content_type = HTAtom_for("image/void");
111: break;
2.20 frystyk 112: case GOPHER_WWW:
2.17 frystyk 113: case GOPHER_HTML:
114: content_type = HTAtom_for("text/void");
115: break;
116: case GOPHER_SOUND:
2.20 frystyk 117: case GOPHER_PLUS_SOUND:
2.17 frystyk 118: content_type = HTAtom_for("audio/void");
119: break;
2.20 frystyk 120: case GOPHER_PLUS_MOVIE:
121: content_type = HTAtom_for("video/void");
2.17 frystyk 122: break;
123: case GOPHER_INDEX:
124: content_type = HTAtom_for("application/x-gopher-index");
125: break;
126: case GOPHER_CSO:
127: content_type = HTAtom_for("application/x-gopher-cso");
128: break;
129: case GOPHER_TELNET:
130: content_type = HTAtom_for("application/x-gopher-telnet");
131: break;
132: case GOPHER_TN3270:
133: content_type = HTAtom_for("application/x-gopher-tn3270");
134: break;
135: case GOPHER_DUPLICATE:
136: content_type = HTAtom_for("application/x-gopher-duplicate");
137: break;
138: case GOPHER_ERROR:
139: content_type = HTAtom_for("www/unknown");
140: break;
141: case GOPHER_MACBINHEX:
142: case GOPHER_PCBINHEX:
143: case GOPHER_UUENCODED:
144: case GOPHER_BINARY:
145: { /* Do our own filetyping -- maybe we get lucky */
146: HTAtom *language;
147: content_type = HTFileFormat(filename, &content_encoding,
148: &language);
149: }
150: default:
151: content_type = HTAtom_for("www/unknown");
152: break;
153: }
154: return HTGetIcon(S_IFMT & S_IFREG, content_type, content_encoding);
155: }
156:
157:
2.20 frystyk 158: /* parse_menu
159: **
160: ** This function parses a gopher menu and puts it into a iconized
161: ** list.
162: **
163: ** Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
164: ** if other error.
1.1 timbl 165: **
166: */
2.20 frystyk 167: PRIVATE int parse_menu ARGS3(HTRequest *, request,
168: gopher_info *, gopher,
169: CONST char *, url)
170: #define TAB '\t'
171: #define HEX_ESCAPE '%'
1.1 timbl 172: {
2.20 frystyk 173: int status = -1;
2.17 frystyk 174: unsigned int files = 0;
175: int ch;
2.20 frystyk 176: HTChunk *chunk = HTChunkCreate(128);
177: char *message = NULL; /* For a gopher message */
2.21 frystyk 178: HTStructured *target = NULL;
2.26 ! frystyk 179:
! 180: gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 181:
182: /* Output the list */
2.26 ! frystyk 183: while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 184: if (ch == CR || ch == LF) {
185: if (chunk->size) { /* Got some text */
186: char *name = NULL; /* Gopher menu fields */
187: char *selector = NULL;
188: char *host = NULL;
189: char *port = NULL;
190: char *strptr;
191: char *errptr;
192: char gtype;
193: HTChunkTerminate(chunk);
194: strptr = chunk->data; /* Scan it to parse it */
195: if (TRACE)
196: fprintf(stderr, "HTGopher.... Menu item: `%s\'\n",
197: chunk->data);
198: gtype = *strptr++;
199:
200: /* If first item is an error, then don't put any header out
201: but wait and see if there is a next item in the list. If not
202: then make error message, else use as list message. */
203: if (gtype == GOPHER_ERROR) {
204: StrAllocCopy(message, chunk->data+1);
205: break;
206: }
1.1 timbl 207:
2.20 frystyk 208: if (!files && (strstr(chunk->data, "error.host") ||
209: strstr(chunk->data, "errorhost"))) {
2.18 luotonen 210:
2.20 frystyk 211: /* If message is already initialized, then add this one. */
212: /* We don't want the gopher type character */
213: if ((errptr = strchr(chunk->data, '\t')) != NULL)
214: *errptr = '\0';
215: if (message) {
216: StrAllocCat(message, "\n");
217: StrAllocCat(message, chunk->data+1);
218: } else
219: StrAllocCopy(message, chunk->data+1);
220: HTChunkClear(chunk);
221: continue;
222: }
2.17 frystyk 223:
2.21 frystyk 224: /* Stop listing if line with a dot by itself */
2.25 frystyk 225: if (!files && message && gtype=='.' && !*strptr) {
2.21 frystyk 226: status = -1;
227: break;
228: }
229:
2.20 frystyk 230: /* Output title, maybe top message and list top */
231: if (!files) {
232: CONST char *title = HTAnchor_title(request->anchor);
233: char *outstr = NULL;
2.21 frystyk 234: target = HTML_new(request, NULL, WWW_HTML,
235: request->output_format,
236: request->output_stream);
2.20 frystyk 237: if (title) {
238: StrAllocCopy(outstr, title);
239: HTUnEscape(outstr);
240: } else
241: StrAllocCopy(outstr, "Gopher Menu");
242: START(HTML_TITLE);
243: PUTS(outstr);
244: END(HTML_TITLE);
245: START(HTML_H1);
246: PUTS(outstr);
247: END(HTML_H1);
248: FREE(outstr);
249:
250: /* Output any message on top of list */
251: if (message && HTDirInfo == HT_DIR_INFO_TOP) {
252: PUTS(message);
253: START(HTML_BR);
254: }
2.17 frystyk 255:
2.20 frystyk 256: /* Make everything in list preformatted */
257: START(HTML_PRE);
1.1 timbl 258:
2.20 frystyk 259: /* Output the header line of the list */
260: if (!icon_blank) icon_blank = icon_unknown;
261: if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
262: HTMLPutImg(target, icon_blank->icon_url,
263: HTIcon_alt_string(icon_blank->icon_alt, NO),
264: NULL);
265: }
2.17 frystyk 266: PUTC(' ');
2.20 frystyk 267: PUTS("Name");
268: PUTC('\n');
269: START(HTML_HR);
270: PUTC('\n');
2.17 frystyk 271: }
272:
2.20 frystyk 273: /* Stop listing if line with a dot by itself */
274: if (gtype=='.' && !*strptr) {
275: status = (!files && message) ? -1 : HT_LOADED;
276: break;
2.7 secret 277: }
2.20 frystyk 278:
279: /* Parse menu item */
280: if (*strptr) {
281: name = strptr;
282: selector = strchr(name, TAB);
283: if (selector) {
284: *selector++ = 0; /* Terminate name */
285: host = strchr(selector, TAB);
286: if (host) {
287: *host++ = 0; /* Terminate selector */
288: port = strchr(host, TAB);
289: if (port) {
290: char *junk;
291: *port = ':'; /* delimit host a la W3 */
292: if ((junk = strchr(port, TAB)) != NULL)
293: *junk = '\0'; /* Chop port */
294: if (*(port+1) == '0' && !*(port+2))
295: *port = '\0';
296: } /* port */
297: } /* host */
298: } /* selector */
299: } /* gtype and name */
300:
301: /* Get Icon type and output the icon */
302: if (HTDirShowMask & HT_DIR_SHOW_ICON) {
303: HTIconNode *icon = get_gopher_icon(url, gtype);
304: if (icon && icon->icon_url) {
305: HTMLPutImg(target, icon->icon_url,
306: HTIcon_alt_string(icon->icon_alt, YES),
307: NULL);
308: PUTC(' ');
309: }
2.7 secret 310: }
2.20 frystyk 311:
312: if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
313: char *escaped = NULL;
314: escaped = HTEscape(selector, URL_PATH);
315: HTStartAnchor(target, NULL, escaped);
316: PUTS(name);
317: END(HTML_A);
318: free(escaped);
319: } else if (port) { /* Other types need port */
320: char *escaped = NULL;
321: char *address = NULL;
322: int addr_len;
323:
324: /* Calculate the length of the WWW-address */
325: if (selector && *selector) {
326: escaped = HTEscape(selector, URL_PATH);
327: addr_len = 15 + strlen(escaped) + strlen(host) + 1;
328: } else {
329: addr_len = 15 + strlen(host) + 1;
330: }
331: if ((address = (char *) malloc(addr_len)) == NULL)
332: outofmem(__FILE__, "Gopher ParseMenu");
333: *address = '\0';
334:
335: if (gtype == GOPHER_TELNET) {
336: if (escaped)
337: sprintf(address, "telnet://%s@%s/",
338: escaped, host);
339: else
340: sprintf(address, "telnet://%s/", host);
341: }
342: else if (gtype == GOPHER_TN3270) {
343: if (escaped)
344: sprintf(address, "tn3270://%s@%s/",
345: escaped, host);
346: else
347: sprintf(address, "tn3270://%s/", host);
348: } else {
349: if (escaped)
350: sprintf(address, "//%s/%c%s", host, gtype,
351: escaped);
352: else
353: sprintf(address, "//%s/%c", host, gtype);
1.1 timbl 354: }
2.20 frystyk 355:
356: /* Now output the anchor if not a Gopher error */
357: if (gtype != GOPHER_ERROR &&
358: !strstr(address, "error.host") &&
359: !strstr(address, "errorhost")) {
360: HTStartAnchor(target, NULL, address);
361: PUTS(name);
362: END(HTML_A);
363: } else
364: PUTS(name+1); /* Just put it out, but skip type */
365: FREE(address);
366: FREE(escaped);
367: } else { /* If parse error */
368: if (TRACE)
369: fprintf(stderr, "HTGopher.... Bad menu item, `%s\'\n",
370: chunk->data);
371: PUTS(chunk->data);
1.1 timbl 372: }
2.17 frystyk 373: PUTC('\n');
2.20 frystyk 374: HTChunkClear(chunk);
375: ++files; /* Update number of files */
376: }
377: } else
378: HTChunkPutc(chunk, ch);
379: }
380: if (ch < 0)
381: status = ch;
1.2 timbl 382:
2.20 frystyk 383: /* If no files and message is initialized then make error message,
384: else output the bottom part of the list*/
385: if (status != HT_INTERRUPTED) {
386: if (!files && status < 0) {
387: if (message) {
388: HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
389: (void *) message, strlen(message), "parse_menu");
390: } else {
391: HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
392: chunk->data, chunk->size, "parse_menu");
393: }
2.21 frystyk 394: } else if (target) {
2.20 frystyk 395: char *outstr;
396: if ((outstr = (char *) malloc(100)) == NULL)
397: outofmem(__FILE__, "parse_menu");
398: if (files == 0)
399: sprintf(outstr, "Empty directory");
400: else if (files == 1)
401: sprintf(outstr, "1 file");
402: else
403: sprintf(outstr, "%u files", files);
404: START(HTML_HR);
405: PUTS(outstr);
406: free(outstr);
407: END(HTML_PRE);
1.1 timbl 408:
2.20 frystyk 409: /* Put out any messages */
410: if (message && HTDirInfo == HT_DIR_INFO_BOTTOM) {
411: PUTS(message);
412: START(HTML_BR);
413: }
2.21 frystyk 414: FREE_TARGET;
415: } else {
416: if (TRACE)
417: fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
2.20 frystyk 418: }
2.17 frystyk 419: }
420:
2.20 frystyk 421: /* Cleanup */
422: FREE(message);
2.26 ! frystyk 423: HTInputSocket_free(gopher->isoc);
2.20 frystyk 424: HTChunkFree(chunk);
425: return status;
1.1 timbl 426: }
2.11 timbl 427:
428:
2.7 secret 429: /* Parse a Gopher CSO document
2.20 frystyk 430: ** ============================
431: **
432: ** Accepts an open socket to a CSO server waiting to send us
433: ** data and puts it on the screen in a reasonable manner.
434: **
435: ** Perhaps this data can be automatically linked to some
436: ** other source as well???
437: **
438: ** Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
439: ** on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret,
440: ** secret@dxcern.cern.ch.
441: **
442: ** Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
443: ** if other error.
444: */
445: PRIVATE int parse_cso ARGS3(HTRequest *, request,
446: gopher_info *, gopher,
447: CONST char *, url)
2.7 secret 448: {
2.20 frystyk 449: int status = -1;
450: unsigned int records = 0;
2.17 frystyk 451: int ch;
2.20 frystyk 452: char *cur_code = NULL;
453: HTChunk *chunk = HTChunkCreate(128);
2.21 frystyk 454: HTStructured *target = NULL;
2.26 ! frystyk 455:
! 456: gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 457:
458: /* Start grabbing chars from the network */
2.26 ! frystyk 459: while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 460: if (ch == CR || ch == LF) {
461: if (chunk->size) {
462: /* OK we now have a line in 'p' lets parse it and print it */
463: char *strptr;
464: HTChunkTerminate(chunk);
465: strptr = chunk->data;
466:
467: /* If line begins with a 1, then more data is coming, so we
468: put out the title */
469: if (*strptr == '1' ||
470: !strncmp(strptr, "501", 3) || !strncmp(strptr, "502", 3)) {
2.21 frystyk 471:
472: /* Put up new stream */
473: target = HTML_new(request, NULL, WWW_HTML,
474: request->output_format,
475: request->output_stream);
2.20 frystyk 476: START(HTML_H1);
477: PUTS("CSO Search Results");
478: END(HTML_H1);
479:
480: /* Output the header line of the list */
481: START(HTML_PRE); /* To make it look as the other headers */
482: if (!icon_blank) icon_blank = icon_unknown;
483: if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
484: HTMLPutImg(target, icon_blank->icon_url,
485: HTIcon_alt_string(icon_blank->icon_alt, NO),
486: NULL);
487: }
488: PUTC(' ');
489: PUTS("Record");
490: PUTC('\n');
491: START(HTML_HR);
492: PUTC('\n');
493: END(HTML_PRE);
494: }
2.7 secret 495:
2.20 frystyk 496: /* Break on line that begins with a 2. It's the end of data. */
497: if (*strptr == '2') {
498: status = HT_LOADED;
499: break;
500: }
501:
502: /* Lines beginning with 5 are errors, generate msg and quit */
503: if (*strptr == '5') {
504: char *msgptr = strchr(chunk->data, ':');
505: if (!msgptr)
506: msgptr = chunk->data;
507: else
508: ++msgptr;
509: if (!strncmp(strptr, "501", 3)) /* No entries */
510: status = HT_LOADED;
511: else if (!strncmp(strptr, "502", 3)) { /* Too many */
512: status = HT_LOADED;
513: PUTS(msgptr);
514: } else {
515: HTErrorAdd(request, ERR_FATAL, NO, HTERR_CSO_SERVER,
516: (void *) msgptr,
517: strlen(msgptr), "parse_cso");
518: }
519: break;
520: }
521:
522: if(*strptr == '-') {
523: /* data lines look like -200:#:
524: * where # is the search result number and can be
525: * multiple digits (infinate?)
526: * find the second colon and check the digit to the
527: * left of it to see if they are diferent
528: * if they are then a different person is starting.
529: * make this line an <h2>
2.7 secret 530: */
2.20 frystyk 531: char *code; /* format: -200:code:field:value */
532: char *field;
533: char *value;
534: if ((code = strchr(strptr, ':')) != NULL &&
535: (field = strchr(++code, ':')) != NULL) {
536: *field++ = '\0';
537:
538: /* Let's do a strcmp instead of numbers */
539: if (!records) { /* Header of first record */
540: records++;
541: START(HTML_H2);
542: PUTS("Record 1");
543: END(HTML_H2);
544: START(HTML_DL);
545: } else if (cur_code && strcmp(code, cur_code)) {
546: char recstr[20];
547: records++;
548: END(HTML_DL);
549: START(HTML_H3);
550: PUTS("Record ");
2.23 frystyk 551: sprintf(recstr, "%u", records);
2.20 frystyk 552: PUTS(recstr);
553: END(HTML_H3);
554: START(HTML_DL);
555: } else
556: START(HTML_DT);
557:
558: /* I'm not sure whether the name field comes in any
559: * special order or if its even required in a
560: * record, so for now the first line is the header
561: * no matter what it is (it's almost always the
562: * alias)
2.7 secret 563: */
2.20 frystyk 564: if ((value = strchr(field, ':')) == NULL)
565: value = "Empty?";
566: else
567: *value++ = '\0';
568: {
569: char *strip = HTStrip(field);
570: PUTS(strip);
571: START(HTML_DD);
572: strip = HTStrip(value);
573: PUTS(strip);
574: }
2.7 secret 575:
2.20 frystyk 576: /* save the code for comparison on the next pass */
577: StrAllocCopy(cur_code, code);
578: }
579: } /* end data line */
580: HTChunkClear(chunk);
581: } /* end new line */
582: } else
583: HTChunkPutc(chunk, ch);
584: }
585: if (ch < 0)
586: status = ch;
587:
588: /* Put out the bottom line */
589: if (status != HT_INTERRUPTED) {
2.21 frystyk 590: if (target) {
591: char *outstr;
592: if ((outstr = (char *) malloc(100)) == NULL)
593: outofmem(__FILE__, "parse_menu");
594: if (!records)
595: sprintf(outstr, "No records");
596: else if (records == 1)
597: sprintf(outstr, "1 record");
598: else
599: sprintf(outstr, "%u records", records);
600: START(HTML_PRE);
601: START(HTML_HR);
602: PUTS(outstr);
603: END(HTML_PRE);
604: free(outstr);
605: FREE_TARGET;
606: } else {
607: if (TRACE) fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
608: }
2.20 frystyk 609: }
610:
611: /* Clean up */
2.26 ! frystyk 612: HTInputSocket_free(gopher->isoc);
2.20 frystyk 613: HTChunkFree(chunk);
614: FREE(cur_code);
615: return status;
616: }
2.7 secret 617:
1.1 timbl 618:
619: /* Display a Gopher Index document
2.20 frystyk 620: ** -------------------------------
621: */
622: PRIVATE void display_index ARGS2(HTRequest *, request,
623: CONST char *, url)
1.1 timbl 624: {
2.20 frystyk 625: HTStructured *target = HTML_new(request, NULL, WWW_HTML,
626: request->output_format,
627: request->output_stream);
2.18 luotonen 628:
1.2 timbl 629: START(HTML_H1);
2.20 frystyk 630: PUTS("Searchable Gopher Index");
1.2 timbl 631: END(HTML_H1);
2.7 secret 632: START(HTML_ISINDEX);
2.20 frystyk 633: if (!HTAnchor_title(request->anchor))
634: HTAnchor_setTitle(request->anchor, url);
2.7 secret 635: FREE_TARGET;
636: return;
637: }
638:
639:
640: /* Display a CSO index document
641: ** -------------------------------
642: */
2.20 frystyk 643: PRIVATE void display_cso ARGS2(HTRequest *, request,
644: CONST char *, url)
2.7 secret 645: {
2.20 frystyk 646: HTStructured *target = HTML_new(request, NULL, WWW_HTML,
647: request->output_format,
648: request->output_stream);
2.7 secret 649: START(HTML_H1);
2.20 frystyk 650: PUTS("Searchable Index of a CSO Name Server");
2.7 secret 651: END(HTML_H1);
652: START(HTML_ISINDEX);
2.20 frystyk 653: if (!HTAnchor_title(request->anchor))
654: HTAnchor_setTitle(request->anchor, url);
1.2 timbl 655: FREE_TARGET;
1.1 timbl 656: return;
657: }
658:
659:
2.20 frystyk 660:
661: /* HTGopher_send_cmd
1.1 timbl 662: **
2.20 frystyk 663: ** This function creates a socket and writes the gopher command to it.
664: ** The command must be terminated with <CRLF>
665: **
666: ** Returns 0 on OK, else <0 but does NOT close the connection
1.1 timbl 667: */
2.26 ! frystyk 668: PRIVATE int HTGopher_send_cmd ARGS3(gopher_info *, gopher,
2.20 frystyk 669: char *, url,
2.26 ! frystyk 670: char *, command)
1.1 timbl 671: {
2.20 frystyk 672: int status = 0;
2.26 ! frystyk 673: if (!gopher || !command) {
2.20 frystyk 674: if (TRACE)
675: fprintf(stderr, "Gopher Tx... Bad argument!\n");
676: return -1;
677: }
2.26 ! frystyk 678: if ((status = HTDoConnect((HTNetInfo *) gopher, url, GOPHER_PORT,
! 679: NULL)) < 0) {
2.20 frystyk 680: if (TRACE)
681: fprintf(stderr, "HTLoadGopher Connection not established!\n");
682: return status;
683: }
684: if (TRACE)
2.26 ! frystyk 685: fprintf(stderr, "Gopher...... Connected, socket %d\n", gopher->sockfd);
2.20 frystyk 686:
687: /* Write the command to the socket */
688: #ifdef NOT_ASCII
689: {
690: char * p;
691: for(p = command; *p; p++) {
692: *p = TOASCII(*p);
1.1 timbl 693: }
694: }
2.20 frystyk 695: #endif
696: if (TRACE)
2.26 ! frystyk 697: fprintf(stderr, "Gopher Tx... %s", command);
! 698: if ((status = NETWRITE(gopher->sockfd, command,
! 699: (int) strlen(command))) < 0) {
2.20 frystyk 700: if (TRACE) fprintf(stderr, "Gopher...... Error sending command: %s\n",
2.26 ! frystyk 701: command);
! 702: HTErrorSysAdd(gopher->request, ERR_FATAL, NO, "NETWRITE");
2.20 frystyk 703: } else
704: status = 0;
705: return status;
1.1 timbl 706: }
707:
708:
709: /* Load by name HTLoadGopher
710: ** ============
711: **
2.24 frystyk 712: ** Given a hypertext address, this routine loads a gopher document
713: **
714: ** On entry,
715: ** request This is the request structure
716: ** On exit,
717: ** returns <0 Error has occured
718: ** HT_LOADED OK
1.1 timbl 719: **
720: */
2.13 timbl 721: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 722: {
2.22 frystyk 723: char *url;
2.20 frystyk 724: int status = -1;
2.26 ! frystyk 725: char *command = NULL;
2.20 frystyk 726: gopher_info *gopher;
727:
2.22 frystyk 728: if (!request || !request->anchor) {
2.20 frystyk 729: if (TRACE) fprintf(stderr, "HTLoadGopher Bad argument\n");
730: return -1;
731: }
2.22 frystyk 732: url = HTAnchor_physical(request->anchor);
2.20 frystyk 733: if (TRACE) fprintf(stderr, "HTGopher.... Looking for `%s\'\n", url);
734:
2.26 ! frystyk 735: /* Initiate a new gopher structure and bind to resuest structure */
2.20 frystyk 736: if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
737: outofmem(__FILE__, "HTLoadGopher");
2.26 ! frystyk 738: gopher->sockfd = -1;
! 739: gopher->request = request;
! 740: request->net_info = (HTNetInfo *) gopher;
2.20 frystyk 741: gopher->type = GOPHER_MENU;
1.1 timbl 742:
2.20 frystyk 743: /* Get entity type, and selector string and generate command */
1.1 timbl 744: {
2.20 frystyk 745: char *path = HTParse(url, "", PARSE_PATH);
746: char *selector = path;
747: char *query = NULL;
748: char *separator = NULL;
749: if (*selector)
750: gopher->type = *selector++; /* Pick up gtype */
751: if (gopher->type == GOPHER_INDEX) {
752: HTAnchor_setIndex(request->anchor); /* Search is allowed */
753: query = strchr(selector, '?'); /* Look for search string */
754:
755: /* Display local "cover page" only if no search requested */
756: if (!query || !*(query+1)) { /* No search required */
757: display_index(request, url);
758: status = HT_LOADED; /* Local function only */
759: } else {
760: *query++ = 0; /* Skip '?' */
761: separator = "\t";
1.1 timbl 762: }
2.20 frystyk 763: } else if (gopher->type == GOPHER_CSO) {
764: HTAnchor_setIndex(request->anchor); /* Search is allowed */
765: query = strchr(selector, '?'); /* Look for search string */
766:
767: /* Display local "cover page" only if no search requested */
768: if (!query || !*(query+1)) { /* No search required */
769: display_cso(request, url);
770: status = HT_LOADED; /* Local function only */
771: } else {
772: *query++ = 0; /* Skip '?' */
773: separator = "query ";
1.1 timbl 774: }
775: }
776:
2.20 frystyk 777: /* Now generate the final command */
778: if (status != HT_LOADED) {
2.24 frystyk 779: char crlf[3];
2.26 ! frystyk 780: StrAllocCopy(command, selector);
2.20 frystyk 781: if (query) {
782: char *p;
783: for (p=query; *p; p++) /* Remove plus signs 921006 */
784: if (*p == '+') *p = ' ';
2.26 ! frystyk 785: StrAllocCat(command, separator);
! 786: StrAllocCat(command, query);
2.20 frystyk 787: }
2.26 ! frystyk 788: HTUnEscape(command);
! 789: HTCleanTelnetString(command); /* Prevent security holes */
2.24 frystyk 790: *crlf = CR; /* Telnet termination */
791: *(crlf+1) = LF;
792: *(crlf+2) = '\0';
2.26 ! frystyk 793: StrAllocCat(command, crlf);
2.20 frystyk 794: }
795: free(path);
1.1 timbl 796: }
797:
2.20 frystyk 798: /* Now we must ask the server for real data :-( */
799: if (status != HT_LOADED) {
2.26 ! frystyk 800: if ((status = HTGopher_send_cmd(gopher, url, command)) == 0) {
2.20 frystyk 801:
802: /* Now read the data from the socket: */
803: switch (gopher->type) {
804: case GOPHER_HTML:
2.26 ! frystyk 805: status = HTParseSocket(WWW_HTML, gopher->sockfd, request);
2.20 frystyk 806: break;
807:
808: case GOPHER_GIF:
809: case GOPHER_IMAGE:
810: case GOPHER_PLUS_IMAGE:
2.26 ! frystyk 811: status = HTParseSocket(HTAtom_for("image/gif"), gopher->sockfd,
2.20 frystyk 812: request);
813: break;
814: case GOPHER_MENU:
815: case GOPHER_INDEX:
816: status = parse_menu(request, gopher, url);
817: break;
818:
819: case GOPHER_CSO:
820: status = parse_cso(request, gopher, url);
821: break;
822:
823: case GOPHER_MACBINHEX:
824: case GOPHER_PCBINHEX:
825: case GOPHER_UUENCODED:
826: case GOPHER_BINARY:
2.26 ! frystyk 827: {
2.20 frystyk 828: /* Do our own filetyping -- maybe we get lucky */
829: HTFormat format;
830: format = HTFileFormat(url, &request->content_encoding,
831: &request->content_language);
832: if (format) {
833: CTRACE(stderr,
834: "Gopher...... Figured out content-type myself: %s\n",
835: HTAtom_name(format));
2.26 ! frystyk 836: status = HTParseSocket(format, gopher->sockfd,
2.20 frystyk 837: request);
838: }
839: else {
840: CTRACE(stderr,"Gopher...... using www/unknown\n");
841: /* Specifying WWW_UNKNOWN forces dump to local disk */
2.26 ! frystyk 842: HTParseSocket(WWW_UNKNOWN, gopher->sockfd, request);
2.20 frystyk 843: }
844: }
845: break;
846:
847: case GOPHER_SOUND:
848: case GOPHER_PLUS_SOUND:
2.26 ! frystyk 849: status = HTParseSocket(WWW_AUDIO, gopher->sockfd, request);
2.20 frystyk 850: break;
851:
852: case GOPHER_PLUS_MOVIE:
2.26 ! frystyk 853: status = HTParseSocket(WWW_VIDEO, gopher->sockfd, request);
2.20 frystyk 854: break;
2.26 ! frystyk 855:
! 856: /* Try and look at the suffix - maybe it is a PostScript file
! 857: so that we should start an externam viewer. */
2.20 frystyk 858: case GOPHER_TEXT:
2.26 ! frystyk 859: default:
! 860: {
! 861: HTFormat format;
! 862: format = HTFileFormat(url, &request->content_encoding,
! 863: &request->content_language);
! 864: if (format) {
! 865: CTRACE(stderr,
! 866: "Gopher...... Figured out content-type myself: %s\n",
! 867: HTAtom_name(format));
! 868: status = HTParseSocket(format, gopher->sockfd,
! 869: request);
! 870: }
! 871: else {
! 872: status = HTParseSocket(WWW_PLAINTEXT, gopher->sockfd,
! 873: request);
! 874: }
! 875: }
2.20 frystyk 876: break;
2.16 luotonen 877: }
878: }
1.2 timbl 879:
2.20 frystyk 880: /* Close the connection */
2.26 ! frystyk 881: if (gopher->sockfd >= 0) {
2.25 frystyk 882: if (TRACE) fprintf(stderr, "Gopher...... Closing socket %d\n",
2.26 ! frystyk 883: gopher->sockfd);
! 884: if (NETCLOSE(gopher->sockfd) < 0)
! 885: status = HTErrorSysAdd(request, ERR_FATAL, NO, "NETCLOSE");
2.25 frystyk 886: }
2.20 frystyk 887: }
888: if (status == HT_INTERRUPTED) {
889: HTErrorAdd(request, ERR_WARNING, NO, HTERR_INTERRUPTED, NULL, 0,
890: "HTLoadGopher");
891: }
2.26 ! frystyk 892: FREE(command);
2.20 frystyk 893: free(gopher);
894:
895: if (status < 0 && status != HT_INTERRUPTED) {
2.21 frystyk 896: char *unescaped = NULL;
897: StrAllocCopy(unescaped, url);
898: HTUnEscape(unescaped);
899: HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
900: (int) strlen(unescaped), "HTLoadGopher");
2.20 frystyk 901: HTAnchor_clearIndex(request->anchor);
2.21 frystyk 902: free(unescaped);
2.20 frystyk 903: }
904: return status;
1.1 timbl 905: }
1.2 timbl 906:
2.10 timbl 907: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1 timbl 908:
Webmaster