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

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

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

Webmaster

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