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

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

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

Webmaster

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