Annotation of libwww/Library/src/HTGopher.c, revision 2.47
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.42 frystyk 17: ** Sep 95 HFN Made non-blocking and state stream oriented
1.1 timbl 18: */
19:
2.20 frystyk 20: /* Library include files */
2.34 frystyk 21: #include "tcp.h"
22: #include "HTUtils.h"
23: #include "HTString.h"
2.20 frystyk 24: #include "HTParse.h"
25: #include "HTTCP.h"
2.17 frystyk 26: #include "HTIcons.h"
2.42 frystyk 27: #include "HTReqMan.h"
2.38 frystyk 28: #include "HTSocket.h"
1.1 timbl 29: #include "HTFormat.h"
2.42 frystyk 30: #include "HTGuess.h"
2.20 frystyk 31: #include "HTError.h"
2.42 frystyk 32: #include "HTWriter.h"
2.43 frystyk 33: #include "HTNetMan.h"
2.36 frystyk 34: #include "HTBind.h"
2.46 frystyk 35: #include "HTMLPDTD.h"
2.40 frystyk 36: #include "HTMLGen.h"
2.45 frystyk 37: #include "HTDir.h"
2.20 frystyk 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.20 frystyk 50:
51: /* Type definitions and global variables etc. local to this module */
52: typedef enum _HTGopherType {
2.42 frystyk 53: GT_TEXT = '0',
54: GT_MENU = '1',
55: GT_CSO = '2',
56: GT_ERROR = '3',
57: GT_MACBINHEX = '4',
58: GT_PCBINHEX = '5',
59: GT_UUENCODED = '6',
60: GT_INDEX = '7',
61: GT_TELNET = '8',
62: GT_BINARY = '9',
63: GT_GIF = 'g',
64: GT_HTML = 'h', /* HTML */
65: GT_INFO = 'i',
66: GT_SOUND = 's',
67: GT_WWW = 'w', /* W3 address */
68: GT_IMAGE = 'I',
69: GT_TN3270 = 'T',
70: GT_DUPLICATE = '+',
71: GT_PLUS_IMAGE = ':', /* Addition from Gopher Plus */
72: GT_PLUS_MOVIE = ';',
73: GT_PLUS_SOUND = '<',
74: GT_EOF = '.'
2.20 frystyk 75: } HTGopherType;
76:
2.42 frystyk 77: /* Final states have negative value */
78: typedef enum _GopherState {
79: GOPHER_ERROR = -3,
80: GOPHER_NO_DATA = -2,
81: GOPHER_GOT_DATA = -1,
82: GOPHER_BEGIN = 0,
83: GOPHER_NEED_CONNECTION,
84: GOPHER_NEED_REQUEST,
85: GOPHER_NEED_RESPONSE
86: } GopherState;
1.2 timbl 87:
2.42 frystyk 88: /* This is the context structure for the this module */
2.20 frystyk 89: typedef struct _gopher_info {
2.30 frystyk 90: HTGopherType type; /* Gopher item type */
2.42 frystyk 91: GopherState state;
92: char * cmd;
2.20 frystyk 93: } gopher_info;
1.1 timbl 94:
2.42 frystyk 95: #define MAX_GOPHER_LINE 256
96:
97: struct _HTStructured {
98: CONST HTStructuredClass * isa;
99: };
100:
101: struct _HTStream {
102: CONST HTStreamClass * isa;
103: HTStructured * target;
104: HTRequest * request;
105: HTSocketEOL state;
106: char * url;
107: BOOL pre; /* Preformatted mode? */
108: BOOL junk; /* For too long lines */
109: BOOL CSO;
110: char cso_rec[10]; /* CSO record number */
111: char buffer[MAX_GOPHER_LINE+1];
112: int buflen;
113: };
114:
2.45 frystyk 115: PRIVATE HTDirShow dir_show = HT_DS_ICON;
116:
2.20 frystyk 117: /* ------------------------------------------------------------------------- */
1.1 timbl 118:
2.42 frystyk 119: /* GopherIcon
120: ** ----------
2.20 frystyk 121: ** This function finds an appopriate icon for the item in the gopher
122: ** list. Actually it is only a shell build upon HTGetIcon().
2.17 frystyk 123: */
2.42 frystyk 124: PRIVATE HTIconNode *GopherIcon (HTGopherType type)
2.17 frystyk 125: {
2.36 frystyk 126: HTFormat content_type = NULL;
127: HTEncoding content_encoding = NULL;
2.45 frystyk 128: HTFileMode mode = HT_IS_FILE;
2.42 frystyk 129: switch(type) {
2.45 frystyk 130: case GT_MENU:
131: mode = HT_IS_DIR;
2.42 frystyk 132: case GT_TEXT:
2.17 frystyk 133: content_type = HTAtom_for("text/void");
134: break;
2.42 frystyk 135: case GT_IMAGE:
136: case GT_PLUS_IMAGE:
137: case GT_GIF:
2.17 frystyk 138: content_type = HTAtom_for("image/void");
139: break;
2.42 frystyk 140: case GT_WWW:
141: case GT_HTML:
2.17 frystyk 142: content_type = HTAtom_for("text/void");
143: break;
2.42 frystyk 144: case GT_SOUND:
145: case GT_PLUS_SOUND:
2.17 frystyk 146: content_type = HTAtom_for("audio/void");
147: break;
2.42 frystyk 148: case GT_PLUS_MOVIE:
2.20 frystyk 149: content_type = HTAtom_for("video/void");
2.17 frystyk 150: break;
2.42 frystyk 151: case GT_INDEX:
2.17 frystyk 152: content_type = HTAtom_for("application/x-gopher-index");
153: break;
2.42 frystyk 154: case GT_CSO:
2.17 frystyk 155: content_type = HTAtom_for("application/x-gopher-cso");
156: break;
2.42 frystyk 157: case GT_TELNET:
2.17 frystyk 158: content_type = HTAtom_for("application/x-gopher-telnet");
159: break;
2.42 frystyk 160: case GT_TN3270:
2.17 frystyk 161: content_type = HTAtom_for("application/x-gopher-tn3270");
162: break;
2.42 frystyk 163: case GT_DUPLICATE:
2.17 frystyk 164: content_type = HTAtom_for("application/x-gopher-duplicate");
165: break;
2.42 frystyk 166: case GT_ERROR:
2.17 frystyk 167: content_type = HTAtom_for("www/unknown");
168: break;
2.42 frystyk 169: case GT_MACBINHEX:
170: case GT_PCBINHEX:
171: case GT_UUENCODED:
2.36 frystyk 172: content_type = WWW_BINARY;
173: content_encoding = WWW_ENC_BASE64;
174: break;
2.42 frystyk 175: case GT_BINARY:
2.36 frystyk 176: content_type = WWW_BINARY;
177: break;
2.17 frystyk 178: default:
179: content_type = HTAtom_for("www/unknown");
180: break;
181: }
2.45 frystyk 182: return HTGetIcon(mode, content_type, content_encoding);
2.17 frystyk 183: }
184:
2.42 frystyk 185: /* ------------------------------------------------------------------------- */
186: /* STREAMS */
187: /* ------------------------------------------------------------------------- */
2.17 frystyk 188:
2.42 frystyk 189: /* GopherTitle
190: ** -----------
191: ** Create the top part of the page
1.1 timbl 192: */
2.42 frystyk 193: PRIVATE BOOL GopherTitle (HTStream *me)
194: {
195: HTStructured *target = me->target;
196: char *str = NULL;
197: StrAllocCopy(str, me->CSO ? "CSO Search " : "GopherMenu");
2.28 frystyk 198:
2.42 frystyk 199: START(HTML_HTML);
200: START(HTML_HEAD);
201: START(HTML_TITLE);
202: if (me->CSO) {
203: char *keyword = strchr(me->url, '?');
204: if (keyword) {
205: StrAllocCat(str, "for ");
206: StrAllocCat(str, ++keyword);
207: }
208: }
209: PUTS(str);
210: END(HTML_TITLE);
211: END(HTML_HEAD);
2.26 frystyk 212:
2.42 frystyk 213: START(HTML_BODY);
214: START(HTML_H1);
215: PUTS(str);
216: END(HTML_H1);
217: free(str);
218: return YES;
219: }
1.1 timbl 220:
2.42 frystyk 221: /* GopherBottom
222: ** ------------
223: ** Create the bottom part of the page
224: */
225: PRIVATE BOOL GopherBottom (HTStream *me)
226: {
227: HTStructured *target = me->target;
228: if (me->pre)
229: END(HTML_PRE);
230: END(HTML_BODY);
231: END(HTML_HTML);
232: return YES;
233: }
2.17 frystyk 234:
2.42 frystyk 235: /* GopherMenuLine
236: ** --------------
237: ** Parses a Gopher Menu Line
238: ** Return YES if more data else NO
239: */
240: PRIVATE BOOL GopherMenuLine (HTStream *me, char *line)
241: {
242: HTStructured *target = me->target;
243: HTGopherType gtype = (HTGopherType) *line++;
244: if (PROT_TRACE)
2.47 ! frystyk 245: TTYPrint(TDEST, "HTGopher.... Menu line: `%s\'\n", line);
2.42 frystyk 246: if (gtype == GT_INFO) {
247: char *stop = strchr(line, '\t');
248: if (stop) *stop = '\0';
249: PUTS(line);
250: } else if (gtype == GT_ERROR) {
251: char *stop = strchr(line, '\t');
252: if (stop) *stop = '\0';
253: PUTS(line);
254: } else if ((strstr(line, "error.host") || strstr(line, "errorhost"))) {
255: char *stop = strchr(line, '\t'); /* Chop off error.host */
256: if (stop) *stop = '\0';
257: PUTS(line);
258: } else if (gtype == GT_EOF) {
259: return NO;
260: } else { /* Parse normal gopher menu line */
261: char *name = line; /* Get link information */
262: char *selector = strchr(name, '\t');
263: char *host = NULL;
264: char *port = NULL;
265: if (selector) {
266: *selector++ = '\0';
267: if ((host = strchr(selector, '\t'))) {
268: *host++ = '\0';
269: if ((port = strchr(host, '\t'))) {
270: char *junk;
271: *port = ':'; /* delimit host a la W3 */
272: if ((junk = strchr(port, '\t')) != NULL)
273: *junk = '\0'; /* Chop port */
274: if (*(port+1) == '0' && !*(port+2))
275: *port = '\0';
2.21 frystyk 276: }
2.42 frystyk 277: }
278: }
279: if (!me->pre) { /* For now we use preformatted listing */
280: START(HTML_PRE);
281: me->pre = YES;
282: }
2.45 frystyk 283: if (dir_show & HT_DS_ICON) { /* Put out the icon */
2.42 frystyk 284: HTIconNode *icon = GopherIcon(gtype);
285: if (icon && icon->icon_url) {
286: HTMLPutImg(target, icon->icon_url,
287: HTIcon_alt_string(icon->icon_alt, YES), NULL);
288: PUTC(' ');
289: }
290: }
291: if (gtype == GT_WWW) { /* Gopher pointer to W3 */
292: char *escaped = NULL;
293: escaped = HTEscape(selector, URL_PATH);
294: HTStartAnchor(target, NULL, escaped);
295: PUTS(name);
296: END(HTML_A);
297: free(escaped);
298: } else if (port) { /* Other types need port */
299: char *escaped = NULL;
300: char *address = NULL;
301: int addr_len;
302:
303: /* Calculate the length of the WWW-address */
304: if (selector && *selector) {
305: escaped = HTEscape(selector, URL_PATH);
306: addr_len = 15 + strlen(escaped) + strlen(host) + 1;
307: } else {
308: addr_len = 15 + strlen(host) + 1;
309: }
310: if ((address = (char *) malloc(addr_len)) == NULL)
311: outofmem(__FILE__, "GopherMenuLine");
312: *address = '\0';
313:
314: if (gtype == GT_TELNET) {
315: if (escaped)
316: sprintf(address, "telnet://%s@%s/", escaped, host);
317: else
318: sprintf(address, "telnet://%s/", host);
319: } else if (gtype == GT_TN3270) {
320: if (escaped)
321: sprintf(address, "tn3270://%s@%s/", escaped, host);
322: else
323: sprintf(address, "tn3270://%s/", host);
324: } else {
325: if (escaped)
326: sprintf(address, "//%s/%c%s", host, gtype, escaped);
327: else
328: sprintf(address, "//%s/%c", host, gtype);
329: }
330:
331: HTStartAnchor(target, NULL, address);
332: PUTS(name);
333: END(HTML_A);
334: FREE(address);
335: FREE(escaped);
336: PUTC('\n');
337: } else { /* If parse error */
338: if (PROT_TRACE)
2.47 ! frystyk 339: TTYPrint(TDEST, "HTGopher.... Bad menu item, `%s\'\n", line);
2.42 frystyk 340: }
341: }
342: return YES;
343: }
2.21 frystyk 344:
2.42 frystyk 345: /* GopherCSOLine
346: ** --------------
347: ** Parses a Gopher Menu Line
348: ** Return YES if more data else NO
349: */
350: PRIVATE BOOL GopherCSOLine (HTStream *me, char *line)
351: {
352: HTStructured *target = me->target;
353: if (*line == '1') { /* Information line */
354: char *start = strchr(line, ':');
355: start = start ? ++start : line;
356: PUTS(start);
357: } else if (*line == '2') { /* Transfer complete */
358: return NO;
359: } else if (*line == '5') { /* Error */
360: char *start = strchr(line, ':');
361: start = start ? ++start : line;
362: PUTS(start);
363: } else if (*line == '-') { /* data */
364: /* data lines look like '-200:code:field:value'
365: * where code is the search result number and can be
366: * multiple digits (infinte?)
367: * find the second colon and check the digit to the
368: * left of it to see if they are diferent
369: * if they are then a different person is starting.
370: */
371: char *code;
372: char *field;
373: if ((code = strchr(line, ':')) && (field = strchr(++code, ':'))) {
374: BOOL newrec = YES;
375: *field++ = '\0';
376: if (!*me->cso_rec) { /* Header of first record */
377: START(HTML_DL);
378: } else if (strcmp(me->cso_rec, code)) { /* Another new record */
379: START(HTML_B);
380: } else
381: newrec = NO;
382: START(HTML_DT);
383:
384: /* I'm not sure whether the name field comes in any
385: * special order or if its even required in a
386: * record, so for now the first line is the header
387: * no matter what it is (it's almost always the
388: * alias)
389: */
390: {
391: char *value = strchr(field, ':');
392: if (!value)
393: value = "Empty value";
394: else
395: *value++ = '\0';
396: {
397: char *strip = HTStrip(field);
398: PUTS(strip);
399: START(HTML_DD);
400: strip = HTStrip(value);
401: if (newrec) {
402: PUTS(strip);
403: END(HTML_B);
2.20 frystyk 404: } else
2.42 frystyk 405: PUTS(strip);
406: }
2.20 frystyk 407:
2.42 frystyk 408: /* Save the code for comparison on the next pass */
409: strcpy(me->cso_rec, code);
2.20 frystyk 410: }
2.42 frystyk 411: }
412: } else { /* Unknown line */
413: char *start = strchr(line, ':');
414: start = start ? ++start : line;
415: PUTS(start);
2.20 frystyk 416: }
2.42 frystyk 417: return YES;
418: }
1.2 timbl 419:
2.42 frystyk 420: /*
421: ** Searches for Gopher line until buffer fills up or a CRLF or LF is found
422: */
423: PRIVATE int GopherMenu_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
424: {
425: while (l-- > 0) {
426: if (me->state == EOL_FCR) {
427: if (*b == LF && me->buflen) {
428: if (!me->junk) {
429: BOOL cont;
430: *(me->buffer+me->buflen) = '\0';
431: cont = me->CSO ? GopherCSOLine(me, me->buffer) :
432: GopherMenuLine(me, me->buffer);
433: if (cont == NO) return HT_LOADED;
434: } else
435: me->junk = NO; /* back to normal */
2.20 frystyk 436: }
2.45 frystyk 437: me->buflen = 0;
438: me->state = EOL_BEGIN;
2.42 frystyk 439: } else if (*b == CR) {
440: me->state = EOL_FCR;
441: } else if (*b == LF && me->buflen) {
442: if (!me->junk) {
443: BOOL cont;
444: *(me->buffer+me->buflen) = '\0';
445: cont = me->CSO ? GopherCSOLine(me, me->buffer) :
446: GopherMenuLine(me, me->buffer);
447: if (cont == NO) return HT_LOADED;
448: } else
449: me->junk = NO; /* back to normal */
450: me->buflen = 0;
451: me->state = EOL_BEGIN;
452: } else {
453: *(me->buffer+me->buflen++) = *b;
454: if (me->buflen >= MAX_GOPHER_LINE) {
455: if (PROT_TRACE)
2.47 ! frystyk 456: TTYPrint(TDEST, "Gopher...... Line too long - ignored\n");
2.42 frystyk 457: me->buflen = 0;
458: me->junk = YES;
2.20 frystyk 459: }
460: }
2.42 frystyk 461: b++;
2.17 frystyk 462: }
2.42 frystyk 463: return HT_OK;
464: }
2.17 frystyk 465:
2.42 frystyk 466: PRIVATE int GopherMenu_put_string ARGS2(HTStream *, me, CONST char*, s)
467: {
468: return GopherMenu_put_block(me, s, (int) strlen(s));
1.1 timbl 469: }
2.11 timbl 470:
2.42 frystyk 471: PRIVATE int GopherMenu_put_character ARGS2(HTStream *, me, char, c)
472: {
473: return GopherMenu_put_block(me, &c, 1);
474: }
2.11 timbl 475:
2.42 frystyk 476: PRIVATE int GopherMenu_flush ARGS1(HTStream *, me)
477: {
478: return (*me->target->isa->flush)(me->target);
479: }
480:
481: PRIVATE int GopherMenu_free ARGS1(HTStream *, me)
482: {
483: int status = HT_OK;
484: GopherBottom(me);
485: if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
486: return HT_WOULD_BLOCK;
487: free(me);
488: return HT_OK;
489: }
490:
491: PRIVATE int GopherMenu_abort ARGS2(HTStream *, me, HTError, e)
492: {
493: (*me->target->isa->abort)(me->target, e);
494: free(me);
495: if (PROT_TRACE)
2.47 ! frystyk 496: TTYPrint(TDEST, "Gopher...... ABORTING...\n");
2.42 frystyk 497: return HT_ERROR;
498: }
499:
500: /* Goper Menu Stream
501: ** -----------------
2.20 frystyk 502: */
2.42 frystyk 503: PRIVATE CONST HTStreamClass GopherMenuClass =
504: {
505: "GopherMenu",
506: GopherMenu_flush,
507: GopherMenu_free,
508: GopherMenu_abort,
509: GopherMenu_put_character,
510: GopherMenu_put_string,
511: GopherMenu_put_block
512: };
2.26 frystyk 513:
2.42 frystyk 514: /*
515: ** Stream for creating a HTML object out of a Gopher Menu object
516: */
517: PRIVATE HTStream * GopherMenu_new (HTRequest * request, char *url, BOOL CSO)
518: {
519: HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
520: if (!me) outofmem(__FILE__, "GopherMenu_new");
521: me->isa = &GopherMenuClass;
522: me->target = HTMLGenerator(request, NULL, WWW_HTML,
523: request->output_format, request->output_stream);
524: HTAnchor_setFormat(request->anchor, WWW_HTML);
525: me->request = request;
526: me->state = EOL_BEGIN;
527: me->url = url;
528: me->CSO = CSO;
529: GopherTitle(me);
530: return me;
531: }
2.20 frystyk 532:
2.42 frystyk 533: /* ------------------------------------------------------------------------- */
534: /* GOPHER STATE MACHINE */
535: /* ------------------------------------------------------------------------- */
2.7 secret 536:
2.42 frystyk 537: /* GopherCleanup
538: ** -------------
539: ** This function closes the connection and frees memory.
540: ** Returns YES if OK, else NO
541: */
542: PRIVATE BOOL GopherCleanup (HTRequest *req, int status)
543: {
544: HTNet *net = req->net;
545: gopher_info *gopher = (gopher_info *) net->context;
2.20 frystyk 546:
2.42 frystyk 547: /* Free stream with data TO network */
548: if (req->input_stream) {
549: if (status == HT_INTERRUPTED)
550: (*req->input_stream->isa->abort)(req->input_stream, NULL);
551: else
552: (*req->input_stream->isa->_free)(req->input_stream);
2.20 frystyk 553: }
554:
2.42 frystyk 555: /* Remove the request object and our own context structure for gopher */
556: HTNet_delete(net, status);
557: if (gopher) {
558: FREE(gopher->cmd);
559: free(gopher);
2.34 frystyk 560: }
2.42 frystyk 561: return YES;
2.20 frystyk 562: }
2.7 secret 563:
1.1 timbl 564: /* Display a Gopher Index document
2.20 frystyk 565: ** -------------------------------
2.42 frystyk 566: ** We create a small HTML object as we have no network activity
2.20 frystyk 567: */
2.42 frystyk 568: PRIVATE void display_index (HTRequest * request)
1.1 timbl 569: {
2.40 frystyk 570: HTStructured *target = HTMLGenerator(request, NULL, WWW_HTML,
2.42 frystyk 571: request->output_format,
572: request->output_stream);
573: /* Update anchor information */
2.36 frystyk 574: HTAnchor_setFormat(request->anchor, WWW_HTML);
2.42 frystyk 575: HTAnchor_setTitle(request->anchor, "Searchable Gopher Index");
576: /* @@@ We don't set Content-Length */
577:
2.34 frystyk 578: START(HTML_HTML);
579: START(HTML_HEAD);
580: START(HTML_TITLE);
581: PUTS("Searchable Gopher Index");
582: END(HTML_TITLE);
583: END(HTML_HEAD);
584: START(HTML_BODY);
585:
1.2 timbl 586: START(HTML_H1);
2.20 frystyk 587: PUTS("Searchable Gopher Index");
1.2 timbl 588: END(HTML_H1);
2.7 secret 589: START(HTML_ISINDEX);
2.34 frystyk 590: END(HTML_BODY);
591: END(HTML_HTML);
2.42 frystyk 592: (*target->isa->_free)(target);
2.7 secret 593: }
594:
595:
596: /* Display a CSO index document
597: ** -------------------------------
2.42 frystyk 598: ** We create a small HTML object as we have no network activity
2.7 secret 599: */
2.42 frystyk 600: PRIVATE void display_cso (HTRequest * request)
2.7 secret 601: {
2.40 frystyk 602: HTStructured *target = HTMLGenerator(request, NULL, WWW_HTML,
2.42 frystyk 603: request->output_format,
604: request->output_stream);
605: /* Update anchor information */
2.36 frystyk 606: HTAnchor_setFormat(request->anchor, WWW_HTML);
2.42 frystyk 607: HTAnchor_setTitle(request->anchor, "Searchable SCO Index");
608: /* @@@ We don't set Content-Length */
609:
2.34 frystyk 610: START(HTML_HTML);
611: START(HTML_HEAD);
612: START(HTML_TITLE);
613: PUTS("Searchable Index of a CSO Name Server");
614: END(HTML_TITLE);
615: END(HTML_HEAD);
616: START(HTML_BODY);
617:
2.7 secret 618: START(HTML_H1);
2.20 frystyk 619: PUTS("Searchable Index of a CSO Name Server");
2.7 secret 620: END(HTML_H1);
2.34 frystyk 621: PUTS("A CSO Name Server usually provides directory information about people.");
2.7 secret 622: START(HTML_ISINDEX);
2.34 frystyk 623: END(HTML_BODY);
624: END(HTML_HTML);
2.42 frystyk 625: (*target->isa->_free)(target);
1.1 timbl 626: }
627:
2.42 frystyk 628: /* HTLoadGopher
629: ** ------------
2.24 frystyk 630: ** Given a hypertext address, this routine loads a gopher document
631: **
632: ** On entry,
2.42 frystyk 633: ** request This is the request structure
634: ** On Exit
635: ** returns HT_ERROR Error has occured in call back
636: ** HT_OK Call back was OK
1.1 timbl 637: */
2.42 frystyk 638: PUBLIC int HTLoadGopher (SOCKET soc, HTRequest * request, SockOps ops)
1.1 timbl 639: {
2.42 frystyk 640: int status = HT_ERROR;
641: HTNet *net = request->net;
2.20 frystyk 642: gopher_info *gopher;
2.42 frystyk 643: char *url = HTAnchor_physical(request->anchor);
2.20 frystyk 644:
2.42 frystyk 645: /*
646: ** Initiate a new gopher structure and bind to request structure
647: ** This is actually state GOPHER_BEGIN, but it can't be in the state
648: ** machine as we need the structure first.
649: */
650: if (ops == FD_NONE) {
2.47 ! frystyk 651: if (PROT_TRACE) TTYPrint(TDEST, "Gopher...... Looking for `%s\'\n",url);
2.42 frystyk 652: if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
653: outofmem(__FILE__, "HTLoadGopher");
654: gopher->type = GT_MENU;
655: gopher->state = GOPHER_BEGIN;
656: net->context = gopher;
2.44 frystyk 657: } else if (ops == FD_CLOSE) { /* Interrupted */
2.42 frystyk 658: GopherCleanup(request, HT_INTERRUPTED);
659: return HT_OK;
660: } else
661: gopher = (gopher_info *) net->context; /* Get existing copy */
662:
663: /* Now jump into the machine. We know the state from the previous run */
664: while (1) {
665: switch (gopher->state) {
666:
667: case GOPHER_BEGIN: /* Get entity type, and selector string */
668: {
669: char *path = HTParse(url, "", PARSE_PATH);
670: char *selector = path;
671: char *query = NULL;
672: char *separator = NULL;
673: if (*selector) gopher->type = (HTGopherType) *selector++;
674: if (gopher->type == GT_INDEX) {
675: HTAnchor_setIndex(request->anchor); /* Is index */
676: query = strchr(selector, '?');
677:
678: /* Display local cover page only if no search requested */
679: if (!query || !*(query+1)) { /* No search required */
680: display_index(request);
681: gopher->state = GOPHER_GOT_DATA;
682: break;
683: } else {
684: *query++ = 0; /* Skip '?' */
685: separator = "\t";
686: }
687: } else if (gopher->type == GT_CSO) {
688: HTAnchor_setIndex(request->anchor); /* Search is allowed */
689: query = strchr(selector, '?'); /* Look for search string */
690:
691: /* Display local cover page only if no search requested */
692: if (!query || !*(query+1)) { /* No search required */
693: display_cso(request);
694: gopher->state = GOPHER_GOT_DATA;
695: break;
696: } else {
697: *query++ = 0; /* Skip '?' */
698: *selector = 0;
699: separator = "query ";
700: }
701: }
2.20 frystyk 702:
2.42 frystyk 703: /* Now generate the final command */
704: {
705: char crlf[3];
706: StrAllocCopy(gopher->cmd, selector);
707: if (query) {
708: char *p;
709: for (p=query; *p; p++) /* Remove plus signs 921006 */
710: if (*p == '+') *p = ' ';
711: StrAllocCat(gopher->cmd, separator);
712: StrAllocCat(gopher->cmd, query);
713: }
714: HTUnEscape(gopher->cmd);
715: HTCleanTelnetString(gopher->cmd); /* Prevent sec holes */
716: *crlf = CR; /* Telnet termination */
717: *(crlf+1) = LF;
718: *(crlf+2) = '\0';
719: StrAllocCat(gopher->cmd, crlf);
720: }
721: free(path);
722: gopher->state = GOPHER_NEED_CONNECTION;
1.1 timbl 723: }
2.42 frystyk 724: break;
725:
726: case GOPHER_NEED_CONNECTION:
2.43 frystyk 727: status = HTDoConnect(net, url, GOPHER_PORT);
2.42 frystyk 728: if (status == HT_OK) {
729: if (PROT_TRACE)
2.47 ! frystyk 730: TTYPrint(TDEST, "Gopher...... Connected, socket %d\n",
2.42 frystyk 731: net->sockfd);
732:
733: /* Set up stream TO network */
734: request->input_stream = HTWriter_new(net, YES);
735:
736: /* Set up stream FROM network and corresponding read buffer */
737: net->isoc = HTInputSocket_new(net->sockfd);
738: if (gopher->type == GT_MENU) {
739: if (!HTImProxy && request->output_format == WWW_SOURCE)
740: net->target = request->output_stream;
741: else
742: net->target = GopherMenu_new(request, url, NO);
743: } else if (gopher->type == GT_CSO) {
744: if (!HTImProxy && request->output_format == WWW_SOURCE)
745: net->target = request->output_stream;
746: else
747: net->target = GopherMenu_new(request, url, YES);
748: } else {
749: /* This is probably better than the gopher types */
750: net->target = HTGuess_new(request, NULL,WWW_UNKNOWN,
751: request->output_format,
752: request->output_stream);
753: }
754: gopher->state = GOPHER_NEED_REQUEST;
755: } else if (status == HT_WOULD_BLOCK)
756: return HT_OK;
757: else
758: gopher->state = GOPHER_ERROR;
759: break;
760:
761: case GOPHER_NEED_REQUEST:
2.47 ! frystyk 762: if (PROT_TRACE) TTYPrint(TDEST, "Gopher Tx... `%s\'", gopher->cmd);
2.42 frystyk 763: status = (*request->input_stream->isa->put_block)
764: (request->input_stream, gopher->cmd, strlen(gopher->cmd));
765: if (status == HT_WOULD_BLOCK)
766: return HT_OK;
767: else if (status == HT_ERROR)
768: gopher->state = GOPHER_ERROR;
769: else
770: gopher->state = GOPHER_NEED_RESPONSE;
771: break;
1.1 timbl 772:
2.42 frystyk 773: case GOPHER_NEED_RESPONSE:
2.44 frystyk 774: status = HTSocketRead(request, net);
2.42 frystyk 775: if (status == HT_WOULD_BLOCK)
776: return HT_OK;
777: else if (status == HT_LOADED)
778: gopher->state = GOPHER_GOT_DATA;
779: else
780: gopher->state = GOPHER_ERROR;
781: break;
1.2 timbl 782:
2.42 frystyk 783: case GOPHER_GOT_DATA:
784: GopherCleanup(request, HT_LOADED);
785: return HT_OK;
786: break;
787:
788: case GOPHER_NO_DATA:
789: GopherCleanup(request, HT_NO_DATA);
790: return HT_OK;
791: break;
792:
793: case GOPHER_ERROR:
794: GopherCleanup(request, HT_ERROR);
795: return HT_OK;
796: break;
2.25 frystyk 797: }
2.42 frystyk 798: } /* while(1) */
1.1 timbl 799: }
Webmaster