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

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

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) {
2.29 ! frystyk 332:          char *filename = HTParse(url, "",
 ! 333:                       PARSE_PATH+PARSE_PUNCTUATION);
 ! 334:          HTIconNode *icon = get_gopher_icon(filename, gtype);
2.20 frystyk 335:          if (icon && icon->icon_url) {
 336:            HTMLPutImg(target, icon->icon_url,
 337:                  HTIcon_alt_string(icon->icon_alt, YES),
 338:                  NULL);
 339:            PUTC(' ');
 340:          }
2.29 ! frystyk 341:          free(filename);
2.7 secret 342:        }
2.20 frystyk 343: 
 344:        if (gtype == GOPHER_WWW) {      /* Gopher pointer to W3 */
 345:          char *escaped = NULL;
 346:          escaped = HTEscape(selector, URL_PATH);
 347:          HTStartAnchor(target, NULL, escaped);
 348:          PUTS(name);
 349:          END(HTML_A);
 350:          free(escaped);
 351:        } else if (port) {         /* Other types need port */
 352:          char *escaped = NULL;
 353:          char *address = NULL;
 354:          int addr_len;
 355: 
 356:          /* Calculate the length of the WWW-address */
 357:          if (selector && *selector) {
 358:            escaped = HTEscape(selector, URL_PATH);
 359:            addr_len = 15 + strlen(escaped) + strlen(host) + 1;
 360:          } else {
 361:            addr_len = 15 + strlen(host) + 1;
 362:          }
 363:          if ((address = (char *) malloc(addr_len)) == NULL)
 364:            outofmem(__FILE__, "Gopher ParseMenu");
 365:          *address = '0円';
 366: 
 367:          if (gtype == GOPHER_TELNET) {
 368:            if (escaped)
 369:              sprintf(address, "telnet://%s@%s/",
 370:                  escaped, host);
 371:            else
 372:              sprintf(address, "telnet://%s/", host);
 373:          }
 374:          else if (gtype == GOPHER_TN3270) {
 375:            if (escaped)
 376:              sprintf(address, "tn3270://%s@%s/",
 377:                  escaped, host);
 378:            else 
 379:              sprintf(address, "tn3270://%s/", host);
 380:          } else {
 381:            if (escaped)
 382:              sprintf(address, "//%s/%c%s", host, gtype,
 383:                  escaped);
 384:            else
 385:              sprintf(address, "//%s/%c", host, gtype);
1.1 timbl 386:          }
2.20 frystyk 387: 
 388:          /* Now output the anchor if not a Gopher error */
 389:          if (gtype != GOPHER_ERROR &&
 390:            !strstr(address, "error.host") &&
 391:            !strstr(address, "errorhost")) {
 392:            HTStartAnchor(target, NULL, address);
 393:            PUTS(name);
 394:            END(HTML_A);
 395:          } else 
2.28 frystyk 396:            PUTS(name);    /* Just put it out, but skip type */
2.20 frystyk 397:          FREE(address);
 398:          FREE(escaped);
 399:        } else {                  /* If parse error */
2.28 frystyk 400:          if (PROT_TRACE)
2.20 frystyk 401:            fprintf(stderr, "HTGopher.... Bad menu item, `%s\'\n",
 402:                chunk->data);
 403:          PUTS(chunk->data);
1.1 timbl 404:        }
2.17 frystyk 405:        PUTC('\n');
2.20 frystyk 406:        HTChunkClear(chunk);
 407:        ++files;              /* Update number of files */
 408:      }
 409:    } else
 410:      HTChunkPutc(chunk, ch);
 411:   }
 412:   if (ch < 0)
 413:    status = ch;
1.2 timbl 414: 
2.20 frystyk 415:   /* If no files and message is initialized then make error message,
 416:    else output the bottom part of the list*/
 417:   if (status != HT_INTERRUPTED) {
 418:    if (!files && status < 0) {
 419:      if (message) {
 420:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
 421:              (void *) message, strlen(message), "parse_menu");
 422:      } else {
 423:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
 424:              chunk->data, chunk->size, "parse_menu");
 425:      }
2.21 frystyk 426:    } else if (target) {
2.28 frystyk 427: #ifdef OLD_CODE
2.20 frystyk 428:      char *outstr;
 429:      if ((outstr = (char *) malloc(100)) == NULL)
 430:        outofmem(__FILE__, "parse_menu");
 431:      if (files == 0)
 432:        sprintf(outstr, "Empty directory");
 433:      else if (files == 1)
 434:        sprintf(outstr, "1 file");
 435:      else
 436:        sprintf(outstr, "%u files", files);
 437:      START(HTML_HR);
 438:      PUTS(outstr);
 439:      free(outstr);
2.28 frystyk 440: #endif /* OLD_CODE */
 441:      START(HTML_HR);
 442:      if (!files) PUTS("Empty Gopher Menu");
2.20 frystyk 443:      END(HTML_PRE);
1.1 timbl 444:      
2.20 frystyk 445:      /* Put out any messages */
2.28 frystyk 446:      if ((message || info) && HTDirInfo == HT_DIR_INFO_BOTTOM) {
 447:        if (message) PUTS(message);
 448:        if (info) PUTS(info);
2.20 frystyk 449:        START(HTML_BR);
 450:      }
2.28 frystyk 451:      END(HTML_BODY);
 452:      END(HTML_HTML);
2.21 frystyk 453:      FREE_TARGET;
 454:    } else {
2.28 frystyk 455:      if (PROT_TRACE)
2.21 frystyk 456:        fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
2.20 frystyk 457:    }
2.17 frystyk 458:   }
 459: 
2.20 frystyk 460:   /* Cleanup */
 461:   FREE(message);
2.28 frystyk 462:   FREE(info);
2.26 frystyk 463:   HTInputSocket_free(gopher->isoc);
2.20 frystyk 464:   HTChunkFree(chunk);
 465:   return status;
1.1 timbl 466: }
2.11 timbl 467: 
 468: 
2.7 secret 469: /*   Parse a Gopher CSO document
2.20 frystyk 470: **   ============================
 471: **
 472: **   Accepts an open socket to a CSO server waiting to send us
 473: **   data and puts it on the screen in a reasonable manner.
 474: **
 475: **   Perhaps this data can be automatically linked to some
 476: **   other source as well???
 477: **
 478: **   Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
 479: **   on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
 480: **   secret@dxcern.cern.ch.
 481: **
 482: **   Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
 483: **   if other error.
 484: */
 485: PRIVATE int parse_cso ARGS3(HTRequest *,    request,
 486:              gopher_info *,   gopher,
 487:              CONST char *,    url)
2.7 secret 488: {
2.20 frystyk 489:   int status = -1;
 490:   unsigned int records = 0;
2.17 frystyk 491:   int ch;
2.20 frystyk 492:   char *cur_code = NULL;
 493:   HTChunk *chunk = HTChunkCreate(128);
2.21 frystyk 494:   HTStructured *target = NULL;
2.26 frystyk 495: 
 496:   gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 497:   
 498:   /* Start grabbing chars from the network */
2.26 frystyk 499:   while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 500:    if (ch == CR || ch == LF) {
 501:      if (chunk->size) {     
 502:        /* OK we now have a line in 'p' lets parse it and print it */
 503:        char *strptr;
 504:        HTChunkTerminate(chunk);
 505:        strptr = chunk->data;
 506: 
 507:        /* If line begins with a 1, then more data is coming, so we
 508:          put out the title */
 509:        if (*strptr == '1' ||
 510:          !strncmp(strptr, "501", 3) || !strncmp(strptr, "502", 3)) {
2.21 frystyk 511: 
 512:          /* Put up new stream */
 513:          target = HTML_new(request, NULL, WWW_HTML,
 514:                   request->output_format,
 515:                   request->output_stream);
2.20 frystyk 516:          START(HTML_H1);
 517:          PUTS("CSO Search Results");
 518:          END(HTML_H1);
 519: 
 520:           /* Output the header line of the list */
 521:           START(HTML_PRE); /* To make it look as the other headers */
 522:           if (!icon_blank) icon_blank = icon_unknown;
 523:           if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
 524:             HTMLPutImg(target, icon_blank->icon_url,
 525:                  HTIcon_alt_string(icon_blank->icon_alt, NO),
 526:                  NULL);
 527:           }
 528:           PUTC(' ');
 529:           PUTS("Record");
 530:           PUTC('\n');
 531:           START(HTML_HR);
 532:           PUTC('\n');
 533:          END(HTML_PRE);
 534:        }
2.7 secret 535: 
2.20 frystyk 536:        /* Break on line that begins with a 2. It's the end of data. */
 537:        if (*strptr == '2') {
 538:          status = HT_LOADED;
 539:          break;
 540:        }
 541:        
 542:        /* Lines beginning with 5 are errors, generate msg and quit */
 543:        if (*strptr == '5') {
 544:          char *msgptr = strchr(chunk->data, ':');
 545:          if (!msgptr)
 546:            msgptr = chunk->data;
 547:          else
 548:            ++msgptr;
 549:          if (!strncmp(strptr, "501", 3))      /* No entries */
 550:            status = HT_LOADED;
 551:          else if (!strncmp(strptr, "502", 3)) {    /* Too many */
 552:            status = HT_LOADED;
 553:            PUTS(msgptr);
 554:          } else {
 555:            HTErrorAdd(request, ERR_FATAL, NO, HTERR_CSO_SERVER,
 556:                  (void *) msgptr,
 557:                  strlen(msgptr), "parse_cso");
 558:          }
 559:          break;
 560:        }
 561:        
 562:        if(*strptr == '-') {
 563:          /* data lines look like -200:#:
 564:           * where # is the search result number and can be 
 565:           * multiple digits (infinate?)
 566:           * find the second colon and check the digit to the
 567:           * left of it to see if they are diferent
 568:           * if they are then a different person is starting. 
 569:           * make this line an <h2>
2.7 secret 570:           */
2.20 frystyk 571:          char *code;       /* format: -200:code:field:value */
 572:          char *field;
 573:          char *value;
 574:          if ((code = strchr(strptr, ':')) != NULL &&
 575:            (field = strchr(++code, ':')) != NULL) {
 576:            *field++ = '0円';
 577:            
 578:            /* Let's do a strcmp instead of numbers */
 579:            if (!records) {      /* Header of first record */
 580:              records++;
 581:              START(HTML_H2);
 582:              PUTS("Record 1");
 583:              END(HTML_H2);
 584:              START(HTML_DL);
 585:            } else if (cur_code && strcmp(code, cur_code)) {
 586:              char recstr[20];
 587:              records++;
 588:              END(HTML_DL);
 589:              START(HTML_H3);
 590:              PUTS("Record ");
2.23 frystyk 591:              sprintf(recstr, "%u", records);
2.20 frystyk 592:              PUTS(recstr);
 593:              END(HTML_H3);
 594:              START(HTML_DL);
 595:            } else
 596:              START(HTML_DT);
 597:            
 598:            /* I'm not sure whether the name field comes in any
 599:             * special order or if its even required in a 
 600:             * record, so for now the first line is the header
 601:             * no matter what it is (it's almost always the
 602:             * alias)
2.7 secret 603:             */
2.20 frystyk 604:            if ((value = strchr(field, ':')) == NULL)
 605:              value = "Empty?";
 606:            else
 607:              *value++ = '0円';
 608:            {
 609:              char *strip = HTStrip(field);
 610:              PUTS(strip);
 611:              START(HTML_DD);
 612:              strip = HTStrip(value);
 613:              PUTS(strip);
 614:            }
2.7 secret 615:            
2.20 frystyk 616:            /* save the code for comparison on the next pass */
 617:            StrAllocCopy(cur_code, code);
 618:          }
 619:        } /* end data line */
 620:        HTChunkClear(chunk);
 621:      } /* end new line */
 622:    } else
 623:      HTChunkPutc(chunk, ch);
 624:   }
 625:   if (ch < 0)
 626:    status = ch;
 627: 
 628:   /* Put out the bottom line */
 629:   if (status != HT_INTERRUPTED) {
2.21 frystyk 630:    if (target) {
 631:      char *outstr;
 632:      if ((outstr = (char *) malloc(100)) == NULL)
 633:        outofmem(__FILE__, "parse_menu");
 634:      if (!records)
 635:        sprintf(outstr, "No records");
 636:      else if (records == 1)
 637:        sprintf(outstr, "1 record");
 638:      else
 639:        sprintf(outstr, "%u records", records);
 640:      START(HTML_PRE);
 641:      START(HTML_HR);
 642:      PUTS(outstr);
 643:      END(HTML_PRE);
 644:      free(outstr);
 645:      FREE_TARGET;
 646:    } else {
2.28 frystyk 647:      if (PROT_TRACE)
 648:        fprintf(stderr, "HTGopher.... Interrupted before any stream was put up.\n");
2.21 frystyk 649:    }
2.20 frystyk 650:   }
 651: 
 652:   /* Clean up */
2.26 frystyk 653:   HTInputSocket_free(gopher->isoc);
2.20 frystyk 654:   HTChunkFree(chunk);
 655:   FREE(cur_code);
 656:   return status;
 657: }
2.7 secret 658: 
1.1 timbl 659: 
 660: /*   Display a Gopher Index document
2.20 frystyk 661: **   -------------------------------
 662: */
 663: PRIVATE void display_index ARGS2(HTRequest *,     request,
 664:                 CONST char *,     url)
1.1 timbl 665: {
2.20 frystyk 666:   HTStructured *target = HTML_new(request, NULL, WWW_HTML,
 667:                  request->output_format,
 668:                  request->output_stream);
2.18 luotonen 669: 
1.2 timbl 670:   START(HTML_H1);
2.20 frystyk 671:   PUTS("Searchable Gopher Index");
1.2 timbl 672:   END(HTML_H1);
2.7 secret 673:   START(HTML_ISINDEX);
2.20 frystyk 674:   if (!HTAnchor_title(request->anchor))
 675:    HTAnchor_setTitle(request->anchor, url);  
2.7 secret 676:   FREE_TARGET;
 677:   return;
 678: }
 679: 
 680: 
 681: /*   Display a CSO index document
 682: **   -------------------------------
 683: */
2.20 frystyk 684: PRIVATE void display_cso ARGS2(HTRequest *,      request,
 685:                CONST char *,      url)
2.7 secret 686: {
2.20 frystyk 687:   HTStructured *target = HTML_new(request, NULL, WWW_HTML,
 688:                  request->output_format,
 689:                  request->output_stream);
2.7 secret 690:   START(HTML_H1);
2.20 frystyk 691:   PUTS("Searchable Index of a CSO Name Server");
2.7 secret 692:   END(HTML_H1);
 693:   START(HTML_ISINDEX);
2.20 frystyk 694:   if (!HTAnchor_title(request->anchor))
 695:    HTAnchor_setTitle(request->anchor, url);
1.2 timbl 696:   FREE_TARGET;
1.1 timbl 697:   return;
 698: }
 699: 
 700: 
2.20 frystyk 701: 
 702: /*                            HTGopher_send_cmd
1.1 timbl 703: **
2.20 frystyk 704: **   This function creates a socket and writes the gopher command to it.
 705: **   The command must be terminated with <CRLF>
 706: **
 707: **   Returns 0 on OK, else <0 but does NOT close the connection
1.1 timbl 708: */
2.26 frystyk 709: PRIVATE int HTGopher_send_cmd ARGS3(gopher_info *,   gopher,
2.20 frystyk 710:                  char *,       url,
2.26 frystyk 711:                  char *,       command)
1.1 timbl 712: {
2.20 frystyk 713:   int status = 0;
2.26 frystyk 714:   if (!gopher || !command) {
2.28 frystyk 715:    if (PROT_TRACE)
2.20 frystyk 716:      fprintf(stderr, "Gopher Tx... Bad argument!\n");
 717:    return -1;
 718:   }
2.26 frystyk 719:   if ((status = HTDoConnect((HTNetInfo *) gopher, url, GOPHER_PORT,
2.28 frystyk 720:               NULL, NO)) < 0) {
 721:    if (PROT_TRACE)
2.20 frystyk 722:      fprintf(stderr, "HTLoadGopher Connection not established!\n");
 723:    return status;
 724:   } 
2.28 frystyk 725:   if (PROT_TRACE)
2.26 frystyk 726:    fprintf(stderr, "Gopher...... Connected, socket %d\n", gopher->sockfd);
2.20 frystyk 727:   
 728:   /* Write the command to the socket */
 729: #ifdef NOT_ASCII
 730:   {
 731:    char * p;
 732:    for(p = command; *p; p++) {
 733:      *p = TOASCII(*p);
1.1 timbl 734:    }
 735:   }
2.20 frystyk 736: #endif
2.28 frystyk 737:   if (PROT_TRACE)
2.26 frystyk 738:    fprintf(stderr, "Gopher Tx... %s", command);
 739:   if ((status = NETWRITE(gopher->sockfd, command,
 740:             (int) strlen(command))) < 0) {
2.28 frystyk 741:    if (PROT_TRACE)
 742:      fprintf(stderr, "Gopher...... Error sending command: %s\n",
 743:          command);
2.26 frystyk 744:    HTErrorSysAdd(gopher->request, ERR_FATAL, NO, "NETWRITE");
2.20 frystyk 745:   } else
 746:    status = 0;
 747:   return status;
1.1 timbl 748: }
 749: 
 750: 
 751: /*       Load by name                  HTLoadGopher
 752: **       ============
 753: **
2.24 frystyk 754: **   Given a hypertext address, this routine loads a gopher document
 755: **
 756: ** On entry,
 757: **   request     This is the request structure
 758: ** On exit,
 759: **   returns     <0       Error has occured
 760: **           HT_LOADED    OK
1.1 timbl 761: **
 762: */
2.13 timbl 763: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 764: {
2.22 frystyk 765:   char *url;
2.20 frystyk 766:   int status = -1;
2.26 frystyk 767:   char *command = NULL;
2.20 frystyk 768:   gopher_info *gopher;
 769:   
2.22 frystyk 770:   if (!request || !request->anchor) {
2.28 frystyk 771:    if (PROT_TRACE) fprintf(stderr, "HTLoadGopher Bad argument\n");
2.20 frystyk 772:    return -1;
 773:   }
2.22 frystyk 774:   url = HTAnchor_physical(request->anchor);
2.28 frystyk 775:   if (PROT_TRACE) fprintf(stderr, "HTGopher.... Looking for `%s\'\n", url);
2.20 frystyk 776: 
2.26 frystyk 777:   /* Initiate a new gopher structure and bind to resuest structure */
2.20 frystyk 778:   if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
 779:    outofmem(__FILE__, "HTLoadGopher");
2.26 frystyk 780:   gopher->sockfd = -1;
 781:   gopher->request = request;
 782:   request->net_info = (HTNetInfo *) gopher;
2.20 frystyk 783:   gopher->type = GOPHER_MENU;
1.1 timbl 784:   
2.20 frystyk 785:   /* Get entity type, and selector string and generate command */
1.1 timbl 786:   {
2.20 frystyk 787:    char *path = HTParse(url, "", PARSE_PATH);
 788:    char *selector = path;
 789:    char *query = NULL;
 790:    char *separator = NULL;
 791:    if (*selector)
2.29 ! frystyk 792:      gopher->type = (HTGopherType) *selector++;   /* Pick up gtype */
2.20 frystyk 793:    if (gopher->type == GOPHER_INDEX) {
 794:       HTAnchor_setIndex(request->anchor);        /* Search is allowed */
 795:      query = strchr(selector, '?');     /* Look for search string */
 796: 
 797:      /* Display local "cover page" only if no search requested */
 798:      if (!query || !*(query+1)) {        /* No search required */
 799:        display_index(request, url);
 800:        status = HT_LOADED;          /* Local function only */
 801:      } else {
 802:        *query++ = 0;                  /* Skip '?' */
 803:        separator = "\t";
1.1 timbl 804:      }
2.20 frystyk 805:     } else if (gopher->type == GOPHER_CSO) {
 806:       HTAnchor_setIndex(request->anchor);     /* Search is allowed */
 807:       query = strchr(selector, '?');    /* Look for search string */
 808: 
 809:      /* Display local "cover page" only if no search requested */
 810:       if (!query || !*(query+1)) {        /* No search required */
 811:         display_cso(request, url);
 812:         status = HT_LOADED;          /* Local function only */
 813:       } else {
 814:        *query++ = 0;                /* Skip '?'   */
 815:        separator = "query ";
1.1 timbl 816:      }
 817:    }
 818: 
2.20 frystyk 819:    /* Now generate the final command */
 820:    if (status != HT_LOADED) {
2.24 frystyk 821:      char crlf[3];
2.26 frystyk 822:      StrAllocCopy(command, selector);
2.20 frystyk 823:      if (query) {
 824:        char *p;
 825:        for (p=query; *p; p++)      /* Remove plus signs 921006 */
 826:          if (*p == '+') *p = ' ';
2.26 frystyk 827:        StrAllocCat(command, separator);
 828:        StrAllocCat(command, query);
2.20 frystyk 829:      }
2.26 frystyk 830:      HTUnEscape(command);
 831:      HTCleanTelnetString(command);     /* Prevent security holes */
2.24 frystyk 832:      *crlf = CR;                /* Telnet termination */
 833:      *(crlf+1) = LF;
 834:      *(crlf+2) = '0円';
2.26 frystyk 835:      StrAllocCat(command, crlf);
2.20 frystyk 836:    } 
 837:    free(path);
1.1 timbl 838:   }
 839:   
2.20 frystyk 840:   /* Now we must ask the server for real data :-( */
 841:   if (status != HT_LOADED) {
2.26 frystyk 842:    if ((status = HTGopher_send_cmd(gopher, url, command)) == 0) {
2.20 frystyk 843:      
 844:      /* Now read the data from the socket: */  
 845:      switch (gopher->type) {
 846:       case GOPHER_HTML:
2.26 frystyk 847:        status = HTParseSocket(WWW_HTML, gopher->sockfd, request);
2.20 frystyk 848:        break;
 849:        
 850:       case GOPHER_GIF:
 851:       case GOPHER_IMAGE:
 852:       case GOPHER_PLUS_IMAGE:
2.26 frystyk 853:        status = HTParseSocket(HTAtom_for("image/gif"), gopher->sockfd,
2.20 frystyk 854:                    request);
 855:        break;
 856:       case GOPHER_MENU:
 857:       case GOPHER_INDEX:
 858:        status = parse_menu(request, gopher, url);
 859:        break;
 860:        
 861:       case GOPHER_CSO:
 862:        status = parse_cso(request, gopher, url);
 863:        break;
 864:        
 865:       case GOPHER_MACBINHEX:
 866:       case GOPHER_PCBINHEX:
 867:       case GOPHER_UUENCODED:
 868:       case GOPHER_BINARY:
2.29 ! frystyk 869:        {    /* Do our own filetyping -- maybe we get lucky */
 ! 870:          char *filename = HTParse(url, "",
 ! 871:                       PARSE_PATH+PARSE_PUNCTUATION);
 ! 872:          HTFormat format = HTFileFormat(filename,
 ! 873:                          &request->content_encoding,
 ! 874:                          &request->content_language);
2.20 frystyk 875:          if (format) {
2.28 frystyk 876:            if (PROT_TRACE)
 877:              fprintf(stderr, "Gopher...... Figured out content-type myself: %s\n", HTAtom_name(format));
2.26 frystyk 878:            status = HTParseSocket(format, gopher->sockfd,
2.20 frystyk 879:                        request);
 880:          }
 881:          else {
2.28 frystyk 882:            if (PROT_TRACE)
 883:              fprintf(stderr,"Gopher...... using www/unknown\n");
2.20 frystyk 884:            /* Specifying WWW_UNKNOWN forces dump to local disk */
2.26 frystyk 885:            HTParseSocket(WWW_UNKNOWN, gopher->sockfd, request);
2.20 frystyk 886:          }
2.29 ! frystyk 887:          free(filename);
2.20 frystyk 888:        }
 889:        break;
 890:        
 891:       case GOPHER_SOUND:
 892:       case GOPHER_PLUS_SOUND:
2.26 frystyk 893:        status = HTParseSocket(WWW_AUDIO, gopher->sockfd, request);
2.20 frystyk 894:        break;
 895:        
 896:       case GOPHER_PLUS_MOVIE:
2.26 frystyk 897:        status = HTParseSocket(WWW_VIDEO, gopher->sockfd, request);
2.20 frystyk 898:        break;
2.26 frystyk 899: 
 900:        /* Try and look at the suffix - maybe it is a PostScript file
2.29 ! frystyk 901:          so that we should start an external viewer. */
2.20 frystyk 902:       case GOPHER_TEXT:
2.26 frystyk 903:       default:
2.29 ! frystyk 904:        {    /* Do our own filetyping -- maybe we get lucky */
 ! 905:          char *filename = HTParse(url, "",
 ! 906:                       PARSE_PATH+PARSE_PUNCTUATION);
 ! 907:          HTFormat format = HTFileFormat(filename,
 ! 908:                          &request->content_encoding,
 ! 909:                          &request->content_language);
2.26 frystyk 910:          if (format) {
2.28 frystyk 911:            if (PROT_TRACE)
 912:              fprintf(stderr, "Gopher...... Figured out content-type myself: %s\n", HTAtom_name(format));
2.26 frystyk 913:            status = HTParseSocket(format, gopher->sockfd,
 914:                        request);
 915:          }
 916:          else {
 917:            status = HTParseSocket(WWW_PLAINTEXT, gopher->sockfd,
 918:                        request);
 919:          }
2.29 ! frystyk 920:          free(filename);
2.26 frystyk 921:        }
2.20 frystyk 922:        break;
2.16 luotonen 923:      }
 924:    }
1.2 timbl 925: 
2.20 frystyk 926:    /* Close the connection */
2.26 frystyk 927:    if (gopher->sockfd >= 0) {
2.28 frystyk 928:      if (PROT_TRACE) fprintf(stderr, "Gopher...... Closing socket %d\n",
 929:                  gopher->sockfd);
2.26 frystyk 930:      if (NETCLOSE(gopher->sockfd) < 0)
 931:        status = HTErrorSysAdd(request, ERR_FATAL, NO, "NETCLOSE");
2.25 frystyk 932:    }
2.20 frystyk 933:   }
 934:   if (status == HT_INTERRUPTED) {
 935:     HTErrorAdd(request, ERR_WARNING, NO, HTERR_INTERRUPTED, NULL, 0,
 936:          "HTLoadGopher");
 937:   }
2.26 frystyk 938:   FREE(command);
2.20 frystyk 939:   free(gopher);
 940: 
 941:   if (status < 0 && status != HT_INTERRUPTED) {
2.21 frystyk 942:    char *unescaped = NULL;
 943:    StrAllocCopy(unescaped, url);
 944:    HTUnEscape(unescaped);
 945:     HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
 946:          (int) strlen(unescaped), "HTLoadGopher");
2.20 frystyk 947:    HTAnchor_clearIndex(request->anchor);
2.21 frystyk 948:    free(unescaped);
2.20 frystyk 949:   }
 950:   return status;
1.1 timbl 951: }
1.2 timbl 952: 
2.10 timbl 953: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1 timbl 954: 

Webmaster

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