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