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

2.32      frystyk     1: /*                                                                  HTGopher.c
                      2: **     GOPHER ACCESS
                      3: **
2.37      frystyk     4: **     (c) COPYRIGHT MIT 1995.
2.32      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
1.1       timbl       6: **
                      7: ** History:
                      8: **     26 Sep 90       Adapted from other accesses (News, HTTP) TBL
                      9: **     29 Nov 91       Downgraded to C, for portable implementation.
2.17      frystyk    10: **     28 Apr 94       target no more global and icons implemented
2.39      frystyk    11: **                     HF, frystyk@w3.org
2.19      luotonen   12: **      2 May 94       Fixed possible security hole when the URL contains
                     13: **                     a newline, that could cause multiple commands to be
                     14: **                     sent to a Gopher server. AL, luotonen@www.cern.ch
2.20      frystyk    15: **     12 May 94       Checked and made ready for multi-threads, Frystyk
2.27      duns       16: **      8 Jul 94  FM   Insulate free() from _free structure element.
2.42      frystyk    17: **        Sep 95  HFN  Made non-blocking and state stream oriented
1.1       timbl      18: */
                     19: 
2.20      frystyk    20: /* Library include files */
2.34      frystyk    21: #include "tcp.h"
                     22: #include "HTUtils.h"
                     23: #include "HTString.h"
2.20      frystyk    24: #include "HTParse.h"
                     25: #include "HTTCP.h"
2.17      frystyk    26: #include "HTIcons.h"
2.42      frystyk    27: #include "HTReqMan.h"
2.38      frystyk    28: #include "HTSocket.h"
1.1       timbl      29: #include "HTFormat.h"
2.42      frystyk    30: #include "HTGuess.h"
2.20      frystyk    31: #include "HTError.h"
2.42      frystyk    32: #include "HTWriter.h"
2.43      frystyk    33: #include "HTNetMan.h"
2.36      frystyk    34: #include "HTBind.h"
2.46      frystyk    35: #include "HTMLPDTD.h"
2.40      frystyk    36: #include "HTMLGen.h"
2.45      frystyk    37: #include "HTDir.h"
2.20      frystyk    38: #include "HTGopher.h"                                   /* Implemented here */
                     39: 
                     40: /* Macros and other defines */
                     41: #ifndef GOPHER_PORT
                     42: #define GOPHER_PORT 70                                 /* See protocol spec */
                     43: #endif
1.2       timbl      44: 
2.20      frystyk    45: /* Hypertext object building machinery */
2.17      frystyk    46: #define PUTC(c) (*target->isa->put_character)(target, c)
                     47: #define PUTS(s) (*target->isa->put_string)(target, s)
                     48: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
                     49: #define END(e) (*target->isa->end_element)(target, e)
2.20      frystyk    50: 
                     51: /* Type definitions and global variables etc. local to this module */
                     52: typedef enum _HTGopherType {
2.42      frystyk    53:     GT_TEXT            = '0',
                     54:     GT_MENU            = '1',
                     55:     GT_CSO             = '2',
                     56:     GT_ERROR           = '3',
                     57:     GT_MACBINHEX       = '4',
                     58:     GT_PCBINHEX                = '5',
                     59:     GT_UUENCODED       = '6',
                     60:     GT_INDEX           = '7',
                     61:     GT_TELNET          = '8',
                     62:     GT_BINARY          = '9',
                     63:     GT_GIF             = 'g',
                     64:     GT_HTML            = 'h',                                       /* HTML */
                     65:     GT_INFO            = 'i',
                     66:     GT_SOUND           = 's',
                     67:     GT_WWW             = 'w',                                 /* W3 address */
                     68:     GT_IMAGE           = 'I',
                     69:     GT_TN3270          = 'T',
                     70:     GT_DUPLICATE       = '+',
                     71:     GT_PLUS_IMAGE      = ':',                  /* Addition from Gopher Plus */
                     72:     GT_PLUS_MOVIE      = ';',
                     73:     GT_PLUS_SOUND      = '<',
                     74:     GT_EOF             = '.'
2.20      frystyk    75: } HTGopherType;
                     76: 
2.42      frystyk    77: /* Final states have negative value */
                     78: typedef enum _GopherState {
                     79:     GOPHER_ERROR       = -3,
                     80:     GOPHER_NO_DATA     = -2,
                     81:     GOPHER_GOT_DATA    = -1,
                     82:     GOPHER_BEGIN       = 0,
                     83:     GOPHER_NEED_CONNECTION,
                     84:     GOPHER_NEED_REQUEST,
                     85:     GOPHER_NEED_RESPONSE
                     86: } GopherState;
1.2       timbl      87: 
2.42      frystyk    88: /* This is the context structure for the this module */
2.20      frystyk    89: typedef struct _gopher_info {
2.30      frystyk    90:     HTGopherType       type;                            /* Gopher item type */
2.42      frystyk    91:     GopherState                state;
                     92:     char *             cmd;
2.20      frystyk    93: } gopher_info;
1.1       timbl      94: 
2.42      frystyk    95: #define MAX_GOPHER_LINE                256
                     96: 
                     97: struct _HTStructured {
                     98:     CONST HTStructuredClass *  isa;
                     99: };
                    100: 
                    101: struct _HTStream {
                    102:     CONST HTStreamClass *      isa;
                    103:     HTStructured *             target;
                    104:     HTRequest *                        request;
                    105:     HTSocketEOL                        state;
                    106:     char *                     url;
                    107:     BOOL                       pre;                   /* Preformatted mode? */
                    108:     BOOL                       junk;                  /* For too long lines */
                    109:     BOOL                       CSO;
                    110:     char                       cso_rec[10];            /* CSO record number */
                    111:     char                       buffer[MAX_GOPHER_LINE+1];
                    112:     int                                buflen;
                    113: };
                    114: 
2.45      frystyk   115: PRIVATE HTDirShow      dir_show = HT_DS_ICON;
                    116: 
2.20      frystyk   117: /* ------------------------------------------------------------------------- */
1.1       timbl     118: 
2.42      frystyk   119: /*     GopherIcon
                    120: **     ----------
2.20      frystyk   121: **     This function finds an appopriate icon for the item in the gopher
                    122: **     list. Actually it is only a shell build upon HTGetIcon().
2.17      frystyk   123: */
2.42      frystyk   124: PRIVATE HTIconNode *GopherIcon (HTGopherType type)
2.17      frystyk   125: {
2.36      frystyk   126:     HTFormat   content_type = NULL;
                    127:     HTEncoding content_encoding = NULL;
2.45      frystyk   128:     HTFileMode mode = HT_IS_FILE;
2.42      frystyk   129:     switch(type) {
2.45      frystyk   130:       case GT_MENU:
                    131:        mode = HT_IS_DIR;
2.42      frystyk   132:       case GT_TEXT:
2.17      frystyk   133:        content_type = HTAtom_for("text/void");
                    134:        break;
2.42      frystyk   135:       case GT_IMAGE:
                    136:       case GT_PLUS_IMAGE:
                    137:       case GT_GIF:
2.17      frystyk   138:        content_type = HTAtom_for("image/void");
                    139:        break;
2.42      frystyk   140:       case GT_WWW:
                    141:       case GT_HTML:
2.17      frystyk   142:        content_type = HTAtom_for("text/void");
                    143:        break;
2.42      frystyk   144:       case GT_SOUND:
                    145:       case GT_PLUS_SOUND:
2.17      frystyk   146:        content_type = HTAtom_for("audio/void");
                    147:        break;
2.42      frystyk   148:       case GT_PLUS_MOVIE:
2.20      frystyk   149:        content_type = HTAtom_for("video/void");
2.17      frystyk   150:        break;
2.42      frystyk   151:       case GT_INDEX:
2.17      frystyk   152:        content_type = HTAtom_for("application/x-gopher-index");
                    153:        break;
2.42      frystyk   154:       case GT_CSO:
2.17      frystyk   155:        content_type = HTAtom_for("application/x-gopher-cso");
                    156:        break;
2.42      frystyk   157:       case GT_TELNET:
2.17      frystyk   158:        content_type = HTAtom_for("application/x-gopher-telnet");
                    159:        break;
2.42      frystyk   160:       case GT_TN3270:
2.17      frystyk   161:        content_type = HTAtom_for("application/x-gopher-tn3270");
                    162:        break;
2.42      frystyk   163:       case GT_DUPLICATE:
2.17      frystyk   164:        content_type = HTAtom_for("application/x-gopher-duplicate");
                    165:        break;
2.42      frystyk   166:       case GT_ERROR:
2.17      frystyk   167:        content_type = HTAtom_for("www/unknown");
                    168:        break;
2.42      frystyk   169:       case GT_MACBINHEX:
                    170:       case GT_PCBINHEX:
                    171:       case GT_UUENCODED:
2.36      frystyk   172:        content_type = WWW_BINARY;
                    173:        content_encoding = WWW_ENC_BASE64;
                    174:        break;
2.42      frystyk   175:       case GT_BINARY:
2.36      frystyk   176:        content_type = WWW_BINARY;
                    177:        break;
2.17      frystyk   178:       default:
                    179:        content_type = HTAtom_for("www/unknown");
                    180:        break;
                    181:     }
2.45      frystyk   182:     return HTGetIcon(mode, content_type, content_encoding);
2.17      frystyk   183: }
                    184: 
2.42      frystyk   185: /* ------------------------------------------------------------------------- */
                    186: /*                                 STREAMS                                  */
                    187: /* ------------------------------------------------------------------------- */
2.17      frystyk   188: 
2.42      frystyk   189: /*     GopherTitle
                    190: **     -----------
                    191: **     Create the top part of the page
1.1       timbl     192: */
2.42      frystyk   193: PRIVATE BOOL GopherTitle (HTStream *me)
                    194: {
                    195:     HTStructured *target = me->target;
                    196:     char *str = NULL;
                    197:     StrAllocCopy(str, me->CSO ? "CSO Search " : "GopherMenu");
2.28      frystyk   198: 
2.42      frystyk   199:     START(HTML_HTML);
                    200:     START(HTML_HEAD);
                    201:     START(HTML_TITLE);
                    202:     if (me->CSO) {
                    203:        char *keyword = strchr(me->url, '?');
                    204:        if (keyword) {
                    205:            StrAllocCat(str, "for ");
                    206:            StrAllocCat(str, ++keyword);
                    207:        }
                    208:     }
                    209:     PUTS(str);
                    210:     END(HTML_TITLE);
                    211:     END(HTML_HEAD);
2.26      frystyk   212: 
2.42      frystyk   213:     START(HTML_BODY);
                    214:     START(HTML_H1);
                    215:     PUTS(str);
                    216:     END(HTML_H1);
                    217:     free(str);
                    218:     return YES;
                    219: }
1.1       timbl     220: 
2.42      frystyk   221: /*     GopherBottom
                    222: **     ------------
                    223: **     Create the bottom part of the page
                    224: */
                    225: PRIVATE BOOL GopherBottom (HTStream *me)
                    226: {
                    227:     HTStructured *target = me->target;
                    228:     if (me->pre)
                    229:        END(HTML_PRE);
                    230:     END(HTML_BODY);
                    231:     END(HTML_HTML);
                    232:     return YES;
                    233: }
2.17      frystyk   234: 
2.42      frystyk   235: /*     GopherMenuLine
                    236: **     --------------
                    237: **     Parses a Gopher Menu Line
                    238: **     Return YES if more data else NO
                    239: */
                    240: PRIVATE BOOL GopherMenuLine (HTStream *me, char *line)
                    241: {
                    242:     HTStructured *target = me->target;
                    243:     HTGopherType gtype = (HTGopherType) *line++;
                    244:     if (PROT_TRACE)
2.47      frystyk   245:        TTYPrint(TDEST, "HTGopher.... Menu line: `%s\'\n", line);
2.42      frystyk   246:     if (gtype == GT_INFO) {
                    247:        char *stop = strchr(line, '\t');
                    248:        if (stop) *stop = '\0';
                    249:        PUTS(line);
                    250:     } else if (gtype == GT_ERROR) {
                    251:        char *stop = strchr(line, '\t');
                    252:        if (stop) *stop = '\0';
                    253:        PUTS(line);
                    254:     } else if ((strstr(line, "error.host") || strstr(line, "errorhost"))) {
                    255:        char *stop = strchr(line, '\t');              /* Chop off error.host */
                    256:        if (stop) *stop = '\0';
                    257:        PUTS(line);
                    258:     } else if (gtype == GT_EOF) {
                    259:        return NO;
                    260:     } else {                               /* Parse normal gopher menu line */
                    261:        char *name = line;                           /* Get link information */
                    262:        char *selector = strchr(name, '\t');
                    263:        char *host = NULL;
                    264:        char *port = NULL;
                    265:        if (selector) {
                    266:            *selector++ = '\0';
                    267:            if ((host = strchr(selector, '\t'))) {
                    268:                *host++ = '\0';
                    269:                if ((port = strchr(host, '\t'))) {
                    270:                    char *junk;
                    271:                    *port = ':';                     /* delimit host a la W3 */
                    272:                    if ((junk = strchr(port, '\t')) != NULL)
                    273:                        *junk = '\0';                           /* Chop port */
                    274:                    if (*(port+1) == '0' && !*(port+2))
                    275:                        *port = '\0';
2.21      frystyk   276:                }
2.42      frystyk   277:            }
                    278:        }
                    279:        if (!me->pre) {               /* For now we use preformatted listing */
                    280:            START(HTML_PRE);
                    281:            me->pre = YES;
                    282:        }
2.45      frystyk   283:        if (dir_show & HT_DS_ICON) {                     /* Put out the icon */
2.42      frystyk   284:            HTIconNode *icon = GopherIcon(gtype);
                    285:            if (icon && icon->icon_url) {
                    286:                HTMLPutImg(target, icon->icon_url,
                    287:                           HTIcon_alt_string(icon->icon_alt, YES), NULL);
                    288:                PUTC(' ');
                    289:            }
                    290:        }
                    291:        if (gtype == GT_WWW) {                       /* Gopher pointer to W3 */
                    292:            char *escaped = NULL;
                    293:            escaped = HTEscape(selector, URL_PATH);
                    294:            HTStartAnchor(target, NULL, escaped);
                    295:            PUTS(name);
                    296:            END(HTML_A);
                    297:            free(escaped);
                    298:        } else if (port) {                          /* Other types need port */
                    299:            char *escaped = NULL;
                    300:            char *address = NULL;
                    301:            int addr_len;
                    302:            
                    303:            /* Calculate the length of the WWW-address */
                    304:            if (selector && *selector) {
                    305:                escaped = HTEscape(selector, URL_PATH);
                    306:                addr_len = 15 + strlen(escaped) + strlen(host) + 1;
                    307:            } else {
                    308:                addr_len = 15 + strlen(host) + 1;
                    309:            }
                    310:            if ((address = (char *) malloc(addr_len)) == NULL)
                    311:                outofmem(__FILE__, "GopherMenuLine");
                    312:            *address = '\0';
                    313: 
                    314:            if (gtype == GT_TELNET) {
                    315:                if (escaped)
                    316:                    sprintf(address, "telnet://%s@%s/", escaped, host);
                    317:                else
                    318:                    sprintf(address, "telnet://%s/", host);
                    319:            } else if (gtype == GT_TN3270) {
                    320:                if (escaped)
                    321:                    sprintf(address, "tn3270://%s@%s/", escaped, host);
                    322:                else
                    323:                    sprintf(address, "tn3270://%s/", host);
                    324:            } else {
                    325:                if (escaped)
                    326:                    sprintf(address, "//%s/%c%s", host, gtype, escaped);
                    327:                else
                    328:                    sprintf(address, "//%s/%c", host, gtype);
                    329:            }
                    330:            
                    331:            HTStartAnchor(target, NULL, address);
                    332:            PUTS(name);
                    333:            END(HTML_A);
                    334:            FREE(address);
                    335:            FREE(escaped);
                    336:            PUTC('\n');
                    337:        } else {                                           /* If parse error */
                    338:            if (PROT_TRACE)
2.47      frystyk   339:                TTYPrint(TDEST, "HTGopher.... Bad menu item, `%s\'\n", line);
2.42      frystyk   340:        }
                    341:     }
                    342:     return YES;
                    343: }
2.21      frystyk   344: 
2.42      frystyk   345: /*     GopherCSOLine
                    346: **     --------------
                    347: **     Parses a Gopher Menu Line
                    348: **     Return YES if more data else NO
                    349: */
                    350: PRIVATE BOOL GopherCSOLine (HTStream *me, char *line)
                    351: {
                    352:     HTStructured *target = me->target;
                    353:     if (*line == '1') {                                         /* Information line */
                    354:        char *start = strchr(line, ':');
                    355:        start = start ? ++start : line;
                    356:        PUTS(start);
                    357:     } else if (*line == '2') {                         /* Transfer complete */
                    358:        return NO;
                    359:     } else if (*line == '5') {                                     /* Error */
                    360:        char *start = strchr(line, ':');
                    361:        start = start ? ++start : line;
                    362:        PUTS(start);
                    363:     } else if (*line == '-') {                                      /* data */
                    364:        /*  data lines look like '-200:code:field:value'
                    365:         *  where code is the search result number and can be  
                    366:         *  multiple digits (infinte?)
                    367:         *  find the second colon and check the digit to the
                    368:         *  left of it to see if they are diferent
                    369:         *  if they are then a different person is starting. 
                    370:         */
                    371:        char *code;
                    372:        char *field;
                    373:        if ((code = strchr(line, ':')) && (field = strchr(++code, ':'))) {
                    374:            BOOL newrec = YES;
                    375:            *field++ = '\0';
                    376:            if (!*me->cso_rec) {                   /* Header of first record */
                    377:                START(HTML_DL);
                    378:            } else if (strcmp(me->cso_rec, code)) {    /* Another new record */
                    379:                START(HTML_B);
                    380:            } else
                    381:                newrec = NO;
                    382:            START(HTML_DT);
                    383:            
                    384:            /* I'm not sure whether the name field comes in any
                    385:             *  special order or if its even required in a 
                    386:             *  record, so for now the first line is the header
                    387:             *  no matter what it is (it's almost always the
                    388:             *  alias)
                    389:             */
                    390:            {
                    391:                char *value = strchr(field, ':');
                    392:                if (!value)
                    393:                    value = "Empty value";
                    394:                else
                    395:                    *value++ = '\0';
                    396:                {
                    397:                    char *strip = HTStrip(field);
                    398:                    PUTS(strip);
                    399:                    START(HTML_DD);
                    400:                    strip = HTStrip(value);
                    401:                    if (newrec) {
                    402:                        PUTS(strip);
                    403:                        END(HTML_B);
2.20      frystyk   404:                    } else
2.42      frystyk   405:                        PUTS(strip);
                    406:            }
2.20      frystyk   407: 
2.42      frystyk   408:            /* Save the code for comparison on the next pass */
                    409:            strcpy(me->cso_rec, code);
2.20      frystyk   410:            }
2.42      frystyk   411:        }
                    412:     } else {                                                /* Unknown line */
                    413:        char *start = strchr(line, ':');
                    414:        start = start ? ++start : line;
                    415:        PUTS(start);
2.20      frystyk   416:     }
2.42      frystyk   417:     return YES;
                    418: }
1.2       timbl     419: 
2.42      frystyk   420: /*
                    421: **  Searches for Gopher line until buffer fills up or a CRLF or LF is found
                    422: */
                    423: PRIVATE int GopherMenu_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
                    424: {
                    425:     while (l-- > 0) {
                    426:        if (me->state == EOL_FCR) {
                    427:            if (*b == LF && me->buflen) {
                    428:                if (!me->junk) {
                    429:                    BOOL cont;
                    430:                    *(me->buffer+me->buflen) = '\0';
                    431:                    cont = me->CSO ? GopherCSOLine(me, me->buffer) :
                    432:                        GopherMenuLine(me, me->buffer);
                    433:                    if (cont == NO) return HT_LOADED;
                    434:                } else
                    435:                    me->junk = NO;                         /* back to normal */
2.20      frystyk   436:            }
2.45      frystyk   437:            me->buflen = 0;
                    438:            me->state = EOL_BEGIN;
2.42      frystyk   439:        } else if (*b == CR) {
                    440:            me->state = EOL_FCR;
                    441:        } else if (*b == LF && me->buflen) {
                    442:            if (!me->junk) {
                    443:                BOOL cont;
                    444:                *(me->buffer+me->buflen) = '\0';
                    445:                cont = me->CSO ? GopherCSOLine(me, me->buffer) :
                    446:                    GopherMenuLine(me, me->buffer);
                    447:                if (cont == NO) return HT_LOADED;
                    448:            } else
                    449:                me->junk = NO;                             /* back to normal */
                    450:            me->buflen = 0;
                    451:            me->state = EOL_BEGIN;
                    452:        } else {
                    453:            *(me->buffer+me->buflen++) = *b;
                    454:            if (me->buflen >= MAX_GOPHER_LINE) {
                    455:                if (PROT_TRACE)
2.47      frystyk   456:                    TTYPrint(TDEST, "Gopher...... Line too long - ignored\n");
2.42      frystyk   457:                me->buflen = 0;
                    458:                me->junk = YES;
2.20      frystyk   459:            }
                    460:        }
2.42      frystyk   461:        b++;
2.17      frystyk   462:     }
2.42      frystyk   463:     return HT_OK;
                    464: }
2.17      frystyk   465: 
2.42      frystyk   466: PRIVATE int GopherMenu_put_string ARGS2(HTStream *, me, CONST char*, s)
                    467: {
                    468:     return GopherMenu_put_block(me, s, (int) strlen(s));
1.1       timbl     469: }
2.11      timbl     470: 
2.42      frystyk   471: PRIVATE int GopherMenu_put_character ARGS2(HTStream *, me, char, c)
                    472: {
                    473:     return GopherMenu_put_block(me, &c, 1);
                    474: }
2.11      timbl     475: 
2.42      frystyk   476: PRIVATE int GopherMenu_flush ARGS1(HTStream *, me)
                    477: {    
                    478:     return (*me->target->isa->flush)(me->target);
                    479: }
                    480: 
                    481: PRIVATE int GopherMenu_free ARGS1(HTStream *, me)
                    482: {
                    483:     int status = HT_OK;
                    484:     GopherBottom(me);
                    485:     if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
                    486:        return HT_WOULD_BLOCK;
                    487:     free(me);
                    488:     return HT_OK;
                    489: }
                    490: 
2.48    ! frystyk   491: PRIVATE int GopherMenu_abort ARGS2(HTStream *, me, HTList *, e)
2.42      frystyk   492: {
                    493:     (*me->target->isa->abort)(me->target, e);
                    494:     free(me);
                    495:     if (PROT_TRACE)
2.47      frystyk   496:        TTYPrint(TDEST, "Gopher...... ABORTING...\n");
2.42      frystyk   497:     return HT_ERROR;
                    498: }
                    499: 
                    500: /*     Goper Menu Stream
                    501: **     -----------------
2.20      frystyk   502: */
2.42      frystyk   503: PRIVATE CONST HTStreamClass GopherMenuClass =
                    504: {              
                    505:     "GopherMenu",
                    506:     GopherMenu_flush,
                    507:     GopherMenu_free,
                    508:     GopherMenu_abort,
                    509:     GopherMenu_put_character,
                    510:     GopherMenu_put_string,
                    511:     GopherMenu_put_block
                    512: };
2.26      frystyk   513: 
2.42      frystyk   514: /*
                    515: **  Stream for creating a HTML object out of a Gopher Menu object
                    516: */
                    517: PRIVATE HTStream * GopherMenu_new (HTRequest * request, char *url, BOOL CSO)
                    518: {
                    519:     HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
                    520:     if (!me) outofmem(__FILE__, "GopherMenu_new");
                    521:     me->isa = &GopherMenuClass;
                    522:     me->target = HTMLGenerator(request, NULL, WWW_HTML,
                    523:                               request->output_format, request->output_stream);
                    524:     HTAnchor_setFormat(request->anchor, WWW_HTML);
                    525:     me->request = request;    
                    526:     me->state = EOL_BEGIN;
                    527:     me->url = url;
                    528:     me->CSO = CSO;
                    529:     GopherTitle(me);
                    530:     return me;
                    531: }
2.20      frystyk   532: 
2.42      frystyk   533: /* ------------------------------------------------------------------------- */
                    534: /*                             GOPHER STATE MACHINE                         */
                    535: /* ------------------------------------------------------------------------- */
2.7       secret    536: 
2.42      frystyk   537: /*     GopherCleanup
                    538: **     -------------
                    539: **      This function closes the connection and frees memory.
                    540: **      Returns YES if OK, else NO
                    541: */
                    542: PRIVATE BOOL GopherCleanup (HTRequest *req, int status)
                    543: {
                    544:     HTNet *net = req->net;
                    545:     gopher_info *gopher = (gopher_info *) net->context;
2.20      frystyk   546: 
2.42      frystyk   547:     /* Free stream with data TO network */
                    548:     if (req->input_stream) {
                    549:        if (status == HT_INTERRUPTED)
                    550:            (*req->input_stream->isa->abort)(req->input_stream, NULL);
                    551:        else
                    552:            (*req->input_stream->isa->_free)(req->input_stream);
2.20      frystyk   553:     }
                    554: 
2.42      frystyk   555:     /* Remove the request object and our own context structure for gopher */
                    556:     HTNet_delete(net, status);
                    557:     if (gopher) {
                    558:        FREE(gopher->cmd);
                    559:        free(gopher);
2.34      frystyk   560:     }
2.42      frystyk   561:     return YES;
2.20      frystyk   562: }
2.7       secret    563: 
1.1       timbl     564: /*     Display a Gopher Index document
2.20      frystyk   565: **     -------------------------------
2.42      frystyk   566: **     We create a small HTML object as we have no network activity
2.20      frystyk   567: */
2.42      frystyk   568: PRIVATE void display_index (HTRequest * request)
1.1       timbl     569: {
2.40      frystyk   570:     HTStructured *target = HTMLGenerator(request, NULL, WWW_HTML,
2.42      frystyk   571:                                         request->output_format,
                    572:                                         request->output_stream);
                    573:     /* Update anchor information */
2.36      frystyk   574:     HTAnchor_setFormat(request->anchor, WWW_HTML);
2.42      frystyk   575:     HTAnchor_setTitle(request->anchor, "Searchable Gopher Index");
                    576:     /* @@@ We don't set Content-Length */
                    577: 
2.34      frystyk   578:     START(HTML_HTML);
                    579:     START(HTML_HEAD);
                    580:     START(HTML_TITLE);
                    581:     PUTS("Searchable Gopher Index");
                    582:     END(HTML_TITLE);
                    583:     END(HTML_HEAD);
                    584:     START(HTML_BODY);
                    585: 
1.2       timbl     586:     START(HTML_H1);
2.20      frystyk   587:     PUTS("Searchable Gopher Index");
1.2       timbl     588:     END(HTML_H1);
2.7       secret    589:     START(HTML_ISINDEX);
2.34      frystyk   590:     END(HTML_BODY);
                    591:     END(HTML_HTML);
2.42      frystyk   592:     (*target->isa->_free)(target);
2.7       secret    593: }
                    594: 
                    595: 
                    596: /*      Display a CSO index document
                    597: **      -------------------------------
2.42      frystyk   598: **     We create a small HTML object as we have no network activity
2.7       secret    599: */
2.42      frystyk   600: PRIVATE void display_cso (HTRequest * request)
2.7       secret    601: {
2.40      frystyk   602:     HTStructured *target = HTMLGenerator(request, NULL, WWW_HTML,
2.42      frystyk   603:                                         request->output_format,
                    604:                                         request->output_stream);
                    605:     /* Update anchor information */
2.36      frystyk   606:     HTAnchor_setFormat(request->anchor, WWW_HTML);
2.42      frystyk   607:     HTAnchor_setTitle(request->anchor, "Searchable SCO Index");
                    608:     /* @@@ We don't set Content-Length */
                    609: 
2.34      frystyk   610:     START(HTML_HTML);
                    611:     START(HTML_HEAD);
                    612:     START(HTML_TITLE);
                    613:     PUTS("Searchable Index of a CSO Name Server");
                    614:     END(HTML_TITLE);
                    615:     END(HTML_HEAD);
                    616:     START(HTML_BODY);
                    617: 
2.7       secret    618:     START(HTML_H1);
2.20      frystyk   619:     PUTS("Searchable Index of a CSO Name Server");
2.7       secret    620:     END(HTML_H1);
2.34      frystyk   621:     PUTS("A CSO Name Server usually provides directory information about people.");
2.7       secret    622:     START(HTML_ISINDEX);
2.34      frystyk   623:     END(HTML_BODY);
                    624:     END(HTML_HTML);
2.42      frystyk   625:     (*target->isa->_free)(target);
1.1       timbl     626: }
                    627: 
2.42      frystyk   628: /*     HTLoadGopher
                    629: **     ------------
2.24      frystyk   630: **     Given a hypertext address, this routine loads a gopher document
                    631: **
                    632: ** On entry,
2.42      frystyk   633: **      request                This is the request structure
                    634: ** On Exit
                    635: **     returns         HT_ERROR        Error has occured in call back
                    636: **                     HT_OK           Call back was OK
1.1       timbl     637: */
2.42      frystyk   638: PUBLIC int HTLoadGopher (SOCKET soc, HTRequest * request, SockOps ops)
1.1       timbl     639: {
2.42      frystyk   640:     int status = HT_ERROR;
                    641:     HTNet *net = request->net;
2.20      frystyk   642:     gopher_info *gopher;
2.42      frystyk   643:     char *url = HTAnchor_physical(request->anchor);
2.20      frystyk   644:     
2.42      frystyk   645:     /*
                    646:     ** Initiate a new gopher structure and bind to request structure
                    647:     ** This is actually state GOPHER_BEGIN, but it can't be in the state
                    648:     ** machine as we need the structure first.
                    649:     */
                    650:     if (ops == FD_NONE) {
2.47      frystyk   651:        if (PROT_TRACE) TTYPrint(TDEST, "Gopher...... Looking for `%s\'\n",url);
2.42      frystyk   652:        if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
                    653:            outofmem(__FILE__, "HTLoadGopher");
                    654:        gopher->type = GT_MENU;
                    655:        gopher->state = GOPHER_BEGIN;
                    656:        net->context = gopher;
2.44      frystyk   657:     } else if (ops == FD_CLOSE) {                            /* Interrupted */
2.42      frystyk   658:        GopherCleanup(request, HT_INTERRUPTED);
                    659:        return HT_OK;
                    660:     } else
                    661:        gopher = (gopher_info *) net->context;          /* Get existing copy */
                    662: 
                    663:     /* Now jump into the machine. We know the state from the previous run */
                    664:     while (1) {
                    665:        switch (gopher->state) {
                    666: 
                    667:          case GOPHER_BEGIN:         /* Get entity type, and selector string */
                    668:            {
                    669:                char *path = HTParse(url, "", PARSE_PATH);
                    670:                char *selector = path;
                    671:                char *query = NULL;
                    672:                char *separator = NULL;
                    673:                if (*selector) gopher->type = (HTGopherType) *selector++;
                    674:                if (gopher->type == GT_INDEX) {
                    675:                    HTAnchor_setIndex(request->anchor);          /* Is index */
                    676:                    query = strchr(selector, '?');
                    677: 
                    678:                    /* Display local cover page only if no search requested */
                    679:                    if (!query || !*(query+1)) {       /* No search required */
                    680:                        display_index(request);
                    681:                        gopher->state = GOPHER_GOT_DATA;
                    682:                        break;
                    683:                    } else {
                    684:                        *query++ = 0;                            /* Skip '?' */
                    685:                        separator = "\t";
                    686:                    }
                    687:                } else if (gopher->type == GT_CSO) {
                    688:                    HTAnchor_setIndex(request->anchor); /* Search is allowed */
                    689:                    query = strchr(selector, '?'); /* Look for search string */
                    690:                    
                    691:                    /* Display local cover page only if no search requested */
                    692:                    if (!query || !*(query+1)) {       /* No search required */
                    693:                        display_cso(request);
                    694:                        gopher->state = GOPHER_GOT_DATA;
                    695:                        break;
                    696:                    } else {
                    697:                        *query++ = 0;                            /* Skip '?' */
                    698:                        *selector = 0;
                    699:                        separator = "query ";
                    700:                    }
                    701:                }
2.20      frystyk   702: 
2.42      frystyk   703:                /* Now generate the final command */
                    704:                {
                    705:                    char crlf[3];
                    706:                    StrAllocCopy(gopher->cmd, selector);
                    707:                    if (query) {
                    708:                        char *p;
                    709:                        for (p=query; *p; p++)   /* Remove plus signs 921006 */
                    710:                            if (*p == '+') *p = ' ';
                    711:                        StrAllocCat(gopher->cmd, separator);
                    712:                        StrAllocCat(gopher->cmd, query);
                    713:                    }
                    714:                    HTUnEscape(gopher->cmd);
                    715:                    HTCleanTelnetString(gopher->cmd);   /* Prevent sec holes */
                    716:                    *crlf = CR;                        /* Telnet termination */
                    717:                    *(crlf+1) = LF;
                    718:                    *(crlf+2) = '\0';
                    719:                    StrAllocCat(gopher->cmd, crlf);
                    720:                }
                    721:                free(path);
                    722:                gopher->state = GOPHER_NEED_CONNECTION;
1.1       timbl     723:            }
2.42      frystyk   724:            break;
                    725: 
                    726:          case GOPHER_NEED_CONNECTION:
2.43      frystyk   727:            status = HTDoConnect(net, url, GOPHER_PORT);
2.42      frystyk   728:            if (status == HT_OK) {
                    729:                if (PROT_TRACE)
2.47      frystyk   730:                    TTYPrint(TDEST, "Gopher...... Connected, socket %d\n",
2.42      frystyk   731:                            net->sockfd);
                    732: 
                    733:                /* Set up stream TO network */
                    734:                request->input_stream = HTWriter_new(net, YES);
                    735: 
                    736:                /* Set up stream FROM network and corresponding read buffer */
                    737:                net->isoc = HTInputSocket_new(net->sockfd);
                    738:                if (gopher->type == GT_MENU) {
                    739:                    if (!HTImProxy && request->output_format == WWW_SOURCE)
                    740:                        net->target = request->output_stream;
                    741:                    else
                    742:                        net->target = GopherMenu_new(request, url, NO);
                    743:                } else if (gopher->type == GT_CSO) {
                    744:                    if (!HTImProxy && request->output_format == WWW_SOURCE)
                    745:                        net->target = request->output_stream;
                    746:                    else
                    747:                        net->target = GopherMenu_new(request, url, YES);
                    748:                } else {
                    749:                    /* This is probably better than the gopher types */
                    750:                    net->target = HTGuess_new(request, NULL,WWW_UNKNOWN,
                    751:                                              request->output_format,
                    752:                                              request->output_stream);
                    753:                }
                    754:                gopher->state = GOPHER_NEED_REQUEST;
                    755:            } else if (status == HT_WOULD_BLOCK)
                    756:                return HT_OK;
                    757:            else
                    758:                gopher->state = GOPHER_ERROR;
                    759:            break;
                    760: 
                    761:          case GOPHER_NEED_REQUEST:
2.47      frystyk   762:            if (PROT_TRACE) TTYPrint(TDEST, "Gopher Tx... `%s\'", gopher->cmd);
2.42      frystyk   763:            status = (*request->input_stream->isa->put_block)
                    764:                (request->input_stream, gopher->cmd, strlen(gopher->cmd));
                    765:            if (status == HT_WOULD_BLOCK)
                    766:                return HT_OK;
                    767:            else if (status == HT_ERROR)
                    768:                gopher->state = GOPHER_ERROR;
                    769:            else
                    770:                gopher->state = GOPHER_NEED_RESPONSE;
                    771:            break;
1.1       timbl     772: 
2.42      frystyk   773:          case GOPHER_NEED_RESPONSE:
2.44      frystyk   774:            status = HTSocketRead(request, net);
2.42      frystyk   775:            if (status == HT_WOULD_BLOCK)
                    776:                return HT_OK;
                    777:            else if (status == HT_LOADED)
                    778:                gopher->state = GOPHER_GOT_DATA;
                    779:            else
                    780:                gopher->state = GOPHER_ERROR;
                    781:            break;
1.2       timbl     782: 
2.42      frystyk   783:          case GOPHER_GOT_DATA:
                    784:            GopherCleanup(request, HT_LOADED);
                    785:            return HT_OK;
                    786:            break;
                    787: 
                    788:          case GOPHER_NO_DATA:
                    789:            GopherCleanup(request, HT_NO_DATA);
                    790:            return HT_OK;
                    791:            break;
                    792: 
                    793:          case GOPHER_ERROR:
                    794:            GopherCleanup(request, HT_ERROR);
                    795:            return HT_OK;
                    796:            break;
2.25      frystyk   797:        }
2.42      frystyk   798:     }  /* while(1) */
1.1       timbl     799: }

Webmaster