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

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

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

Webmaster

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