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

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
1.1       timbl      12: */
                     13: 
1.2       timbl      14: /* Implements:
                     15: */
                     16: #include "HTGopher.h"
                     17: 
1.3       timbl      18: 
1.1       timbl      19: #define GOPHER_PORT 70         /* See protocol spec */
                     20: #define BIG 1024               /* Bug */
                     21: #define LINE_LENGTH 256                /* Bug */
                     22: 
                     23: /*     Gopher entity types:
                     24: */
                     25: #define GOPHER_TEXT            '0'
                     26: #define GOPHER_MENU            '1'
                     27: #define GOPHER_CSO             '2'
                     28: #define GOPHER_ERROR           '3'
                     29: #define GOPHER_MACBINHEX       '4'
                     30: #define GOPHER_PCBINHEX                '5'
                     31: #define GOPHER_UUENCODED       '6'
                     32: #define GOPHER_INDEX           '7'
                     33: #define GOPHER_TELNET          '8'
2.7       secret     34: #define GOPHER_BINARY           '9'
1.3       timbl      35: #define GOPHER_GIF              'g'
2.7       secret     36: #define GOPHER_HTML            'h'             /* HTML */
                     37: #define GOPHER_SOUND            's'
                     38: #define GOPHER_WWW             'w'             /* W3 address */
1.3       timbl      39: #define GOPHER_IMAGE            'I'
2.7       secret     40: #define GOPHER_TN3270           'T'
1.1       timbl      41: #define GOPHER_DUPLICATE       '+'
                     42: 
                     43: #include <ctype.h>
                     44: #include "HTUtils.h"           /* Coding convention macros */
                     45: #include "tcp.h"
                     46: 
2.17      frystyk    47: #include "HTIcons.h"
1.1       timbl      48: #include "HTParse.h"
                     49: #include "HTFormat.h"
                     50: #include "HTTCP.h"
2.16      luotonen   51: #include "HTFile.h"            /* HTFileFormat() */
1.1       timbl      52: 
1.2       timbl      53: /*             Hypertext object building machinery
                     54: */
                     55: #include "HTML.h"
                     56: 
2.17      frystyk    57: #define PUTC(c) (*target->isa->put_character)(target, c)
                     58: #define PUTS(s) (*target->isa->put_string)(target, s)
                     59: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
                     60: #define END(e) (*target->isa->end_element)(target, e)
                     61: #define FREE_TARGET (*target->isa->free)(target)
1.2       timbl      62: struct _HTStructured {
                     63:        CONST HTStructuredClass *       isa;
                     64:        /* ... */
                     65: };
                     66: 
2.17      frystyk    67: #ifdef OLD_CODE
1.2       timbl      68: PRIVATE HTStructured *target;                  /* the new hypertext */
                     69: PRIVATE HTStructuredClass targetClass;         /* Its action routines */
2.17      frystyk    70: #endif /* OLD_CODE */
1.2       timbl      71: 
2.8       timbl      72: #define GOPHER_PROGRESS(foo) HTAlert(foo)
1.1       timbl      73: 
                     74: 
2.12      timbl      75: #define NEXT_CHAR HTInputSocket_getCharacter(isoc) 
1.1       timbl      76: 
                     77: 
2.8       timbl      78: 
1.1       timbl      79: /*     Module-wide variables
                     80: */
                     81: PRIVATE int s;                                 /* Socket for GopherHost */
                     82: 
                     83: 
1.2       timbl      84: 
1.1       timbl      85: /*     Matrix of allowed characters in filenames
                     86: **     -----------------------------------------
                     87: */
                     88: 
                     89: PRIVATE BOOL acceptable[256];
                     90: PRIVATE BOOL acceptable_inited = NO;
                     91: 
                     92: PRIVATE void init_acceptable NOARGS
                     93: {
                     94:     unsigned int i;
                     95:     char * good = 
                     96:       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
                     97:     for(i=0; i<256; i++) acceptable[i] = NO;
                     98:     for(;*good; good++) acceptable[(unsigned int)*good] = YES;
                     99:     acceptable_inited = YES;
                    100: }
                    101: 
                    102: PRIVATE CONST char hex[17] = "0123456789abcdef";
                    103: 
                    104: /*     Decdoe one hex character
                    105: */
                    106: 
                    107: PRIVATE char from_hex ARGS1(char, c)
                    108: {
                    109:     return               (c>='0')&&(c<='9') ? c-'0'
                    110:                        : (c>='A')&&(c<='F') ? c-'A'+10
                    111:                        : (c>='a')&&(c<='f') ? c-'a'+10
                    112:                        :                      0;
                    113: }
                    114: 
                    115: 
                    116: 
                    117: /*     Paste in an Anchor
                    118: **     ------------------
                    119: **
                    120: **     The title of the destination is set, as there is no way
                    121: **     of knowing what the title is when we arrive.
                    122: **
                    123: ** On entry,
                    124: **     HT      is in append mode.
                    125: **     text    points to the text to be put into the file, 0 terminated.
                    126: **     addr    points to the hypertext refernce address 0 terminated.
                    127: */
2.17      frystyk   128: PRIVATE void write_anchor ARGS3(HTStructured *, target,
                    129:                                CONST char *, text,
                    130:                                CONST char *, addr)
1.1       timbl     131: {
1.2       timbl     132:     BOOL present[HTML_A_ATTRIBUTES];
                    133:     CONST char * value[HTML_A_ATTRIBUTES];
1.1       timbl     134:     
1.2       timbl     135:     int i;
                    136:     
                    137:     for (i=0; i<HTML_A_ATTRIBUTES; i++) present[i]=0;
                    138:     present[HTML_A_HREF] = YES;
                    139:     value[HTML_A_HREF] = addr;
                    140:     present[HTML_A_TITLE] = YES;
                    141:     value[HTML_A_TITLE] = text;
                    142:     
2.17      frystyk   143:     (*target->isa->start_element)(target, HTML_A, present, value);
1.1       timbl     144:            
1.2       timbl     145:     PUTS(text);
                    146:     END(HTML_A);
1.1       timbl     147: }
                    148: 
                    149: 
2.17      frystyk   150: /*     Find a icon
                    151: **     ===========
                    152: **
                    153: */
                    154: PRIVATE HTIconNode *get_gopher_icon ARGS2(CONST char *, filename,
                    155:                                          int, gopher_type)
                    156: {
                    157:     HTFormat content_type = NULL;
                    158:     HTAtom *content_encoding = NULL;
                    159: 
                    160:     if (gopher_type == GOPHER_MENU)
                    161:        return icon_dir ? icon_dir : icon_unknown;
                    162: 
                    163:     switch(gopher_type) {
                    164:       case GOPHER_TEXT:
                    165:        content_type = HTAtom_for("text/void");
                    166:        break;
                    167:       case GOPHER_GIF:
                    168:        content_type = HTAtom_for("image/void");
                    169:        break;
                    170:       case GOPHER_HTML:
                    171:        content_type = HTAtom_for("text/void");
                    172:        break;
                    173:       case GOPHER_SOUND:
                    174:        content_type = HTAtom_for("audio/void");
                    175:        break;
                    176:       case GOPHER_WWW:
                    177:        content_type = HTAtom_for("text/void");
                    178:        break;
                    179:       case GOPHER_IMAGE:
                    180:        content_type = HTAtom_for("image/void");
                    181:        break;
                    182:       case GOPHER_INDEX:
                    183:        content_type = HTAtom_for("application/x-gopher-index");
                    184:        break;
                    185:       case GOPHER_CSO:
                    186:        content_type = HTAtom_for("application/x-gopher-cso");
                    187:        break;
                    188:       case GOPHER_TELNET:
                    189:        content_type = HTAtom_for("application/x-gopher-telnet");
                    190:        break;
                    191:       case GOPHER_TN3270:
                    192:        content_type = HTAtom_for("application/x-gopher-tn3270");
                    193:        break;
                    194:       case GOPHER_DUPLICATE:
                    195:        content_type = HTAtom_for("application/x-gopher-duplicate");
                    196:        break;
                    197:       case GOPHER_ERROR:
                    198:        content_type = HTAtom_for("www/unknown");
                    199:        break;
                    200:       case GOPHER_MACBINHEX:
                    201:       case GOPHER_PCBINHEX:
                    202:       case GOPHER_UUENCODED:
                    203:       case GOPHER_BINARY:
                    204:        {       /* Do our own filetyping -- maybe we get lucky */
                    205:             HTAtom *language;
                    206:             content_type = HTFileFormat(filename, &content_encoding,
                    207:                                        &language);
                    208:        }
                    209:       default:
                    210:        content_type = HTAtom_for("www/unknown");
                    211:        break;
                    212:     }
                    213:     return HTGetIcon(S_IFMT & S_IFREG, content_type, content_encoding);
                    214: }
                    215: 
                    216: 
1.1       timbl     217: /*     Parse a Gopher Menu document
                    218: **     ============================
                    219: **
                    220: */
                    221: 
2.17      frystyk   222: PRIVATE void parse_menu ARGS4(HTStructured *,   target,
                    223:                              int ,             s,
                    224:                              CONST char *,     arg,
                    225:                              HTParentAnchor *, anAnchor)
1.1       timbl     226: {
2.17      frystyk   227:     unsigned int files = 0;
1.1       timbl     228:     char gtype;
2.17      frystyk   229:     int ch;
1.1       timbl     230:     char line[BIG];
                    231:     char address[BIG];
2.14      luotonen  232:     char *name = "";
                    233:     char *selector = "";               /* Gopher menu fields */
                    234:     char *host = "";
1.1       timbl     235:     char *port;
                    236:     char *p = line;
2.12      timbl     237:     HTInputSocket * isoc = HTInputSocket_new(s);
2.11      timbl     238:     
1.1       timbl     239: #define TAB            '\t'
                    240: #define HEX_ESCAPE     '%'
                    241: 
2.17      frystyk   242:     /* Output title */
                    243:     {
                    244:         CONST char *title = HTAnchor_title(anAnchor);
                    245:        char *outstr = NULL;
                    246:        if (title)
                    247:            StrAllocCopy(outstr, title);
                    248:        else
2.18      luotonen  249:            StrAllocCopy(outstr, "Gopher Menu");
                    250: 
2.17      frystyk   251:         START(HTML_TITLE);
                    252:        HTUnEscape(outstr);
                    253:         PUTS(outstr);
                    254:         END(HTML_TITLE);
1.2       timbl     255:         START(HTML_H1);
2.17      frystyk   256:         PUTS(outstr);
                    257:         END(HTML_H1);
                    258:        free(outstr);
                    259:     }
                    260: 
                    261:     /* Make everything in list preformatted */
                    262:     START(HTML_PRE);
                    263: 
                    264:     /* Output the header line of the list */
                    265:     if (!icon_blank) icon_blank = icon_unknown;
                    266:     if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
                    267:         HTMLPutImg(target, icon_blank->icon_url,
                    268:                    HTIcon_alt_string(icon_blank->icon_alt, NO), NULL);
                    269:     }
                    270:     PUTC(' ');
                    271:     PUTS("Name");
                    272:     PUTC('\n');
                    273:     START(HTML_HR);
                    274:     PUTC('\n');
                    275: 
                    276:     while ((ch = NEXT_CHAR) >= 0) {
1.3       timbl     277:         if (ch != LF) {
1.1       timbl     278:            *p = ch;            /* Put character in line */
                    279:            if (p< &line[BIG-1]) p++;
                    280:            
                    281:        } else {
                    282:            *p++ = 0;           /* Terminate line */
                    283:            p = line;           /* Scan it to parse it */
                    284:            port = 0;           /* Flag "not parsed" */
2.17      frystyk   285:            if (TRACE)
                    286:                fprintf(stderr, "HTGopher.... Menu item: `%s\'\n", line);
1.1       timbl     287:            gtype = *p++;
                    288:            
                    289:            /* Break on line with a dot by itself */
                    290:            if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
                    291: 
                    292:            if (gtype && *p) {
                    293:                name = p;
                    294:                selector = strchr(name, TAB);
                    295:                if (selector) {
                    296:                    *selector++ = 0;    /* Terminate name */
                    297:                    host = strchr(selector, TAB);
                    298:                    if (host) {
                    299:                        *host++ = 0;    /* Terminate selector */
                    300:                        port = strchr(host, TAB);
                    301:                        if (port) {
                    302:                            char *junk;
                    303:                            port[0] = ':';      /* delimit host a la W3 */
                    304:                            junk = strchr(port, TAB);
                    305:                            if (junk) *junk++ = 0;      /* Chop port */
                    306:                            if ((port[1]=='0') && (!port[2]))
                    307:                                port[0] = 0;    /* 0 means none */
                    308:                        } /* no port */
                    309:                    } /* host ok */
                    310:                } /* selector ok */
                    311:            } /* gtype and name ok */
2.17      frystyk   312: 
                    313:            /* Get Icon type and output the icon */
                    314:            if (HTDirShowMask & HT_DIR_SHOW_ICON) {
                    315:                HTIconNode *icon = get_gopher_icon(arg, gtype);
                    316:                if (icon && icon->icon_url) {
                    317:                    HTMLPutImg(target, icon->icon_url,
                    318:                               HTIcon_alt_string(icon->icon_alt, YES),
                    319:                               NULL);
                    320:                    PUTC(' ');
                    321:                }
                    322:            }
1.1       timbl     323:            
                    324:            if (gtype == GOPHER_WWW) {  /* Gopher pointer to W3 */
2.17      frystyk   325:                write_anchor(target, name, selector);
                    326: 
1.1       timbl     327:            } else if (port) {          /* Other types need port */
                    328:                if (gtype == GOPHER_TELNET) {
                    329:                    if (*selector) sprintf(address, "telnet://%s@%s/",
2.7       secret    330:                                           selector, host);
1.1       timbl     331:                    else sprintf(address, "telnet://%s/", host);
2.7       secret    332:                }
                    333:                else if (gtype == GOPHER_TN3270) 
                    334:                {
                    335:                    if (*selector) 
                    336:                        sprintf(address, "tn3270://%s@%s/",
                    337:                                selector, host);
                    338:                    else 
                    339:                        sprintf(address, "tn3270://%s/", host);
                    340:                }
                    341:                else {                  /* If parsed ok */
1.1       timbl     342:                    char *q;
                    343:                    char *p;
                    344:                    sprintf(address, "//%s/%c", host, gtype);
                    345:                    q = address+ strlen(address);
                    346:                    for(p=selector; *p; p++) {  /* Encode selector string */
2.14      luotonen  347:                        if (acceptable[(int)*p]) *q++ = *p;
1.1       timbl     348:                        else {
                    349:                            *q++ = HEX_ESCAPE;  /* Means hex coming */
                    350:                            *q++ = hex[(TOASCII(*p)) >> 4];
                    351:                            *q++ = hex[(TOASCII(*p)) & 15];
                    352:                        }
                    353:                    }
                    354:                    *q++ = 0;                   /* terminate address */
                    355:                }
2.17      frystyk   356:                
2.7       secret    357:                /* Error response from Gopher doesn't deserve to
                    358:                   be a hyperlink. */
                    359:                if (strcmp (address, "gopher://error.host:1/0"))
2.17      frystyk   360:                    write_anchor(target, name, address);
2.7       secret    361:                else
                    362:                    PUTS(name);
2.17      frystyk   363:                PUTC('\n');
1.1       timbl     364:            } else { /* parse error */
2.17      frystyk   365:                if (TRACE) fprintf(stderr, "HTGopher.... Bad menu item.\n");
1.2       timbl     366:                PUTS(line);
                    367: 
1.1       timbl     368:            } /* parse error */
                    369:            
                    370:            p = line;   /* Start again at beginning of line */
2.17      frystyk   371:            ++files;    /* Update number of files */
                    372: 
1.1       timbl     373:        } /* if end of line */
                    374:        
                    375:     } /* Loop over characters */
                    376:        
2.17      frystyk   377:     {
                    378:        char *outstr;
                    379:        
                    380:        if ((outstr = (char *) malloc(100)) == NULL)
                    381:            outofmem(__FILE__, "parse_menu");
                    382:        if (files == 0)
                    383:            sprintf(outstr, "Empty directory");
                    384:        else if (files == 1)
                    385:            sprintf(outstr, "1 file");
                    386:        else
                    387:            sprintf(outstr, "%u files", files);
                    388:        START(HTML_HR);
                    389:        PUTS(outstr);
                    390:        free(outstr);
                    391:        END(HTML_PRE);
                    392:     }
                    393: 
1.2       timbl     394:     FREE_TARGET;
2.11      timbl     395:     HTInputSocket_free(isoc);
1.1       timbl     396:     return;
                    397: }
2.11      timbl     398: 
                    399: 
2.7       secret    400: /*     Parse a Gopher CSO document
                    401:  **    ============================
                    402:  **
                    403:  **   Accepts an open socket to a CSO server waiting to send us
                    404:  **   data and puts it on the screen in a reasonable manner.
                    405:  **
                    406:  **   Perhaps this data can be automatically linked to some
                    407:  **   other source as well???
                    408:  **
                    409:  **  Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
                    410:  **  on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
                    411:  **  secret@dxcern.cern.ch .
                    412:  */
                    413: 
2.17      frystyk   414: PRIVATE void parse_cso ARGS4 (HTStructured *, target,
                    415:                              int, s,
                    416:                              CONST char *, arg,
                    417:                              HTParentAnchor *, anAnchor)
2.7       secret    418: {
2.17      frystyk   419:     int ch;
2.7       secret    420:     char line[BIG];
                    421:     char *p = line;
                    422:     char *second_colon, last_char='\0';
                    423:     CONST char *title;
2.11      timbl     424:     HTInputSocket * isoc = HTInputSocket_new(s);
2.7       secret    425:     
                    426:     title = HTAnchor_title(anAnchor);
                    427:     START(HTML_H1);
                    428:     PUTS("CSO Search Results");
                    429:     END(HTML_H1);
                    430:     START(HTML_PRE);
                    431: 
                    432:     /* start grabbing chars from the network */
2.17      frystyk   433:     while ((ch = NEXT_CHAR) >= 0) 
2.7       secret    434:        {
                    435:            if (ch != '\n') 
                    436:                {
                    437:                    *p = ch;            /* Put character in line */
                    438:                    if (p< &line[BIG-1]) p++;
                    439:                } 
                    440:            else 
                    441:                {
                    442:                    *p++ = 0;           /* Terminate line */
                    443:                    p = line;           /* Scan it to parse it */
                    444:                    
                    445:                    /* OK we now have a line in 'p' lets parse it and 
                    446:                       print it */
                    447:                    
                    448:                    /* Break on line that begins with a 2. It's the end of
                    449:                     * data.
                    450:                     */
                    451:                    if (*p == '2')
                    452:                        break;
                    453:                    
                    454:                    /*  lines beginning with 5 are errors, 
                    455:                     *  print them and quit
                    456:                     */
                    457:                    if (*p == '5') {
                    458:                        START(HTML_H2);
                    459:                        PUTS(p+4);
                    460:                        END(HTML_H2);
                    461:                        break;
                    462:                    }
                    463:                    
                    464:                    if(*p == '-') {
                    465:                        /*  data lines look like  -200:#:
                    466:                         *  where # is the search result number and can be  
                    467:                         *  multiple digits (infinate?)
                    468:                         *  find the second colon and check the digit to the
                    469:                         *  left of it to see if they are diferent
                    470:                         *  if they are then a different person is starting. 
                    471:                         *  make this line an <h2>
                    472:                         */
                    473:                        
                    474:                        /* find the second_colon */
                    475:                        second_colon = strchr( strchr(p,':')+1, ':');
                    476:                        
                    477:                        if(second_colon != NULL) {  /* error check */
                    478:                            
                    479:                            if (*(second_colon-1) != last_char)   
                    480:                                /* print seperator */
                    481:                            {
                    482:                                END(HTML_PRE);
                    483:                                START(HTML_H2);
                    484:                            }
                    485:                                
                    486:                            
                    487:                            /* right now the record appears with the alias 
                    488:                             * (first line)
                    489:                             * as the header and the rest as <pre> text
                    490:                             * It might look better with the name as the
                    491:                             * header and the rest as a <ul> with <li> tags
                    492:                             * I'm not sure whether the name field comes in any
                    493:                             * special order or if its even required in a 
                    494:                             * record,
                    495:                             * so for now the first line is the header no 
                    496:                             * matter
                    497:                             * what it is (it's almost always the alias)
                    498:                             * A <dl> with the first line as the <DT> and
                    499:                             * the rest as some form of <DD> might good also?
                    500:                             */
                    501:                            
                    502:                            /* print data */
                    503:                            PUTS(second_colon+1);
                    504:                            PUTS("\n");
                    505:                            
                    506:                            if (*(second_colon-1) != last_char)   
                    507:                                /* end seperator */
                    508:                            {
                    509:                                END(HTML_H2);
                    510:                                START(HTML_PRE);
                    511:                            }
                    512:                                                            
                    513:                            /* save the char before the second colon
                    514:                             * for comparison on the next pass
                    515:                             */
                    516:                            last_char =  *(second_colon-1) ;
                    517:                            
                    518:                        } /* end if second_colon */
                    519:                    } /* end if *p == '-' */
                    520:                } /* if end of line */
                    521:            
                    522:        } /* Loop over characters */
                    523:     
                    524:     /* end the text block */
                    525:     PUTS("\n");
                    526:     END(HTML_PRE);
                    527:     PUTS("\n");
                    528:     FREE_TARGET;
2.11      timbl     529:     HTInputSocket_free(isoc);
2.7       secret    530: 
                    531:     return;  /* all done */
                    532: } /* end of procedure */
1.1       timbl     533: 
                    534: /*     Display a Gopher Index document
2.7       secret    535:  **    -------------------------------
                    536:  */
1.1       timbl     537: 
2.17      frystyk   538: PRIVATE void display_index ARGS3 (HTStructured *,      target,
                    539:                                  CONST char *,         arg,
                    540:                                  HTParentAnchor *,     anAnchor)
1.1       timbl     541: {
2.18      luotonen  542:     char * decoded = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
                    543:     char * t = NULL;
                    544: 
                    545:     HTUnEscape(decoded);
2.19    ! luotonen  546:     HTCleanTelnetString(decoded);      /* Prevent security holes */
2.18      luotonen  547:     if (strlen(decoded) > 2) {
                    548:        t = strchr(decoded+1,'/');
                    549:        if (t) t++;
                    550:     }
                    551:     if (!t) t = decoded;
                    552: 
1.2       timbl     553:     START(HTML_H1);
2.18      luotonen  554:     PUTS(t);
1.2       timbl     555:     END(HTML_H1);
2.7       secret    556:     START(HTML_ISINDEX);
                    557:     PUTS("\nThis is a searchable Gopher index.");
                    558:     PUTS(" Please enter keywords to search for.\n");
                    559:     
                    560:     if (!HTAnchor_title(anAnchor))
                    561:        HTAnchor_setTitle(anAnchor, arg);
1.2       timbl     562:     
2.7       secret    563:     FREE_TARGET;
2.18      luotonen  564:     free(decoded);
2.7       secret    565:     return;
                    566: }
                    567: 
                    568: 
                    569: /*      Display a CSO index document
                    570: **      -------------------------------
                    571: */
                    572: 
2.17      frystyk   573: PRIVATE void display_cso ARGS3(HTStructured *, target,
                    574:                               CONST char *,   arg,
                    575:                               HTParentAnchor *, anAnchor)
2.7       secret    576: {
                    577:     START(HTML_H1);
                    578:     PUTS(arg);
                    579:     PUTS(" index");
                    580:     END(HTML_H1);
                    581:     START(HTML_ISINDEX);
                    582:     PUTS("\nThis is a searchable index of a CSO database.\n");
                    583:     PUTS(" Please enter keywords to search for. The keywords that you enter");
                    584:     PUTS(" will allow you to search on a person's name in the database.\n");
                    585: 
1.1       timbl     586:     if (!HTAnchor_title(anAnchor))
1.2       timbl     587:        HTAnchor_setTitle(anAnchor, arg);
1.1       timbl     588:     
1.2       timbl     589:     FREE_TARGET;
1.1       timbl     590:     return;
                    591: }
                    592: 
                    593: 
                    594: /*             De-escape a selector into a command
                    595: **             -----------------------------------
                    596: **
                    597: **     The % hex escapes are converted. Otheriwse, the string is copied.
                    598: */
                    599: PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
                    600: {
                    601:     CONST char * p = selector;
                    602:     char * q = command;
                    603:        if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
                    604:     while (*p) {               /* Decode hex */
                    605:        if (*p == HEX_ESCAPE) {
                    606:            char c;
                    607:            unsigned int b;
                    608:            p++;
                    609:            c = *p++;
                    610:            b =   from_hex(c);
                    611:            c = *p++;
                    612:            if (!c) break;      /* Odd number of chars! */
                    613:            *q++ = FROMASCII((b<<4) + from_hex(c));
                    614:        } else {
                    615:            *q++ = *p++;        /* Record */
                    616:        }
                    617:     }
                    618:     *q++ = 0;  /* Terminate command */
                    619: 
                    620: }
                    621: 
                    622: 
                    623: /*             Load by name                                    HTLoadGopher
                    624: **             ============
                    625: **
                    626: **      Bug:   No decoding of strange data types as yet.
                    627: **
                    628: */
2.13      timbl     629: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1       timbl     630: {
2.13      timbl     631:     CONST char * arg = HTAnchor_physical(request->anchor);
1.1       timbl     632:     char *command;                     /* The whole command */
                    633:     int status;                                /* tcp return */
                    634:     char gtype;                                /* Gopher Node type */
                    635:     char * selector;                   /* Selector string */
                    636:     struct sockaddr_in soc_address;    /* Binary network address */
                    637:     struct sockaddr_in* sin = &soc_address;
2.17      frystyk   638:     HTStructured *target;                                     /* HTML object */
                    639:     HTStructuredClass targetClass;
1.1       timbl     640:     
                    641:     if (!acceptable_inited) init_acceptable();
                    642:     
                    643:     if (!arg) return -3;               /* Bad if no name sepcified     */
                    644:     if (!*arg) return -2;              /* Bad if name had zero length  */
                    645:     
2.17      frystyk   646:     if (TRACE) fprintf(stderr, "HTGopher.... Looking for %s\n", arg);
1.1       timbl     647:     
                    648:     
                    649: /*  Set up defaults:
                    650: */
                    651:     sin->sin_family = AF_INET;                 /* Family, host order  */
                    652:     sin->sin_port = htons(GOPHER_PORT);                /* Default: new port,  */
                    653: 
                    654: /* Get node name and optional port number:
                    655: */
                    656:     {
                    657:        char *p1 = HTParse(arg, "", PARSE_HOST);
                    658:        int status = HTParseInet(sin, p1);
                    659:         free(p1);
                    660:         if (status) return status;   /* Bad */
                    661:     }
                    662:     
                    663: /* Get entity type, and selector string.
                    664: */        
                    665:     {
                    666:        char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
                    667:         gtype = '1';           /* Default = menu */
                    668:        selector = p1;
                    669:        if ((*selector++=='/') && (*selector)) {        /* Skip first slash */
                    670:            gtype = *selector++;                        /* Pick up gtype */
                    671:        }
                    672:        if (gtype == GOPHER_INDEX) {
                    673:            char * query;
2.10      timbl     674:             HTAnchor_setIndex(request->anchor);        /* Search is allowed */
1.1       timbl     675:            query = strchr(selector, '?');      /* Look for search string */
                    676:            if (!query || !query[1]) {          /* No search required */
2.11      timbl     677:                target = HTML_new(request, NULL, WWW_HTML,
                    678:                        request->output_format, request->output_stream);
1.2       timbl     679:                targetClass = *target->isa;
2.17      frystyk   680:                /* Display "cover page" */
                    681:                display_index(target, arg, request->anchor);
2.15      luotonen  682:                free(p1);               /* Leak fixed Henrik 27 Feb 94 */
2.6       timbl     683:                return HT_LOADED;               /* Local function only */
1.1       timbl     684:            }
                    685:            *query++ = 0;                       /* Skip '?'     */
                    686:            command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
                    687:               if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
                    688:              
                    689:            de_escape(command, selector);       /* Bug fix TBL 921208 */
                    690: 
                    691:            strcat(command, "\t");
                    692:          
                    693:            {                                   /* Remove plus signs 921006 */
                    694:                char *p;
                    695:                for (p=query; *p; p++) {
                    696:                    if (*p == '+') *p = ' ';
                    697:                }
                    698:            }
                    699:            strcat(command, query);
2.7       secret    700:         } else if (gtype == GOPHER_CSO) {
                    701:             char * query;
2.10      timbl     702:             HTAnchor_setIndex(request->anchor);        /* Search is allowed */
2.7       secret    703:             query = strchr(selector, '?');      /* Look for search string */
                    704:             if (!query || !query[1]) {          /* No search required */
2.11      timbl     705:                target = HTML_new(request, NULL, WWW_HTML,
                    706:                        request->output_format, request->output_stream);
2.7       secret    707:                targetClass = *target->isa;
2.17      frystyk   708:                /* Display "cover page" */
                    709:                 display_cso(target, arg, request->anchor);
2.15      luotonen  710:                free(p1);               /* Leak fixed Henrik 27 Feb 94 */
2.7       secret    711:                 return HT_LOADED;                 /* Local function only */
                    712:             }
                    713:             *query++ = 0;                       /* Skip '?'     */
                    714:             command = malloc(strlen("query")+ 1 + strlen(query)+ 2 + 1);
                    715:               if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
                    716: 
                    717:             de_escape(command, selector);       /* Bug fix TBL 921208 */
                    718: 
                    719:             strcpy(command, "query ");
                    720: 
                    721:             {                                   /* Remove plus signs 921006 */
                    722:                 char *p;
                    723:                 for (p=query; *p; p++) {
                    724:                     if (*p == '+') *p = ' ';
                    725:                 }
                    726:             }
                    727:             strcat(command, query);
                    728: 
1.1       timbl     729:            
                    730:        } else {                                /* Not index */
                    731:            command = command = malloc(strlen(selector)+2+1);
                    732:            de_escape(command, selector);
                    733:        }
                    734:        free(p1);
                    735:     }
                    736:     
1.3       timbl     737:     {
                    738:        char * p = command + strlen(command);
                    739:        *p++ = CR;              /* Macros to be correct on Mac */
                    740:        *p++ = LF;
                    741:        *p++ = 0;
                    742:        /* strcat(command, "\r\n");     */      /* CR LF, as in rfc 977 */
                    743:     }
1.1       timbl     744: 
                    745: /*     Set up a socket to the server for the data:
                    746: */      
                    747:     s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                    748:     status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
                    749:     if (status<0){
                    750:        if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
                    751:            arg);
                    752:        free(command);
                    753:        return HTInetStatus("connect");
                    754:     }
                    755:     
                    756:     
                    757:     if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
                    758:     
                    759: #ifdef NOT_ASCII
                    760:     {
                    761:        char * p;
                    762:        for(p = command; *p; p++) {
                    763:            *p = TOASCII(*p);
                    764:        }
                    765:     }
                    766: #endif
                    767: 
                    768:     status = NETWRITE(s, command, (int)strlen(command));
                    769:     free(command);
                    770:     if (status<0){
                    771:        if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
                    772:            return HTInetStatus("send");
                    773:     }
                    774: 
                    775: /*     Now read the data from the socket:
                    776: */    
                    777:     switch (gtype) {
                    778:     
                    779:     case GOPHER_HTML :
2.11      timbl     780:        HTParseSocket(WWW_HTML,  s, request);
1.2       timbl     781:        break;
1.1       timbl     782: 
1.3       timbl     783:     case GOPHER_GIF:
                    784:     case GOPHER_IMAGE:
2.11      timbl     785:        HTParseSocket(HTAtom_for("image/gif"), s, request);
1.3       timbl     786:        break;
1.1       timbl     787:     case GOPHER_MENU :
                    788:     case GOPHER_INDEX :
2.11      timbl     789:        target = HTML_new(request, NULL, WWW_HTML,
                    790:                        request->output_format, request->output_stream);
1.2       timbl     791:        targetClass = *target->isa;
2.17      frystyk   792:         parse_menu(target, s,arg, request->anchor);
1.2       timbl     793:        break;
2.7       secret    794:         
                    795:     case GOPHER_CSO:
2.11      timbl     796:        target = HTML_new(request, NULL, WWW_HTML,
                    797:                        request->output_format, request->output_stream);
2.7       secret    798:        targetClass = *target->isa;
2.17      frystyk   799:        parse_cso(target, s, arg, request->anchor);
2.7       secret    800:        break;
                    801:        
                    802:       case GOPHER_MACBINHEX:
                    803:       case GOPHER_PCBINHEX:
                    804:       case GOPHER_UUENCODED:
                    805:       case GOPHER_BINARY:
2.16      luotonen  806:        {       /* Do our own filetyping -- maybe we get lucky */
                    807:            HTFormat format = HTFileFormat(arg,
                    808:                                           &request->content_encoding,
                    809:                                           &request->content_language);
                    810:            if (format) {
                    811:                CTRACE(stderr,
                    812:                       "Gopher...... figured out content-type myself: %s\n",
                    813:                       HTAtom_name(format));
                    814:                HTParseSocket(format, s, request);
                    815:            }
                    816:            else {
                    817:                CTRACE(stderr,"Gopher...... using www/unknown\n");
                    818:                /* Specifying WWW_UNKNOWN forces dump to local disk. */
                    819:                HTParseSocket (WWW_UNKNOWN, s, request);
                    820:            }
                    821:        }
2.7       secret    822:        break;
                    823: 
1.1       timbl     824:     case GOPHER_TEXT :
                    825:     default:                   /* @@ parse as plain text */
2.11      timbl     826:        HTParseSocket(WWW_PLAINTEXT, s, request);
2.7       secret    827:        break;
                    828: 
                    829:       case GOPHER_SOUND :
2.11      timbl     830:        HTParseSocket(WWW_AUDIO,  s, request);
1.2       timbl     831:        break;
                    832:        
1.1       timbl     833:     } /* switch(gtype) */
1.2       timbl     834: 
                    835:     NETCLOSE(s);
                    836:     return HT_LOADED;
1.1       timbl     837: }
1.2       timbl     838: 
2.10      timbl     839: GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL, NULL };
1.1       timbl     840: 

Webmaster