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