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

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

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

Webmaster

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