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