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

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

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

Webmaster

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