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

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

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
2.39 frystyk 11: **           HF, frystyk@w3.org
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"
2.40 ! frystyk 36: #include "HTMLGen.h"
2.20 frystyk 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: */
2.36 frystyk 105: PRIVATE HTIconNode *get_gopher_icon ARGS1(int, gopher_type)
2.17 frystyk 106: {
2.36 frystyk 107:   HTFormat  content_type = NULL;
 108:   HTEncoding content_encoding = NULL;
2.17 frystyk 109: 
 110:   if (gopher_type == GOPHER_MENU)
 111:    return icon_dir ? icon_dir : icon_unknown;
 112: 
 113:   switch(gopher_type) {
 114:    case GOPHER_TEXT:
 115:    content_type = HTAtom_for("text/void");
 116:    break;
2.20 frystyk 117:    case GOPHER_IMAGE:
 118:    case GOPHER_PLUS_IMAGE:
2.17 frystyk 119:    case GOPHER_GIF:
 120:    content_type = HTAtom_for("image/void");
 121:    break;
2.20 frystyk 122:    case GOPHER_WWW:
2.17 frystyk 123:    case GOPHER_HTML:
 124:    content_type = HTAtom_for("text/void");
 125:    break;
 126:    case GOPHER_SOUND:
2.20 frystyk 127:    case GOPHER_PLUS_SOUND:
2.17 frystyk 128:    content_type = HTAtom_for("audio/void");
 129:    break;
2.20 frystyk 130:    case GOPHER_PLUS_MOVIE:
 131:    content_type = HTAtom_for("video/void");
2.17 frystyk 132:    break;
 133:    case GOPHER_INDEX:
 134:    content_type = HTAtom_for("application/x-gopher-index");
 135:    break;
 136:    case GOPHER_CSO:
 137:    content_type = HTAtom_for("application/x-gopher-cso");
 138:    break;
 139:    case GOPHER_TELNET:
 140:    content_type = HTAtom_for("application/x-gopher-telnet");
 141:    break;
 142:    case GOPHER_TN3270:
 143:    content_type = HTAtom_for("application/x-gopher-tn3270");
 144:    break;
 145:    case GOPHER_DUPLICATE:
 146:    content_type = HTAtom_for("application/x-gopher-duplicate");
 147:    break;
 148:    case GOPHER_ERROR:
 149:    content_type = HTAtom_for("www/unknown");
 150:    break;
 151:    case GOPHER_MACBINHEX:
 152:    case GOPHER_PCBINHEX:
 153:    case GOPHER_UUENCODED:
2.36 frystyk 154:    content_type = WWW_BINARY;
 155:    content_encoding = WWW_ENC_BASE64;
 156:    break;
2.17 frystyk 157:    case GOPHER_BINARY:
2.36 frystyk 158:    content_type = WWW_BINARY;
 159:    break;
2.17 frystyk 160:    default:
 161:    content_type = HTAtom_for("www/unknown");
 162:    break;
 163:   }
 164:   return HTGetIcon(S_IFMT & S_IFREG, content_type, content_encoding);
 165: }
 166: 
 167: 
2.20 frystyk 168: /*                              parse_menu
 169: **
 170: **   This function parses a gopher menu and puts it into a iconized
 171: **   list.
 172: **
 173: **   Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
 174: **   if other error.
1.1 timbl 175: **
 176: */
2.20 frystyk 177: PRIVATE int parse_menu ARGS3(HTRequest *,   request,
 178:               gopher_info *,   gopher,
 179:               CONST char *,   url)
 180: #define TAB      '\t'
 181: #define HEX_ESCAPE   '%'
1.1 timbl 182: {
2.20 frystyk 183:   int status = -1;
2.17 frystyk 184:   unsigned int files = 0;
 185:   int ch;
2.20 frystyk 186:   HTChunk *chunk = HTChunkCreate(128);
 187:   char *message = NULL;              /* For a gopher message */
2.28 frystyk 188:   char *info = NULL;      /* For gopher information send as `i' type */
 189: 
2.21 frystyk 190:   HTStructured *target = NULL;
2.26 frystyk 191: 
 192:   gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20 frystyk 193:   
 194:   /* Output the list */
2.26 frystyk 195:   while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20 frystyk 196:     if (ch == CR || ch == LF) {
 197:      if (chunk->size) {               /* Got some text */
 198:        char *name = NULL;           /* Gopher menu fields */
 199:        char *selector = NULL;
 200:        char *host = NULL;
 201:        char *port = NULL;
 202:        char *strptr;
 203:        char *errptr;
 204:        char gtype;
 205:        HTChunkTerminate(chunk);
 206:        strptr = chunk->data;         /* Scan it to parse it */
2.28 frystyk 207:        if (PROT_TRACE)
2.34 frystyk 208:          fprintf(TDEST, "HTGopher.... Menu item: `%s\'\n",
2.20 frystyk 209:              chunk->data);
 210:        gtype = *strptr++;
 211: 
 212:        if (gtype == GOPHER_ERROR) {
2.28 frystyk 213:          StrAllocCat(message, chunk->data+1);
2.31 frystyk 214:          continue;
2.20 frystyk 215:        }
2.28 frystyk 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.40 ! frystyk 258:          target = HTMLGenerator(request, NULL, WWW_HTML,
2.21 frystyk 259:                   request->output_format,
 260:                   request->output_stream);
2.36 frystyk 261:          HTAnchor_setFormat(request->anchor, WWW_HTML);
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);
2.36 frystyk 338:          HTIconNode *icon = get_gopher_icon(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 */
2.40 ! frystyk 520:          target = HTMLGenerator(request, NULL, WWW_HTML,
2.21 frystyk 521:                   request->output_format,
 522:                   request->output_stream);
2.36 frystyk 523:          HTAnchor_setFormat(request->anchor, WWW_HTML);
2.34 frystyk 524:          START(HTML_HTML);
 525:          START(HTML_HEAD);
 526:          START(HTML_TITLE);
 527:          PUTS("CSO Search: ");
 528:          if (keyword)
 529:            PUTS(keyword);
 530:          else
 531:            PUTS("empty");
 532:          END(HTML_TITLE);
 533:          END(HTML_HEAD);
 534:          START(HTML_BODY);
 535: 
2.20 frystyk 536:          START(HTML_H1);
2.34 frystyk 537:          PUTS("CSO Search: ");
 538:          if (keyword)
 539:            PUTS(keyword);
 540:          else
 541:            PUTS("empty");
2.20 frystyk 542:          END(HTML_H1);
 543: 
2.34 frystyk 544: #if 0
2.20 frystyk 545:           /* Output the header line of the list */
 546:           START(HTML_PRE); /* To make it look as the other headers */
 547:           if (!icon_blank) icon_blank = icon_unknown;
 548:           if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
 549:             HTMLPutImg(target, icon_blank->icon_url,
 550:                  HTIcon_alt_string(icon_blank->icon_alt, NO),
 551:                  NULL);
 552:           }
 553:           PUTC(' ');
 554:           PUTS("Record");
 555:           PUTC('\n');
 556:           START(HTML_HR);
 557:           PUTC('\n');
 558:          END(HTML_PRE);
2.34 frystyk 559: #endif
2.20 frystyk 560:        }
2.7 secret 561: 
2.20 frystyk 562:        /* Break on line that begins with a 2. It's the end of data. */
 563:        if (*strptr == '2') {
 564:          status = HT_LOADED;
 565:          break;
 566:        }
 567:        
 568:        /* Lines beginning with 5 are errors, generate msg and quit */
 569:        if (*strptr == '5') {
 570:          char *msgptr = strchr(chunk->data, ':');
 571:          if (!msgptr)
 572:            msgptr = chunk->data;
 573:          else
 574:            ++msgptr;
 575:          if (!strncmp(strptr, "501", 3))      /* No entries */
 576:            status = HT_LOADED;
 577:          else if (!strncmp(strptr, "502", 3)) {    /* Too many */
 578:            status = HT_LOADED;
 579:            PUTS(msgptr);
 580:          } else {
 581:            HTErrorAdd(request, ERR_FATAL, NO, HTERR_CSO_SERVER,
 582:                  (void *) msgptr,
 583:                  strlen(msgptr), "parse_cso");
 584:          }
 585:          break;
 586:        }
 587:        
 588:        if(*strptr == '-') {
 589:          /* data lines look like -200:#:
 590:           * where # is the search result number and can be 
 591:           * multiple digits (infinate?)
 592:           * find the second colon and check the digit to the
 593:           * left of it to see if they are diferent
 594:           * if they are then a different person is starting. 
 595:           * make this line an <h2>
2.7 secret 596:           */
2.20 frystyk 597:          char *code;       /* format: -200:code:field:value */
 598:          char *field;
 599:          char *value;
 600:          if ((code = strchr(strptr, ':')) != NULL &&
 601:            (field = strchr(++code, ':')) != NULL) {
 602:            *field++ = '0円';
 603:            
 604:            /* Let's do a strcmp instead of numbers */
 605:            if (!records) {      /* Header of first record */
2.34 frystyk 606: #if 0
2.20 frystyk 607:              START(HTML_H2);
 608:              PUTS("Record 1");
 609:              END(HTML_H2);
2.34 frystyk 610: #endif
2.20 frystyk 611:              START(HTML_DL);
 612:              START(HTML_DT);
2.34 frystyk 613:            } else {
 614:              if (cur_code && strcmp(code, cur_code))
 615:                START(HTML_P);
 616:              START(HTML_DT);
 617:            }
2.20 frystyk 618:            
 619:            /* I'm not sure whether the name field comes in any
 620:             * special order or if its even required in a 
 621:             * record, so for now the first line is the header
 622:             * no matter what it is (it's almost always the
 623:             * alias)
2.7 secret 624:             */
2.20 frystyk 625:            if ((value = strchr(field, ':')) == NULL)
 626:              value = "Empty?";
 627:            else
 628:              *value++ = '0円';
 629:            {
 630:              char *strip = HTStrip(field);
 631:              PUTS(strip);
 632:              START(HTML_DD);
 633:              strip = HTStrip(value);
2.34 frystyk 634:              if (!records ||
 635:                (cur_code && strcmp(code, cur_code))) {
 636:                START(HTML_B);
 637:                PUTS(strip);
 638:                END(HTML_B);
 639:              } else
 640:                PUTS(strip);
2.20 frystyk 641:            }
2.34 frystyk 642:            records++;
2.7 secret 643:            
2.20 frystyk 644:            /* save the code for comparison on the next pass */
 645:            StrAllocCopy(cur_code, code);
 646:          }
 647:        } /* end data line */
 648:        HTChunkClear(chunk);
 649:      } /* end new line */
 650:    } else
 651:      HTChunkPutc(chunk, ch);
 652:   }
 653:   if (ch < 0)
 654:    status = ch;
 655: 
 656:   /* Put out the bottom line */
2.34 frystyk 657: #if 0
2.20 frystyk 658:   if (status != HT_INTERRUPTED) {
2.21 frystyk 659:    if (target) {
 660:      char *outstr;
 661:      if ((outstr = (char *) malloc(100)) == NULL)
 662:        outofmem(__FILE__, "parse_menu");
 663:      if (!records)
 664:        sprintf(outstr, "No records");
 665:      else if (records == 1)
 666:        sprintf(outstr, "1 record");
 667:      else
 668:        sprintf(outstr, "%u records", records);
 669:      START(HTML_PRE);
 670:      START(HTML_HR);
 671:      PUTS(outstr);
 672:      END(HTML_PRE);
 673:      free(outstr);
 674:      FREE_TARGET;
 675:    } else {
2.28 frystyk 676:      if (PROT_TRACE)
2.34 frystyk 677:        fprintf(TDEST, "HTGopher.... Interrupted before any stream was put up.\n");
2.21 frystyk 678:    }
2.20 frystyk 679:   }
2.34 frystyk 680: #endif
2.20 frystyk 681: 
2.34 frystyk 682:   if (target) {
 683:    if (records)
 684:      END(HTML_DL);
 685:    else
 686:      PUTS("Nothing matched you query");
 687:    END(HTML_BODY);
 688:    END(HTML_HTML);
 689:    FREE_TARGET;
 690:   }
2.20 frystyk 691:   /* Clean up */
2.26 frystyk 692:   HTInputSocket_free(gopher->isoc);
2.20 frystyk 693:   HTChunkFree(chunk);
 694:   FREE(cur_code);
 695:   return status;
 696: }
2.7 secret 697: 
1.1 timbl 698: 
 699: /*   Display a Gopher Index document
2.20 frystyk 700: **   -------------------------------
 701: */
 702: PRIVATE void display_index ARGS2(HTRequest *,     request,
 703:                 CONST char *,     url)
1.1 timbl 704: {
2.40 ! frystyk 705:   HTStructured *target = HTMLGenerator(request, NULL, WWW_HTML,
2.20 frystyk 706:                  request->output_format,
 707:                  request->output_stream);
2.36 frystyk 708:   HTAnchor_setFormat(request->anchor, WWW_HTML);
2.34 frystyk 709:   START(HTML_HTML);
 710:   START(HTML_HEAD);
 711:   START(HTML_TITLE);
 712:   PUTS("Searchable Gopher Index");
 713:   END(HTML_TITLE);
 714:   END(HTML_HEAD);
 715:   START(HTML_BODY);
 716: 
1.2 timbl 717:   START(HTML_H1);
2.20 frystyk 718:   PUTS("Searchable Gopher Index");
1.2 timbl 719:   END(HTML_H1);
2.7 secret 720:   START(HTML_ISINDEX);
2.20 frystyk 721:   if (!HTAnchor_title(request->anchor))
 722:    HTAnchor_setTitle(request->anchor, url);  
2.34 frystyk 723:   END(HTML_BODY);
 724:   END(HTML_HTML);
2.7 secret 725:   FREE_TARGET;
 726:   return;
 727: }
 728: 
 729: 
 730: /*   Display a CSO index document
 731: **   -------------------------------
 732: */
2.20 frystyk 733: PRIVATE void display_cso ARGS2(HTRequest *,      request,
 734:                CONST char *,      url)
2.7 secret 735: {
2.40 ! frystyk 736:   HTStructured *target = HTMLGenerator(request, NULL, WWW_HTML,
2.20 frystyk 737:                  request->output_format,
 738:                  request->output_stream);
2.36 frystyk 739:   HTAnchor_setFormat(request->anchor, WWW_HTML);
2.34 frystyk 740:   START(HTML_HTML);
 741:   START(HTML_HEAD);
 742:   START(HTML_TITLE);
 743:   PUTS("Searchable Index of a CSO Name Server");
 744:   END(HTML_TITLE);
 745:   END(HTML_HEAD);
 746:   START(HTML_BODY);
 747: 
2.7 secret 748:   START(HTML_H1);
2.20 frystyk 749:   PUTS("Searchable Index of a CSO Name Server");
2.7 secret 750:   END(HTML_H1);
2.34 frystyk 751:   PUTS("A CSO Name Server usually provides directory information about people.");
2.7 secret 752:   START(HTML_ISINDEX);
2.20 frystyk 753:   if (!HTAnchor_title(request->anchor))
 754:    HTAnchor_setTitle(request->anchor, url);
2.34 frystyk 755:   END(HTML_BODY);
 756:   END(HTML_HTML);
1.2 timbl 757:   FREE_TARGET;
1.1 timbl 758:   return;
 759: }
 760: 
 761: 
2.20 frystyk 762: 
 763: /*                            HTGopher_send_cmd
1.1 timbl 764: **
2.20 frystyk 765: **   This function creates a socket and writes the gopher command to it.
 766: **   The command must be terminated with <CRLF>
 767: **
 768: **   Returns 0 on OK, else <0 but does NOT close the connection
1.1 timbl 769: */
2.26 frystyk 770: PRIVATE int HTGopher_send_cmd ARGS3(gopher_info *,   gopher,
2.20 frystyk 771:                  char *,       url,
2.26 frystyk 772:                  char *,       command)
1.1 timbl 773: {
2.20 frystyk 774:   int status = 0;
2.26 frystyk 775:   if (!gopher || !command) {
2.28 frystyk 776:    if (PROT_TRACE)
2.34 frystyk 777:      fprintf(TDEST, "Gopher Tx... Bad argument!\n");
2.20 frystyk 778:    return -1;
 779:   }
2.26 frystyk 780:   if ((status = HTDoConnect((HTNetInfo *) gopher, url, GOPHER_PORT,
2.28 frystyk 781:               NULL, NO)) < 0) {
 782:    if (PROT_TRACE)
2.34 frystyk 783:      fprintf(TDEST, "HTLoadGopher Connection not established!\n");
2.20 frystyk 784:    return status;
 785:   } 
2.28 frystyk 786:   if (PROT_TRACE)
2.34 frystyk 787:    fprintf(TDEST, "Gopher...... Connected, socket %d\n", gopher->sockfd);
2.20 frystyk 788:   
 789:   /* Write the command to the socket */
 790: #ifdef NOT_ASCII
 791:   {
 792:    char * p;
 793:    for(p = command; *p; p++) {
 794:      *p = TOASCII(*p);
1.1 timbl 795:    }
 796:   }
2.20 frystyk 797: #endif
2.28 frystyk 798:   if (PROT_TRACE)
2.34 frystyk 799:    fprintf(TDEST, "Gopher Tx... %s", command);
2.26 frystyk 800:   if ((status = NETWRITE(gopher->sockfd, command,
 801:             (int) strlen(command))) < 0) {
2.28 frystyk 802:    if (PROT_TRACE)
2.34 frystyk 803:      fprintf(TDEST, "Gopher...... Error sending command: %s\n",
2.28 frystyk 804:          command);
2.34 frystyk 805:    HTErrorSysAdd(gopher->request, ERR_FATAL, socerrno, NO, "NETWRITE");
2.20 frystyk 806:   } else
 807:    status = 0;
 808:   return status;
1.1 timbl 809: }
 810: 
 811: 
 812: /*       Load by name                  HTLoadGopher
 813: **       ============
 814: **
2.24 frystyk 815: **   Given a hypertext address, this routine loads a gopher document
 816: **
 817: ** On entry,
 818: **   request     This is the request structure
 819: ** On exit,
 820: **   returns     <0       Error has occured
 821: **           HT_LOADED    OK
1.1 timbl 822: **
 823: */
2.13 timbl 824: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1 timbl 825: {
2.22 frystyk 826:   char *url;
2.20 frystyk 827:   int status = -1;
2.26 frystyk 828:   char *command = NULL;
2.20 frystyk 829:   gopher_info *gopher;
 830:   
2.22 frystyk 831:   if (!request || !request->anchor) {
2.34 frystyk 832:    if (PROT_TRACE) fprintf(TDEST, "HTLoadGopher Bad argument\n");
2.20 frystyk 833:    return -1;
 834:   }
2.22 frystyk 835:   url = HTAnchor_physical(request->anchor);
2.34 frystyk 836:   if (PROT_TRACE) fprintf(TDEST, "HTGopher.... Looking for `%s\'\n", url);
2.20 frystyk 837: 
2.26 frystyk 838:   /* Initiate a new gopher structure and bind to resuest structure */
2.20 frystyk 839:   if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
 840:    outofmem(__FILE__, "HTLoadGopher");
2.34 frystyk 841:   gopher->sockfd = INVSOC;
2.26 frystyk 842:   gopher->request = request;
 843:   request->net_info = (HTNetInfo *) gopher;
2.20 frystyk 844:   gopher->type = GOPHER_MENU;
1.1 timbl 845:   
2.20 frystyk 846:   /* Get entity type, and selector string and generate command */
1.1 timbl 847:   {
2.20 frystyk 848:    char *path = HTParse(url, "", PARSE_PATH);
 849:    char *selector = path;
 850:    char *query = NULL;
 851:    char *separator = NULL;
 852:    if (*selector)
2.29 frystyk 853:      gopher->type = (HTGopherType) *selector++;   /* Pick up gtype */
2.20 frystyk 854:    if (gopher->type == GOPHER_INDEX) {
 855:       HTAnchor_setIndex(request->anchor);        /* Search is allowed */
 856:      query = strchr(selector, '?');     /* Look for search string */
 857: 
 858:      /* Display local "cover page" only if no search requested */
 859:      if (!query || !*(query+1)) {        /* No search required */
 860:        display_index(request, url);
 861:        status = HT_LOADED;          /* Local function only */
 862:      } else {
 863:        *query++ = 0;                  /* Skip '?' */
 864:        separator = "\t";
1.1 timbl 865:      }
2.20 frystyk 866:     } else if (gopher->type == GOPHER_CSO) {
 867:       HTAnchor_setIndex(request->anchor);     /* Search is allowed */
 868:       query = strchr(selector, '?');    /* Look for search string */
 869: 
 870:      /* Display local "cover page" only if no search requested */
 871:       if (!query || !*(query+1)) {        /* No search required */
 872:         display_cso(request, url);
 873:         status = HT_LOADED;          /* Local function only */
 874:       } else {
 875:        *query++ = 0;                /* Skip '?'   */
2.34 frystyk 876:        *selector = 0;
2.20 frystyk 877:        separator = "query ";
1.1 timbl 878:      }
 879:    }
 880: 
2.20 frystyk 881:    /* Now generate the final command */
 882:    if (status != HT_LOADED) {
2.24 frystyk 883:      char crlf[3];
2.26 frystyk 884:      StrAllocCopy(command, selector);
2.20 frystyk 885:      if (query) {
 886:        char *p;
 887:        for (p=query; *p; p++)      /* Remove plus signs 921006 */
 888:          if (*p == '+') *p = ' ';
2.26 frystyk 889:        StrAllocCat(command, separator);
 890:        StrAllocCat(command, query);
2.20 frystyk 891:      }
2.26 frystyk 892:      HTUnEscape(command);
 893:      HTCleanTelnetString(command);     /* Prevent security holes */
2.24 frystyk 894:      *crlf = CR;                /* Telnet termination */
 895:      *(crlf+1) = LF;
 896:      *(crlf+2) = '0円';
2.26 frystyk 897:      StrAllocCat(command, crlf);
2.20 frystyk 898:    } 
 899:    free(path);
1.1 timbl 900:   }
 901:   
2.20 frystyk 902:   /* Now we must ask the server for real data :-( */
 903:   if (status != HT_LOADED) {
2.26 frystyk 904:    if ((status = HTGopher_send_cmd(gopher, url, command)) == 0) {
2.20 frystyk 905:      
 906:      /* Now read the data from the socket: */  
 907:      switch (gopher->type) {
 908:       case GOPHER_HTML:
2.36 frystyk 909:        HTAnchor_setFormat(request->anchor, WWW_HTML);
2.26 frystyk 910:        status = HTParseSocket(WWW_HTML, gopher->sockfd, request);
2.20 frystyk 911:        break;
 912:        
 913:       case GOPHER_MENU:
 914:       case GOPHER_INDEX:
 915:        status = parse_menu(request, gopher, url);
 916:        break;
 917:        
 918:       case GOPHER_CSO:
 919:        status = parse_cso(request, gopher, url);
 920:        break;
 921:        
2.36 frystyk 922:       case GOPHER_GIF:
 923:       case GOPHER_IMAGE:
 924:       case GOPHER_PLUS_IMAGE:
2.20 frystyk 925:       case GOPHER_MACBINHEX:
 926:       case GOPHER_PCBINHEX:
 927:       case GOPHER_UUENCODED:
2.36 frystyk 928:       case GOPHER_BINARY:          /* Do our own filetyping */
 929:        HTBind_getBindings(request->anchor);
 930:        status = HTParseSocket(HTAnchor_format(request->anchor),
 931:                    gopher->sockfd, request);
2.20 frystyk 932:        break;
 933:        
 934:       case GOPHER_SOUND:
 935:       case GOPHER_PLUS_SOUND:
2.36 frystyk 936:        HTAnchor_setFormat(request->anchor, WWW_AUDIO);
2.26 frystyk 937:        status = HTParseSocket(WWW_AUDIO, gopher->sockfd, request);
2.20 frystyk 938:        break;
 939:        
 940:       case GOPHER_PLUS_MOVIE:
2.36 frystyk 941:        HTAnchor_setFormat(request->anchor, WWW_VIDEO);
2.26 frystyk 942:        status = HTParseSocket(WWW_VIDEO, gopher->sockfd, request);
2.20 frystyk 943:        break;
2.26 frystyk 944: 
 945:        /* Try and look at the suffix - maybe it is a PostScript file
2.29 frystyk 946:          so that we should start an external viewer. */
2.20 frystyk 947:       case GOPHER_TEXT:
2.26 frystyk 948:       default:
2.36 frystyk 949:        HTBind_getBindings(request->anchor);
 950:        status = HTParseSocket(HTAnchor_format(request->anchor),
 951:                    gopher->sockfd, request);
2.20 frystyk 952:        break;
2.16 luotonen 953:      }
 954:    }
1.2 timbl 955: 
2.20 frystyk 956:    /* Close the connection */
2.34 frystyk 957:    if (gopher->sockfd != INVSOC) {
 958:      if (PROT_TRACE) fprintf(TDEST, "Gopher...... Closing socket %d\n",
2.28 frystyk 959:                  gopher->sockfd);
2.26 frystyk 960:      if (NETCLOSE(gopher->sockfd) < 0)
2.34 frystyk 961:        status = HTErrorSysAdd(request, ERR_FATAL, socerrno, NO,
 962:                    "NETCLOSE");
2.25 frystyk 963:    }
2.20 frystyk 964:   }
 965:   if (status == HT_INTERRUPTED) {
2.34 frystyk 966:     HTErrorAdd(request, ERR_WARN, NO, HTERR_INTERRUPTED, NULL, 0,
2.20 frystyk 967:          "HTLoadGopher");
 968:   }
2.26 frystyk 969:   FREE(command);
2.30 frystyk 970:   gopher->request->net_info = NULL;
2.20 frystyk 971:   free(gopher);
 972: 
 973:   if (status < 0 && status != HT_INTERRUPTED) {
2.21 frystyk 974:    char *unescaped = NULL;
 975:    StrAllocCopy(unescaped, url);
 976:    HTUnEscape(unescaped);
 977:     HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
 978:          (int) strlen(unescaped), "HTLoadGopher");
2.20 frystyk 979:    HTAnchor_clearIndex(request->anchor);
2.21 frystyk 980:    free(unescaped);
2.20 frystyk 981:   }
 982:   return status;
1.1 timbl 983: }
1.2 timbl 984: 
2.30 frystyk 985: GLOBALDEF PUBLIC HTProtocol HTGopher = {
 986:   "gopher", SOC_BLOCK, HTLoadGopher, NULL, NULL
 987: };
1.1 timbl 988: 

Webmaster

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