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

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
                     11: **                     HF, frystyk@dxcern.cern.ch
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.21      frystyk    17: **
                     18: ** NOTE:
                     19: **     When parsing a gopher menu, we can't use the default HTParseSocket
                     20: **     but we will hav eto take care of the stram ourselves :-(
                     21: **
1.1       timbl      22: */
                     23: 
2.20      frystyk    24: /* Library include files */
2.34      frystyk    25: #include "tcp.h"
                     26: #include "HTUtils.h"
                     27: #include "HTString.h"
2.20      frystyk    28: #include "HTParse.h"
                     29: #include "HTTCP.h"
2.17      frystyk    30: #include "HTIcons.h"
2.20      frystyk    31: #include "HTAccess.h"
2.38    ! frystyk    32: #include "HTSocket.h"
1.1       timbl      33: #include "HTFormat.h"
2.20      frystyk    34: #include "HTError.h"
2.36      frystyk    35: #include "HTBind.h"
1.2       timbl      36: #include "HTML.h"
2.20      frystyk    37: #include "HTMLPDTD.h"
                     38: #include "HTDirBrw.h"
                     39: #include "HTGopher.h"                                   /* Implemented here */
                     40: 
                     41: /* Macros and other defines */
                     42: #ifndef GOPHER_PORT
                     43: #define GOPHER_PORT 70                                 /* See protocol spec */
                     44: #endif
1.2       timbl      45: 
2.20      frystyk    46: /* Hypertext object building machinery */
2.17      frystyk    47: #define PUTC(c) (*target->isa->put_character)(target, c)
                     48: #define PUTS(s) (*target->isa->put_string)(target, s)
                     49: #define START(e) (*target->isa->start_element)(target, e, 0, 0)
                     50: #define END(e) (*target->isa->end_element)(target, e)
2.27      duns       51: #define FREE_TARGET (*target->isa->_free)(target)
2.20      frystyk    52: 
                     53: /* Type definitions and global variables etc. local to this module */
                     54: typedef enum _HTGopherType {
                     55:     GOPHER_TEXT                = '0',
                     56:     GOPHER_MENU                = '1',
                     57:     GOPHER_CSO         = '2',
                     58:     GOPHER_ERROR       = '3',
                     59:     GOPHER_MACBINHEX   = '4',
                     60:     GOPHER_PCBINHEX    = '5',
                     61:     GOPHER_UUENCODED   = '6',
                     62:     GOPHER_INDEX       = '7',
                     63:     GOPHER_TELNET      = '8',
                     64:     GOPHER_BINARY       = '9',
                     65:     GOPHER_GIF          = 'g',
                     66:     GOPHER_HTML                = 'h',                                       /* HTML */
2.28      frystyk    67:     GOPHER_INFO                = 'i',
2.20      frystyk    68:     GOPHER_SOUND        = 's',
                     69:     GOPHER_WWW         = 'w',                                 /* W3 address */
                     70:     GOPHER_IMAGE        = 'I',
                     71:     GOPHER_TN3270       = 'T',
                     72:     GOPHER_DUPLICATE   = '+',
                     73:     GOPHER_PLUS_IMAGE  = ':',                  /* Addition from Gopher Plus */
                     74:     GOPHER_PLUS_MOVIE  = ';',
                     75:     GOPHER_PLUS_SOUND  = '<'
                     76: } HTGopherType;
                     77: 
1.2       timbl      78: struct _HTStructured {
                     79:        CONST HTStructuredClass *       isa;
                     80:        /* ... */
                     81: };
                     82: 
2.26      frystyk    83: /* This is the local definition of HTRequest->net_info */
2.20      frystyk    84: typedef struct _gopher_info {
2.34      frystyk    85:     SOCKFD             sockfd;                         /* Socket descripter */
2.30      frystyk    86:     SockA              sock_addr;              /* SockA is defined in tcp.h */
                     87:     HTInputSocket *    isoc;                                /* Input buffer */
2.35      frystyk    88:     SocAction          action;                 /* Result of the select call */
                     89:     HTStream *         target;                             /* Target stream */
2.30      frystyk    90:     int                addressCount;        /* Attempts if multi-homed host */
                     91:     time_t             connecttime;             /* Used on multihomed hosts */
                     92:     struct _HTRequest *        request;           /* Link back to request structure */
2.28      frystyk    93: 
2.35      frystyk    94:     HTChunk *          transmit;                         /* Line to be send */
2.30      frystyk    95:     HTGopherType       type;                            /* Gopher item type */
2.20      frystyk    96: } gopher_info;
1.1       timbl      97: 
2.20      frystyk    98: /* ------------------------------------------------------------------------- */
1.1       timbl      99: 
2.20      frystyk   100: /*                                                           get_gopher_icon
1.1       timbl     101: **
2.20      frystyk   102: **     This function finds an appopriate icon for the item in the gopher
                    103: **     list. Actually it is only a shell build upon HTGetIcon().
2.17      frystyk   104: **
                    105: */
2.36      frystyk   106: PRIVATE HTIconNode *get_gopher_icon ARGS1(int, gopher_type)
2.17      frystyk   107: {
2.36      frystyk   108:     HTFormat   content_type = NULL;
                    109:     HTEncoding content_encoding = NULL;
2.17      frystyk   110: 
                    111:     if (gopher_type == GOPHER_MENU)
                    112:        return icon_dir ? icon_dir : icon_unknown;
                    113: 
                    114:     switch(gopher_type) {
                    115:       case GOPHER_TEXT:
                    116:        content_type = HTAtom_for("text/void");
                    117:        break;
2.20      frystyk   118:       case GOPHER_IMAGE:
                    119:       case GOPHER_PLUS_IMAGE:
2.17      frystyk   120:       case GOPHER_GIF:
                    121:        content_type = HTAtom_for("image/void");
                    122:        break;
2.20      frystyk   123:       case GOPHER_WWW:
2.17      frystyk   124:       case GOPHER_HTML:
                    125:        content_type = HTAtom_for("text/void");
                    126:        break;
                    127:       case GOPHER_SOUND:
2.20      frystyk   128:       case GOPHER_PLUS_SOUND:
2.17      frystyk   129:        content_type = HTAtom_for("audio/void");
                    130:        break;
2.20      frystyk   131:       case GOPHER_PLUS_MOVIE:
                    132:        content_type = HTAtom_for("video/void");
2.17      frystyk   133:        break;
                    134:       case GOPHER_INDEX:
                    135:        content_type = HTAtom_for("application/x-gopher-index");
                    136:        break;
                    137:       case GOPHER_CSO:
                    138:        content_type = HTAtom_for("application/x-gopher-cso");
                    139:        break;
                    140:       case GOPHER_TELNET:
                    141:        content_type = HTAtom_for("application/x-gopher-telnet");
                    142:        break;
                    143:       case GOPHER_TN3270:
                    144:        content_type = HTAtom_for("application/x-gopher-tn3270");
                    145:        break;
                    146:       case GOPHER_DUPLICATE:
                    147:        content_type = HTAtom_for("application/x-gopher-duplicate");
                    148:        break;
                    149:       case GOPHER_ERROR:
                    150:        content_type = HTAtom_for("www/unknown");
                    151:        break;
                    152:       case GOPHER_MACBINHEX:
                    153:       case GOPHER_PCBINHEX:
                    154:       case GOPHER_UUENCODED:
2.36      frystyk   155:        content_type = WWW_BINARY;
                    156:        content_encoding = WWW_ENC_BASE64;
                    157:        break;
2.17      frystyk   158:       case GOPHER_BINARY:
2.36      frystyk   159:        content_type = WWW_BINARY;
                    160:        break;
2.17      frystyk   161:       default:
                    162:        content_type = HTAtom_for("www/unknown");
                    163:        break;
                    164:     }
                    165:     return HTGetIcon(S_IFMT & S_IFREG, content_type, content_encoding);
                    166: }
                    167: 
                    168: 
2.20      frystyk   169: /*                                                           parse_menu
                    170: **
                    171: **     This function parses a gopher menu and puts it into a iconized
                    172: **     list.
                    173: **
                    174: **     Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
                    175: **     if other error.
1.1       timbl     176: **
                    177: */
2.20      frystyk   178: PRIVATE int parse_menu ARGS3(HTRequest *,      request,
                    179:                             gopher_info *,     gopher,
                    180:                             CONST char *,      url)
                    181: #define TAB            '\t'
                    182: #define HEX_ESCAPE     '%'
1.1       timbl     183: {
2.20      frystyk   184:     int status = -1;
2.17      frystyk   185:     unsigned int files = 0;
                    186:     int ch;
2.20      frystyk   187:     HTChunk *chunk = HTChunkCreate(128);
                    188:     char *message = NULL;                           /* For a gopher message */
2.28      frystyk   189:     char *info = NULL;           /* For gopher information send as `i' type */
                    190: 
2.21      frystyk   191:     HTStructured *target = NULL;
2.26      frystyk   192: 
                    193:     gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20      frystyk   194:     
                    195:     /* Output the list */
2.26      frystyk   196:     while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20      frystyk   197:         if (ch == CR || ch == LF) {
                    198:            if (chunk->size) {                              /* Got some text */
                    199:                char *name = NULL;                     /* Gopher menu fields */
                    200:                char *selector = NULL;
                    201:                char *host = NULL;
                    202:                char *port = NULL;
                    203:                char *strptr;
                    204:                char *errptr;
                    205:                char gtype;
                    206:                HTChunkTerminate(chunk);
                    207:                strptr = chunk->data;                 /* Scan it to parse it */
2.28      frystyk   208:                if (PROT_TRACE)
2.34      frystyk   209:                    fprintf(TDEST, "HTGopher.... Menu item: `%s\'\n",
2.20      frystyk   210:                            chunk->data);
                    211:                gtype = *strptr++;
                    212: 
                    213:                if (gtype == GOPHER_ERROR) {
2.28      frystyk   214:                    StrAllocCat(message, chunk->data+1);
2.31      frystyk   215:                    continue;
2.20      frystyk   216:                }
2.28      frystyk   217:                /* If information then add it to the info string */
                    218:                if (gtype == GOPHER_INFO) {
                    219:                    if ((errptr = strchr(chunk->data, '\t')) != NULL)
                    220:                        *errptr = '\0';
                    221:                    if (info) {
                    222:                        StrAllocCat(info, "\n");
                    223:                        StrAllocCat(info, chunk->data+1);
                    224:                    } else
                    225:                        StrAllocCopy(info, chunk->data+1);
                    226:                    HTChunkClear(chunk);
                    227:                    continue;
                    228:                }
1.1       timbl     229: 
2.28      frystyk   230:                /* If first item is an error, then don't put any header out
                    231:                   but wait and see if there is a next item in the list. If not
                    232:                   then make error message, else use as list message. */
2.20      frystyk   233:                if (!files && (strstr(chunk->data, "error.host") ||
                    234:                    strstr(chunk->data, "errorhost"))) {
2.18      luotonen  235: 
2.20      frystyk   236:                    /* If message is already initialized, then add this one. */
                    237:                    /* We don't want the gopher type character */
                    238:                    if ((errptr = strchr(chunk->data, '\t')) != NULL)
                    239:                        *errptr = '\0';
                    240:                    if (message) {
                    241:                        StrAllocCat(message, "\n");
                    242:                        StrAllocCat(message, chunk->data+1);
                    243:                    } else
                    244:                        StrAllocCopy(message, chunk->data+1);
                    245:                    HTChunkClear(chunk);
                    246:                    continue;
                    247:                }
2.17      frystyk   248: 
2.21      frystyk   249:                /* Stop listing if line with a dot by itself */
2.25      frystyk   250:                if (!files && message && gtype=='.' && !*strptr) {
2.21      frystyk   251:                    status = -1;
                    252:                    break;
                    253:                }
                    254: 
2.20      frystyk   255:                /* Output title, maybe top message and list top  */
                    256:                if (!files) {
                    257:                    CONST char *title = HTAnchor_title(request->anchor);
                    258:                    char *outstr = NULL;
2.21      frystyk   259:                    target = HTML_new(request, NULL, WWW_HTML,
                    260:                                      request->output_format,
                    261:                                      request->output_stream);
2.36      frystyk   262:                    HTAnchor_setFormat(request->anchor, WWW_HTML);
2.20      frystyk   263:                    if (title) {
                    264:                        StrAllocCopy(outstr, title);
                    265:                        HTUnEscape(outstr);
                    266:                    } else
                    267:                        StrAllocCopy(outstr, "Gopher Menu");
2.28      frystyk   268:                    START(HTML_HTML);
                    269:                    START(HTML_HEAD);
2.20      frystyk   270:                    START(HTML_TITLE);
                    271:                    PUTS(outstr);
                    272:                    END(HTML_TITLE);
2.28      frystyk   273:                    END(HTML_HEAD);
                    274: 
                    275:                    START(HTML_BODY);
2.20      frystyk   276:                    START(HTML_H1);
                    277:                    PUTS(outstr);
                    278:                    END(HTML_H1);
                    279:                    FREE(outstr);
                    280:                
                    281:                    /* Output any message on top of list */
2.28      frystyk   282:                    if ((message || info) && HTDirInfo == HT_DIR_INFO_TOP) {
                    283:                        if (message) PUTS(message);
                    284:                        if (info) PUTS(info);
2.20      frystyk   285:                        START(HTML_BR);
                    286:                    }
2.17      frystyk   287: 
2.20      frystyk   288:                    /* Make everything in list preformatted */
                    289:                    START(HTML_PRE);
1.1       timbl     290: 
2.28      frystyk   291: #ifdef OLD_CODE
2.20      frystyk   292:                    /* Output the header line of the list */
                    293:                    if (!icon_blank) icon_blank = icon_unknown;
                    294:                    if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
                    295:                        HTMLPutImg(target, icon_blank->icon_url,
                    296:                                   HTIcon_alt_string(icon_blank->icon_alt, NO),
                    297:                                   NULL);
                    298:                    }
2.17      frystyk   299:                    PUTC(' ');
2.20      frystyk   300:                    PUTS("Name");
                    301:                    PUTC('\n');
2.28      frystyk   302: #endif /* OLD_CODE */
2.20      frystyk   303:                    START(HTML_HR);
                    304:                    PUTC('\n');
2.17      frystyk   305:                }
                    306: 
2.20      frystyk   307:                /* Stop listing if line with a dot by itself */
                    308:                if (gtype=='.' && !*strptr) {
                    309:                    status = (!files && message) ? -1 : HT_LOADED;
                    310:                    break;
2.7       secret    311:                }
2.20      frystyk   312: 
                    313:                /* Parse menu item */
                    314:                if (*strptr) {
                    315:                    name = strptr;
                    316:                    selector = strchr(name, TAB);
                    317:                    if (selector) {
                    318:                        *selector++ = 0;                   /* Terminate name */
                    319:                        host = strchr(selector, TAB);
                    320:                        if (host) {
                    321:                            *host++ = 0;               /* Terminate selector */
                    322:                            port = strchr(host, TAB);
                    323:                            if (port) {
                    324:                                char *junk;
                    325:                                *port = ':';         /* delimit host a la W3 */
                    326:                                if ((junk = strchr(port, TAB)) != NULL)
                    327:                                    *junk = '\0';               /* Chop port */
                    328:                                if (*(port+1) == '0' && !*(port+2))
                    329:                                    *port = '\0';
                    330:                            } /* port */
                    331:                        } /* host */
                    332:                    } /* selector */
                    333:                } /* gtype and name */
                    334:                
                    335:                /* Get Icon type and output the icon */
                    336:                if (HTDirShowMask & HT_DIR_SHOW_ICON) {
2.29      frystyk   337:                    char *filename = HTParse(url, "",
                    338:                                             PARSE_PATH+PARSE_PUNCTUATION);
2.36      frystyk   339:                    HTIconNode *icon = get_gopher_icon(gtype);
2.20      frystyk   340:                    if (icon && icon->icon_url) {
                    341:                        HTMLPutImg(target, icon->icon_url,
                    342:                                   HTIcon_alt_string(icon->icon_alt, YES),
                    343:                                   NULL);
                    344:                        PUTC(' ');
                    345:                    }
2.29      frystyk   346:                    free(filename);
2.7       secret    347:                }
2.20      frystyk   348: 
                    349:                if (gtype == GOPHER_WWW) {           /* Gopher pointer to W3 */
                    350:                    char *escaped = NULL;
                    351:                    escaped = HTEscape(selector, URL_PATH);
                    352:                    HTStartAnchor(target, NULL, escaped);
                    353:                    PUTS(name);
                    354:                    END(HTML_A);
                    355:                    free(escaped);
                    356:                } else if (port) {                  /* Other types need port */
                    357:                    char *escaped = NULL;
                    358:                    char *address = NULL;
                    359:                    int addr_len;
                    360: 
                    361:                    /* Calculate the length of the WWW-address */
                    362:                    if (selector && *selector) {
                    363:                        escaped = HTEscape(selector, URL_PATH);
                    364:                        addr_len = 15 + strlen(escaped) + strlen(host) + 1;
                    365:                    } else {
                    366:                        addr_len = 15 + strlen(host) + 1;
                    367:                    }
                    368:                    if ((address = (char *) malloc(addr_len)) == NULL)
                    369:                        outofmem(__FILE__, "Gopher ParseMenu");
                    370:                    *address = '\0';
                    371: 
                    372:                    if (gtype == GOPHER_TELNET) {
                    373:                        if (escaped)
                    374:                            sprintf(address, "telnet://%s@%s/",
                    375:                                    escaped, host);
                    376:                        else
                    377:                            sprintf(address, "telnet://%s/", host);
                    378:                    }
                    379:                    else if (gtype == GOPHER_TN3270) {
                    380:                        if (escaped)
                    381:                            sprintf(address, "tn3270://%s@%s/",
                    382:                                    escaped, host);
                    383:                        else 
                    384:                            sprintf(address, "tn3270://%s/", host);
                    385:                    } else {
                    386:                        if (escaped)
                    387:                            sprintf(address, "//%s/%c%s", host, gtype,
                    388:                                    escaped);
                    389:                        else
                    390:                            sprintf(address, "//%s/%c", host, gtype);
1.1       timbl     391:                    }
2.20      frystyk   392: 
                    393:                    /* Now output the anchor if not a Gopher error */
                    394:                    if (gtype != GOPHER_ERROR &&
                    395:                        !strstr(address, "error.host") &&
                    396:                        !strstr(address, "errorhost")) {
                    397:                        HTStartAnchor(target, NULL, address);
                    398:                        PUTS(name);
                    399:                        END(HTML_A);
                    400:                    } else 
2.28      frystyk   401:                        PUTS(name);        /* Just put it out, but skip type */
2.20      frystyk   402:                    FREE(address);
                    403:                    FREE(escaped);
                    404:                } else {                                   /* If parse error */
2.28      frystyk   405:                    if (PROT_TRACE)
2.34      frystyk   406:                        fprintf(TDEST, "HTGopher.... Bad menu item, `%s\'\n",
2.20      frystyk   407:                                chunk->data);
                    408:                    PUTS(chunk->data);
1.1       timbl     409:                }
2.17      frystyk   410:                PUTC('\n');
2.20      frystyk   411:                HTChunkClear(chunk);
                    412:                ++files;                           /* Update number of files */
                    413:            }
                    414:        } else
                    415:            HTChunkPutc(chunk, ch);
                    416:     }
                    417:     if (ch < 0)
                    418:        status = ch;
1.2       timbl     419: 
2.20      frystyk   420:     /* If no files and message is initialized then make error message,
                    421:        else output the bottom part of the list*/
                    422:     if (status != HT_INTERRUPTED) {
                    423:        if (!files && status < 0) {
                    424:            if (message) {
                    425:                HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
                    426:                           (void *) message, strlen(message), "parse_menu");
                    427:            } else {
                    428:                HTErrorAdd(request, ERR_FATAL, NO, HTERR_GOPHER_SERVER,
                    429:                           chunk->data, chunk->size, "parse_menu");
                    430:            }
2.21      frystyk   431:        } else if (target) {
2.28      frystyk   432: #ifdef OLD_CODE
2.20      frystyk   433:            char *outstr;
                    434:            if ((outstr = (char *) malloc(100)) == NULL)
                    435:                outofmem(__FILE__, "parse_menu");
                    436:            if (files == 0)
                    437:                sprintf(outstr, "Empty directory");
                    438:            else if (files == 1)
                    439:                sprintf(outstr, "1 file");
                    440:            else
                    441:                sprintf(outstr, "%u files", files);
                    442:            START(HTML_HR);
                    443:            PUTS(outstr);
                    444:            free(outstr);
2.28      frystyk   445: #endif /* OLD_CODE */
                    446:            START(HTML_HR);
                    447:            if (!files) PUTS("Empty Gopher Menu");
2.20      frystyk   448:            END(HTML_PRE);
1.1       timbl     449:            
2.20      frystyk   450:            /* Put out any messages */
2.28      frystyk   451:            if ((message || info) && HTDirInfo == HT_DIR_INFO_BOTTOM) {
                    452:                if (message) PUTS(message);
                    453:                if (info) PUTS(info);
2.20      frystyk   454:                START(HTML_BR);
                    455:            }
2.28      frystyk   456:            END(HTML_BODY);
                    457:            END(HTML_HTML);
2.21      frystyk   458:            FREE_TARGET;
                    459:        } else {
2.28      frystyk   460:            if (PROT_TRACE)
2.34      frystyk   461:                fprintf(TDEST, "HTGopher.... Interrupted before any stream was put up.\n");
2.20      frystyk   462:        }
2.17      frystyk   463:     }
                    464: 
2.20      frystyk   465:     /* Cleanup */
                    466:     FREE(message);
2.28      frystyk   467:     FREE(info);
2.26      frystyk   468:     HTInputSocket_free(gopher->isoc);
2.20      frystyk   469:     HTChunkFree(chunk);
                    470:     return status;
1.1       timbl     471: }
2.11      timbl     472: 
                    473: 
2.7       secret    474: /*     Parse a Gopher CSO document
2.20      frystyk   475: **     ============================
                    476: **
                    477: **     Accepts an open socket to a CSO server waiting to send us
                    478: **     data and puts it on the screen in a reasonable manner.
                    479: **
                    480: **     Perhaps this data can be automatically linked to some
                    481: **     other source as well???
                    482: **
                    483: **     Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
                    484: **     on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
                    485: **     secret@dxcern.cern.ch.
                    486: **
                    487: **     Returns HT_LOADED on succed, HT_INTERRUPTED if interrupted and -1
                    488: **     if other error.
                    489: */
                    490: PRIVATE int parse_cso ARGS3(HTRequest *,       request,
                    491:                            gopher_info *,      gopher,
                    492:                            CONST char *,       url)
2.7       secret    493: {
2.20      frystyk   494:     int status = -1;
                    495:     unsigned int records = 0;
2.17      frystyk   496:     int ch;
2.20      frystyk   497:     char *cur_code = NULL;
                    498:     HTChunk *chunk = HTChunkCreate(128);
2.21      frystyk   499:     HTStructured *target = NULL;
2.34      frystyk   500:     char *keyword;
                    501:     if ((keyword = strchr(url, '?')) != NULL)
                    502:        keyword++;
2.26      frystyk   503: 
                    504:     gopher->isoc = HTInputSocket_new(gopher->sockfd);
2.20      frystyk   505:     
                    506:     /* Start grabbing chars from the network */
2.26      frystyk   507:     while ((ch = HTInputSocket_getCharacter(gopher->isoc)) >= 0) {
2.20      frystyk   508:        if (ch == CR || ch == LF) {
                    509:            if (chunk->size) {          
                    510:                /* OK we now have a line in 'p' lets parse it and print it */
                    511:                char *strptr;
                    512:                HTChunkTerminate(chunk);
                    513:                strptr = chunk->data;
                    514: 
                    515:                /* If line begins with a 1, then more data is coming, so we
                    516:                   put out the title */
                    517:                if (*strptr == '1' ||
                    518:                    !strncmp(strptr, "501", 3) || !strncmp(strptr, "502", 3)) {
2.21      frystyk   519: 
                    520:                    /* Put up new stream */
                    521:                    target = HTML_new(request, NULL, WWW_HTML,
                    522:                                      request->output_format,
                    523:                                      request->output_stream);
2.36      frystyk   524:                    HTAnchor_setFormat(request->anchor, WWW_HTML);
2.34      frystyk   525:                    START(HTML_HTML);
                    526:                    START(HTML_HEAD);
                    527:                    START(HTML_TITLE);
                    528:                    PUTS("CSO Search: ");
                    529:                    if (keyword)
                    530:                        PUTS(keyword);
                    531:                    else
                    532:                        PUTS("empty");
                    533:                    END(HTML_TITLE);
                    534:                    END(HTML_HEAD);
                    535:                    START(HTML_BODY);
                    536: 
2.20      frystyk   537:                    START(HTML_H1);
2.34      frystyk   538:                    PUTS("CSO Search: ");
                    539:                    if (keyword)
                    540:                        PUTS(keyword);
                    541:                    else
                    542:                        PUTS("empty");
2.20      frystyk   543:                    END(HTML_H1);
                    544: 
2.34      frystyk   545: #if 0
2.20      frystyk   546:                     /* Output the header line of the list */
                    547:                     START(HTML_PRE); /* To make it look as the other headers */
                    548:                     if (!icon_blank) icon_blank = icon_unknown;
                    549:                     if (HTDirShowMask & HT_DIR_SHOW_ICON && icon_blank) {
                    550:                         HTMLPutImg(target, icon_blank->icon_url,
                    551:                                    HTIcon_alt_string(icon_blank->icon_alt, NO),
                    552:                                    NULL);
                    553:                     }
                    554:                     PUTC(' ');
                    555:                     PUTS("Record");
                    556:                     PUTC('\n');
                    557:                     START(HTML_HR);
                    558:                     PUTC('\n');
                    559:                    END(HTML_PRE);
2.34      frystyk   560: #endif
2.20      frystyk   561:                }
2.7       secret    562: 
2.20      frystyk   563:                /* Break on line that begins with a 2. It's the end of data. */
                    564:                if (*strptr == '2') {
                    565:                    status = HT_LOADED;
                    566:                    break;
                    567:                }
                    568:                
                    569:                /* Lines beginning with 5 are errors, generate msg and quit */
                    570:                if (*strptr == '5') {
                    571:                    char *msgptr = strchr(chunk->data, ':');
                    572:                    if (!msgptr)
                    573:                        msgptr = chunk->data;
                    574:                    else
                    575:                        ++msgptr;
                    576:                    if (!strncmp(strptr, "501", 3))            /* No entries */
                    577:                        status = HT_LOADED;
                    578:                    else if (!strncmp(strptr, "502", 3)) {       /* Too many */
                    579:                        status = HT_LOADED;
                    580:                        PUTS(msgptr);
                    581:                    } else {
                    582:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_CSO_SERVER,
                    583:                                   (void *) msgptr,
                    584:                                   strlen(msgptr), "parse_cso");
                    585:                    }
                    586:                    break;
                    587:                }
                    588:                
                    589:                if(*strptr == '-') {
                    590:                    /*  data lines look like  -200:#:
                    591:                     *  where # is the search result number and can be  
                    592:                     *  multiple digits (infinate?)
                    593:                     *  find the second colon and check the digit to the
                    594:                     *  left of it to see if they are diferent
                    595:                     *  if they are then a different person is starting. 
                    596:                     *  make this line an <h2>
2.7       secret    597:                     */
2.20      frystyk   598:                    char *code;             /* format: -200:code:field:value */
                    599:                    char *field;
                    600:                    char *value;
                    601:                    if ((code = strchr(strptr, ':')) != NULL &&
                    602:                        (field = strchr(++code, ':')) != NULL) {
                    603:                        *field++ = '\0';
                    604:                        
                    605:                        /* Let's do a strcmp instead of numbers */
                    606:                        if (!records) {            /* Header of first record */
2.34      frystyk   607: #if 0
2.20      frystyk   608:                            START(HTML_H2);
                    609:                            PUTS("Record 1");
                    610:                            END(HTML_H2);
2.34      frystyk   611: #endif
2.20      frystyk   612:                            START(HTML_DL);
                    613:                            START(HTML_DT);
2.34      frystyk   614:                        } else {
                    615:                            if (cur_code && strcmp(code, cur_code))
                    616:                                START(HTML_P);
                    617:                            START(HTML_DT);
                    618:                        }
2.20      frystyk   619:                        
                    620:                        /* I'm not sure whether the name field comes in any
                    621:                         *  special order or if its even required in a 
                    622:                         *  record, so for now the first line is the header
                    623:                         *  no matter what it is (it's almost always the
                    624:                         *  alias)
2.7       secret    625:                         */
2.20      frystyk   626:                        if ((value = strchr(field, ':')) == NULL)
                    627:                            value = "Empty?";
                    628:                        else
                    629:                            *value++ = '\0';
                    630:                        {
                    631:                            char *strip = HTStrip(field);
                    632:                            PUTS(strip);
                    633:                            START(HTML_DD);
                    634:                            strip = HTStrip(value);
2.34      frystyk   635:                            if (!records ||
                    636:                                (cur_code && strcmp(code, cur_code))) {
                    637:                                START(HTML_B);
                    638:                                PUTS(strip);
                    639:                                END(HTML_B);
                    640:                            } else
                    641:                                PUTS(strip);
2.20      frystyk   642:                        }
2.34      frystyk   643:                        records++;
2.7       secret    644:                        
2.20      frystyk   645:                        /* save the code for comparison on the next pass */
                    646:                        StrAllocCopy(cur_code, code);
                    647:                    }
                    648:                } /* end data line */
                    649:                HTChunkClear(chunk);
                    650:            } /* end new line */
                    651:        } else
                    652:            HTChunkPutc(chunk, ch);
                    653:     }
                    654:     if (ch < 0)
                    655:        status = ch;
                    656: 
                    657:     /* Put out the bottom line */
2.34      frystyk   658: #if 0
2.20      frystyk   659:     if (status != HT_INTERRUPTED) {
2.21      frystyk   660:        if (target) {
                    661:            char *outstr;
                    662:            if ((outstr = (char *) malloc(100)) == NULL)
                    663:                outofmem(__FILE__, "parse_menu");
                    664:            if (!records)
                    665:                sprintf(outstr, "No records");
                    666:            else if (records == 1)
                    667:                sprintf(outstr, "1 record");
                    668:            else
                    669:                sprintf(outstr, "%u records", records);
                    670:            START(HTML_PRE);
                    671:            START(HTML_HR);
                    672:            PUTS(outstr);
                    673:            END(HTML_PRE);
                    674:            free(outstr);
                    675:            FREE_TARGET;
                    676:        } else {
2.28      frystyk   677:            if (PROT_TRACE)
2.34      frystyk   678:                fprintf(TDEST, "HTGopher.... Interrupted before any stream was put up.\n");
2.21      frystyk   679:        }
2.20      frystyk   680:     }
2.34      frystyk   681: #endif
2.20      frystyk   682: 
2.34      frystyk   683:     if (target) {
                    684:        if (records)
                    685:            END(HTML_DL);
                    686:        else
                    687:            PUTS("Nothing matched you query");
                    688:        END(HTML_BODY);
                    689:        END(HTML_HTML);
                    690:        FREE_TARGET;
                    691:     }
2.20      frystyk   692:     /* Clean up */
2.26      frystyk   693:     HTInputSocket_free(gopher->isoc);
2.20      frystyk   694:     HTChunkFree(chunk);
                    695:     FREE(cur_code);
                    696:     return status;
                    697: }
2.7       secret    698: 
1.1       timbl     699: 
                    700: /*     Display a Gopher Index document
2.20      frystyk   701: **     -------------------------------
                    702: */
                    703: PRIVATE void display_index ARGS2(HTRequest *,          request,
                    704:                                 CONST char *,          url)
1.1       timbl     705: {
2.20      frystyk   706:     HTStructured *target = HTML_new(request, NULL, WWW_HTML,
                    707:                                    request->output_format,
                    708:                                    request->output_stream);
2.36      frystyk   709:     HTAnchor_setFormat(request->anchor, WWW_HTML);
2.34      frystyk   710:     START(HTML_HTML);
                    711:     START(HTML_HEAD);
                    712:     START(HTML_TITLE);
                    713:     PUTS("Searchable Gopher Index");
                    714:     END(HTML_TITLE);
                    715:     END(HTML_HEAD);
                    716:     START(HTML_BODY);
                    717: 
1.2       timbl     718:     START(HTML_H1);
2.20      frystyk   719:     PUTS("Searchable Gopher Index");
1.2       timbl     720:     END(HTML_H1);
2.7       secret    721:     START(HTML_ISINDEX);
2.20      frystyk   722:     if (!HTAnchor_title(request->anchor))
                    723:        HTAnchor_setTitle(request->anchor, url);    
2.34      frystyk   724:     END(HTML_BODY);
                    725:     END(HTML_HTML);
2.7       secret    726:     FREE_TARGET;
                    727:     return;
                    728: }
                    729: 
                    730: 
                    731: /*      Display a CSO index document
                    732: **      -------------------------------
                    733: */
2.20      frystyk   734: PRIVATE void display_cso ARGS2(HTRequest *,            request,
                    735:                               CONST char *,            url)
2.7       secret    736: {
2.20      frystyk   737:     HTStructured *target = HTML_new(request, NULL, WWW_HTML,
                    738:                                    request->output_format,
                    739:                                    request->output_stream);
2.36      frystyk   740:     HTAnchor_setFormat(request->anchor, WWW_HTML);
2.34      frystyk   741:     START(HTML_HTML);
                    742:     START(HTML_HEAD);
                    743:     START(HTML_TITLE);
                    744:     PUTS("Searchable Index of a CSO Name Server");
                    745:     END(HTML_TITLE);
                    746:     END(HTML_HEAD);
                    747:     START(HTML_BODY);
                    748: 
2.7       secret    749:     START(HTML_H1);
2.20      frystyk   750:     PUTS("Searchable Index of a CSO Name Server");
2.7       secret    751:     END(HTML_H1);
2.34      frystyk   752:     PUTS("A CSO Name Server usually provides directory information about people.");
2.7       secret    753:     START(HTML_ISINDEX);
2.20      frystyk   754:     if (!HTAnchor_title(request->anchor))
                    755:        HTAnchor_setTitle(request->anchor, url);
2.34      frystyk   756:     END(HTML_BODY);
                    757:     END(HTML_HTML);
1.2       timbl     758:     FREE_TARGET;
1.1       timbl     759:     return;
                    760: }
                    761: 
                    762: 
2.20      frystyk   763: 
                    764: /*                                                        HTGopher_send_cmd
1.1       timbl     765: **
2.20      frystyk   766: **      This function creates a socket and writes the gopher command to it.
                    767: **     The command must be terminated with <CRLF>
                    768: **
                    769: **      Returns 0 on OK, else <0 but does NOT close the connection
1.1       timbl     770: */
2.26      frystyk   771: PRIVATE int HTGopher_send_cmd ARGS3(gopher_info *,     gopher,
2.20      frystyk   772:                                    char *,             url,
2.26      frystyk   773:                                    char *,             command)
1.1       timbl     774: {
2.20      frystyk   775:     int status = 0;
2.26      frystyk   776:     if (!gopher || !command) {
2.28      frystyk   777:        if (PROT_TRACE)
2.34      frystyk   778:            fprintf(TDEST, "Gopher Tx... Bad argument!\n");
2.20      frystyk   779:        return -1;
                    780:     }
2.26      frystyk   781:     if ((status = HTDoConnect((HTNetInfo *) gopher, url, GOPHER_PORT,
2.28      frystyk   782:                              NULL, NO)) < 0) {
                    783:        if (PROT_TRACE)
2.34      frystyk   784:            fprintf(TDEST, "HTLoadGopher Connection not established!\n");
2.20      frystyk   785:        return status;
                    786:     }  
2.28      frystyk   787:     if (PROT_TRACE)
2.34      frystyk   788:        fprintf(TDEST, "Gopher...... Connected, socket %d\n", gopher->sockfd);
2.20      frystyk   789:     
                    790:     /* Write the command to the socket */
                    791: #ifdef NOT_ASCII
                    792:     {
                    793:        char * p;
                    794:        for(p = command; *p; p++) {
                    795:            *p = TOASCII(*p);
1.1       timbl     796:        }
                    797:     }
2.20      frystyk   798: #endif
2.28      frystyk   799:     if (PROT_TRACE)
2.34      frystyk   800:        fprintf(TDEST, "Gopher Tx... %s", command);
2.26      frystyk   801:     if ((status = NETWRITE(gopher->sockfd, command,
                    802:                          (int) strlen(command))) < 0) {
2.28      frystyk   803:        if (PROT_TRACE)
2.34      frystyk   804:            fprintf(TDEST, "Gopher...... Error sending command: %s\n",
2.28      frystyk   805:                    command);
2.34      frystyk   806:        HTErrorSysAdd(gopher->request, ERR_FATAL, socerrno, NO, "NETWRITE");
2.20      frystyk   807:     } else
                    808:        status = 0;
                    809:     return status;
1.1       timbl     810: }
                    811: 
                    812: 
                    813: /*             Load by name                                    HTLoadGopher
                    814: **             ============
                    815: **
2.24      frystyk   816: **     Given a hypertext address, this routine loads a gopher document
                    817: **
                    818: ** On entry,
                    819: **      request         This is the request structure
                    820: ** On exit,
                    821: **      returns         <0              Error has occured
                    822: **                      HT_LOADED       OK
1.1       timbl     823: **
                    824: */
2.13      timbl     825: PUBLIC int HTLoadGopher ARGS1(HTRequest *, request)
1.1       timbl     826: {
2.22      frystyk   827:     char *url;
2.20      frystyk   828:     int status = -1;
2.26      frystyk   829:     char *command = NULL;
2.20      frystyk   830:     gopher_info *gopher;
                    831:     
2.22      frystyk   832:     if (!request || !request->anchor) {
2.34      frystyk   833:        if (PROT_TRACE) fprintf(TDEST, "HTLoadGopher Bad argument\n");
2.20      frystyk   834:        return -1;
                    835:     }
2.22      frystyk   836:     url = HTAnchor_physical(request->anchor);
2.34      frystyk   837:     if (PROT_TRACE) fprintf(TDEST, "HTGopher.... Looking for `%s\'\n", url);
2.20      frystyk   838: 
2.26      frystyk   839:     /* Initiate a new gopher structure and bind to resuest structure */
2.20      frystyk   840:     if ((gopher = (gopher_info *) calloc(1, sizeof(gopher_info))) == NULL)
                    841:        outofmem(__FILE__, "HTLoadGopher");
2.34      frystyk   842:     gopher->sockfd = INVSOC;
2.26      frystyk   843:     gopher->request = request;
                    844:     request->net_info = (HTNetInfo *) gopher;
2.20      frystyk   845:     gopher->type = GOPHER_MENU;
1.1       timbl     846:     
2.20      frystyk   847:     /* Get entity type, and selector string and generate command  */
1.1       timbl     848:     {
2.20      frystyk   849:        char *path = HTParse(url, "", PARSE_PATH);
                    850:        char *selector = path;
                    851:        char *query = NULL;
                    852:        char *separator = NULL;
                    853:        if (*selector)
2.29      frystyk   854:            gopher->type = (HTGopherType) *selector++;      /* Pick up gtype */
2.20      frystyk   855:        if (gopher->type == GOPHER_INDEX) {
                    856:             HTAnchor_setIndex(request->anchor);                /* Search is allowed */
                    857:            query = strchr(selector, '?');         /* Look for search string */
                    858: 
                    859:            /* Display local "cover page" only if no search requested */
                    860:            if (!query || !*(query+1)) {               /* No search required */
                    861:                display_index(request, url);
                    862:                status = HT_LOADED;                   /* Local function only */
                    863:            } else {
                    864:                *query++ = 0;                                    /* Skip '?' */
                    865:                separator = "\t";
1.1       timbl     866:            }
2.20      frystyk   867:         } else if (gopher->type == GOPHER_CSO) {
                    868:             HTAnchor_setIndex(request->anchor);         /* Search is allowed */
                    869:             query = strchr(selector, '?');        /* Look for search string */
                    870: 
                    871:            /* Display local "cover page" only if no search requested */
                    872:             if (!query || !*(query+1)) {               /* No search required */
                    873:                 display_cso(request, url);
                    874:                 status = HT_LOADED;                   /* Local function only */
                    875:             } else {
                    876:                *query++ = 0;                                /* Skip '?'     */
2.34      frystyk   877:                *selector = 0;
2.20      frystyk   878:                separator = "query ";
1.1       timbl     879:            }
                    880:        }
                    881: 
2.20      frystyk   882:        /* Now generate the final command */
                    883:        if (status != HT_LOADED) {
2.24      frystyk   884:            char crlf[3];
2.26      frystyk   885:            StrAllocCopy(command, selector);
2.20      frystyk   886:            if (query) {
                    887:                char *p;
                    888:                for (p=query; *p; p++)           /* Remove plus signs 921006 */
                    889:                    if (*p == '+') *p = ' ';
2.26      frystyk   890:                StrAllocCat(command, separator);
                    891:                StrAllocCat(command, query);
2.20      frystyk   892:            }
2.26      frystyk   893:            HTUnEscape(command);
                    894:            HTCleanTelnetString(command);          /* Prevent security holes */
2.24      frystyk   895:            *crlf = CR;                                /* Telnet termination */
                    896:            *(crlf+1) = LF;
                    897:            *(crlf+2) = '\0';
2.26      frystyk   898:            StrAllocCat(command, crlf);
2.20      frystyk   899:        } 
                    900:        free(path);
1.1       timbl     901:     }
                    902:     
2.20      frystyk   903:     /* Now we must ask the server for real data :-( */
                    904:     if (status != HT_LOADED) {
2.26      frystyk   905:        if ((status = HTGopher_send_cmd(gopher, url, command)) == 0) {
2.20      frystyk   906:            
                    907:            /* Now read the data from the socket: */    
                    908:            switch (gopher->type) {
                    909:              case GOPHER_HTML:
2.36      frystyk   910:                HTAnchor_setFormat(request->anchor, WWW_HTML);
2.26      frystyk   911:                status = HTParseSocket(WWW_HTML,  gopher->sockfd, request);
2.20      frystyk   912:                break;
                    913:                
                    914:              case GOPHER_MENU:
                    915:              case GOPHER_INDEX:
                    916:                status = parse_menu(request, gopher, url);
                    917:                break;
                    918:                
                    919:              case GOPHER_CSO:
                    920:                status = parse_cso(request, gopher, url);
                    921:                break;
                    922:                
2.36      frystyk   923:              case GOPHER_GIF:
                    924:              case GOPHER_IMAGE:
                    925:              case GOPHER_PLUS_IMAGE:
2.20      frystyk   926:              case GOPHER_MACBINHEX:
                    927:              case GOPHER_PCBINHEX:
                    928:              case GOPHER_UUENCODED:
2.36      frystyk   929:              case GOPHER_BINARY:                   /* Do our own filetyping */
                    930:                HTBind_getBindings(request->anchor);
                    931:                status = HTParseSocket(HTAnchor_format(request->anchor),
                    932:                                       gopher->sockfd, request);
2.20      frystyk   933:                break;
                    934:                
                    935:              case GOPHER_SOUND:
                    936:              case GOPHER_PLUS_SOUND:
2.36      frystyk   937:                HTAnchor_setFormat(request->anchor, WWW_AUDIO);
2.26      frystyk   938:                status = HTParseSocket(WWW_AUDIO,  gopher->sockfd, request);
2.20      frystyk   939:                break;
                    940:                
                    941:              case GOPHER_PLUS_MOVIE:
2.36      frystyk   942:                HTAnchor_setFormat(request->anchor, WWW_VIDEO);
2.26      frystyk   943:                status = HTParseSocket(WWW_VIDEO,  gopher->sockfd, request);
2.20      frystyk   944:                break;
2.26      frystyk   945: 
                    946:                /* Try and look at the suffix - maybe it is a PostScript file
2.29      frystyk   947:                   so that we should start an external viewer. */
2.20      frystyk   948:              case GOPHER_TEXT:
2.26      frystyk   949:              default:
2.36      frystyk   950:                HTBind_getBindings(request->anchor);
                    951:                status = HTParseSocket(HTAnchor_format(request->anchor),
                    952:                                       gopher->sockfd, request);
2.20      frystyk   953:                break;
2.16      luotonen  954:            }
                    955:        }
1.2       timbl     956: 
2.20      frystyk   957:        /* Close the connection */
2.34      frystyk   958:        if (gopher->sockfd != INVSOC) {
                    959:            if (PROT_TRACE) fprintf(TDEST, "Gopher...... Closing socket %d\n",
2.28      frystyk   960:                                    gopher->sockfd);
2.26      frystyk   961:            if (NETCLOSE(gopher->sockfd) < 0)
2.34      frystyk   962:                status = HTErrorSysAdd(request, ERR_FATAL, socerrno, NO,
                    963:                                       "NETCLOSE");
2.25      frystyk   964:        }
2.20      frystyk   965:     }
                    966:     if (status == HT_INTERRUPTED) {
2.34      frystyk   967:         HTErrorAdd(request, ERR_WARN, NO, HTERR_INTERRUPTED, NULL, 0,
2.20      frystyk   968:                   "HTLoadGopher");
                    969:     }
2.26      frystyk   970:     FREE(command);
2.30      frystyk   971:     gopher->request->net_info = NULL;
2.20      frystyk   972:     free(gopher);
                    973: 
                    974:     if (status < 0 && status != HT_INTERRUPTED) {
2.21      frystyk   975:        char *unescaped = NULL;
                    976:        StrAllocCopy(unescaped, url);
                    977:        HTUnEscape(unescaped);
                    978:         HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
                    979:                   (int) strlen(unescaped), "HTLoadGopher");
2.20      frystyk   980:        HTAnchor_clearIndex(request->anchor);
2.21      frystyk   981:        free(unescaped);
2.20      frystyk   982:     }
                    983:     return status;
1.1       timbl     984: }
1.2       timbl     985: 
2.30      frystyk   986: GLOBALDEF PUBLIC HTProtocol HTGopher = {
                    987:     "gopher", SOC_BLOCK, HTLoadGopher, NULL, NULL
                    988: };
1.1       timbl     989: 

Webmaster