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

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

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

Webmaster

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