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

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

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

Webmaster

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