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

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

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

Webmaster

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