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

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

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.
 ! 7: */
 ! 8: 
 ! 9: #define GOPHER_PORT 70     /* See protocol spec */
 ! 10: #define BIG 1024        /* Bug */
 ! 11: #define LINE_LENGTH 256        /* Bug */
 ! 12: 
 ! 13: /*   Gopher entity types:
 ! 14: */
 ! 15: #define GOPHER_TEXT      '0'
 ! 16: #define GOPHER_MENU      '1'
 ! 17: #define GOPHER_CSO       '2'
 ! 18: #define GOPHER_ERROR      '3'
 ! 19: #define GOPHER_MACBINHEX    '4'
 ! 20: #define GOPHER_PCBINHEX        '5'
 ! 21: #define GOPHER_UUENCODED    '6'
 ! 22: #define GOPHER_INDEX      '7'
 ! 23: #define GOPHER_TELNET     '8'
 ! 24: #define GOPHER_HTML      'h'       /* HTML */
 ! 25: #define GOPHER_DUPLICATE    '+'
 ! 26: #define GOPHER_WWW       'w'       /* W3 address */
 ! 27: 
 ! 28: #include <ctype.h>
 ! 29: #include "HTUtils.h"      /* Coding convention macros */
 ! 30: #include "tcp.h"
 ! 31: 
 ! 32: #include "HTGopher.h"
 ! 33: 
 ! 34: #include "HText.h"
 ! 35: #include "HTParse.h"
 ! 36: #include "HTFormat.h"
 ! 37: #include "HTTCP.h"
 ! 38: 
 ! 39: #ifdef NeXTStep
 ! 40: #include <appkit/defaults.h>
 ! 41: #define GOPHER_PROGRESS(foo)
 ! 42: #else
 ! 43: #define GOPHER_PROGRESS(foo) fprintf(stderr, "%s\n", (foo))
 ! 44: #endif
 ! 45: 
 ! 46: extern HTStyleSheet * styleSheet;
 ! 47: 
 ! 48: #define NEXT_CHAR HTGetChararcter()
 ! 49: 
 ! 50: 
 ! 51: 
 ! 52: /*   Module-wide variables
 ! 53: */
 ! 54: PRIVATE int s;                 /* Socket for GopherHost */
 ! 55: PRIVATE HText *    HT;               /* the new hypertext */
 ! 56: PRIVATE HTParentAnchor *node_anchor;      /* Its anchor */
 ! 57: PRIVATE int  diagnostic;           /* level: 0=none 2=source */
 ! 58: 
 ! 59: PRIVATE HTStyle *addressStyle;         /* For address etc */
 ! 60: PRIVATE HTStyle *heading1Style;            /* For heading level 1 */
 ! 61: PRIVATE HTStyle *textStyle;          /* Text style */
 ! 62: 
 ! 63: 
 ! 64: /*   Matrix of allowed characters in filenames
 ! 65: **   -----------------------------------------
 ! 66: */
 ! 67: 
 ! 68: PRIVATE BOOL acceptable[256];
 ! 69: PRIVATE BOOL acceptable_inited = NO;
 ! 70: 
 ! 71: PRIVATE void init_acceptable NOARGS
 ! 72: {
 ! 73:   unsigned int i;
 ! 74:   char * good = 
 ! 75:    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
 ! 76:   for(i=0; i<256; i++) acceptable[i] = NO;
 ! 77:   for(;*good; good++) acceptable[(unsigned int)*good] = YES;
 ! 78:   acceptable_inited = YES;
 ! 79: }
 ! 80: 
 ! 81: PRIVATE CONST char hex[17] = "0123456789abcdef";
 ! 82: 
 ! 83: /*   Decdoe one hex character
 ! 84: */
 ! 85: 
 ! 86: PRIVATE char from_hex ARGS1(char, c)
 ! 87: {
 ! 88:   return        (c>='0')&&(c<='9') ? c-'0'
 ! 89:            : (c>='A')&&(c<='F') ? c-'A'+10
 ! 90:            : (c>='a')&&(c<='f') ? c-'a'+10
 ! 91:            :           0;
 ! 92: }
 ! 93: 
 ! 94: 
 ! 95: 
 ! 96: /*   Get Styles from stylesheet
 ! 97: **   --------------------------
 ! 98: */
 ! 99: PRIVATE void get_styles NOARGS
 ! 100: {
 ! 101:   if (!heading1Style) heading1Style = HTStyleNamed(styleSheet, "Heading1");
 ! 102:   if (!addressStyle) addressStyle = HTStyleNamed(styleSheet, "Address");
 ! 103:   if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
 ! 104: }
 ! 105: 
 ! 106: 
 ! 107: /*   Paste in an Anchor
 ! 108: **   ------------------
 ! 109: **
 ! 110: **   The title of the destination is set, as there is no way
 ! 111: **   of knowing what the title is when we arrive.
 ! 112: **
 ! 113: ** On entry,
 ! 114: **   HT   is in append mode.
 ! 115: **   text  points to the text to be put into the file, 0 terminated.
 ! 116: **   addr  points to the hypertext refernce address 0 terminated.
 ! 117: */
 ! 118: PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
 ! 119: {
 ! 120:   HTChildAnchor   *anchor;
 ! 121:   HTParentAnchor   *dest;
 ! 122:   
 ! 123:   HText_beginAnchor(HT,
 ! 124:        anchor = HTAnchor_findChildAndLink(node_anchor, "", addr, 0));
 ! 125:   dest = HTAnchor_parent(
 ! 126:      HTAnchor_followMainLink((HTAnchor *)anchor));
 ! 127:      
 ! 128:   if (!HTAnchor_title(dest)) HTAnchor_setTitle(dest, text);
 ! 129:      
 ! 130:   HText_appendText(HT, text);
 ! 131:   HText_endAnchor(HT);
 ! 132: }
 ! 133: 
 ! 134: 
 ! 135: /*   Parse a Gopher Menu document
 ! 136: **   ============================
 ! 137: **
 ! 138: */
 ! 139: 
 ! 140: PRIVATE void parse_menu ARGS2 (
 ! 141:    CONST char *,  arg,
 ! 142:    HTParentAnchor *,anAnchor)
 ! 143: {
 ! 144:   char gtype;
 ! 145:   char ch;
 ! 146:   char line[BIG];
 ! 147:   char address[BIG];
 ! 148:   char *name, *selector;       /* Gopher menu fields */
 ! 149:   char *host;
 ! 150:   char *port;
 ! 151:   char *p = line;
 ! 152:   
 ! 153: 
 ! 154: #define TAB      '\t'
 ! 155: #define HEX_ESCAPE   '%'
 ! 156: 
 ! 157:   if (!HTAnchor_title(anAnchor))
 ! 158:    HTAnchor_setTitle(anAnchor, arg);/* Tell user something's happening */
 ! 159:   
 ! 160:   node_anchor = anAnchor;
 ! 161:   HT = HText_new(anAnchor);
 ! 162:   
 ! 163:   HText_beginAppend(HT);
 ! 164:   HText_appendText(HT, "Select one of:\n\n");
 ! 165:   
 ! 166:   while ((ch=NEXT_CHAR) != (char)EOF) {
 ! 167:   
 ! 168:     if (ch != '\n') {
 ! 169:      *p = ch;      /* Put character in line */
 ! 170:      if (p< &line[BIG-1]) p++;
 ! 171:      
 ! 172:    } else {
 ! 173:      *p++ = 0;      /* Terminate line */
 ! 174:      p = line;      /* Scan it to parse it */
 ! 175:      port = 0;      /* Flag "not parsed" */
 ! 176:      if (TRACE) fprintf(stderr, "HTGopher: Menu item: %s\n", line);
 ! 177:      gtype = *p++;
 ! 178:      
 ! 179:      /* Break on line with a dot by itself */
 ! 180:      if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
 ! 181: 
 ! 182:      if (gtype && *p) {
 ! 183:        name = p;
 ! 184:        selector = strchr(name, TAB);
 ! 185:        if (selector) {
 ! 186:          *selector++ = 0;  /* Terminate name */
 ! 187:          host = strchr(selector, TAB);
 ! 188:          if (host) {
 ! 189:            *host++ = 0;  /* Terminate selector */
 ! 190:            port = strchr(host, TAB);
 ! 191:            if (port) {
 ! 192:              char *junk;
 ! 193:              port[0] = ':';   /* delimit host a la W3 */
 ! 194:              junk = strchr(port, TAB);
 ! 195:              if (junk) *junk++ = 0;   /* Chop port */
 ! 196:              if ((port[1]=='0') && (!port[2]))
 ! 197:                port[0] = 0;  /* 0 means none */
 ! 198:            } /* no port */
 ! 199:          } /* host ok */
 ! 200:        } /* selector ok */
 ! 201:      } /* gtype and name ok */
 ! 202:      
 ! 203:      if (gtype == GOPHER_WWW) { /* Gopher pointer to W3 */
 ! 204:        write_anchor(name, selector);
 ! 205:        HText_appendParagraph(HT);
 ! 206: 
 ! 207:      } else if (port) {     /* Other types need port */
 ! 208:        if (gtype == GOPHER_TELNET) {
 ! 209:          if (*selector) sprintf(address, "telnet://%s@%s/",
 ! 210:            selector, host);
 ! 211:          else sprintf(address, "telnet://%s/", host);
 ! 212:          
 ! 213:        } else {            /* If parsed ok */
 ! 214:          char *q;
 ! 215:          char *p;
 ! 216:          sprintf(address, "//%s/%c", host, gtype);
 ! 217:          q = address+ strlen(address);
 ! 218:          for(p=selector; *p; p++) { /* Encode selector string */
 ! 219:            if (acceptable[*p]) *q++ = *p;
 ! 220:            else {
 ! 221:              *q++ = HEX_ESCAPE; /* Means hex coming */
 ! 222:              *q++ = hex[(TOASCII(*p)) >> 4];
 ! 223:              *q++ = hex[(TOASCII(*p)) & 15];
 ! 224:            }
 ! 225:          }
 ! 226:          *q++ = 0;          /* terminate address */
 ! 227:        }
 ! 228:        HText_appendText(HT, "    "); /* Prettier JW/TBL */
 ! 229:        write_anchor(name, address);
 ! 230:        HText_appendParagraph(HT);
 ! 231:      } else { /* parse error */
 ! 232:        if (TRACE) fprintf(stderr,
 ! 233:            "HTGopher: Bad menu item.\n");
 ! 234:        HText_appendText(HT, line);
 ! 235:        HText_appendParagraph(HT);
 ! 236:      } /* parse error */
 ! 237:      
 ! 238:      p = line;  /* Start again at beginning of line */
 ! 239:      
 ! 240:    } /* if end of line */
 ! 241:    
 ! 242:   } /* Loop over characters */
 ! 243:    
 ! 244:   HText_endAppend(HT);
 ! 245:   return;
 ! 246: }
 ! 247: 
 ! 248: /*   Display a Gopher Index document
 ! 249: **   -------------------------------
 ! 250: */
 ! 251: 
 ! 252: PRIVATE void display_index ARGS2 (
 ! 253:    CONST char *,  arg,
 ! 254:    HTParentAnchor *,anAnchor)
 ! 255: {
 ! 256:   node_anchor = anAnchor;
 ! 257:   HT = HText_new(anAnchor);
 ! 258:   HText_beginAppend(HT);
 ! 259:   HText_setStyle(HT, heading1Style);
 ! 260:   HText_appendText(HT, arg);
 ! 261:   HText_setStyle(HT, textStyle);
 ! 262:   HText_appendText(HT, "\nThis is a searchable index.\n");
 ! 263:    
 ! 264:   if (!HTAnchor_title(anAnchor))
 ! 265:    HTAnchor_setTitle(anAnchor, arg);/* Tell user something's happening */
 ! 266:   
 ! 267:   HText_endAppend(HT);
 ! 268:   return;
 ! 269: }
 ! 270: 
 ! 271: 
 ! 272: /*       De-escape a selector into a command
 ! 273: **       -----------------------------------
 ! 274: **
 ! 275: **   The % hex escapes are converted. Otheriwse, the string is copied.
 ! 276: */
 ! 277: PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
 ! 278: {
 ! 279:   CONST char * p = selector;
 ! 280:   char * q = command;
 ! 281:    if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
 ! 282:   while (*p) {        /* Decode hex */
 ! 283:    if (*p == HEX_ESCAPE) {
 ! 284:      char c;
 ! 285:      unsigned int b;
 ! 286:      p++;
 ! 287:      c = *p++;
 ! 288:      b =  from_hex(c);
 ! 289:      c = *p++;
 ! 290:      if (!c) break;   /* Odd number of chars! */
 ! 291:      *q++ = FROMASCII((b<<4) + from_hex(c));
 ! 292:    } else {
 ! 293:      *q++ = *p++;    /* Record */
 ! 294:    }
 ! 295:   }
 ! 296:   *q++ = 0; /* Terminate command */
 ! 297: 
 ! 298: }
 ! 299: 
 ! 300: 
 ! 301: /*       Load by name                  HTLoadGopher
 ! 302: **       ============
 ! 303: **
 ! 304: **   Bug:  No decoding of strange data types as yet.
 ! 305: **
 ! 306: */
 ! 307: PUBLIC int HTLoadGopher ARGS3(
 ! 308:    CONST char *,arg,
 ! 309:    HTParentAnchor *,anAnchor,
 ! 310:    int,diag)
 ! 311: {
 ! 312:   char *command;           /* The whole command */
 ! 313:   int status;                /* tcp return */
 ! 314:   char gtype;                /* Gopher Node type */
 ! 315:   char * selector;          /* Selector string */
 ! 316: 
 ! 317:   struct sockaddr_in soc_address;  /* Binary network address */
 ! 318:   struct sockaddr_in* sin = &soc_address;
 ! 319: 
 ! 320:   diagnostic = diag;         /* set global flag */
 ! 321:   
 ! 322:   if (!acceptable_inited) init_acceptable();
 ! 323:   
 ! 324:   if (!arg) return -3;        /* Bad if no name sepcified   */
 ! 325:   if (!*arg) return -2;       /* Bad if name had zero length */
 ! 326:   
 ! 327:   if (TRACE) fprintf(stderr, "HTGopher: Looking for %s\n", arg);
 ! 328:   get_styles();
 ! 329:   
 ! 330:   
 ! 331: /* Set up defaults:
 ! 332: */
 ! 333:   sin->sin_family = AF_INET;         /* Family, host order */
 ! 334:   sin->sin_port = htons(GOPHER_PORT);        /* Default: new port, */
 ! 335: 
 ! 336:   if (TRACE) fprintf(stderr, "HTTPAccess: Looking for %s\n", arg);
 ! 337: 
 ! 338: /* Get node name and optional port number:
 ! 339: */
 ! 340:   {
 ! 341:    char *p1 = HTParse(arg, "", PARSE_HOST);
 ! 342:    int status = HTParseInet(sin, p1);
 ! 343:     free(p1);
 ! 344:     if (status) return status;  /* Bad */
 ! 345:   }
 ! 346:   
 ! 347: /* Get entity type, and selector string.
 ! 348: */    
 ! 349:   {
 ! 350:    char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
 ! 351:     gtype = '1';      /* Default = menu */
 ! 352:    selector = p1;
 ! 353:    if ((*selector++=='/') && (*selector)) {    /* Skip first slash */
 ! 354:      gtype = *selector++;            /* Pick up gtype */
 ! 355:    }
 ! 356:    if (gtype == GOPHER_INDEX) {
 ! 357:      char * query;
 ! 358:       HTAnchor_setIndex(anAnchor);    /* Search is allowed */
 ! 359:      query = strchr(selector, '?');   /* Look for search string */
 ! 360:      if (!query || !query[1]) {     /* No search required */
 ! 361:        display_index(arg, anAnchor);  /* Display "cover page" */
 ! 362:        return 1;            /* Local function only */
 ! 363:      }
 ! 364:      *query++ = 0;            /* Skip '?'   */
 ! 365:      command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
 ! 366:        if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
 ! 367:       
 ! 368:      de_escape(command, selector);    /* Bug fix TBL 921208 */
 ! 369: 
 ! 370:      strcat(command, "\t");
 ! 371:     
 ! 372:      {                  /* Remove plus signs 921006 */
 ! 373:        char *p;
 ! 374:        for (p=query; *p; p++) {
 ! 375:          if (*p == '+') *p = ' ';
 ! 376:        }
 ! 377:      }
 ! 378:      strcat(command, query);
 ! 379:      
 ! 380:    } else {                /* Not index */
 ! 381:      command = command = malloc(strlen(selector)+2+1);
 ! 382:      de_escape(command, selector);
 ! 383:    }
 ! 384:    free(p1);
 ! 385:   }
 ! 386:   
 ! 387:   strcat(command, "\r\n");      /* Include CR for telnet compat. */
 ! 388:   
 ! 389: 
 ! 390: /*   Set up a socket to the server for the data:
 ! 391: */   
 ! 392:   s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 ! 393:   status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
 ! 394:   if (status<0){
 ! 395:    if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
 ! 396:      arg);
 ! 397:    free(command);
 ! 398:    return HTInetStatus("connect");
 ! 399:   }
 ! 400:   
 ! 401:   HTInitInput(s);      /* Set up input buffering */
 ! 402:   
 ! 403:   if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
 ! 404:   
 ! 405: #ifdef NOT_ASCII
 ! 406:   {
 ! 407:    char * p;
 ! 408:    for(p = command; *p; p++) {
 ! 409:      *p = TOASCII(*p);
 ! 410:    }
 ! 411:   }
 ! 412: #endif
 ! 413: 
 ! 414:   status = NETWRITE(s, command, (int)strlen(command));
 ! 415:   free(command);
 ! 416:   if (status<0){
 ! 417:    if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
 ! 418:      return HTInetStatus("send");
 ! 419:   }
 ! 420: 
 ! 421: /*   Now read the data from the socket:
 ! 422: */  
 ! 423:   if (diagnostic==2) gtype = GOPHER_TEXT;  /* Read as plain text anyway */
 ! 424:   
 ! 425:   switch (gtype) {
 ! 426:   
 ! 427:   case GOPHER_HTML :
 ! 428:    HTParseFormat(WWW_HTML, anAnchor, s);
 ! 429:    NETCLOSE(s);
 ! 430:    return 1;
 ! 431: 
 ! 432:   case GOPHER_MENU :
 ! 433:   case GOPHER_INDEX :
 ! 434:     parse_menu(arg, anAnchor);
 ! 435:    NETCLOSE(s);
 ! 436:    return 1;
 ! 437:    
 ! 438:   case GOPHER_TEXT :
 ! 439:   default:          /* @@ parse as plain text */
 ! 440:    HTParseFormat(WWW_PLAINTEXT, anAnchor, s);
 ! 441:    NETCLOSE(s);
 ! 442:    return 1;
 ! 443:   } /* switch(gtype) */
 ! 444:   /*NOTREACHED*/
 ! 445: }
 ! 446: 

Webmaster

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