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

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

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

Webmaster

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