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

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

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

Webmaster

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