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

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

Webmaster