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