Annotation of libwww/Library/src/HTGopher.c, revision 2.17
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
! 246: StrAllocCopy(outstr, "Menu");
! 247: START(HTML_TITLE);
! 248: PUTS("Index of ");
! 249: HTUnEscape(outstr);
! 250: PUTS(outstr);
! 251: END(HTML_TITLE);
1.2 timbl 252: START(HTML_H1);
2.17 ! frystyk 253: PUTS("Index of ");
! 254: PUTS(outstr);
! 255: END(HTML_H1);
! 256: free(outstr);
! 257: }
! 258:
! 259: /* Make everything in list preformatted */
! 260: START(HTML_PRE);
! 261:
! 262: /* Output the header line of the list */
! 263: if (!icon_blank) icon_blank = icon_unknown;
! 264: if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
! 265: HTMLPutImg(target, icon_blank->icon_url,
! 266: HTIcon_alt_string(icon_blank->icon_alt, NO), NULL);
! 267: }
! 268: PUTC(' ');
! 269: PUTS("Name");
! 270: PUTC('\n');
! 271: START(HTML_HR);
! 272: PUTC('\n');
! 273:
! 274: while ((ch = NEXT_CHAR) >= 0) {
1.3 timbl 275: if (ch != LF) {
1.1 timbl 276: *p = ch; /* Put character in line */
277: if (p< &line[BIG-1]) p++;
278:
279: } else {
280: *p++ = 0; /* Terminate line */
281: p = line; /* Scan it to parse it */
282: port = 0; /* Flag "not parsed" */
2.17 ! frystyk 283: if (TRACE)
! 284: fprintf(stderr, "HTGopher.... Menu item: `%s\'\n", line);
1.1 timbl 285: gtype = *p++;
286:
287: /* Break on line with a dot by itself */
288: if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
289:
290: if (gtype && *p) {
291: name = p;
292: selector = strchr(name, TAB);
293: if (selector) {
294: *selector++ = 0; /* Terminate name */
295: host = strchr(selector, TAB);
296: if (host) {
297: *host++ = 0; /* Terminate selector */
298: port = strchr(host, TAB);
299: if (port) {
300: char *junk;
301: port[0] = ':'; /* delimit host a la W3 */
302: junk = strchr(port, TAB);
303: if (junk) *junk++ = 0; /* Chop port */
304: if ((port[1]=='0') && (!port[2]))
305: port[0] = 0; /* 0 means none */
306: } /* no port */
307: } /* host ok */
308: } /* selector ok */
309: } /* gtype and name ok */
2.17 ! frystyk 310:
! 311: /* Get Icon type and output the icon */
! 312: if (HTDirShowMask & HT_DIR_SHOW_ICON) {
! 313: HTIconNode *icon = get_gopher_icon(arg, gtype);
! 314: if (icon && icon->icon_url) {
! 315: HTMLPutImg(target, icon->icon_url,
! 316: HTIcon_alt_string(icon->icon_alt, YES),
! 317: NULL);
! 318: PUTC(' ');
! 319: }
! 320: }
1.1 timbl 321:
322: if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
2.17 ! frystyk 323: write_anchor(target, name, selector);
! 324:
1.1 timbl 325: } else if (port) { /* Other types need port */
326: if (gtype == GOPHER_TELNET) {
327: if (*selector) sprintf(address, "telnet://%s@%s/",
2.7 secret 328: selector, host);
1.1 timbl 329: else sprintf(address, "telnet://%s/", host);
2.7 secret 330: }
331: else if (gtype == GOPHER_TN3270)
332: {
333: if (*selector)
334: sprintf(address, "tn3270://%s@%s/",
335: selector, host);
336: else
337: sprintf(address, "tn3270://%s/", host);
338: }
339: else { /* If parsed ok */
1.1 timbl 340: char *q;
341: char *p;
342: sprintf(address, "//%s/%c", host, gtype);
343: q = address+ strlen(address);
344: for(p=selector; *p; p++) { /* Encode selector string */
2.14 luotonen 345: if (acceptable[(int)*p]) *q++ = *p;
1.1 timbl 346: else {
347: *q++ = HEX_ESCAPE; /* Means hex coming */
348: *q++ = hex[(TOASCII(*p)) >> 4];
349: *q++ = hex[(TOASCII(*p)) & 15];
350: }
351: }
352: *q++ = 0; /* terminate address */
353: }
2.17 ! frystyk 354:
2.7 secret 355: /* Error response from Gopher doesn't deserve to
356: be a hyperlink. */
357: if (strcmp (address, "gopher://error.host:1/0"))
2.17 ! frystyk 358: write_anchor(target, name, address);
2.7 secret 359: else
360: PUTS(name);
2.17 ! frystyk 361: PUTC('\n');
1.1 timbl 362: } else { /* parse error */
2.17 ! frystyk 363: if (TRACE) fprintf(stderr, "HTGopher.... Bad menu item.\n");
1.2 timbl 364: PUTS(line);
365:
1.1 timbl 366: } /* parse error */
367:
368: p = line; /* Start again at beginning of line */
2.17 ! frystyk 369: ++files; /* Update number of files */
! 370:
1.1 timbl 371: } /* if end of line */
372:
373: } /* Loop over characters */
374:
2.17 ! frystyk 375: {
! 376: char *outstr;
! 377:
! 378: if ((outstr = (char *) malloc(100)) == NULL)
! 379: outofmem(__FILE__, "parse_menu");
! 380: if (files == 0)
! 381: sprintf(outstr, "Empty directory");
! 382: else if (files == 1)
! 383: sprintf(outstr, "1 file");
! 384: else
! 385: sprintf(outstr, "%u files", files);
! 386: START(HTML_HR);
! 387: PUTS(outstr);
! 388: free(outstr);
! 389: END(HTML_PRE);
! 390: }
! 391:
1.2 timbl 392: FREE_TARGET;
2.11 timbl 393: HTInputSocket_free(isoc);
1.1 timbl 394: return;
395: }
2.11 timbl 396:
397:
2.7 secret 398: /* Parse a Gopher CSO document
399: ** ============================
400: **
401: ** Accepts an open socket to a CSO server waiting to send us
402: ** data and puts it on the screen in a reasonable manner.
403: **
404: ** Perhaps this data can be automatically linked to some
405: ** other source as well???
406: **
407: ** Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
408: ** on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret,
409: ** secret@dxcern.cern.ch .
410: */
411:
2.17 ! frystyk 412: PRIVATE void parse_cso ARGS4 (HTStructured *, target,
! 413: int, s,
! 414: CONST char *, arg,
! 415: HTParentAnchor *, anAnchor)
2.7 secret 416: {
2.17 ! frystyk 417: int ch;
2.7 secret 418: char line[BIG];
419: char *p = line;
420: char *second_colon, last_char='\0';
421: CONST char *title;
2.11 timbl 422: HTInputSocket * isoc = HTInputSocket_new(s);
2.7 secret 423:
424: title = HTAnchor_title(anAnchor);
425: START(HTML_H1);
426: PUTS("CSO Search Results");
427: END(HTML_H1);
428: START(HTML_PRE);
429:
430: /* start grabbing chars from the network */
2.17 ! frystyk 431: while ((ch = NEXT_CHAR) >= 0)
2.7 secret 432: {
433: if (ch != '\n')
434: {
435: *p = ch; /* Put character in line */
436: if (p< &line[BIG-1]) p++;
437: }
438: else
439: {
440: *p++ = 0; /* Terminate line */
441: p = line; /* Scan it to parse it */
442:
443: /* OK we now have a line in 'p' lets parse it and
444: print it */
445:
446: /* Break on line that begins with a 2. It's the end of
447: * data.
448: */
449: if (*p == '2')
450: break;
451:
452: /* lines beginning with 5 are errors,
453: * print them and quit
454: */
455: if (*p == '5') {
456: START(HTML_H2);
457: PUTS(p+4);
458: END(HTML_H2);
459: break;
460: }
461:
462: if(*p == '-') {
463: /* data lines look like -200:#:
464: * where # is the search result number and can be
465: * multiple digits (infinate?)
466: * find the second colon and check the digit to the
467: * left of it to see if they are diferent
468: * if they are then a different person is starting.
469: * make this line an <h2>
470: */
471:
472: /* find the second_colon */
473: second_colon = strchr( strchr(p,':')+1, ':');
474:
475: if(second_colon != NULL) { /* error check */
476:
477: if (*(second_colon-1) != last_char)
478: /* print seperator */
479: {
480: END(HTML_PRE);
481: START(HTML_H2);
482: }
483:
484:
485: /* right now the record appears with the alias
486: * (first line)
487: * as the header and the rest as <pre> text
488: * It might look better with the name as the
489: * header and the rest as a <ul> with <li> tags
490: * I'm not sure whether the name field comes in any
491: * special order or if its even required in a
492: * record,
493: * so for now the first line is the header no
494: * matter
495: * what it is (it's almost always the alias)
496: * A <dl> with the first line as the <DT> and
497: * the rest as some form of <DD> might good also?
498: */
499:
500: /* print data */
501: PUTS(second_colon+1);
502: PUTS("\n");
503:
504: if (*(second_colon-1) != last_char)
505: /* end seperator */
506: {
507: END(HTML_H2);
508: START(HTML_PRE);
509: }
510:
511: /* save the char before the second colon
512: * for comparison on the next pass
513: */
514: last_char = *(second_colon-1) ;
515:
516: } /* end if second_colon */
517: } /* end if *p == '-' */
518: } /* if end of line */
519:
520: } /* Loop over characters */
521:
522: /* end the text block */
523: PUTS("\n");
524: END(HTML_PRE);
525: PUTS("\n");
526: FREE_TARGET;
2.11 timbl 527: HTInputSocket_free(isoc);
2.7 secret 528:
529: return; /* all done */
530: } /* end of procedure */
1.1 timbl 531:
532: /* Display a Gopher Index document
2.7 secret 533: ** -------------------------------
534: */
1.1 timbl 535:
2.17 ! frystyk 536: PRIVATE void display_index ARGS3 (HTStructured *, target,
! 537: CONST char *, arg,
! 538: HTParentAnchor *, anAnchor)
1.1 timbl 539: {
1.2 timbl 540:
541: START(HTML_H1);
542: PUTS(arg);
2.7 secret 543: PUTS(" index");
1.2 timbl 544: END(HTML_H1);
2.7 secret 545: START(HTML_ISINDEX);
546: PUTS("\nThis is a searchable Gopher index.");
547: PUTS(" Please enter keywords to search for.\n");
548:
549: if (!HTAnchor_title(anAnchor))
550: HTAnchor_setTitle(anAnchor, arg);
1.2 timbl 551:
2.7 secret 552: FREE_TARGET;
553: return;
554: }
555:
556:
557: /* Display a CSO index document
558: ** -------------------------------
559: */
560:
2.17 ! frystyk 561: PRIVATE void display_cso ARGS3(HTStructured *, target,
! 562: CONST char *, arg,
! 563: HTParentAnchor *, anAnchor)
2.7 secret 564: {
565: START(HTML_H1);
566: PUTS(arg);
567: PUTS(" index");
568: END(HTML_H1);
569: START(HTML_ISINDEX);
570: PUTS("\nThis is a searchable index of a CSO database.\n");
571: PUTS(" Please enter keywords to search for. The keywords that you enter");
572: PUTS(" will allow you to search on a person's name in the database.\n");
573:
1.1 timbl 574: if (!HTAnchor_title(anAnchor))
1.2 timbl 575: HTAnchor_setTitle(anAnchor, arg);
1.1 timbl 576:
1.2 timbl 577: FREE_TARGET;
1.1 timbl 578: return;
579: }
580:
581:
582: /* De-escape a selector into a command
583: ** -----------------------------------
584: **
585: ** The % hex escapes are converted. Otheriwse, the string is copied.
586: */
587: PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
588: {
589: CONST char * p = selector;
590: char * q = command;
591: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
592: while (*p) { /* Decode hex */
593: if (*p == HEX_ESCAPE) {
594: char c;
595: unsigned int b;
596: p++;
597: c = *p++;
598: b = from_hex(c);
599: c = *p++;
600: if (!c) break; /* Odd number of chars! */
601: *q++ = FROMASCII((b<<4) + from_hex(c));
602: } else {
603: *q++ = *p++; /* Record */
604: }
605: }
606: *q++ = 0; /* Terminate command */
607:
608: }
609:
610:
611: /* Load by name HTLoadGopher
612: ** ============
613: **
614: ** Bug: No decoding of strange data types as yet.
615: **
616: */
2.13 timbl 617: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 618: {
2.13 timbl 619: CONST char * arg = HTAnchor_physical(request->anchor);
1.1 timbl 620: char *command; /* The whole command */
621: int status; /* tcp return */
622: char gtype; /* Gopher Node type */
623: char * selector; /* Selector string */
624: struct sockaddr_in soc_address; /* Binary network address */
625: struct sockaddr_in* sin = &soc_address;
2.17 ! frystyk 626: HTStructured *target; /* HTML object */
! 627: HTStructuredClass targetClass;
1.1 timbl 628:
629: if (!acceptable_inited) init_acceptable();
630:
631: if (!arg) return -3; /* Bad if no name sepcified */
632: if (!*arg) return -2; /* Bad if name had zero length */
633:
2.17 ! frystyk 634: if (TRACE) fprintf(stderr, "HTGopher.... Looking for %s\n", arg);
1.1 timbl 635:
636:
637: /* Set up defaults:
638: */
639: sin->sin_family = AF_INET; /* Family, host order */
640: sin->sin_port = htons(GOPHER_PORT); /* Default: new port, */
641:
642: /* Get node name and optional port number:
643: */
644: {
645: char *p1 = HTParse(arg, "", PARSE_HOST);
646: int status = HTParseInet(sin, p1);
647: free(p1);
648: if (status) return status; /* Bad */
649: }
650:
651: /* Get entity type, and selector string.
652: */
653: {
654: char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
655: gtype = '1'; /* Default = menu */
656: selector = p1;
657: if ((*selector++=='/') && (*selector)) { /* Skip first slash */
658: gtype = *selector++; /* Pick up gtype */
659: }
660: if (gtype == GOPHER_INDEX) {
661: char * query;
2.10 timbl 662: HTAnchor_setIndex(request->anchor); /* Search is allowed */
1.1 timbl 663: query = strchr(selector, '?'); /* Look for search string */
664: if (!query || !query[1]) { /* No search required */
2.11 timbl 665: target = HTML_new(request, NULL, WWW_HTML,
666: request->output_format, request->output_stream);
1.2 timbl 667: targetClass = *target->isa;
2.17 ! frystyk 668: /* Display "cover page" */
! 669: display_index(target, arg, request->anchor);
2.15 luotonen 670: free(p1); /* Leak fixed Henrik 27 Feb 94 */
2.6 timbl 671: return HT_LOADED; /* Local function only */
1.1 timbl 672: }
673: *query++ = 0; /* Skip '?' */
674: command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
675: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
676:
677: de_escape(command, selector); /* Bug fix TBL 921208 */
678:
679: strcat(command, "\t");
680:
681: { /* Remove plus signs 921006 */
682: char *p;
683: for (p=query; *p; p++) {
684: if (*p == '+') *p = ' ';
685: }
686: }
687: strcat(command, query);
2.7 secret 688: } else if (gtype == GOPHER_CSO) {
689: char * query;
2.10 timbl 690: HTAnchor_setIndex(request->anchor); /* Search is allowed */
2.7 secret 691: query = strchr(selector, '?'); /* Look for search string */
692: if (!query || !query[1]) { /* No search required */
2.11 timbl 693: target = HTML_new(request, NULL, WWW_HTML,
694: request->output_format, request->output_stream);
2.7 secret 695: targetClass = *target->isa;
2.17 ! frystyk 696: /* Display "cover page" */
! 697: display_cso(target, arg, request->anchor);
2.15 luotonen 698: free(p1); /* Leak fixed Henrik 27 Feb 94 */
2.7 secret 699: return HT_LOADED; /* Local function only */
700: }
701: *query++ = 0; /* Skip '?' */
702: command = malloc(strlen("query")+ 1 + strlen(query)+ 2 + 1);
703: if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
704:
705: de_escape(command, selector); /* Bug fix TBL 921208 */
706:
707: strcpy(command, "query ");
708:
709: { /* Remove plus signs 921006 */
710: char *p;
711: for (p=query; *p; p++) {
712: if (*p == '+') *p = ' ';
713: }
714: }
715: strcat(command, query);
716:
1.1 timbl 717:
718: } else { /* Not index */
719: command = command = malloc(strlen(selector)+2+1);
720: de_escape(command, selector);
721: }
722: free(p1);
723: }
724:
1.3 timbl 725: {
726: char * p = command + strlen(command);
727: *p++ = CR; /* Macros to be correct on Mac */
728: *p++ = LF;
729: *p++ = 0;
730: /* strcat(command, "\r\n"); */ /* CR LF, as in rfc 977 */
731: }
1.1 timbl 732:
733: /* Set up a socket to the server for the data:
734: */
735: s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
736: status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
737: if (status<0){
738: if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
739: arg);
740: free(command);
741: return HTInetStatus("connect");
742: }
743:
744:
745: if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
746:
747: #ifdef NOT_ASCII
748: {
749: char * p;
750: for(p = command; *p; p++) {
751: *p = TOASCII(*p);
752: }
753: }
754: #endif
755:
756: status = NETWRITE(s, command, (int)strlen(command));
757: free(command);
758: if (status<0){
759: if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
760: return HTInetStatus("send");
761: }
762:
763: /* Now read the data from the socket:
764: */
765: switch (gtype) {
766:
767: case GOPHER_HTML :
2.11 timbl 768: HTParseSocket(WWW_HTML, s, request);
1.2 timbl 769: break;
1.1 timbl 770:
1.3 timbl 771: case GOPHER_GIF:
772: case GOPHER_IMAGE:
2.11 timbl 773: HTParseSocket(HTAtom_for("image/gif"), s, request);
1.3 timbl 774: break;
1.1 timbl 775: case GOPHER_MENU :
776: case GOPHER_INDEX :
2.11 timbl 777: target = HTML_new(request, NULL, WWW_HTML,
778: request->output_format, request->output_stream);
1.2 timbl 779: targetClass = *target->isa;
2.17 ! frystyk 780: parse_menu(target, s,arg, request->anchor);
1.2 timbl 781: break;
2.7 secret 782:
783: case GOPHER_CSO:
2.11 timbl 784: target = HTML_new(request, NULL, WWW_HTML,
785: request->output_format, request->output_stream);
2.7 secret 786: targetClass = *target->isa;
2.17 ! frystyk 787: parse_cso(target, s, arg, request->anchor);
2.7 secret 788: break;
789:
790: case GOPHER_MACBINHEX:
791: case GOPHER_PCBINHEX:
792: case GOPHER_UUENCODED:
793: case GOPHER_BINARY:
2.16 luotonen 794: { /* Do our own filetyping -- maybe we get lucky */
795: HTFormat format = HTFileFormat(arg,
796: &request->content_encoding,
797: &request->content_language);
798: if (format) {
799: CTRACE(stderr,
800: "Gopher...... figured out content-type myself: %s\n",
801: HTAtom_name(format));
802: HTParseSocket(format, s, request);
803: }
804: else {
805: CTRACE(stderr,"Gopher...... using www/unknown\n");
806: /* Specifying WWW_UNKNOWN forces dump to local disk. */
807: HTParseSocket (WWW_UNKNOWN, s, request);
808: }
809: }
2.7 secret 810: break;
811:
1.1 timbl 812: case GOPHER_TEXT :
813: default: /* @@ parse as plain text */
2.11 timbl 814: HTParseSocket(WWW_PLAINTEXT, s, request);
2.7 secret 815: break;
816:
817: case GOPHER_SOUND :
2.11 timbl 818: HTParseSocket(WWW_AUDIO, s, request);
1.2 timbl 819: break;
820:
1.1 timbl 821: } /* switch(gtype) */
1.2 timbl 822:
823: NETCLOSE(s);
824: return HT_LOADED;
1.1 timbl 825: }
1.2 timbl 826:
2.10 timbl 827: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1 timbl 828:
Webmaster