[BACK] Return to HTGopher.c CVS log [TXT] [DIR] Up to [Public] / libwww / Library / src

Annotation of libwww/Library/src/HTGopher.c, revision 2.58

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.57 frystyk 21: #include "sysdep.h"
2.34 frystyk 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 {
2.57 frystyk 97:   const HTStructuredClass * isa;
2.42 frystyk 98: };
 99: 
 100: struct _HTStream {
2.57 frystyk 101:   const HTStreamClass *   isa;
2.42 frystyk 102:   HTStructured *       target;
 103:   HTRequest *            request;
2.58 ! frystyk 104:   HTEOLState         state;
2.42 frystyk 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);
2.55 frystyk 216:   HT_FREE(str);
2.42 frystyk 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.56 eric 244:    HTTrace("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);
2.55 frystyk 296:      HT_FREE(escaped);
2.42 frystyk 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:      }
2.55 frystyk 309:      if ((address = (char *) HT_MALLOC(addr_len)) == NULL)
 310:        HT_OUTOFMEM("GopherMenuLine");
2.42 frystyk 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);
2.55 frystyk 333:      HT_FREE(address);
 334:      HT_FREE(escaped);
2.42 frystyk 335:      PUTC('\n');
 336:    } else {                      /* If parse error */
 337:      if (PROT_TRACE)
2.56 eric 338:        HTTrace("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.57 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.56 eric 455:          HTTrace("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.57 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;
2.55 frystyk 486:   HT_FREE(me);
2.42 frystyk 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);
2.55 frystyk 493:   HT_FREE(me);
2.42 frystyk 494:   if (PROT_TRACE)
2.56 eric 495:    HTTrace("Gopher...... ABORTING...\n");
2.42 frystyk 496:   return HT_ERROR;
 497: }
 498: 
 499: /*   Goper Menu Stream
 500: **   -----------------
2.20 frystyk 501: */
2.57 frystyk 502: PRIVATE const HTStreamClass GopherMenuClass =
2.42 frystyk 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: {
2.55 frystyk 518:   HTStream * me;
 519:   if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
 520:     HT_OUTOFMEM("GopherMenu_new");
2.42 frystyk 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) {
2.55 frystyk 558:    HT_FREE(gopher->cmd);
 559:    HT_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.56 eric 651:    if (PROT_TRACE) HTTrace("Gopher...... Looking for `%s\'\n",url);
2.55 frystyk 652:    if ((gopher = (gopher_info *) HT_CALLOC(1, sizeof(gopher_info))) == NULL)
 653:      HT_OUTOFMEM("HTLoadGopher");
2.42 frystyk 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;
2.55 frystyk 682:            HT_FREE(path);
2.42 frystyk 683:            break;
 684:          } else {
 685:            *query++ = 0;              /* Skip '?' */
 686:            separator = "\t";
 687:          }
 688:        } else if (gopher->type == GT_CSO) {
 689:          HTAnchor_setIndex(request->anchor); /* Search is allowed */
 690:          query = strchr(selector, '?'); /* Look for search string */
 691:          
 692:          /* Display local cover page only if no search requested */
 693:          if (!query || !*(query+1)) {    /* No search required */
 694:            display_cso(request);
 695:            gopher->state = GOPHER_GOT_DATA;
2.55 frystyk 696:            HT_FREE(path);
2.42 frystyk 697:            break;
 698:          } else {
 699:            *query++ = 0;              /* Skip '?' */
 700:            *selector = 0;
 701:            separator = "query ";
 702:          }
 703:        }
2.20 frystyk 704: 
2.42 frystyk 705:        /* Now generate the final command */
 706:        {
 707:          char crlf[3];
 708:          StrAllocCopy(gopher->cmd, selector);
 709:          if (query) {
 710:            char *p;
 711:            for (p=query; *p; p++)  /* Remove plus signs 921006 */
 712:              if (*p == '+') *p = ' ';
 713:            StrAllocCat(gopher->cmd, separator);
 714:            StrAllocCat(gopher->cmd, query);
 715:          }
 716:          HTUnEscape(gopher->cmd);
 717:          HTCleanTelnetString(gopher->cmd);  /* Prevent sec holes */
 718:          *crlf = CR;            /* Telnet termination */
 719:          *(crlf+1) = LF;
 720:          *(crlf+2) = '0円';
 721:          StrAllocCat(gopher->cmd, crlf);
 722:        }
2.55 frystyk 723:        HT_FREE(path);
2.42 frystyk 724:        gopher->state = GOPHER_NEED_CONNECTION;
1.1 timbl 725:      }
2.42 frystyk 726:      break;
 727: 
 728:     case GOPHER_NEED_CONNECTION:
2.43 frystyk 729:      status = HTDoConnect(net, url, GOPHER_PORT);
2.42 frystyk 730:      if (status == HT_OK) {
 731:        if (PROT_TRACE)
2.56 eric 732:          HTTrace("Gopher...... Connected, socket %d\n",
2.42 frystyk 733:              net->sockfd);
 734: 
 735:        /* Set up stream TO network */
 736:        request->input_stream = HTWriter_new(net, YES);
 737: 
 738:        /* Set up stream FROM network and corresponding read buffer */
2.54 frystyk 739:         if (gopher->type == GT_MENU || gopher->type == GT_INDEX)
2.49 frystyk 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.56 eric 756:      if (PROT_TRACE) HTTrace("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.58 ! frystyk 768:      status = HTChannel_readSocket(request, net);
2.42 frystyk 769:      if (status == HT_WOULD_BLOCK)
 770:        return HT_OK;
2.53 frystyk 771:      else if (status == HT_LOADED || status == HT_CLOSED)
2.42 frystyk 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

AltStyle によって変換されたページ (->オリジナル) /