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

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

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

Webmaster

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