Annotation of libwww/Library/src/HTGopher.c, revision 2.19
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
1.1 timbl 12: */
13:
1.2 timbl 14: /* Implements:
15: */
16: #include "HTGopher.h"
17:
1.3 timbl 18:
1.1 timbl 19: #define GOPHER_PORT 70 /* See protocol spec */
20: #define BIG 1024 /* Bug */
21: #define LINE_LENGTH 256 /* Bug */
22:
23: /* Gopher entity types:
24: */
25: #define GOPHER_TEXT '0'
26: #define GOPHER_MENU '1'
27: #define GOPHER_CSO '2'
28: #define GOPHER_ERROR '3'
29: #define GOPHER_MACBINHEX '4'
30: #define GOPHER_PCBINHEX '5'
31: #define GOPHER_UUENCODED '6'
32: #define GOPHER_INDEX '7'
33: #define GOPHER_TELNET '8'
2.7 secret 34: #define GOPHER_BINARY '9'
1.3 timbl 35: #define GOPHER_GIF 'g'
2.7 secret 36: #define GOPHER_HTML 'h' /* HTML */
37: #define GOPHER_SOUND 's'
38: #define GOPHER_WWW 'w' /* W3 address */
1.3 timbl 39: #define GOPHER_IMAGE 'I'
2.7 secret 40: #define GOPHER_TN3270 'T'
1.1 timbl 41: #define GOPHER_DUPLICATE '+'
42:
43: #include <ctype.h>
44: #include "HTUtils.h" /* Coding convention macros */
45: #include "tcp.h"
46:
2.17 frystyk 47: #include "HTIcons.h"
1.1 timbl 48: #include "HTParse.h"
49: #include "HTFormat.h"
50: #include "HTTCP.h"
2.16 luotonen 51: #include "HTFile.h" /* HTFileFormat() */
1.1 timbl 52:
1.2 timbl 53: /* Hypertext object building machinery
54: */
55: #include "HTML.h"
56:
2.17 frystyk 57: #define PUTC(c) (*target->isa->put_character)(target, c)
58: #define PUTS(s) (*target->isa->put_string)(target, s)
59: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
60: #define END(e) (*target->isa->end_element)(target, e)
61: #define FREE_TARGET (*target->isa->free)(target)
1.2 timbl 62: struct _HTStructured {
63: CONST HTStructuredClass * isa;
64: /* ... */
65: };
66:
2.17 frystyk 67: #ifdef OLD_CODE
1.2 timbl 68: PRIVATE HTStructured *target; /* the new hypertext */
69: PRIVATE HTStructuredClass targetClass; /* Its action routines */
2.17 frystyk 70: #endif /* OLD_CODE */
1.2 timbl 71:
2.8 timbl 72: #define GOPHER_PROGRESS(foo) HTAlert(foo)
1.1 timbl 73:
74:
2.12 timbl 75: #define NEXT_CHAR HTInputSocket_getCharacter(isoc)
1.1 timbl 76:
77:
2.8 timbl 78:
1.1 timbl 79: /* Module-wide variables
80: */
81: PRIVATE int s; /* Socket for GopherHost */
82:
83:
1.2 timbl 84:
1.1 timbl 85: /* Matrix of allowed characters in filenames
86: ** -----------------------------------------
87: */
88:
89: PRIVATE BOOL acceptable[256];
90: PRIVATE BOOL acceptable_inited = NO;
91:
92: PRIVATE void init_acceptable NOARGS
93: {
94: unsigned int i;
95: char * good =
96: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
97: for(i=0; i<256; i++) acceptable[i] = NO;
98: for(;*good; good++) acceptable[(unsigned int)*good] = YES;
99: acceptable_inited = YES;
100: }
101:
102: PRIVATE CONST char hex[17] = "0123456789abcdef";
103:
104: /* Decdoe one hex character
105: */
106:
107: PRIVATE char from_hex ARGS1(char, c)
108: {
109: return (c>='0')&&(c<='9') ? c-'0'
110: : (c>='A')&&(c<='F') ? c-'A'+10
111: : (c>='a')&&(c<='f') ? c-'a'+10
112: : 0;
113: }
114:
115:
116:
117: /* Paste in an Anchor
118: ** ------------------
119: **
120: ** The title of the destination is set, as there is no way
121: ** of knowing what the title is when we arrive.
122: **
123: ** On entry,
124: ** HT is in append mode.
125: ** text points to the text to be put into the file, 0 terminated.
126: ** addr points to the hypertext refernce address 0 terminated.
127: */
2.17 frystyk 128: PRIVATE void write_anchor ARGS3(HTStructured *, target,
129: CONST char *, text,
130: CONST char *, addr)
1.1 timbl 131: {
1.2 timbl 132: BOOL present[HTML_A_ATTRIBUTES];
133: CONST char * value[HTML_A_ATTRIBUTES];
1.1 timbl 134:
1.2 timbl 135: int i;
136:
137: for (i=0; i<HTML_A_ATTRIBUTES; i++) present[i]=0;
138: present[HTML_A_HREF] = YES;
139: value[HTML_A_HREF] = addr;
140: present[HTML_A_TITLE] = YES;
141: value[HTML_A_TITLE] = text;
142:
2.17 frystyk 143: (*target->isa->start_element)(target, HTML_A, present, value);
1.1 timbl 144:
1.2 timbl 145: PUTS(text);
146: END(HTML_A);
1.1 timbl 147: }
148:
149:
2.17 frystyk 150: /* Find a icon
151: ** ===========
152: **
153: */
154: PRIVATE HTIconNode *get_gopher_icon ARGS2(CONST char *, filename,
155: int, gopher_type)
156: {
157: HTFormat content_type = NULL;
158: HTAtom *content_encoding = NULL;
159:
160: if (gopher_type == GOPHER_MENU)
161: return icon_dir ? icon_dir : icon_unknown;
162:
163: switch(gopher_type) {
164: case GOPHER_TEXT:
165: content_type = HTAtom_for("text/void");
166: break;
167: case GOPHER_GIF:
168: content_type = HTAtom_for("image/void");
169: break;
170: case GOPHER_HTML:
171: content_type = HTAtom_for("text/void");
172: break;
173: case GOPHER_SOUND:
174: content_type = HTAtom_for("audio/void");
175: break;
176: case GOPHER_WWW:
177: content_type = HTAtom_for("text/void");
178: break;
179: case GOPHER_IMAGE:
180: content_type = HTAtom_for("image/void");
181: break;
182: case GOPHER_INDEX:
183: content_type = HTAtom_for("application/x-gopher-index");
184: break;
185: case GOPHER_CSO:
186: content_type = HTAtom_for("application/x-gopher-cso");
187: break;
188: case GOPHER_TELNET:
189: content_type = HTAtom_for("application/x-gopher-telnet");
190: break;
191: case GOPHER_TN3270:
192: content_type = HTAtom_for("application/x-gopher-tn3270");
193: break;
194: case GOPHER_DUPLICATE:
195: content_type = HTAtom_for("application/x-gopher-duplicate");
196: break;
197: case GOPHER_ERROR:
198: content_type = HTAtom_for("www/unknown");
199: break;
200: case GOPHER_MACBINHEX:
201: case GOPHER_PCBINHEX:
202: case GOPHER_UUENCODED:
203: case GOPHER_BINARY:
204: { /* Do our own filetyping -- maybe we get lucky */
205: HTAtom *language;
206: content_type = HTFileFormat(filename, &content_encoding,
207: &language);
208: }
209: default:
210: content_type = HTAtom_for("www/unknown");
211: break;
212: }
213: return HTGetIcon(S_IFMT & S_IFREG, content_type, content_encoding);
214: }
215:
216:
1.1 timbl 217: /* Parse a Gopher Menu document
218: ** ============================
219: **
220: */
221:
2.17 frystyk 222: PRIVATE void parse_menu ARGS4(HTStructured *, target,
223: int , s,
224: CONST char *, arg,
225: HTParentAnchor *, anAnchor)
1.1 timbl 226: {
2.17 frystyk 227: unsigned int files = 0;
1.1 timbl 228: char gtype;
2.17 frystyk 229: int ch;
1.1 timbl 230: char line[BIG];
231: char address[BIG];
2.14 luotonen 232: char *name = "";
233: char *selector = ""; /* Gopher menu fields */
234: char *host = "";
1.1 timbl 235: char *port;
236: char *p = line;
2.12 timbl 237: HTInputSocket * isoc = HTInputSocket_new(s);
2.11 timbl 238:
1.1 timbl 239: #define TAB '\t'
240: #define HEX_ESCAPE '%'
241:
2.17 frystyk 242: /* Output title */
243: {
244: CONST char *title = HTAnchor_title(anAnchor);
245: char *outstr = NULL;
246: if (title)
247: StrAllocCopy(outstr, title);
248: else
2.18 luotonen 249: StrAllocCopy(outstr, "Gopher Menu");
250:
2.17 frystyk 251: START(HTML_TITLE);
252: HTUnEscape(outstr);
253: PUTS(outstr);
254: END(HTML_TITLE);
1.2 timbl 255: START(HTML_H1);
2.17 frystyk 256: PUTS(outstr);
257: END(HTML_H1);
258: free(outstr);
259: }
260:
261: /* Make everything in list preformatted */
262: START(HTML_PRE);
263:
264: /* Output the header line of the list */
265: if (!icon_blank) icon_blank = icon_unknown;
266: if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
267: HTMLPutImg(target, icon_blank->icon_url,
268: HTIcon_alt_string(icon_blank->icon_alt, NO), NULL);
269: }
270: PUTC(' ');
271: PUTS("Name");
272: PUTC('\n');
273: START(HTML_HR);
274: PUTC('\n');
275:
276: while ((ch = NEXT_CHAR) >= 0) {
1.3 timbl 277: if (ch != LF) {
1.1 timbl 278: *p = ch; /* Put character in line */
279: if (p< &line[BIG-1]) p++;
280:
281: } else {
282: *p++ = 0; /* Terminate line */
283: p = line; /* Scan it to parse it */
284: port = 0; /* Flag "not parsed" */
2.17 frystyk 285: if (TRACE)
286: fprintf(stderr, "HTGopher.... Menu item: `%s\'\n", line);
1.1 timbl 287: gtype = *p++;
288:
289: /* Break on line with a dot by itself */
290: if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
291:
292: if (gtype && *p) {
293: name = p;
294: selector = strchr(name, TAB);
295: if (selector) {
296: *selector++ = 0; /* Terminate name */
297: host = strchr(selector, TAB);
298: if (host) {
299: *host++ = 0; /* Terminate selector */
300: port = strchr(host, TAB);
301: if (port) {
302: char *junk;
303: port[0] = ':'; /* delimit host a la W3 */
304: junk = strchr(port, TAB);
305: if (junk) *junk++ = 0; /* Chop port */
306: if ((port[1]=='0') && (!port[2]))
307: port[0] = 0; /* 0 means none */
308: } /* no port */
309: } /* host ok */
310: } /* selector ok */
311: } /* gtype and name ok */
2.17 frystyk 312:
313: /* Get Icon type and output the icon */
314: if (HTDirShowMask & HT_DIR_SHOW_ICON) {
315: HTIconNode *icon = get_gopher_icon(arg, gtype);
316: if (icon && icon->icon_url) {
317: HTMLPutImg(target, icon->icon_url,
318: HTIcon_alt_string(icon->icon_alt, YES),
319: NULL);
320: PUTC(' ');
321: }
322: }
1.1 timbl 323:
324: if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
2.17 frystyk 325: write_anchor(target, name, selector);
326:
1.1 timbl 327: } else if (port) { /* Other types need port */
328: if (gtype == GOPHER_TELNET) {
329: if (*selector) sprintf(address, "telnet://%s@%s/",
2.7 secret 330: selector, host);
1.1 timbl 331: else sprintf(address, "telnet://%s/", host);
2.7 secret 332: }
333: else if (gtype == GOPHER_TN3270)
334: {
335: if (*selector)
336: sprintf(address, "tn3270://%s@%s/",
337: selector, host);
338: else
339: sprintf(address, "tn3270://%s/", host);
340: }
341: else { /* If parsed ok */
1.1 timbl 342: char *q;
343: char *p;
344: sprintf(address, "//%s/%c", host, gtype);
345: q = address+ strlen(address);
346: for(p=selector; *p; p++) { /* Encode selector string */
2.14 luotonen 347: if (acceptable[(int)*p]) *q++ = *p;
1.1 timbl 348: else {
349: *q++ = HEX_ESCAPE; /* Means hex coming */
350: *q++ = hex[(TOASCII(*p)) >> 4];
351: *q++ = hex[(TOASCII(*p)) & 15];
352: }
353: }
354: *q++ = 0; /* terminate address */
355: }
2.17 frystyk 356:
2.7 secret 357: /* Error response from Gopher doesn't deserve to
358: be a hyperlink. */
359: if (strcmp (address, "gopher://error.host:1/0"))
2.17 frystyk 360: write_anchor(target, name, address);
2.7 secret 361: else
362: PUTS(name);
2.17 frystyk 363: PUTC('\n');
1.1 timbl 364: } else { /* parse error */
2.17 frystyk 365: if (TRACE) fprintf(stderr, "HTGopher.... Bad menu item.\n");
1.2 timbl 366: PUTS(line);
367:
1.1 timbl 368: } /* parse error */
369:
370: p = line; /* Start again at beginning of line */
2.17 frystyk 371: ++files; /* Update number of files */
372:
1.1 timbl 373: } /* if end of line */
374:
375: } /* Loop over characters */
376:
2.17 frystyk 377: {
378: char *outstr;
379:
380: if ((outstr = (char *) malloc(100)) == NULL)
381: outofmem(__FILE__, "parse_menu");
382: if (files == 0)
383: sprintf(outstr, "Empty directory");
384: else if (files == 1)
385: sprintf(outstr, "1 file");
386: else
387: sprintf(outstr, "%u files", files);
388: START(HTML_HR);
389: PUTS(outstr);
390: free(outstr);
391: END(HTML_PRE);
392: }
393:
1.2 timbl 394: FREE_TARGET;
2.11 timbl 395: HTInputSocket_free(isoc);
1.1 timbl 396: return;
397: }
2.11 timbl 398:
399:
2.7 secret 400: /* Parse a Gopher CSO document
401: ** ============================
402: **
403: ** Accepts an open socket to a CSO server waiting to send us
404: ** data and puts it on the screen in a reasonable manner.
405: **
406: ** Perhaps this data can be automatically linked to some
407: ** other source as well???
408: **
409: ** Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
410: ** on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret,
411: ** secret@dxcern.cern.ch .
412: */
413:
2.17 frystyk 414: PRIVATE void parse_cso ARGS4 (HTStructured *, target,
415: int, s,
416: CONST char *, arg,
417: HTParentAnchor *, anAnchor)
2.7 secret 418: {
2.17 frystyk 419: int ch;
2.7 secret 420: char line[BIG];
421: char *p = line;
422: char *second_colon, last_char='\0';
423: CONST char *title;
2.11 timbl 424: HTInputSocket * isoc = HTInputSocket_new(s);
2.7 secret 425:
426: title = HTAnchor_title(anAnchor);
427: START(HTML_H1);
428: PUTS("CSO Search Results");
429: END(HTML_H1);
430: START(HTML_PRE);
431:
432: /* start grabbing chars from the network */
2.17 frystyk 433: while ((ch = NEXT_CHAR) >= 0)
2.7 secret 434: {
435: if (ch != '\n')
436: {
437: *p = ch; /* Put character in line */
438: if (p< &line[BIG-1]) p++;
439: }
440: else
441: {
442: *p++ = 0; /* Terminate line */
443: p = line; /* Scan it to parse it */
444:
445: /* OK we now have a line in 'p' lets parse it and
446: print it */
447:
448: /* Break on line that begins with a 2. It's the end of
449: * data.
450: */
451: if (*p == '2')
452: break;
453:
454: /* lines beginning with 5 are errors,
455: * print them and quit
456: */
457: if (*p == '5') {
458: START(HTML_H2);
459: PUTS(p+4);
460: END(HTML_H2);
461: break;
462: }
463:
464: if(*p == '-') {
465: /* data lines look like -200:#:
466: * where # is the search result number and can be
467: * multiple digits (infinate?)
468: * find the second colon and check the digit to the
469: * left of it to see if they are diferent
470: * if they are then a different person is starting.
471: * make this line an <h2>
472: */
473:
474: /* find the second_colon */
475: second_colon = strchr( strchr(p,':')+1, ':');
476:
477: if(second_colon != NULL) { /* error check */
478:
479: if (*(second_colon-1) != last_char)
480: /* print seperator */
481: {
482: END(HTML_PRE);
483: START(HTML_H2);
484: }
485:
486:
487: /* right now the record appears with the alias
488: * (first line)
489: * as the header and the rest as <pre> text
490: * It might look better with the name as the
491: * header and the rest as a <ul> with <li> tags
492: * I'm not sure whether the name field comes in any
493: * special order or if its even required in a
494: * record,
495: * so for now the first line is the header no
496: * matter
497: * what it is (it's almost always the alias)
498: * A <dl> with the first line as the <DT> and
499: * the rest as some form of <DD> might good also?
500: */
501:
502: /* print data */
503: PUTS(second_colon+1);
504: PUTS("\n");
505:
506: if (*(second_colon-1) != last_char)
507: /* end seperator */
508: {
509: END(HTML_H2);
510: START(HTML_PRE);
511: }
512:
513: /* save the char before the second colon
514: * for comparison on the next pass
515: */
516: last_char = *(second_colon-1) ;
517:
518: } /* end if second_colon */
519: } /* end if *p == '-' */
520: } /* if end of line */
521:
522: } /* Loop over characters */
523:
524: /* end the text block */
525: PUTS("\n");
526: END(HTML_PRE);
527: PUTS("\n");
528: FREE_TARGET;
2.11 timbl 529: HTInputSocket_free(isoc);
2.7 secret 530:
531: return; /* all done */
532: } /* end of procedure */
1.1 timbl 533:
534: /* Display a Gopher Index document
2.7 secret 535: ** -------------------------------
536: */
1.1 timbl 537:
2.17 frystyk 538: PRIVATE void display_index ARGS3 (HTStructured *, target,
539: CONST char *, arg,
540: HTParentAnchor *, anAnchor)
1.1 timbl 541: {
2.18 luotonen 542: char * decoded = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
543: char * t = NULL;
544:
545: HTUnEscape(decoded);
2.19 ! luotonen 546: HTCleanTelnetString(decoded); /* Prevent security holes */
2.18 luotonen 547: if (strlen(decoded) > 2) {
548: t = strchr(decoded+1,'/');
549: if (t) t++;
550: }
551: if (!t) t = decoded;
552:
1.2 timbl 553: START(HTML_H1);
2.18 luotonen 554: PUTS(t);
1.2 timbl 555: END(HTML_H1);
2.7 secret 556: START(HTML_ISINDEX);
557: PUTS("\nThis is a searchable Gopher index.");
558: PUTS(" Please enter keywords to search for.\n");
559:
560: if (!HTAnchor_title(anAnchor))
561: HTAnchor_setTitle(anAnchor, arg);
1.2 timbl 562:
2.7 secret 563: FREE_TARGET;
2.18 luotonen 564: free(decoded);
2.7 secret 565: return;
566: }
567:
568:
569: /* Display a CSO index document
570: ** -------------------------------
571: */
572:
2.17 frystyk 573: PRIVATE void display_cso ARGS3(HTStructured *, target,
574: CONST char *, arg,
575: HTParentAnchor *, anAnchor)
2.7 secret 576: {
577: START(HTML_H1);
578: PUTS(arg);
579: PUTS(" index");
580: END(HTML_H1);
581: START(HTML_ISINDEX);
582: PUTS("\nThis is a searchable index of a CSO database.\n");
583: PUTS(" Please enter keywords to search for. The keywords that you enter");
584: PUTS(" will allow you to search on a person's name in the database.\n");
585:
1.1 timbl 586: if (!HTAnchor_title(anAnchor))
1.2 timbl 587: HTAnchor_setTitle(anAnchor, arg);
1.1 timbl 588:
1.2 timbl 589: FREE_TARGET;
1.1 timbl 590: return;
591: }
592:
593:
594: /* De-escape a selector into a command
595: ** -----------------------------------
596: **
597: ** The % hex escapes are converted. Otheriwse, the string is copied.
598: */
599: PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
600: {
601: CONST char * p = selector;
602: char * q = command;
603: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
604: while (*p) { /* Decode hex */
605: if (*p == HEX_ESCAPE) {
606: char c;
607: unsigned int b;
608: p++;
609: c = *p++;
610: b = from_hex(c);
611: c = *p++;
612: if (!c) break; /* Odd number of chars! */
613: *q++ = FROMASCII((b<<4) + from_hex(c));
614: } else {
615: *q++ = *p++; /* Record */
616: }
617: }
618: *q++ = 0; /* Terminate command */
619:
620: }
621:
622:
623: /* Load by name HTLoadGopher
624: ** ============
625: **
626: ** Bug: No decoding of strange data types as yet.
627: **
628: */
2.13 timbl 629: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 630: {
2.13 timbl 631: CONST char * arg = HTAnchor_physical(request->anchor);
1.1 timbl 632: char *command; /* The whole command */
633: int status; /* tcp return */
634: char gtype; /* Gopher Node type */
635: char * selector; /* Selector string */
636: struct sockaddr_in soc_address; /* Binary network address */
637: struct sockaddr_in* sin = &soc_address;
2.17 frystyk 638: HTStructured *target; /* HTML object */
639: HTStructuredClass targetClass;
1.1 timbl 640:
641: if (!acceptable_inited) init_acceptable();
642:
643: if (!arg) return -3; /* Bad if no name sepcified */
644: if (!*arg) return -2; /* Bad if name had zero length */
645:
2.17 frystyk 646: if (TRACE) fprintf(stderr, "HTGopher.... Looking for %s\n", arg);
1.1 timbl 647:
648:
649: /* Set up defaults:
650: */
651: sin->sin_family = AF_INET; /* Family, host order */
652: sin->sin_port = htons(GOPHER_PORT); /* Default: new port, */
653:
654: /* Get node name and optional port number:
655: */
656: {
657: char *p1 = HTParse(arg, "", PARSE_HOST);
658: int status = HTParseInet(sin, p1);
659: free(p1);
660: if (status) return status; /* Bad */
661: }
662:
663: /* Get entity type, and selector string.
664: */
665: {
666: char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
667: gtype = '1'; /* Default = menu */
668: selector = p1;
669: if ((*selector++=='/') && (*selector)) { /* Skip first slash */
670: gtype = *selector++; /* Pick up gtype */
671: }
672: if (gtype == GOPHER_INDEX) {
673: char * query;
2.10 timbl 674: HTAnchor_setIndex(request->anchor); /* Search is allowed */
1.1 timbl 675: query = strchr(selector, '?'); /* Look for search string */
676: if (!query || !query[1]) { /* No search required */
2.11 timbl 677: target = HTML_new(request, NULL, WWW_HTML,
678: request->output_format, request->output_stream);
1.2 timbl 679: targetClass = *target->isa;
2.17 frystyk 680: /* Display "cover page" */
681: display_index(target, arg, request->anchor);
2.15 luotonen 682: free(p1); /* Leak fixed Henrik 27 Feb 94 */
2.6 timbl 683: return HT_LOADED; /* Local function only */
1.1 timbl 684: }
685: *query++ = 0; /* Skip '?' */
686: command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
687: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
688:
689: de_escape(command, selector); /* Bug fix TBL 921208 */
690:
691: strcat(command, "\t");
692:
693: { /* Remove plus signs 921006 */
694: char *p;
695: for (p=query; *p; p++) {
696: if (*p == '+') *p = ' ';
697: }
698: }
699: strcat(command, query);
2.7 secret 700: } else if (gtype == GOPHER_CSO) {
701: char * query;
2.10 timbl 702: HTAnchor_setIndex(request->anchor); /* Search is allowed */
2.7 secret 703: query = strchr(selector, '?'); /* Look for search string */
704: if (!query || !query[1]) { /* No search required */
2.11 timbl 705: target = HTML_new(request, NULL, WWW_HTML,
706: request->output_format, request->output_stream);
2.7 secret 707: targetClass = *target->isa;
2.17 frystyk 708: /* Display "cover page" */
709: display_cso(target, arg, request->anchor);
2.15 luotonen 710: free(p1); /* Leak fixed Henrik 27 Feb 94 */
2.7 secret 711: return HT_LOADED; /* Local function only */
712: }
713: *query++ = 0; /* Skip '?' */
714: command = malloc(strlen("query")+ 1 + strlen(query)+ 2 + 1);
715: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
716:
717: de_escape(command, selector); /* Bug fix TBL 921208 */
718:
719: strcpy(command, "query ");
720:
721: { /* Remove plus signs 921006 */
722: char *p;
723: for (p=query; *p; p++) {
724: if (*p == '+') *p = ' ';
725: }
726: }
727: strcat(command, query);
728:
1.1 timbl 729:
730: } else { /* Not index */
731: command = command = malloc(strlen(selector)+2+1);
732: de_escape(command, selector);
733: }
734: free(p1);
735: }
736:
1.3 timbl 737: {
738: char * p = command + strlen(command);
739: *p++ = CR; /* Macros to be correct on Mac */
740: *p++ = LF;
741: *p++ = 0;
742: /* strcat(command, "\r\n"); */ /* CR LF, as in rfc 977 */
743: }
1.1 timbl 744:
745: /* Set up a socket to the server for the data:
746: */
747: s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
748: status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
749: if (status<0){
750: if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
751: arg);
752: free(command);
753: return HTInetStatus("connect");
754: }
755:
756:
757: if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
758:
759: #ifdef NOT_ASCII
760: {
761: char * p;
762: for(p = command; *p; p++) {
763: *p = TOASCII(*p);
764: }
765: }
766: #endif
767:
768: status = NETWRITE(s, command, (int)strlen(command));
769: free(command);
770: if (status<0){
771: if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
772: return HTInetStatus("send");
773: }
774:
775: /* Now read the data from the socket:
776: */
777: switch (gtype) {
778:
779: case GOPHER_HTML :
2.11 timbl 780: HTParseSocket(WWW_HTML, s, request);
1.2 timbl 781: break;
1.1 timbl 782:
1.3 timbl 783: case GOPHER_GIF:
784: case GOPHER_IMAGE:
2.11 timbl 785: HTParseSocket(HTAtom_for("image/gif"), s, request);
1.3 timbl 786: break;
1.1 timbl 787: case GOPHER_MENU :
788: case GOPHER_INDEX :
2.11 timbl 789: target = HTML_new(request, NULL, WWW_HTML,
790: request->output_format, request->output_stream);
1.2 timbl 791: targetClass = *target->isa;
2.17 frystyk 792: parse_menu(target, s,arg, request->anchor);
1.2 timbl 793: break;
2.7 secret 794:
795: case GOPHER_CSO:
2.11 timbl 796: target = HTML_new(request, NULL, WWW_HTML,
797: request->output_format, request->output_stream);
2.7 secret 798: targetClass = *target->isa;
2.17 frystyk 799: parse_cso(target, s, arg, request->anchor);
2.7 secret 800: break;
801:
802: case GOPHER_MACBINHEX:
803: case GOPHER_PCBINHEX:
804: case GOPHER_UUENCODED:
805: case GOPHER_BINARY:
2.16 luotonen 806: { /* Do our own filetyping -- maybe we get lucky */
807: HTFormat format = HTFileFormat(arg,
808: &request->content_encoding,
809: &request->content_language);
810: if (format) {
811: CTRACE(stderr,
812: "Gopher...... figured out content-type myself: %s\n",
813: HTAtom_name(format));
814: HTParseSocket(format, s, request);
815: }
816: else {
817: CTRACE(stderr,"Gopher...... using www/unknown\n");
818: /* Specifying WWW_UNKNOWN forces dump to local disk. */
819: HTParseSocket (WWW_UNKNOWN, s, request);
820: }
821: }
2.7 secret 822: break;
823:
1.1 timbl 824: case GOPHER_TEXT :
825: default: /* @@ parse as plain text */
2.11 timbl 826: HTParseSocket(WWW_PLAINTEXT, s, request);
2.7 secret 827: break;
828:
829: case GOPHER_SOUND :
2.11 timbl 830: HTParseSocket(WWW_AUDIO, s, request);
1.2 timbl 831: break;
832:
1.1 timbl 833: } /* switch(gtype) */
1.2 timbl 834:
835: NETCLOSE(s);
836: return HT_LOADED;
1.1 timbl 837: }
1.2 timbl 838:
2.10 timbl 839: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1 timbl 840:
Webmaster