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

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

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.27 ! duns 13: **   8 Jul 94 FM  Insulate free() from _free structure element.
2.21 frystyk 14: **
 15: ** NOTE:
 16: **   When parsing a gopher menu, we can't use the default HTParseSocket
 17: **   but we will hav eto take care of the stram ourselves :-(
 18: **
1.1 timbl 19: */
 20: 
2.20 frystyk 21: /* Implementation dependent include files */
1.1 timbl 22: #include "tcp.h"
 23: 
2.20 frystyk 24: /* Library include files */
 25: #include "HTParse.h"
 26: #include "HTUtils.h"
 27: #include "HTTCP.h"
2.17 frystyk 28: #include "HTIcons.h"
2.20 frystyk 29: #include "HTAccess.h"
1.1 timbl 30: #include "HTFormat.h"
2.20 frystyk 31: #include "HTError.h"
 32: #include "HTFile.h"
1.2 timbl 33: #include "HTML.h"
2.20 frystyk 34: #include "HTMLPDTD.h"
 35: #include "HTDirBrw.h"
 36: #include "HTGopher.h"                  /* Implemented here */
 37: 
 38: /* Macros and other defines */
 39: #ifndef GOPHER_PORT
 40: #define GOPHER_PORT 70                 /* See protocol spec */
 41: #endif
1.2 timbl 42: 
2.20 frystyk 43: /* Hypertext object building machinery */
2.17 frystyk 44: #define PUTC(c) (*target->isa->put_character)(target, c)
 45: #define PUTS(s) (*target->isa->put_string)(target, s)
 46: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
 47: #define END(e) (*target->isa->end_element)(target, e)
2.27 ! duns 48: #define FREE_TARGET (*target->isa->_free)(target)
2.20 frystyk 49: 
 50: /* Type definitions and global variables etc. local to this module */
 51: typedef enum _HTGopherType {
 52:   GOPHER_TEXT        = '0',
 53:   GOPHER_MENU        = '1',
 54:   GOPHER_CSO     = '2',
 55:   GOPHER_ERROR    = '3',
 56:   GOPHER_MACBINHEX  = '4',
 57:   GOPHER_PCBINHEX  = '5',
 58:   GOPHER_UUENCODED  = '6',
 59:   GOPHER_INDEX    = '7',
 60:   GOPHER_TELNET   = '8',
 61:   GOPHER_BINARY    = '9',
 62:   GOPHER_GIF     = 'g',
 63:   GOPHER_HTML        = 'h',                    /* HTML */
 64:   GOPHER_SOUND    = 's',
 65:   GOPHER_WWW     = 'w',                 /* W3 address */
 66:   GOPHER_IMAGE    = 'I',
 67:   GOPHER_TN3270    = 'T',
 68:   GOPHER_DUPLICATE  = '+',
 69:   GOPHER_PLUS_IMAGE = ':',         /* Addition from Gopher Plus */
 70:   GOPHER_PLUS_MOVIE = ';',
 71:   GOPHER_PLUS_SOUND = '<'
 72: } HTGopherType;
 73: 
1.2 timbl 74: struct _HTStructured {
 75:    CONST HTStructuredClass *    isa;
 76:    /* ... */
 77: };
 78: 
2.26 frystyk 79: /* This is the local definition of HTRequest->net_info */
2.20 frystyk 80: typedef struct _gopher_info {
2.26 frystyk 81:   int             sockfd;  /* Socket number for communication */
 82:   HTInputSocket *      isoc;            /* Input buffer */
 83:   HTRequest *            request;        /* Request structure */
2.20 frystyk 84:   HTGopherType        type;          /* Gopher item type */
 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: **
 167: */
2.20 frystyk 168: PRIVATE int parse_menu ARGS3(HTRequest *,   request,
 169:               gopher_info *,   gopher,
 170:               CONST char *,   url)
 171: #define TAB      '\t'
 172: #define HEX_ESCAPE   '%'
1.1 timbl 173: {
2.20 frystyk 174:   int status = -1;
2.17 frystyk 175:   unsigned int files = 0;
 176:   int ch;
2.20 frystyk 177:   HTChunk *chunk = HTChunkCreate(128);
 178:   char *message = NULL;              /* For a gopher message */
2.21 frystyk 179:   HTStructured *target = NULL;
2.26 frystyk 180: 
 181:   gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 182:   
 183:   /* Output the list */
2.26 frystyk 184:   while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 185:     if (ch == CR || ch == LF) {
 186:      if (chunk->size) {               /* Got some text */
 187:        char *name = NULL;           /* Gopher menu fields */
 188:        char *selector = NULL;
 189:        char *host = NULL;
 190:        char *port = NULL;
 191:        char *strptr;
 192:        char *errptr;
 193:        char gtype;
 194:        HTChunkTerminate(chunk);
 195:        strptr = chunk->data;         /* Scan it to parse it */
 196:        if (TRACE)
 197:          fprintf(stderr, "HTGopher.... Menu item: `%s\'\n",
 198:              chunk->data);
 199:        gtype = *strptr++;
 200: 
 201:        /* If first item is an error, then don't put any header out
 202:          but wait and see if there is a next item in the list. If not
 203:          then make error message, else use as list message. */
 204:        if (gtype == GOPHER_ERROR) {
 205:          StrAllocCopy(message, chunk->data+1);
 206:          break;
 207:        }
1.1 timbl 208: 
2.20 frystyk 209:        if (!files && (strstr(chunk->data, "error.host") ||
 210:          strstr(chunk->data, "errorhost"))) {
2.18 luotonen 211: 
2.20 frystyk 212:          /* If message is already initialized, then add this one. */
 213:          /* We don't want the gopher type character */
 214:          if ((errptr = strchr(chunk->data, '\t')) != NULL)
 215:            *errptr = '0円';
 216:          if (message) {
 217:            StrAllocCat(message, "\n");
 218:            StrAllocCat(message, chunk->data+1);
 219:          } else
 220:            StrAllocCopy(message, chunk->data+1);
 221:          HTChunkClear(chunk);
 222:          continue;
 223:        }
2.17 frystyk 224: 
2.21 frystyk 225:        /* Stop listing if line with a dot by itself */
2.25 frystyk 226:        if (!files && message && gtype=='.' && !*strptr) {
2.21 frystyk 227:          status = -1;
 228:          break;
 229:        }
 230: 
2.20 frystyk 231:        /* Output title, maybe top message and list top */
 232:        if (!files) {
 233:          CONST char *title = HTAnchor_title(request->anchor);
 234:          char *outstr = NULL;
2.21 frystyk 235:          target = HTML_new(request, NULL, WWW_HTML,
 236:                   request->output_format,
 237:                   request->output_stream);
2.20 frystyk 238:          if (title) {
 239:            StrAllocCopy(outstr, title);
 240:            HTUnEscape(outstr);
 241:          } else
 242:            StrAllocCopy(outstr, "Gopher Menu");
 243:          START(HTML_TITLE);
 244:          PUTS(outstr);
 245:          END(HTML_TITLE);
 246:          START(HTML_H1);
 247:          PUTS(outstr);
 248:          END(HTML_H1);
 249:          FREE(outstr);
 250:        
 251:          /* Output any message on top of list */
 252:          if (message && HTDirInfo == HT_DIR_INFO_TOP) {
 253:            PUTS(message);
 254:            START(HTML_BR);
 255:          }
2.17 frystyk 256: 
2.20 frystyk 257:          /* Make everything in list preformatted */
 258:          START(HTML_PRE);
1.1 timbl 259: 
2.20 frystyk 260:          /* Output the header line of the list */
 261:          if (!icon_blank) icon_blank = icon_unknown;
 262:          if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
 263:            HTMLPutImg(target, icon_blank->icon_url,
 264:                  HTIcon_alt_string(icon_blank->icon_alt, NO),
 265:                  NULL);
 266:          }
2.17 frystyk 267:          PUTC(' ');
2.20 frystyk 268:          PUTS("Name");
 269:          PUTC('\n');
 270:          START(HTML_HR);
 271:          PUTC('\n');
2.17 frystyk 272:        }
 273: 
2.20 frystyk 274:        /* Stop listing if line with a dot by itself */
 275:        if (gtype=='.' && !*strptr) {
 276:          status = (!files && message) ? -1 : HT_LOADED;
 277:          break;
2.7 secret 278:        }
2.20 frystyk 279: 
 280:        /* Parse menu item */
 281:        if (*strptr) {
 282:          name = strptr;
 283:          selector = strchr(name, TAB);
 284:          if (selector) {
 285:            *selector++ = 0;          /* Terminate name */
 286:            host = strchr(selector, TAB);
 287:            if (host) {
 288:              *host++ = 0;        /* Terminate selector */
 289:              port = strchr(host, TAB);
 290:              if (port) {
 291:                char *junk;
 292:                *port = ':';     /* delimit host a la W3 */
 293:                if ((junk = strchr(port, TAB)) != NULL)
 294:                  *junk = '0円';        /* Chop port */
 295:                if (*(port+1) == '0' && !*(port+2))
 296:                  *port = '0円';
 297:              } /* port */
 298:            } /* host */
 299:          } /* selector */
 300:        } /* gtype and name */
 301:        
 302:        /* Get Icon type and output the icon */
 303:        if (HTDirShowMask & HT_DIR_SHOW_ICON) {
 304:          HTIconNode *icon = get_gopher_icon(url, gtype);
 305:          if (icon && icon->icon_url) {
 306:            HTMLPutImg(target, icon->icon_url,
 307:                  HTIcon_alt_string(icon->icon_alt, YES),
 308:                  NULL);
 309:            PUTC(' ');
 310:          }
2.7 secret 311:        }
2.20 frystyk 312: 
 313:        if (gtype == GOPHER_WWW) {      /* Gopher pointer to W3 */
 314:          char *escaped = NULL;
 315:          escaped = HTEscape(selector, URL_PATH);
 316:          HTStartAnchor(target, NULL, escaped);
 317:          PUTS(name);
 318:          END(HTML_A);
 319:          free(escaped);
 320:        } else if (port) {         /* Other types need port */
 321:          char *escaped = NULL;
 322:          char *address = NULL;
 323:          int addr_len;
 324: 
 325:          /* Calculate the length of the WWW-address */
 326:          if (selector && *selector) {
 327:            escaped = HTEscape(selector, URL_PATH);
 328:            addr_len = 15 + strlen(escaped) + strlen(host) + 1;
 329:          } else {
 330:            addr_len = 15 + strlen(host) + 1;
 331:          }
 332:          if ((address = (char *) malloc(addr_len)) == NULL)
 333:            outofmem(__FILE__, "Gopher ParseMenu");
 334:          *address = '0円';
 335: 
 336:          if (gtype == GOPHER_TELNET) {
 337:            if (escaped)
 338:              sprintf(address, "telnet://%s@%s/",
 339:                  escaped, host);
 340:            else
 341:              sprintf(address, "telnet://%s/", host);
 342:          }
 343:          else if (gtype == GOPHER_TN3270) {
 344:            if (escaped)
 345:              sprintf(address, "tn3270://%s@%s/",
 346:                  escaped, host);
 347:            else 
 348:              sprintf(address, "tn3270://%s/", host);
 349:          } else {
 350:            if (escaped)
 351:              sprintf(address, "//%s/%c%s", host, gtype,
 352:                  escaped);
 353:            else
 354:              sprintf(address, "//%s/%c", host, gtype);
1.1 timbl 355:          }
2.20 frystyk 356: 
 357:          /* Now output the anchor if not a Gopher error */
 358:          if (gtype != GOPHER_ERROR &&
 359:            !strstr(address, "error.host") &&
 360:            !strstr(address, "errorhost")) {
 361:            HTStartAnchor(target, NULL, address);
 362:            PUTS(name);
 363:            END(HTML_A);
 364:          } else 
 365:            PUTS(name+1);   /* Just put it out, but skip type */
 366:          FREE(address);
 367:          FREE(escaped);
 368:        } else {                  /* If parse error */
 369:          if (TRACE)
 370:            fprintf(stderr, "HTGopher.... Bad menu item, `%s\'\n",
 371:                chunk->data);
 372:          PUTS(chunk->data);
1.1 timbl 373:        }
2.17 frystyk 374:        PUTC('\n');
2.20 frystyk 375:        HTChunkClear(chunk);
 376:        ++files;              /* Update number of files */
 377:      }
 378:    } else
 379:      HTChunkPutc(chunk, ch);
 380:   }
 381:   if (ch < 0)
 382:    status = ch;
1.2 timbl 383: 
2.20 frystyk 384:   /* If no files and message is initialized then make error message,
 385:    else output the bottom part of the list*/
 386:   if (status != HT_INTERRUPTED) {
 387:    if (!files && status < 0) {
 388:      if (message) {
 389:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
 390:              (void *) message, strlen(message), "parse_menu");
 391:      } else {
 392:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
 393:              chunk->data, chunk->size, "parse_menu");
 394:      }
2.21 frystyk 395:    } else if (target) {
2.20 frystyk 396:      char *outstr;
 397:      if ((outstr = (char *) malloc(100)) == NULL)
 398:        outofmem(__FILE__, "parse_menu");
 399:      if (files == 0)
 400:        sprintf(outstr, "Empty directory");
 401:      else if (files == 1)
 402:        sprintf(outstr, "1 file");
 403:      else
 404:        sprintf(outstr, "%u files", files);
 405:      START(HTML_HR);
 406:      PUTS(outstr);
 407:      free(outstr);
 408:      END(HTML_PRE);
1.1 timbl 409:      
2.20 frystyk 410:      /* Put out any messages */
 411:      if (message && HTDirInfo == HT_DIR_INFO_BOTTOM) {
 412:        PUTS(message);
 413:        START(HTML_BR);
 414:      }
2.21 frystyk 415:      FREE_TARGET;
 416:    } else {
 417:      if (TRACE)
 418:        fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
2.20 frystyk 419:    }
2.17 frystyk 420:   }
 421: 
2.20 frystyk 422:   /* Cleanup */
 423:   FREE(message);
2.26 frystyk 424:   HTInputSocket_free(gopher->isoc);
2.20 frystyk 425:   HTChunkFree(chunk);
 426:   return status;
1.1 timbl 427: }
2.11 timbl 428: 
 429: 
2.7 secret 430: /*   Parse a Gopher CSO document
2.20 frystyk 431: **   ============================
 432: **
 433: **   Accepts an open socket to a CSO server waiting to send us
 434: **   data and puts it on the screen in a reasonable manner.
 435: **
 436: **   Perhaps this data can be automatically linked to some
 437: **   other source as well???
 438: **
 439: **   Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
 440: **   on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
 441: **   secret@dxcern.cern.ch.
 442: **
 443: **   Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
 444: **   if other error.
 445: */
 446: PRIVATE int parse_cso ARGS3(HTRequest *,    request,
 447:              gopher_info *,   gopher,
 448:              CONST char *,    url)
2.7 secret 449: {
2.20 frystyk 450:   int status = -1;
 451:   unsigned int records = 0;
2.17 frystyk 452:   int ch;
2.20 frystyk 453:   char *cur_code = NULL;
 454:   HTChunk *chunk = HTChunkCreate(128);
2.21 frystyk 455:   HTStructured *target = NULL;
2.26 frystyk 456: 
 457:   gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 458:   
 459:   /* Start grabbing chars from the network */
2.26 frystyk 460:   while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 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 ");
2.23 frystyk 552:              sprintf(recstr, "%u", records);
2.20 frystyk 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.26 frystyk 613:   HTInputSocket_free(gopher->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.26 frystyk 669: PRIVATE int HTGopher_send_cmd ARGS3(gopher_info *,   gopher,
2.20 frystyk 670:                  char *,       url,
2.26 frystyk 671:                  char *,       command)
1.1 timbl 672: {
2.20 frystyk 673:   int status = 0;
2.26 frystyk 674:   if (!gopher || !command) {
2.20 frystyk 675:    if (TRACE)
 676:      fprintf(stderr, "Gopher Tx... Bad argument!\n");
 677:    return -1;
 678:   }
2.26 frystyk 679:   if ((status = HTDoConnect((HTNetInfo *) gopher, url, GOPHER_PORT,
 680:               NULL)) < 0) {
2.20 frystyk 681:    if (TRACE)
 682:      fprintf(stderr, "HTLoadGopher Connection not established!\n");
 683:    return status;
 684:   } 
 685:   if (TRACE)
2.26 frystyk 686:    fprintf(stderr, "Gopher...... Connected, socket %d\n", gopher->sockfd);
2.20 frystyk 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)
2.26 frystyk 698:    fprintf(stderr, "Gopher Tx... %s", command);
 699:   if ((status = NETWRITE(gopher->sockfd, command,
 700:             (int) strlen(command))) < 0) {
2.20 frystyk 701:    if (TRACE) fprintf(stderr, "Gopher...... Error sending command: %s\n",
2.26 frystyk 702:              command);
 703:    HTErrorSysAdd(gopher->request, ERR_FATAL, NO, "NETWRITE");
2.20 frystyk 704:   } else
 705:    status = 0;
 706:   return status;
1.1 timbl 707: }
 708: 
 709: 
 710: /*       Load by name                  HTLoadGopher
 711: **       ============
 712: **
2.24 frystyk 713: **   Given a hypertext address, this routine loads a gopher document
 714: **
 715: ** On entry,
 716: **   request     This is the request structure
 717: ** On exit,
 718: **   returns     <0       Error has occured
 719: **           HT_LOADED    OK
1.1 timbl 720: **
 721: */
2.13 timbl 722: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 723: {
2.22 frystyk 724:   char *url;
2.20 frystyk 725:   int status = -1;
2.26 frystyk 726:   char *command = NULL;
2.20 frystyk 727:   gopher_info *gopher;
 728:   
2.22 frystyk 729:   if (!request || !request->anchor) {
2.20 frystyk 730:    if (TRACE) fprintf(stderr, "HTLoadGopher Bad argument\n");
 731:    return -1;
 732:   }
2.22 frystyk 733:   url = HTAnchor_physical(request->anchor);
2.20 frystyk 734:   if (TRACE) fprintf(stderr, "HTGopher.... Looking for `%s\'\n", url);
 735: 
2.26 frystyk 736:   /* Initiate a new gopher structure and bind to resuest structure */
2.20 frystyk 737:   if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
 738:    outofmem(__FILE__, "HTLoadGopher");
2.26 frystyk 739:   gopher->sockfd = -1;
 740:   gopher->request = request;
 741:   request->net_info = (HTNetInfo *) gopher;
2.20 frystyk 742:   gopher->type = GOPHER_MENU;
1.1 timbl 743:   
2.20 frystyk 744:   /* Get entity type, and selector string and generate command */
1.1 timbl 745:   {
2.20 frystyk 746:    char *path = HTParse(url, "", PARSE_PATH);
 747:    char *selector = path;
 748:    char *query = NULL;
 749:    char *separator = NULL;
 750:    if (*selector)
 751:      gopher->type = *selector++;           /* Pick up gtype */
 752:    if (gopher->type == GOPHER_INDEX) {
 753:       HTAnchor_setIndex(request->anchor);        /* Search is allowed */
 754:      query = strchr(selector, '?');     /* Look for search string */
 755: 
 756:      /* Display local "cover page" only if no search requested */
 757:      if (!query || !*(query+1)) {        /* No search required */
 758:        display_index(request, url);
 759:        status = HT_LOADED;          /* Local function only */
 760:      } else {
 761:        *query++ = 0;                  /* Skip '?' */
 762:        separator = "\t";
1.1 timbl 763:      }
2.20 frystyk 764:     } else if (gopher->type == GOPHER_CSO) {
 765:       HTAnchor_setIndex(request->anchor);     /* Search is allowed */
 766:       query = strchr(selector, '?');    /* Look for search string */
 767: 
 768:      /* Display local "cover page" only if no search requested */
 769:       if (!query || !*(query+1)) {        /* No search required */
 770:         display_cso(request, url);
 771:         status = HT_LOADED;          /* Local function only */
 772:       } else {
 773:        *query++ = 0;                /* Skip '?'   */
 774:        separator = "query ";
1.1 timbl 775:      }
 776:    }
 777: 
2.20 frystyk 778:    /* Now generate the final command */
 779:    if (status != HT_LOADED) {
2.24 frystyk 780:      char crlf[3];
2.26 frystyk 781:      StrAllocCopy(command, selector);
2.20 frystyk 782:      if (query) {
 783:        char *p;
 784:        for (p=query; *p; p++)      /* Remove plus signs 921006 */
 785:          if (*p == '+') *p = ' ';
2.26 frystyk 786:        StrAllocCat(command, separator);
 787:        StrAllocCat(command, query);
2.20 frystyk 788:      }
2.26 frystyk 789:      HTUnEscape(command);
 790:      HTCleanTelnetString(command);     /* Prevent security holes */
2.24 frystyk 791:      *crlf = CR;                /* Telnet termination */
 792:      *(crlf+1) = LF;
 793:      *(crlf+2) = '0円';
2.26 frystyk 794:      StrAllocCat(command, crlf);
2.20 frystyk 795:    } 
 796:    free(path);
1.1 timbl 797:   }
 798:   
2.20 frystyk 799:   /* Now we must ask the server for real data :-( */
 800:   if (status != HT_LOADED) {
2.26 frystyk 801:    if ((status = HTGopher_send_cmd(gopher, url, command)) == 0) {
2.20 frystyk 802:      
 803:      /* Now read the data from the socket: */  
 804:      switch (gopher->type) {
 805:       case GOPHER_HTML:
2.26 frystyk 806:        status = HTParseSocket(WWW_HTML, gopher->sockfd, request);
2.20 frystyk 807:        break;
 808:        
 809:       case GOPHER_GIF:
 810:       case GOPHER_IMAGE:
 811:       case GOPHER_PLUS_IMAGE:
2.26 frystyk 812:        status = HTParseSocket(HTAtom_for("image/gif"), gopher->sockfd,
2.20 frystyk 813:                    request);
 814:        break;
 815:       case GOPHER_MENU:
 816:       case GOPHER_INDEX:
 817:        status = parse_menu(request, gopher, url);
 818:        break;
 819:        
 820:       case GOPHER_CSO:
 821:        status = parse_cso(request, gopher, url);
 822:        break;
 823:        
 824:       case GOPHER_MACBINHEX:
 825:       case GOPHER_PCBINHEX:
 826:       case GOPHER_UUENCODED:
 827:       case GOPHER_BINARY:
2.26 frystyk 828:        {
2.20 frystyk 829:          /* Do our own filetyping -- maybe we get lucky */
 830:          HTFormat format;
 831:          format = HTFileFormat(url, &request->content_encoding,
 832:                     &request->content_language);
 833:          if (format) {
 834:            CTRACE(stderr,
 835:                "Gopher...... Figured out content-type myself: %s\n",
 836:                HTAtom_name(format));
2.26 frystyk 837:            status = HTParseSocket(format, gopher->sockfd,
2.20 frystyk 838:                        request);
 839:          }
 840:          else {
 841:            CTRACE(stderr,"Gopher...... using www/unknown\n");
 842:            /* Specifying WWW_UNKNOWN forces dump to local disk */
2.26 frystyk 843:            HTParseSocket(WWW_UNKNOWN, gopher->sockfd, request);
2.20 frystyk 844:          }
 845:        }
 846:        break;
 847:        
 848:       case GOPHER_SOUND:
 849:       case GOPHER_PLUS_SOUND:
2.26 frystyk 850:        status = HTParseSocket(WWW_AUDIO, gopher->sockfd, request);
2.20 frystyk 851:        break;
 852:        
 853:       case GOPHER_PLUS_MOVIE:
2.26 frystyk 854:        status = HTParseSocket(WWW_VIDEO, gopher->sockfd, request);
2.20 frystyk 855:        break;
2.26 frystyk 856: 
 857:        /* Try and look at the suffix - maybe it is a PostScript file
 858:          so that we should start an externam viewer. */
2.20 frystyk 859:       case GOPHER_TEXT:
2.26 frystyk 860:       default:
 861:        {
 862:          HTFormat format;
 863:          format = HTFileFormat(url, &request->content_encoding,
 864:                     &request->content_language);
 865:          if (format) {
 866:            CTRACE(stderr,
 867:                "Gopher...... Figured out content-type myself: %s\n",
 868:                HTAtom_name(format));
 869:            status = HTParseSocket(format, gopher->sockfd,
 870:                        request);
 871:          }
 872:          else {
 873:            status = HTParseSocket(WWW_PLAINTEXT, gopher->sockfd,
 874:                        request);
 875:          }
 876:        }
2.20 frystyk 877:        break;
2.16 luotonen 878:      }
 879:    }
1.2 timbl 880: 
2.20 frystyk 881:    /* Close the connection */
2.26 frystyk 882:    if (gopher->sockfd >= 0) {
2.25 frystyk 883:      if (TRACE) fprintf(stderr, "Gopher...... Closing socket %d\n",
2.26 frystyk 884:                gopher->sockfd);
 885:      if (NETCLOSE(gopher->sockfd) < 0)
 886:        status = HTErrorSysAdd(request, ERR_FATAL, NO, "NETCLOSE");
2.25 frystyk 887:    }
2.20 frystyk 888:   }
 889:   if (status == HT_INTERRUPTED) {
 890:     HTErrorAdd(request, ERR_WARNING, NO, HTERR_INTERRUPTED, NULL, 0,
 891:          "HTLoadGopher");
 892:   }
2.26 frystyk 893:   FREE(command);
2.20 frystyk 894:   free(gopher);
 895: 
 896:   if (status < 0 && status != HT_INTERRUPTED) {
2.21 frystyk 897:    char *unescaped = NULL;
 898:    StrAllocCopy(unescaped, url);
 899:    HTUnEscape(unescaped);
 900:     HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
 901:          (int) strlen(unescaped), "HTLoadGopher");
2.20 frystyk 902:    HTAnchor_clearIndex(request->anchor);
2.21 frystyk 903:    free(unescaped);
2.20 frystyk 904:   }
 905:   return status;
1.1 timbl 906: }
1.2 timbl 907: 
2.10 timbl 908: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1 timbl 909: 

Webmaster

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