Annotation of libwww/Library/src/HTGopher.c, revision 1.1.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