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