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

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

1.1 timbl 1: /*           GOPHER ACCESS              HTGopher.c
 2: **           =============
 3: **
 4: ** History:
 5: **   26 Sep 90    Adapted from other accesses (News, HTTP) TBL
 6: **   29 Nov 91    Downgraded to C, for portable implementation.
2.17 frystyk 7: **   28 Apr 94    target no more global and icons implemented
 8: **           HF, frystyk@dxcern.cern.ch
2.19 luotonen 9: **   2 May 94    Fixed possible security hole when the URL contains
 10: **           a newline, that could cause multiple commands to be
 11: **           sent to a Gopher server. AL, luotonen@www.cern.ch
2.20 frystyk 12: **   12 May 94    Checked and made ready for multi-threads, Frystyk
2.21 frystyk 13: **
 14: ** NOTE:
 15: **   When parsing a gopher menu, we can't use the default HTParseSocket
 16: **   but we will hav eto take care of the stram ourselves :-(
 17: **
1.1 timbl 18: */
 19: 
2.20 frystyk 20: /* Implementation dependent include files */
1.1 timbl 21: #include "tcp.h"
 22: 
2.20 frystyk 23: /* Library include files */
 24: #include "HTParse.h"
 25: #include "HTUtils.h"
 26: #include "HTTCP.h"
2.17 frystyk 27: #include "HTIcons.h"
2.20 frystyk 28: #include "HTAccess.h"
1.1 timbl 29: #include "HTFormat.h"
2.20 frystyk 30: #include "HTError.h"
 31: #include "HTFile.h"
1.2 timbl 32: #include "HTML.h"
2.20 frystyk 33: #include "HTMLPDTD.h"
 34: #include "HTDirBrw.h"
 35: #include "HTGopher.h"                  /* Implemented here */
 36: 
 37: /* Macros and other defines */
 38: #ifndef GOPHER_PORT
 39: #define GOPHER_PORT 70                 /* See protocol spec */
 40: #endif
1.2 timbl 41: 
2.20 frystyk 42: /* Hypertext object building machinery */
2.17 frystyk 43: #define PUTC(c) (*target->isa->put_character)(target, c)
 44: #define PUTS(s) (*target->isa->put_string)(target, s)
 45: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
 46: #define END(e) (*target->isa->end_element)(target, e)
 47: #define FREE_TARGET (*target->isa->free)(target)
2.20 frystyk 48: 
 49: /* Type definitions and global variables etc. local to this module */
 50: typedef enum _HTGopherType {
 51:   GOPHER_TEXT        = '0',
 52:   GOPHER_MENU        = '1',
 53:   GOPHER_CSO     = '2',
 54:   GOPHER_ERROR    = '3',
 55:   GOPHER_MACBINHEX  = '4',
 56:   GOPHER_PCBINHEX  = '5',
 57:   GOPHER_UUENCODED  = '6',
 58:   GOPHER_INDEX    = '7',
 59:   GOPHER_TELNET   = '8',
 60:   GOPHER_BINARY    = '9',
 61:   GOPHER_GIF     = 'g',
 62:   GOPHER_HTML        = 'h',                    /* HTML */
 63:   GOPHER_SOUND    = 's',
 64:   GOPHER_WWW     = 'w',                 /* W3 address */
 65:   GOPHER_IMAGE    = 'I',
 66:   GOPHER_TN3270    = 'T',
 67:   GOPHER_DUPLICATE  = '+',
 68:   GOPHER_PLUS_IMAGE = ':',         /* Addition from Gopher Plus */
 69:   GOPHER_PLUS_MOVIE = ';',
 70:   GOPHER_PLUS_SOUND = '<'
 71: } HTGopherType;
 72: 
1.2 timbl 73: struct _HTStructured {
 74:    CONST HTStructuredClass *    isa;
 75:    /* ... */
 76: };
 77: 
2.26 ! frystyk 78: /* This is the local definition of HTRequest->net_info */
2.20 frystyk 79: typedef struct _gopher_info {
2.26 ! frystyk 80:   int             sockfd;  /* Socket number for communication */
 ! 81:   HTInputSocket *      isoc;            /* Input buffer */
 ! 82:   HTRequest *            request;        /* Request structure */
2.20 frystyk 83:   HTGopherType        type;          /* Gopher item type */
 84: } gopher_info;
1.1 timbl 85: 
2.20 frystyk 86: /* ------------------------------------------------------------------------- */
1.1 timbl 87: 
2.20 frystyk 88: /*                              get_gopher_icon
1.1 timbl 89: **
2.20 frystyk 90: **   This function finds an appopriate icon for the item in the gopher
 91: **   list. Actually it is only a shell build upon HTGetIcon().
2.17 frystyk 92: **
 93: */
 94: PRIVATE HTIconNode *get_gopher_icon ARGS2(CONST char *, filename,
 95:                     int, gopher_type)
 96: {
 97:   HTFormat content_type = NULL;
 98:   HTAtom *content_encoding = NULL;
 99: 
 100:   if (gopher_type == GOPHER_MENU)
 101:    return icon_dir ? icon_dir : icon_unknown;
 102: 
 103:   switch(gopher_type) {
 104:    case GOPHER_TEXT:
 105:    content_type = HTAtom_for("text/void");
 106:    break;
2.20 frystyk 107:    case GOPHER_IMAGE:
 108:    case GOPHER_PLUS_IMAGE:
2.17 frystyk 109:    case GOPHER_GIF:
 110:    content_type = HTAtom_for("image/void");
 111:    break;
2.20 frystyk 112:    case GOPHER_WWW:
2.17 frystyk 113:    case GOPHER_HTML:
 114:    content_type = HTAtom_for("text/void");
 115:    break;
 116:    case GOPHER_SOUND:
2.20 frystyk 117:    case GOPHER_PLUS_SOUND:
2.17 frystyk 118:    content_type = HTAtom_for("audio/void");
 119:    break;
2.20 frystyk 120:    case GOPHER_PLUS_MOVIE:
 121:    content_type = HTAtom_for("video/void");
2.17 frystyk 122:    break;
 123:    case GOPHER_INDEX:
 124:    content_type = HTAtom_for("application/x-gopher-index");
 125:    break;
 126:    case GOPHER_CSO:
 127:    content_type = HTAtom_for("application/x-gopher-cso");
 128:    break;
 129:    case GOPHER_TELNET:
 130:    content_type = HTAtom_for("application/x-gopher-telnet");
 131:    break;
 132:    case GOPHER_TN3270:
 133:    content_type = HTAtom_for("application/x-gopher-tn3270");
 134:    break;
 135:    case GOPHER_DUPLICATE:
 136:    content_type = HTAtom_for("application/x-gopher-duplicate");
 137:    break;
 138:    case GOPHER_ERROR:
 139:    content_type = HTAtom_for("www/unknown");
 140:    break;
 141:    case GOPHER_MACBINHEX:
 142:    case GOPHER_PCBINHEX:
 143:    case GOPHER_UUENCODED:
 144:    case GOPHER_BINARY:
 145:    {    /* Do our own filetyping -- maybe we get lucky */
 146:       HTAtom *language;
 147:       content_type = HTFileFormat(filename, &content_encoding,
 148:                    &language);
 149:    }
 150:    default:
 151:    content_type = HTAtom_for("www/unknown");
 152:    break;
 153:   }
 154:   return HTGetIcon(S_IFMT & S_IFREG, content_type, content_encoding);
 155: }
 156: 
 157: 
2.20 frystyk 158: /*                              parse_menu
 159: **
 160: **   This function parses a gopher menu and puts it into a iconized
 161: **   list.
 162: **
 163: **   Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
 164: **   if other error.
1.1 timbl 165: **
 166: */
2.20 frystyk 167: PRIVATE int parse_menu ARGS3(HTRequest *,   request,
 168:               gopher_info *,   gopher,
 169:               CONST char *,   url)
 170: #define TAB      '\t'
 171: #define HEX_ESCAPE   '%'
1.1 timbl 172: {
2.20 frystyk 173:   int status = -1;
2.17 frystyk 174:   unsigned int files = 0;
 175:   int ch;
2.20 frystyk 176:   HTChunk *chunk = HTChunkCreate(128);
 177:   char *message = NULL;              /* For a gopher message */
2.21 frystyk 178:   HTStructured *target = NULL;
2.26 ! frystyk 179: 
 ! 180:   gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 181:   
 182:   /* Output the list */
2.26 ! frystyk 183:   while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 184:     if (ch == CR || ch == LF) {
 185:      if (chunk->size) {               /* Got some text */
 186:        char *name = NULL;           /* Gopher menu fields */
 187:        char *selector = NULL;
 188:        char *host = NULL;
 189:        char *port = NULL;
 190:        char *strptr;
 191:        char *errptr;
 192:        char gtype;
 193:        HTChunkTerminate(chunk);
 194:        strptr = chunk->data;         /* Scan it to parse it */
 195:        if (TRACE)
 196:          fprintf(stderr, "HTGopher.... Menu item: `%s\'\n",
 197:              chunk->data);
 198:        gtype = *strptr++;
 199: 
 200:        /* If first item is an error, then don't put any header out
 201:          but wait and see if there is a next item in the list. If not
 202:          then make error message, else use as list message. */
 203:        if (gtype == GOPHER_ERROR) {
 204:          StrAllocCopy(message, chunk->data+1);
 205:          break;
 206:        }
1.1 timbl 207: 
2.20 frystyk 208:        if (!files && (strstr(chunk->data, "error.host") ||
 209:          strstr(chunk->data, "errorhost"))) {
2.18 luotonen 210: 
2.20 frystyk 211:          /* If message is already initialized, then add this one. */
 212:          /* We don't want the gopher type character */
 213:          if ((errptr = strchr(chunk->data, '\t')) != NULL)
 214:            *errptr = '0円';
 215:          if (message) {
 216:            StrAllocCat(message, "\n");
 217:            StrAllocCat(message, chunk->data+1);
 218:          } else
 219:            StrAllocCopy(message, chunk->data+1);
 220:          HTChunkClear(chunk);
 221:          continue;
 222:        }
2.17 frystyk 223: 
2.21 frystyk 224:        /* Stop listing if line with a dot by itself */
2.25 frystyk 225:        if (!files && message && gtype=='.' && !*strptr) {
2.21 frystyk 226:          status = -1;
 227:          break;
 228:        }
 229: 
2.20 frystyk 230:        /* Output title, maybe top message and list top */
 231:        if (!files) {
 232:          CONST char *title = HTAnchor_title(request->anchor);
 233:          char *outstr = NULL;
2.21 frystyk 234:          target = HTML_new(request, NULL, WWW_HTML,
 235:                   request->output_format,
 236:                   request->output_stream);
2.20 frystyk 237:          if (title) {
 238:            StrAllocCopy(outstr, title);
 239:            HTUnEscape(outstr);
 240:          } else
 241:            StrAllocCopy(outstr, "Gopher Menu");
 242:          START(HTML_TITLE);
 243:          PUTS(outstr);
 244:          END(HTML_TITLE);
 245:          START(HTML_H1);
 246:          PUTS(outstr);
 247:          END(HTML_H1);
 248:          FREE(outstr);
 249:        
 250:          /* Output any message on top of list */
 251:          if (message && HTDirInfo == HT_DIR_INFO_TOP) {
 252:            PUTS(message);
 253:            START(HTML_BR);
 254:          }
2.17 frystyk 255: 
2.20 frystyk 256:          /* Make everything in list preformatted */
 257:          START(HTML_PRE);
1.1 timbl 258: 
2.20 frystyk 259:          /* Output the header line of the list */
 260:          if (!icon_blank) icon_blank = icon_unknown;
 261:          if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
 262:            HTMLPutImg(target, icon_blank->icon_url,
 263:                  HTIcon_alt_string(icon_blank->icon_alt, NO),
 264:                  NULL);
 265:          }
2.17 frystyk 266:          PUTC(' ');
2.20 frystyk 267:          PUTS("Name");
 268:          PUTC('\n');
 269:          START(HTML_HR);
 270:          PUTC('\n');
2.17 frystyk 271:        }
 272: 
2.20 frystyk 273:        /* Stop listing if line with a dot by itself */
 274:        if (gtype=='.' && !*strptr) {
 275:          status = (!files && message) ? -1 : HT_LOADED;
 276:          break;
2.7 secret 277:        }
2.20 frystyk 278: 
 279:        /* Parse menu item */
 280:        if (*strptr) {
 281:          name = strptr;
 282:          selector = strchr(name, TAB);
 283:          if (selector) {
 284:            *selector++ = 0;          /* Terminate name */
 285:            host = strchr(selector, TAB);
 286:            if (host) {
 287:              *host++ = 0;        /* Terminate selector */
 288:              port = strchr(host, TAB);
 289:              if (port) {
 290:                char *junk;
 291:                *port = ':';     /* delimit host a la W3 */
 292:                if ((junk = strchr(port, TAB)) != NULL)
 293:                  *junk = '0円';        /* Chop port */
 294:                if (*(port+1) == '0' && !*(port+2))
 295:                  *port = '0円';
 296:              } /* port */
 297:            } /* host */
 298:          } /* selector */
 299:        } /* gtype and name */
 300:        
 301:        /* Get Icon type and output the icon */
 302:        if (HTDirShowMask & HT_DIR_SHOW_ICON) {
 303:          HTIconNode *icon = get_gopher_icon(url, gtype);
 304:          if (icon && icon->icon_url) {
 305:            HTMLPutImg(target, icon->icon_url,
 306:                  HTIcon_alt_string(icon->icon_alt, YES),
 307:                  NULL);
 308:            PUTC(' ');
 309:          }
2.7 secret 310:        }
2.20 frystyk 311: 
 312:        if (gtype == GOPHER_WWW) {      /* Gopher pointer to W3 */
 313:          char *escaped = NULL;
 314:          escaped = HTEscape(selector, URL_PATH);
 315:          HTStartAnchor(target, NULL, escaped);
 316:          PUTS(name);
 317:          END(HTML_A);
 318:          free(escaped);
 319:        } else if (port) {         /* Other types need port */
 320:          char *escaped = NULL;
 321:          char *address = NULL;
 322:          int addr_len;
 323: 
 324:          /* Calculate the length of the WWW-address */
 325:          if (selector && *selector) {
 326:            escaped = HTEscape(selector, URL_PATH);
 327:            addr_len = 15 + strlen(escaped) + strlen(host) + 1;
 328:          } else {
 329:            addr_len = 15 + strlen(host) + 1;
 330:          }
 331:          if ((address = (char *) malloc(addr_len)) == NULL)
 332:            outofmem(__FILE__, "Gopher ParseMenu");
 333:          *address = '0円';
 334: 
 335:          if (gtype == GOPHER_TELNET) {
 336:            if (escaped)
 337:              sprintf(address, "telnet://%s@%s/",
 338:                  escaped, host);
 339:            else
 340:              sprintf(address, "telnet://%s/", host);
 341:          }
 342:          else if (gtype == GOPHER_TN3270) {
 343:            if (escaped)
 344:              sprintf(address, "tn3270://%s@%s/",
 345:                  escaped, host);
 346:            else 
 347:              sprintf(address, "tn3270://%s/", host);
 348:          } else {
 349:            if (escaped)
 350:              sprintf(address, "//%s/%c%s", host, gtype,
 351:                  escaped);
 352:            else
 353:              sprintf(address, "//%s/%c", host, gtype);
1.1 timbl 354:          }
2.20 frystyk 355: 
 356:          /* Now output the anchor if not a Gopher error */
 357:          if (gtype != GOPHER_ERROR &&
 358:            !strstr(address, "error.host") &&
 359:            !strstr(address, "errorhost")) {
 360:            HTStartAnchor(target, NULL, address);
 361:            PUTS(name);
 362:            END(HTML_A);
 363:          } else 
 364:            PUTS(name+1);   /* Just put it out, but skip type */
 365:          FREE(address);
 366:          FREE(escaped);
 367:        } else {                  /* If parse error */
 368:          if (TRACE)
 369:            fprintf(stderr, "HTGopher.... Bad menu item, `%s\'\n",
 370:                chunk->data);
 371:          PUTS(chunk->data);
1.1 timbl 372:        }
2.17 frystyk 373:        PUTC('\n');
2.20 frystyk 374:        HTChunkClear(chunk);
 375:        ++files;              /* Update number of files */
 376:      }
 377:    } else
 378:      HTChunkPutc(chunk, ch);
 379:   }
 380:   if (ch < 0)
 381:    status = ch;
1.2 timbl 382: 
2.20 frystyk 383:   /* If no files and message is initialized then make error message,
 384:    else output the bottom part of the list*/
 385:   if (status != HT_INTERRUPTED) {
 386:    if (!files && status < 0) {
 387:      if (message) {
 388:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
 389:              (void *) message, strlen(message), "parse_menu");
 390:      } else {
 391:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
 392:              chunk->data, chunk->size, "parse_menu");
 393:      }
2.21 frystyk 394:    } else if (target) {
2.20 frystyk 395:      char *outstr;
 396:      if ((outstr = (char *) malloc(100)) == NULL)
 397:        outofmem(__FILE__, "parse_menu");
 398:      if (files == 0)
 399:        sprintf(outstr, "Empty directory");
 400:      else if (files == 1)
 401:        sprintf(outstr, "1 file");
 402:      else
 403:        sprintf(outstr, "%u files", files);
 404:      START(HTML_HR);
 405:      PUTS(outstr);
 406:      free(outstr);
 407:      END(HTML_PRE);
1.1 timbl 408:      
2.20 frystyk 409:      /* Put out any messages */
 410:      if (message && HTDirInfo == HT_DIR_INFO_BOTTOM) {
 411:        PUTS(message);
 412:        START(HTML_BR);
 413:      }
2.21 frystyk 414:      FREE_TARGET;
 415:    } else {
 416:      if (TRACE)
 417:        fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
2.20 frystyk 418:    }
2.17 frystyk 419:   }
 420: 
2.20 frystyk 421:   /* Cleanup */
 422:   FREE(message);
2.26 ! frystyk 423:   HTInputSocket_free(gopher->isoc);
2.20 frystyk 424:   HTChunkFree(chunk);
 425:   return status;
1.1 timbl 426: }
2.11 timbl 427: 
 428: 
2.7 secret 429: /*   Parse a Gopher CSO document
2.20 frystyk 430: **   ============================
 431: **
 432: **   Accepts an open socket to a CSO server waiting to send us
 433: **   data and puts it on the screen in a reasonable manner.
 434: **
 435: **   Perhaps this data can be automatically linked to some
 436: **   other source as well???
 437: **
 438: **   Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
 439: **   on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
 440: **   secret@dxcern.cern.ch.
 441: **
 442: **   Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
 443: **   if other error.
 444: */
 445: PRIVATE int parse_cso ARGS3(HTRequest *,    request,
 446:              gopher_info *,   gopher,
 447:              CONST char *,    url)
2.7 secret 448: {
2.20 frystyk 449:   int status = -1;
 450:   unsigned int records = 0;
2.17 frystyk 451:   int ch;
2.20 frystyk 452:   char *cur_code = NULL;
 453:   HTChunk *chunk = HTChunkCreate(128);
2.21 frystyk 454:   HTStructured *target = NULL;
2.26 ! frystyk 455: 
 ! 456:   gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 457:   
 458:   /* Start grabbing chars from the network */
2.26 ! frystyk 459:   while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 460:    if (ch == CR || ch == LF) {
 461:      if (chunk->size) {     
 462:        /* OK we now have a line in 'p' lets parse it and print it */
 463:        char *strptr;
 464:        HTChunkTerminate(chunk);
 465:        strptr = chunk->data;
 466: 
 467:        /* If line begins with a 1, then more data is coming, so we
 468:          put out the title */
 469:        if (*strptr == '1' ||
 470:          !strncmp(strptr, "501", 3) || !strncmp(strptr, "502", 3)) {
2.21 frystyk 471: 
 472:          /* Put up new stream */
 473:          target = HTML_new(request, NULL, WWW_HTML,
 474:                   request->output_format,
 475:                   request->output_stream);
2.20 frystyk 476:          START(HTML_H1);
 477:          PUTS("CSO Search Results");
 478:          END(HTML_H1);
 479: 
 480:           /* Output the header line of the list */
 481:           START(HTML_PRE); /* To make it look as the other headers */
 482:           if (!icon_blank) icon_blank = icon_unknown;
 483:           if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
 484:             HTMLPutImg(target, icon_blank->icon_url,
 485:                  HTIcon_alt_string(icon_blank->icon_alt, NO),
 486:                  NULL);
 487:           }
 488:           PUTC(' ');
 489:           PUTS("Record");
 490:           PUTC('\n');
 491:           START(HTML_HR);
 492:           PUTC('\n');
 493:          END(HTML_PRE);
 494:        }
2.7 secret 495: 
2.20 frystyk 496:        /* Break on line that begins with a 2. It's the end of data. */
 497:        if (*strptr == '2') {
 498:          status = HT_LOADED;
 499:          break;
 500:        }
 501:        
 502:        /* Lines beginning with 5 are errors, generate msg and quit */
 503:        if (*strptr == '5') {
 504:          char *msgptr = strchr(chunk->data, ':');
 505:          if (!msgptr)
 506:            msgptr = chunk->data;
 507:          else
 508:            ++msgptr;
 509:          if (!strncmp(strptr, "501", 3))      /* No entries */
 510:            status = HT_LOADED;
 511:          else if (!strncmp(strptr, "502", 3)) {    /* Too many */
 512:            status = HT_LOADED;
 513:            PUTS(msgptr);
 514:          } else {
 515:            HTErrorAdd(request, ERR_FATAL, NO, HTERR_CSO_SERVER,
 516:                  (void *) msgptr,
 517:                  strlen(msgptr), "parse_cso");
 518:          }
 519:          break;
 520:        }
 521:        
 522:        if(*strptr == '-') {
 523:          /* data lines look like -200:#:
 524:           * where # is the search result number and can be 
 525:           * multiple digits (infinate?)
 526:           * find the second colon and check the digit to the
 527:           * left of it to see if they are diferent
 528:           * if they are then a different person is starting. 
 529:           * make this line an <h2>
2.7 secret 530:           */
2.20 frystyk 531:          char *code;       /* format: -200:code:field:value */
 532:          char *field;
 533:          char *value;
 534:          if ((code = strchr(strptr, ':')) != NULL &&
 535:            (field = strchr(++code, ':')) != NULL) {
 536:            *field++ = '0円';
 537:            
 538:            /* Let's do a strcmp instead of numbers */
 539:            if (!records) {      /* Header of first record */
 540:              records++;
 541:              START(HTML_H2);
 542:              PUTS("Record 1");
 543:              END(HTML_H2);
 544:              START(HTML_DL);
 545:            } else if (cur_code && strcmp(code, cur_code)) {
 546:              char recstr[20];
 547:              records++;
 548:              END(HTML_DL);
 549:              START(HTML_H3);
 550:              PUTS("Record ");
2.23 frystyk 551:              sprintf(recstr, "%u", records);
2.20 frystyk 552:              PUTS(recstr);
 553:              END(HTML_H3);
 554:              START(HTML_DL);
 555:            } else
 556:              START(HTML_DT);
 557:            
 558:            /* I'm not sure whether the name field comes in any
 559:             * special order or if its even required in a 
 560:             * record, so for now the first line is the header
 561:             * no matter what it is (it's almost always the
 562:             * alias)
2.7 secret 563:             */
2.20 frystyk 564:            if ((value = strchr(field, ':')) == NULL)
 565:              value = "Empty?";
 566:            else
 567:              *value++ = '0円';
 568:            {
 569:              char *strip = HTStrip(field);
 570:              PUTS(strip);
 571:              START(HTML_DD);
 572:              strip = HTStrip(value);
 573:              PUTS(strip);
 574:            }
2.7 secret 575:            
2.20 frystyk 576:            /* save the code for comparison on the next pass */
 577:            StrAllocCopy(cur_code, code);
 578:          }
 579:        } /* end data line */
 580:        HTChunkClear(chunk);
 581:      } /* end new line */
 582:    } else
 583:      HTChunkPutc(chunk, ch);
 584:   }
 585:   if (ch < 0)
 586:    status = ch;
 587: 
 588:   /* Put out the bottom line */
 589:   if (status != HT_INTERRUPTED) {
2.21 frystyk 590:    if (target) {
 591:      char *outstr;
 592:      if ((outstr = (char *) malloc(100)) == NULL)
 593:        outofmem(__FILE__, "parse_menu");
 594:      if (!records)
 595:        sprintf(outstr, "No records");
 596:      else if (records == 1)
 597:        sprintf(outstr, "1 record");
 598:      else
 599:        sprintf(outstr, "%u records", records);
 600:      START(HTML_PRE);
 601:      START(HTML_HR);
 602:      PUTS(outstr);
 603:      END(HTML_PRE);
 604:      free(outstr);
 605:      FREE_TARGET;
 606:    } else {
 607:      if (TRACE) fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
 608:    }
2.20 frystyk 609:   }
 610: 
 611:   /* Clean up */
2.26 ! frystyk 612:   HTInputSocket_free(gopher->isoc);
2.20 frystyk 613:   HTChunkFree(chunk);
 614:   FREE(cur_code);
 615:   return status;
 616: }
2.7 secret 617: 
1.1 timbl 618: 
 619: /*   Display a Gopher Index document
2.20 frystyk 620: **   -------------------------------
 621: */
 622: PRIVATE void display_index ARGS2(HTRequest *,     request,
 623:                 CONST char *,     url)
1.1 timbl 624: {
2.20 frystyk 625:   HTStructured *target = HTML_new(request, NULL, WWW_HTML,
 626:                  request->output_format,
 627:                  request->output_stream);
2.18 luotonen 628: 
1.2 timbl 629:   START(HTML_H1);
2.20 frystyk 630:   PUTS("Searchable Gopher Index");
1.2 timbl 631:   END(HTML_H1);
2.7 secret 632:   START(HTML_ISINDEX);
2.20 frystyk 633:   if (!HTAnchor_title(request->anchor))
 634:    HTAnchor_setTitle(request->anchor, url);  
2.7 secret 635:   FREE_TARGET;
 636:   return;
 637: }
 638: 
 639: 
 640: /*   Display a CSO index document
 641: **   -------------------------------
 642: */
2.20 frystyk 643: PRIVATE void display_cso ARGS2(HTRequest *,      request,
 644:                CONST char *,      url)
2.7 secret 645: {
2.20 frystyk 646:   HTStructured *target = HTML_new(request, NULL, WWW_HTML,
 647:                  request->output_format,
 648:                  request->output_stream);
2.7 secret 649:   START(HTML_H1);
2.20 frystyk 650:   PUTS("Searchable Index of a CSO Name Server");
2.7 secret 651:   END(HTML_H1);
 652:   START(HTML_ISINDEX);
2.20 frystyk 653:   if (!HTAnchor_title(request->anchor))
 654:    HTAnchor_setTitle(request->anchor, url);
1.2 timbl 655:   FREE_TARGET;
1.1 timbl 656:   return;
 657: }
 658: 
 659: 
2.20 frystyk 660: 
 661: /*                            HTGopher_send_cmd
1.1 timbl 662: **
2.20 frystyk 663: **   This function creates a socket and writes the gopher command to it.
 664: **   The command must be terminated with <CRLF>
 665: **
 666: **   Returns 0 on OK, else <0 but does NOT close the connection
1.1 timbl 667: */
2.26 ! frystyk 668: PRIVATE int HTGopher_send_cmd ARGS3(gopher_info *,   gopher,
2.20 frystyk 669:                  char *,       url,
2.26 ! frystyk 670:                  char *,       command)
1.1 timbl 671: {
2.20 frystyk 672:   int status = 0;
2.26 ! frystyk 673:   if (!gopher || !command) {
2.20 frystyk 674:    if (TRACE)
 675:      fprintf(stderr, "Gopher Tx... Bad argument!\n");
 676:    return -1;
 677:   }
2.26 ! frystyk 678:   if ((status = HTDoConnect((HTNetInfo *) gopher, url, GOPHER_PORT,
 ! 679:               NULL)) < 0) {
2.20 frystyk 680:    if (TRACE)
 681:      fprintf(stderr, "HTLoadGopher Connection not established!\n");
 682:    return status;
 683:   } 
 684:   if (TRACE)
2.26 ! frystyk 685:    fprintf(stderr, "Gopher...... Connected, socket %d\n", gopher->sockfd);
2.20 frystyk 686:   
 687:   /* Write the command to the socket */
 688: #ifdef NOT_ASCII
 689:   {
 690:    char * p;
 691:    for(p = command; *p; p++) {
 692:      *p = TOASCII(*p);
1.1 timbl 693:    }
 694:   }
2.20 frystyk 695: #endif
 696:   if (TRACE)
2.26 ! frystyk 697:    fprintf(stderr, "Gopher Tx... %s", command);
 ! 698:   if ((status = NETWRITE(gopher->sockfd, command,
 ! 699:             (int) strlen(command))) < 0) {
2.20 frystyk 700:    if (TRACE) fprintf(stderr, "Gopher...... Error sending command: %s\n",
2.26 ! frystyk 701:              command);
 ! 702:    HTErrorSysAdd(gopher->request, ERR_FATAL, NO, "NETWRITE");
2.20 frystyk 703:   } else
 704:    status = 0;
 705:   return status;
1.1 timbl 706: }
 707: 
 708: 
 709: /*       Load by name                  HTLoadGopher
 710: **       ============
 711: **
2.24 frystyk 712: **   Given a hypertext address, this routine loads a gopher document
 713: **
 714: ** On entry,
 715: **   request     This is the request structure
 716: ** On exit,
 717: **   returns     <0       Error has occured
 718: **           HT_LOADED    OK
1.1 timbl 719: **
 720: */
2.13 timbl 721: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 722: {
2.22 frystyk 723:   char *url;
2.20 frystyk 724:   int status = -1;
2.26 ! frystyk 725:   char *command = NULL;
2.20 frystyk 726:   gopher_info *gopher;
 727:   
2.22 frystyk 728:   if (!request || !request->anchor) {
2.20 frystyk 729:    if (TRACE) fprintf(stderr, "HTLoadGopher Bad argument\n");
 730:    return -1;
 731:   }
2.22 frystyk 732:   url = HTAnchor_physical(request->anchor);
2.20 frystyk 733:   if (TRACE) fprintf(stderr, "HTGopher.... Looking for `%s\'\n", url);
 734: 
2.26 ! frystyk 735:   /* Initiate a new gopher structure and bind to resuest structure */
2.20 frystyk 736:   if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
 737:    outofmem(__FILE__, "HTLoadGopher");
2.26 ! frystyk 738:   gopher->sockfd = -1;
 ! 739:   gopher->request = request;
 ! 740:   request->net_info = (HTNetInfo *) gopher;
2.20 frystyk 741:   gopher->type = GOPHER_MENU;
1.1 timbl 742:   
2.20 frystyk 743:   /* Get entity type, and selector string and generate command */
1.1 timbl 744:   {
2.20 frystyk 745:    char *path = HTParse(url, "", PARSE_PATH);
 746:    char *selector = path;
 747:    char *query = NULL;
 748:    char *separator = NULL;
 749:    if (*selector)
 750:      gopher->type = *selector++;           /* Pick up gtype */
 751:    if (gopher->type == GOPHER_INDEX) {
 752:       HTAnchor_setIndex(request->anchor);        /* Search is allowed */
 753:      query = strchr(selector, '?');     /* Look for search string */
 754: 
 755:      /* Display local "cover page" only if no search requested */
 756:      if (!query || !*(query+1)) {        /* No search required */
 757:        display_index(request, url);
 758:        status = HT_LOADED;          /* Local function only */
 759:      } else {
 760:        *query++ = 0;                  /* Skip '?' */
 761:        separator = "\t";
1.1 timbl 762:      }
2.20 frystyk 763:     } else if (gopher->type == GOPHER_CSO) {
 764:       HTAnchor_setIndex(request->anchor);     /* Search is allowed */
 765:       query = strchr(selector, '?');    /* Look for search string */
 766: 
 767:      /* Display local "cover page" only if no search requested */
 768:       if (!query || !*(query+1)) {        /* No search required */
 769:         display_cso(request, url);
 770:         status = HT_LOADED;          /* Local function only */
 771:       } else {
 772:        *query++ = 0;                /* Skip '?'   */
 773:        separator = "query ";
1.1 timbl 774:      }
 775:    }
 776: 
2.20 frystyk 777:    /* Now generate the final command */
 778:    if (status != HT_LOADED) {
2.24 frystyk 779:      char crlf[3];
2.26 ! frystyk 780:      StrAllocCopy(command, selector);
2.20 frystyk 781:      if (query) {
 782:        char *p;
 783:        for (p=query; *p; p++)      /* Remove plus signs 921006 */
 784:          if (*p == '+') *p = ' ';
2.26 ! frystyk 785:        StrAllocCat(command, separator);
 ! 786:        StrAllocCat(command, query);
2.20 frystyk 787:      }
2.26 ! frystyk 788:      HTUnEscape(command);
 ! 789:      HTCleanTelnetString(command);     /* Prevent security holes */
2.24 frystyk 790:      *crlf = CR;                /* Telnet termination */
 791:      *(crlf+1) = LF;
 792:      *(crlf+2) = '0円';
2.26 ! frystyk 793:      StrAllocCat(command, crlf);
2.20 frystyk 794:    } 
 795:    free(path);
1.1 timbl 796:   }
 797:   
2.20 frystyk 798:   /* Now we must ask the server for real data :-( */
 799:   if (status != HT_LOADED) {
2.26 ! frystyk 800:    if ((status = HTGopher_send_cmd(gopher, url, command)) == 0) {
2.20 frystyk 801:      
 802:      /* Now read the data from the socket: */  
 803:      switch (gopher->type) {
 804:       case GOPHER_HTML:
2.26 ! frystyk 805:        status = HTParseSocket(WWW_HTML, gopher->sockfd, request);
2.20 frystyk 806:        break;
 807:        
 808:       case GOPHER_GIF:
 809:       case GOPHER_IMAGE:
 810:       case GOPHER_PLUS_IMAGE:
2.26 ! frystyk 811:        status = HTParseSocket(HTAtom_for("image/gif"), gopher->sockfd,
2.20 frystyk 812:                    request);
 813:        break;
 814:       case GOPHER_MENU:
 815:       case GOPHER_INDEX:
 816:        status = parse_menu(request, gopher, url);
 817:        break;
 818:        
 819:       case GOPHER_CSO:
 820:        status = parse_cso(request, gopher, url);
 821:        break;
 822:        
 823:       case GOPHER_MACBINHEX:
 824:       case GOPHER_PCBINHEX:
 825:       case GOPHER_UUENCODED:
 826:       case GOPHER_BINARY:
2.26 ! frystyk 827:        {
2.20 frystyk 828:          /* Do our own filetyping -- maybe we get lucky */
 829:          HTFormat format;
 830:          format = HTFileFormat(url, &request->content_encoding,
 831:                     &request->content_language);
 832:          if (format) {
 833:            CTRACE(stderr,
 834:                "Gopher...... Figured out content-type myself: %s\n",
 835:                HTAtom_name(format));
2.26 ! frystyk 836:            status = HTParseSocket(format, gopher->sockfd,
2.20 frystyk 837:                        request);
 838:          }
 839:          else {
 840:            CTRACE(stderr,"Gopher...... using www/unknown\n");
 841:            /* Specifying WWW_UNKNOWN forces dump to local disk */
2.26 ! frystyk 842:            HTParseSocket(WWW_UNKNOWN, gopher->sockfd, request);
2.20 frystyk 843:          }
 844:        }
 845:        break;
 846:        
 847:       case GOPHER_SOUND:
 848:       case GOPHER_PLUS_SOUND:
2.26 ! frystyk 849:        status = HTParseSocket(WWW_AUDIO, gopher->sockfd, request);
2.20 frystyk 850:        break;
 851:        
 852:       case GOPHER_PLUS_MOVIE:
2.26 ! frystyk 853:        status = HTParseSocket(WWW_VIDEO, gopher->sockfd, request);
2.20 frystyk 854:        break;
2.26 ! frystyk 855: 
 ! 856:        /* Try and look at the suffix - maybe it is a PostScript file
 ! 857:          so that we should start an externam viewer. */
2.20 frystyk 858:       case GOPHER_TEXT:
2.26 ! frystyk 859:       default:
 ! 860:        {
 ! 861:          HTFormat format;
 ! 862:          format = HTFileFormat(url, &request->content_encoding,
 ! 863:                     &request->content_language);
 ! 864:          if (format) {
 ! 865:            CTRACE(stderr,
 ! 866:                "Gopher...... Figured out content-type myself: %s\n",
 ! 867:                HTAtom_name(format));
 ! 868:            status = HTParseSocket(format, gopher->sockfd,
 ! 869:                        request);
 ! 870:          }
 ! 871:          else {
 ! 872:            status = HTParseSocket(WWW_PLAINTEXT, gopher->sockfd,
 ! 873:                        request);
 ! 874:          }
 ! 875:        }
2.20 frystyk 876:        break;
2.16 luotonen 877:      }
 878:    }
1.2 timbl 879: 
2.20 frystyk 880:    /* Close the connection */
2.26 ! frystyk 881:    if (gopher->sockfd >= 0) {
2.25 frystyk 882:      if (TRACE) fprintf(stderr, "Gopher...... Closing socket %d\n",
2.26 ! frystyk 883:                gopher->sockfd);
 ! 884:      if (NETCLOSE(gopher->sockfd) < 0)
 ! 885:        status = HTErrorSysAdd(request, ERR_FATAL, NO, "NETCLOSE");
2.25 frystyk 886:    }
2.20 frystyk 887:   }
 888:   if (status == HT_INTERRUPTED) {
 889:     HTErrorAdd(request, ERR_WARNING, NO, HTERR_INTERRUPTED, NULL, 0,
 890:          "HTLoadGopher");
 891:   }
2.26 ! frystyk 892:   FREE(command);
2.20 frystyk 893:   free(gopher);
 894: 
 895:   if (status < 0 && status != HT_INTERRUPTED) {
2.21 frystyk 896:    char *unescaped = NULL;
 897:    StrAllocCopy(unescaped, url);
 898:    HTUnEscape(unescaped);
 899:     HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
 900:          (int) strlen(unescaped), "HTLoadGopher");
2.20 frystyk 901:    HTAnchor_clearIndex(request->anchor);
2.21 frystyk 902:    free(unescaped);
2.20 frystyk 903:   }
 904:   return status;
1.1 timbl 905: }
1.2 timbl 906: 
2.10 timbl 907: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1 timbl 908: 

Webmaster

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