Annotation of libwww/Library/src/HTTP.c, revision 1.24

1.1       timbl       1: /*     HyperText Tranfer Protocol      - Client implementation         HTTP.c
                      2: **     ==========================
1.2       timbl       3: **
                      4: ** Bugs:
                      5: **     Not implemented:
                      6: **             Forward
                      7: **             Redirection
                      8: **             Error handling
1.1       timbl       9: */
                     10: 
                     11: /*     Module parameters:
                     12: **     -----------------
                     13: **
                     14: **  These may be undefined and redefined by syspec.h
                     15: */
1.2       timbl      16: 
                     17: /* Implements:
                     18: */
                     19: #include "HTTP.h"
                     20: 
                     21: #define HTTP_VERSION   "HTTP/1.0"
                     22: #define HTTP2                          /* Version is greater than 0.9 */
                     23: 
                     24: #define INIT_LINE_SIZE         1024    /* Start with line buffer this big */
                     25: #define LINE_EXTEND_THRESH     256     /* Minimum read size */
                     26: #define VERSION_LENGTH                 20      /* for returned protocol version */
                     27: 
                     28: /* Uses:
                     29: */
1.1       timbl      30: #include "HTParse.h"
                     31: #include "HTUtils.h"
                     32: #include "tcp.h"
                     33: #include "HTTCP.h"
                     34: #include "HTFormat.h"
1.2       timbl      35: #include <ctype.h>
                     36: #include "HTAlert.h"
                     37: #include "HTMIME.h"
1.5       timbl      38: #include "HTML.h"              /* SCW */
                     39: #include "HTInit.h"            /* SCW */
1.21      luotonen   40: #include "HTAccess.h"          /* HTRequest */
1.14      luotonen   41: #include "HTAABrow.h"          /* Access Authorization */
1.20      timbl      42: #include "HTTee.h"             /* Tee off a cache stream */
                     43: #include "HTFWriter.h"         /* Write to cache file */
1.1       timbl      44: 
1.2       timbl      45: struct _HTStream {
                     46:        HTStreamClass * isa;            /* all we need to know */
                     47: };
                     48: 
                     49: 
1.6       timbl      50: extern char * HTAppName;       /* Application name: please supply */
                     51: extern char * HTAppVersion;    /* Application version: please supply */
                     52: 
1.19      timbl      53: PUBLIC BOOL HTCacheHTTP = YES; /* Enable caching of HTTP-retrieved files */
1.24    ! luotonen   54: PUBLIC char * HTGatewayCacheRoot = NULL;
        !            55: PUBLIC unsigned long HTBytesCached = 0;
1.19      timbl      56: 
1.23      luotonen   57: PRIVATE char * gateway_cache_filename ARGS1(CONST char *, name)
                     58: {
                     59:     char * access;
                     60:     char * host;
                     61:     char * path;
                     62:     char * cache_filename;
                     63: 
1.24    ! luotonen   64:     if (HTGatewayCacheRoot && name && !strchr(name, '?') &&
1.23      luotonen   65:        (access = HTParse(name, "", PARSE_ACCESS))) {
                     66: 
                     67:        if (!strcmp(access, "file")) {
                     68:            free(access);
                     69:            return NULL;
                     70:        }
                     71: 
                     72:        host   = HTParse(name, "", PARSE_HOST);
                     73:        path   = HTParse(name, "", PARSE_PATH | PARSE_PUNCTUATION);
1.24    ! luotonen   74:        cache_filename = (char*)malloc(strlen(HTGatewayCacheRoot) +
        !            75:                                       strlen(access) +
1.23      luotonen   76:                                       (host ? strlen(host) : 0) +
1.24    ! luotonen   77:                                       (path ? strlen(path) : 0) + 20);
1.23      luotonen   78:        if (!cache_filename) outofmem(__FILE__, "cache_filename");
1.24    ! luotonen   79:        sprintf(cache_filename, "%s/%s/%s%s", 
        !            80:                HTGatewayCacheRoot, access, host, path);
        !            81: 
        !            82:        if (path[ strlen(path)-1 ] == '/')
        !            83:            strcat(cache_filename, "CERNhttpdINDEX");
1.23      luotonen   84: 
                     85:        FREE(access); FREE(host); FREE(path);
                     86:        return cache_filename;
                     87:     }
                     88:     return NULL;
                     89: }
                     90: 
                     91: 
                     92: PRIVATE FILE * gateway_cache_lookup ARGS1(CONST char *, name)
                     93: {
                     94:     char * cache_filename = gateway_cache_filename(name);
                     95: 
                     96:     if (cache_filename) {
1.24    ! luotonen   97:        struct stat stat_info;
        !            98: 
        !            99:        if (stat(cache_filename, &stat_info) != -1) {
        !           100:            if (S_ISDIR(stat_info.st_mode)) {
        !           101:                StrAllocCat(cache_filename, "/CERNhttpdINDEX");
        !           102:                if (stat(cache_filename, &stat_info) == -1) {
        !           103:                    free(cache_filename);
        !           104:                    return NULL;
        !           105:                }
        !           106:            }
        !           107:            if (S_ISREG(stat_info.st_mode)) {
        !           108:                FILE * cache_file = fopen(cache_filename, "r");
        !           109:                if (cache_file && TRACE)
        !           110:                    fprintf(stderr, "Cache: HIT \"%s\"\n", cache_filename);
        !           111:                free(cache_filename);
        !           112:                return cache_file;
        !           113:            }
        !           114:        }
1.23      luotonen  115:        free(cache_filename);
                    116:     }
                    117:     return NULL;
                    118: }
                    119: 
                    120: 
1.24    ! luotonen  121: PRIVATE FILE * gateway_cache_create ARGS1(char *, cache_filename)
1.23      luotonen  122: {
1.24    ! luotonen  123:     if (HTGatewayCacheRoot  &&  cache_filename) {
        !           124:        char * cur = cache_filename + strlen(HTGatewayCacheRoot) + 1;
1.23      luotonen  125:        FILE * cache_file;
                    126:        struct stat stat_info;
1.24    ! luotonen  127:        BOOL create = NO;
1.23      luotonen  128: 
                    129:        while ((cur = strchr(cur, '/'))) {
                    130:            *cur = 0;
1.24    ! luotonen  131:            if (create || -1 == stat(cache_filename, &stat_info)) {
        !           132:                create = YES;   /* To avoid doing stat()s in vain */
1.23      luotonen  133:                CTRACE(stderr, "Gateway cache: creating cache dir \"%s\"\n",
                    134:                       cache_filename);
                    135:                if (-1 == mkdir(cache_filename, 0775)) {
                    136:                    CTRACE(stderr, "Gateway cache: can't create dir \"%s\"\n",
                    137:                           cache_filename);
                    138:                    return NULL;
                    139:                }
                    140:                CTRACE(stderr, "Gateway cache: created directory \"%s\"\n",
                    141:                       cache_filename);
                    142:            }
1.24    ! luotonen  143:            else {
        !           144:                if (S_ISREG(stat_info.st_mode)) {
        !           145:                    char * tmp1 = (char*)malloc(strlen(cache_filename)+25);
        !           146:                    char * tmp2 = (char*)malloc(strlen(cache_filename)+25);
        !           147: 
        !           148:                    sprintf(tmp1, "%s.CERNhttpdINTERNAL", cache_filename);
        !           149:                    sprintf(tmp2, "%s/CERNhttpdINDEX", cache_filename);
        !           150: 
        !           151:                    CTRACE(stderr, "Moving \"%s\" to \"%s\"\n",
        !           152:                           cache_filename, tmp1);
        !           153:                    rename(cache_filename, tmp1);
        !           154: 
        !           155:                    CTRACE(stderr, "Creating dir \"%s\"\n", cache_filename);
        !           156:                    mkdir(cache_filename, 0775);
        !           157: 
        !           158:                    CTRACE(stderr, "Moving \"%s\" to \"%s\"\n", tmp1, tmp2);
        !           159:                    rename(tmp1, tmp2);
        !           160: 
        !           161:                    free(tmp1);
        !           162:                    free(tmp2);
        !           163:                }
        !           164:                else {
        !           165:                    CTRACE(stderr,
        !           166:                           "Gateway cache: dir \"%s\" already exists\n",
        !           167:                           cache_filename);
        !           168:                }
        !           169:            }
1.23      luotonen  170:            *cur = '/';
                    171:            cur++;
                    172:        }
                    173:        cache_file = fopen(cache_filename, "w");
                    174:        if (!cache_file) {
                    175:            CTRACE(stderr, "Gateway cache: can't create cache file \"%s\"\n",
                    176:                   cache_filename);
                    177:        }
                    178:        return cache_file;
                    179:     }
                    180:     return NULL;
                    181: }
                    182: 
                    183: 
                    184: 
                    185: 
1.21      luotonen  186: PRIVATE void parse_401_headers ARGS2(HTRequest *,      req,
                    187:                                     HTInputSocket *,   isoc)
                    188: {
                    189:     HTAAScheme scheme;
                    190:     char *line;
                    191:     int num_schemes = 0;
                    192:     HTList *valid_schemes = HTList_new();
                    193:     HTAssocList **scheme_specifics = NULL;
                    194:     char *template = NULL;
                    195: 
                    196:     /* Read server reply header lines */
                    197: 
                    198:     if (TRACE)
                    199:        fprintf(stderr, "Server 401 reply header lines:\n");
                    200: 
                    201:     while (NULL != (line = HTInputSocket_getUnfoldedLine(isoc)) &&
                    202:           *line != 0) {
                    203: 
                    204:        if (TRACE) fprintf(stderr, "%s\n", line);
                    205: 
                    206:        if (strchr(line, ':')) {        /* Valid header line */
                    207: 
                    208:            char *p = line;
                    209:            char *fieldname = HTNextField(&p);
                    210:            char *arg1 = HTNextField(&p);
                    211:            char *args = p;
                    212:            
                    213:            if (0==strcasecomp(fieldname, "WWW-Authenticate:")) {
                    214:                if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
                    215:                    HTList_addObject(valid_schemes, (void*)scheme);
                    216:                    if (!scheme_specifics) {
                    217:                        int i;
                    218:                        scheme_specifics = (HTAssocList**)
                    219:                            malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
                    220:                        if (!scheme_specifics)
                    221:                            outofmem(__FILE__, "parse_401_headers");
                    222:                        for (i=0; i < HTAA_MAX_SCHEMES; i++)
                    223:                            scheme_specifics[i] = NULL;
                    224:                    }
                    225:                    scheme_specifics[scheme] = HTAA_parseArgList(args);
                    226:                    num_schemes++;
                    227:                }
                    228:                else if (TRACE) {
                    229:                    fprintf(stderr, "Unknown scheme `%s' %s\n",
                    230:                            (arg1 ? arg1 : "(null)"),
                    231:                            "in WWW-Authenticate: field");
                    232:                }
                    233:            }
                    234: 
                    235:            else if (0==strcasecomp(fieldname, "WWW-Protection-Template:")) {
                    236:                if (TRACE)
                    237:                    fprintf(stderr, "Protection template set to `%s'\n", arg1);
                    238:                StrAllocCopy(template, arg1);
                    239:            }
                    240: 
                    241:        } /* if a valid header line */
                    242:        else if (TRACE) {
                    243:            fprintf(stderr, "Invalid header line `%s' ignored\n", line);
                    244:        } /* else invalid header line */
                    245:     } /* while header lines remain */
                    246: 
                    247:     req->valid_schemes = valid_schemes;
                    248:     req->scheme_specifics = scheme_specifics;
                    249:     req->prot_template = template;
                    250: }
                    251: 
                    252: 
                    253: 
1.1       timbl     254: /*             Load Document from HTTP Server                  HTLoadHTTP()
                    255: **             ==============================
                    256: **
                    257: **     Given a hypertext address, this routine loads a document.
                    258: **
                    259: **
                    260: ** On entry,
                    261: **     arg     is the hypertext reference of the article to be loaded.
                    262: **
                    263: ** On exit,
                    264: **     returns >=0     If no error, a good socket number
                    265: **             <0      Error.
                    266: **
                    267: **     The socket must be closed by the caller after the document has been
                    268: **     read.
                    269: **
                    270: */
1.19      timbl     271: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
1.1       timbl     272: {
1.22      luotonen  273:     CONST char * arg = NULL;
1.1       timbl     274:     int s;                             /* Socket number for returned data */
                    275:     int status;                                /* tcp return */
1.10      timbl     276:     char crlf[3];                      /* A CR LF equivalent string */
1.3       timbl     277:     HTStream * target = NULL;          /* Unconverted data */
                    278:     
1.2       timbl     279:     CONST char* gate = 0;              /* disable this feature */
1.1       timbl     280:     SockA soc_address;                 /* Binary network address */
                    281:     SockA * sin = &soc_address;
1.2       timbl     282:     BOOL extensions = YES;             /* Assume good HTTP server */
1.17      timbl     283: 
1.22      luotonen  284:     if (request->reason == HTAA_OK_GATEWAY) {
                    285:        arg = request->translated;
1.24    ! luotonen  286:        HTBytesCached = 0;
1.23      luotonen  287: 
                    288:        /*
                    289:        ** Cache lookup
                    290:        */
1.24    ! luotonen  291:        if (HTGatewayCacheRoot  &&  request->method == METHOD_GET &&
1.23      luotonen  292:            !request->authorization && !request->arg_keywords) {
                    293: 
                    294:            FILE * cache_file = gateway_cache_lookup(arg);
                    295: 
                    296:            if (cache_file) {
                    297:                HTFileCopy(cache_file, request->output_stream);
                    298:                return HT_LOADED;
                    299:            }
                    300: 
                    301:        } /* Cache lookup */
                    302: 
1.22      luotonen  303:     }
                    304:     else {
                    305:        arg = HTAnchor_physical(request->anchor);
                    306:        StrAllocCopy(request->argument, arg);
                    307:     }
                    308: 
1.1       timbl     309:     if (!arg) return -3;               /* Bad if no name sepcified     */
                    310:     if (!*arg) return -2;              /* Bad if name had zero length  */
                    311: 
                    312: /*  Set up defaults:
                    313: */
                    314: #ifdef DECNET
1.2       timbl     315:     sin->sdn_family = AF_DECnet;           /* Family = DECnet, host order */
                    316:     sin->sdn_objnum = DNP_OBJ;          /* Default: http object number */
1.1       timbl     317: #else  /* Internet */
1.2       timbl     318:     sin->sin_family = AF_INET;     /* Family = internet, host order */
                    319:     sin->sin_port = htons(TCP_PORT);    /* Default: http port    */
1.1       timbl     320: #endif
                    321: 
1.10      timbl     322:     sprintf(crlf, "%c%c", CR, LF);     /* To be corect on Mac, VM, etc */
                    323:     
1.1       timbl     324:     if (TRACE) {
                    325:         if (gate) fprintf(stderr,
                    326:                "HTTPAccess: Using gateway %s for %s\n", gate, arg);
                    327:         else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg);
                    328:     }
                    329:     
                    330: /* Get node name and optional port number:
                    331: */
                    332:     {
                    333:        char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
                    334:        int status = HTParseInet(sin, p1);  /* TBL 920622 */
                    335:         free(p1);
                    336:        if (status) return status;   /* No such host for example */
                    337:     }
                    338:     
1.15      luotonen  339: /*
                    340: ** Compose authorization information (this was moved here
                    341: ** from after the making of the connection so that the connection
                    342: ** wouldn't have to wait while prompting username and password
                    343: ** from the user).                             -- AL 13.10.93
                    344: */
                    345: #ifdef ACCESS_AUTH
1.21      luotonen  346:     HTAA_composeAuth(request);
                    347:     if (TRACE) {
                    348:        if (request->authorization)
                    349:            fprintf(stderr, "HTTP: Sending Authorization: %s\n",
                    350:                    request->authorization);
                    351:        else
                    352:            fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
1.15      luotonen  353:     }
                    354: #endif /* ACCESS_AUTH */
1.1       timbl     355:    
1.10      timbl     356: /*     Now, let's get a socket set up from the server for the data:
1.1       timbl     357: */      
                    358: #ifdef DECNET
                    359:     s = socket(AF_DECnet, SOCK_STREAM, 0);
                    360: #else
                    361:     s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                    362: #endif
                    363:     status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
                    364:     if (status < 0) {
                    365:            if (TRACE) fprintf(stderr, 
                    366:              "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno);
1.17      timbl     367: 
1.1       timbl     368:            return HTInetStatus("connect");
                    369:       }
                    370:     
                    371:     if (TRACE) fprintf(stderr, "HTTP connected, socket %d\n", s);
                    372: 
1.17      timbl     373: 
                    374: /*     Compose and send command
                    375: **     ------------------------
                    376: */
                    377:     {
                    378:         char *command;                 /* The whole command */
                    379:        
1.1       timbl     380: /*     Ask that node for the document,
                    381: **     omitting the host name & anchor if not gatewayed.
                    382: */        
1.17      timbl     383:        if (gate) {
                    384:            command = malloc(4 + strlen(arg)+ 2 + 31);
                    385:            if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
                    386:            strcpy(command, "GET ");
                    387:            strcat(command, arg);
                    388:        } else { /* not gatewayed */
                    389:            char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
                    390:            command = malloc(4 + strlen(p1)+ 2 + 31);
                    391:            if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
1.23      luotonen  392:            if (request->method != METHOD_INVALID) {
                    393:                strcpy(command, HTMethod_name(request->method));
1.22      luotonen  394:                strcat(command, " ");
                    395:            }
                    396:            else {
                    397:                strcpy(command, "GET ");
                    398:            }
1.17      timbl     399:            strcat(command, p1);
                    400:            free(p1);
                    401:        }
1.2       timbl     402: #ifdef HTTP2
1.17      timbl     403:        if (extensions) {
                    404:            strcat(command, " ");
                    405:            strcat(command, HTTP_VERSION);
                    406:        }
1.2       timbl     407: #endif
1.17      timbl     408:     
                    409:        strcat(command, crlf);  /* CR LF, as in rfc 977 */
                    410:     
                    411:        if (extensions) {
1.21      luotonen  412: 
1.17      timbl     413:            int i;
                    414:            HTAtom * present = WWW_PRESENT;
                    415:            char line[256];    /*@@@@ */
1.21      luotonen  416:            HTList *conversions[2];
                    417: 
1.22      luotonen  418:            if (!HTConversions) {
                    419:                HTConversions = HTList_new();
                    420:                HTFormatInit(HTConversions);
                    421:            }
1.21      luotonen  422:            conversions[0] = HTConversions;
                    423:            conversions[1] = request->conversions;
                    424: 
                    425:            for (i=0; i<2; i++) {
                    426:                HTList *cur = conversions[i];
                    427:                HTPresentation *pres;
                    428: 
                    429:                while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
                    430:                    if (pres->rep_out == present) {
                    431:                        if (pres->quality != 1.0) {
                    432:                            sprintf(line, "Accept: %s q=%.3f%c%c",
                    433:                                    HTAtom_name(pres->rep),
                    434:                                    pres->quality, CR, LF);
                    435:                        } else {
                    436:                            sprintf(line, "Accept: %s%c%c",
                    437:                                    HTAtom_name(pres->rep), CR, LF);
                    438:                        }
                    439:                        StrAllocCat(command, line);
1.17      timbl     440:                    }
                    441:                }
1.2       timbl     442:            }
1.22      luotonen  443: 
                    444:            sprintf(line, "User-Agent: %s%s %s/%s  libwww/%s%c%c",
                    445:                    request->user_agent ? request->user_agent : "",
                    446:                    request->user_agent ? "  VIA Gateway" : "",
1.17      timbl     447:                    HTAppName ? HTAppName : "unknown",
                    448:                    HTAppVersion ? HTAppVersion : "0.0",
                    449:                    HTLibraryVersion, CR, LF);
                    450:                    StrAllocCat(command, line);
                    451:     
1.22      luotonen  452:            if (request->from) {
                    453:                sprintf(line, "From: %s%c%c", request->from, CR, LF);
                    454:                StrAllocCat(command, line);
                    455:            }
                    456: 
1.14      luotonen  457: #ifdef ACCESS_AUTH
1.21      luotonen  458:            if (request->authorization != NULL) {
                    459:                sprintf(line, "Authorization: %s%c%c",
                    460:                        request->authorization, CR, LF);
1.17      timbl     461:                StrAllocCat(command, line);
                    462:            }
                    463: #endif /* ACCESS_AUTH */
1.22      luotonen  464: 
                    465:            if (request->content_type) {
                    466:                sprintf(line, "Content-Type: %s%c%c",
                    467:                        HTAtom_name(request->content_type), CR, LF);
                    468:                StrAllocCat(command, line);
                    469:            }
                    470: 
                    471:            if (request->content_length > 0) {
                    472:                sprintf(line, "Content-Length: %d%c%c",
                    473:                        request->content_length, CR, LF);
                    474:                StrAllocCat(command, line);
                    475:            }
                    476: 
                    477: 
1.14      luotonen  478:        }
1.17      timbl     479:     
                    480:        StrAllocCat(command, crlf);     /* Blank line means "end" */
                    481:     
                    482:        if (TRACE) fprintf(stderr, "HTTP Tx: %s\n", command);
                    483:     
                    484:     /* Translate into ASCII if necessary
                    485:     */
1.4       timbl     486: #ifdef NOT_ASCII
1.17      timbl     487:        {
                    488:            char * p;
                    489:            for(p = command; *p; p++) {
                    490:                *p = TOASCII(*p);
                    491:            }
1.1       timbl     492:        }
1.3       timbl     493: #endif
1.17      timbl     494:     
                    495:        status = NETWRITE(s, command, (int)strlen(command));
                    496:        free(command);
                    497:        if (status<0) {
                    498:            if (TRACE) fprintf(stderr,
                    499:                "HTTPAccess: Unable to send command.\n");
1.1       timbl     500:            return HTInetStatus("send");
1.17      timbl     501:        }
                    502:     } /* compose and send command */
                    503:     
1.2       timbl     504: 
1.17      timbl     505: /*     Read the response
                    506: **     -----------------
1.11      timbl     507: **
                    508: **     HTTP0 servers must return ASCII style text, though it can in
                    509: **     principle be just text without any markup at all.
                    510: **     Full HTTP servers must return a response
                    511: **     line and RFC822 style header.  The response must therefore in
                    512: **     either case have a CRLF somewhere soon.
                    513: **
                    514: **     This is the theory.  In practice, there are (1993) unfortunately
                    515: **     many binary documents just served up with HTTP0.9.  This
                    516: **     means we have to preserve the binary buffer (on the assumption that
                    517: **     conversion from ASCII may lose information) in case it turns
                    518: **     out that we want the binary original.
1.2       timbl     519: */
1.3       timbl     520: 
1.22      luotonen  521:     if (request->reason == HTAA_OK_GATEWAY) {
1.24    ! luotonen  522: 
        !           523:        char * cache_filename = NULL;
        !           524: 
1.22      luotonen  525:        /*
                    526:        ** Server as a gateway -- send body of the message
                    527:        ** received from client (if any).
                    528:        */
                    529:        if (request->isoc && request->content_length > 0) {
                    530:            int remain = request->content_length;
                    531:            int i = remain;
                    532:            char * buf;
                    533: 
                    534:            while (remain > 0  &&
                    535:                   (buf = HTInputSocket_getBlock(request->isoc, &i))) {
                    536:                int status = NETWRITE(s, buf, i);
                    537:                if (status < 0) {
                    538:                    CTRACE(stderr, "HTTPAccess: Unable to forward body\n");
                    539:                    return HTInetStatus("send");
                    540:                }
                    541:                remain -= i;
                    542:                i = remain;
                    543:            }
                    544:        }
1.23      luotonen  545: 
                    546:        /*
                    547:        ** Cache the document if it a GET request,
                    548:        ** not protected and not a search request.
                    549:        */
1.24    ! luotonen  550:        if (HTGatewayCacheRoot  &&  request->method == METHOD_GET &&
1.23      luotonen  551:            !request->authorization && !request->arg_keywords) {
                    552: 
1.24    ! luotonen  553:            cache_filename = gateway_cache_filename(request->translated);
        !           554:            if (cache_filename) {
        !           555:                FILE * cache_file = gateway_cache_create(cache_filename);
        !           556: 
        !           557:                if (cache_file) {
        !           558:                    request->output_stream = HTTee(request->output_stream,
        !           559:                                                   HTFWriter_new(cache_file));
        !           560:                    CTRACE(stderr, "Gateway cache: writing to cache file\n");
        !           561:                }
        !           562:                else {
        !           563:                    FREE(cache_filename);
        !           564:                    CTRACE(stderr,
        !           565:                           "Gateway cache: couldn't create cache file\n");
        !           566:                }
1.23      luotonen  567:            }
                    568:        }
                    569:        else CTRACE(stderr, "Gateway cache: not caching\n");
                    570: 
1.22      luotonen  571:        /*
                    572:        ** Load results directly to client
                    573:        */
                    574:        HTCopy(s, request->output_stream);
1.24    ! luotonen  575:        if (cache_filename) {
        !           576:            struct stat stat_info;
        !           577:            if (stat(cache_filename, &stat_info) != -1) {
        !           578:                HTBytesCached = stat_info.st_size;
        !           579:                CTRACE(stderr, "Cached %lu bytes to \"%s\"\n",
        !           580:                       HTBytesCached, cache_filename);
        !           581:            }
        !           582:            else CTRACE(stderr, "Couldn't stat cache file \"%s\"\n",
        !           583:                        cache_filename);
        !           584:        }
1.22      luotonen  585:        return HT_LOADED;
                    586:     }
                    587:     else {     /* read response */
1.21      luotonen  588: 
1.17      timbl     589:        HTFormat format_in;             /* Format arriving in the message */
1.21      luotonen  590:        HTInputSocket *isoc = HTInputSocket_new(s);
                    591:        char * status_line = HTInputSocket_getStatusLine(isoc);
1.2       timbl     592: 
1.11      timbl     593: /* Kludge to trap binary responses from illegal HTTP0.9 servers.
                    594: ** First time we have enough, look at the stub in ASCII
                    595: ** and get out of here if it doesn't look right.
                    596: **
                    597: ** We also check for characters above 128 in the first few bytes, and
                    598: ** if we find them we forget the html default.
                    599: **
                    600: ** Bugs: A HTTP0.9 server returning a document starting "HTTP/"
                    601: **     will be taken as a HTTP 1.0 server.  Failure.
                    602: **     An HTTP 0.9 server returning a binary document with
                    603: **     characters < 128 will be read as ASCII.
                    604: */
1.21      luotonen  605:        if (!status_line) {     /* HTTP0 response */
                    606:            if (HTInputSocket_seemsBinary(isoc)) {
                    607:                format_in = HTAtom_for("www/unknown");
                    608:            }
                    609:            else {
                    610:                format_in = WWW_HTML;
                    611:            }
                    612:            goto copy;
                    613:        } /* end kludge */
                    614: 
                    615:        if (status_line) {      /* Decode full HTTP response */
                    616:            /*
                    617:            ** We now have a terminated server status line, and we have
                    618:            ** checked that it is most probably a legal one.  Parse it.
                    619:            */
                    620:            char server_version[VERSION_LENGTH+1];
                    621:            int server_status;
                    622: 
                    623:            if (TRACE)
                    624:                fprintf(stderr, "HTTP Status Line: Rx: %.70s\n", status_line);
1.17      timbl     625:     
1.21      luotonen  626:            sscanf(status_line, "%20s%d", server_version, &server_status);
1.2       timbl     627: 
1.21      luotonen  628:            format_in = HTAtom_for("www/mime");
1.7       timbl     629:     
1.21      luotonen  630:            switch (server_status / 100) {
1.2       timbl     631: 
1.21      luotonen  632:              default:          /* bad number */
                    633:                HTAlert("Unknown status reply from server!");
                    634:                break;
1.17      timbl     635:                    
1.21      luotonen  636:              case 3:           /* Various forms of redirection */
                    637:                HTAlert(
1.17      timbl     638:            "Redirection response from server is not handled by this client");
1.21      luotonen  639:                break;
1.17      timbl     640:                    
1.21      luotonen  641:              case 4:           /* Access Authorization problem */
1.14      luotonen  642: #ifdef ACCESS_AUTH
1.21      luotonen  643:                switch (server_status) {
                    644:                  case 401:
                    645:                    parse_401_headers(request, isoc);
                    646: 
                    647:                    if (TRACE) fprintf(stderr, "%s %d %s\n",
                    648:                                       "HTTP: close socket", s,
                    649:                                       "to retry with Access Authorization");
                    650:                    HTInputSocket_free(isoc);
                    651:                    (void)NETCLOSE(s);
1.24    ! luotonen  652:                    if (HTAA_retryWithAuth(request, HTLoadHTTP)) {
1.21      luotonen  653:                        status = HT_LOADED;/* @@ THIS ONLY WORKS ON LINEMODE */
                    654:                        goto clean_up;
                    655:                    }
                    656:                    /* else falltrough */
                    657:                  default:
1.14      luotonen  658:                    {
1.21      luotonen  659:                        char *p1 = HTParse(gate ? gate : arg, "",
                    660:                                           PARSE_HOST);
                    661:                        char * message;
                    662: 
                    663:                        if (!(message = (char*)malloc(strlen(status_line) +
                    664:                                                      strlen(p1) + 100)))
                    665:                            outofmem(__FILE__, "HTTP 4xx status");
1.14      luotonen  666:                        sprintf(message,
1.21      luotonen  667:                                "HTTP server at %s replies:\n%s\n\n%s\n",
                    668:                                p1, status_line,
                    669:                                ((server_status == 401) 
                    670:                                 ? "Access Authorization package giving up.\n"
                    671:                                 : ""));
1.22      luotonen  672:                        status = HTLoadError(request, server_status, message);
1.14      luotonen  673:                        free(message);
                    674:                        free(p1);
                    675:                        goto clean_up;
                    676:                    }
1.21      luotonen  677:                } /* switch */
                    678:                goto clean_up;
                    679:                break;
                    680: #else
                    681:                /* case 4 without Access Authorization falls through */
                    682:                /* to case 5 (previously "I think I goofed").  -- AL */
                    683: #endif /* ACCESS_AUTH */
                    684: 
                    685:              case 5:           /* I think you goofed */
                    686:                {
                    687:                    char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
                    688:                    char * message = (char*)malloc(strlen(status_line) + 
                    689:                                                   strlen(p1) + 100);
                    690:                    if (!message) outofmem(__FILE__, "HTTP 5xx status");
                    691:                    sprintf(message,
                    692:                            "HTTP server at %s replies:\n%s", p1, status_line);
1.22      luotonen  693:                    status = HTLoadError(request, server_status, message);
1.21      luotonen  694:                    free(message);
                    695:                    free(p1);
                    696:                    goto clean_up;
                    697:                }
                    698:                break;
1.17      timbl     699:                    
1.21      luotonen  700:              case 2:           /* Good: Got MIME object */
                    701:                break;
1.17      timbl     702:     
1.21      luotonen  703:            } /* switch on response code */
1.17      timbl     704:            
1.21      luotonen  705:        } /* Full HTTP reply */
1.17      timbl     706:            
                    707:     
1.3       timbl     708: /*     Set up the stream stack to handle the body of the message
                    709: */
1.21      luotonen  710: 
1.13      duns      711: copy:
1.21      luotonen  712: 
1.18      timbl     713:        target = HTStreamStack(format_in, request);
1.21      luotonen  714: 
1.17      timbl     715:        if (!target) {
                    716:            char buffer[1024];  /* @@@@@@@@ */
                    717:            sprintf(buffer, "Sorry, no known way of converting %s to %s.",
                    718:                    HTAtom_name(format_in), HTAtom_name(request->output_format));
                    719:            fprintf(stderr, "HTTP: %s", buffer);
1.22      luotonen  720:            status = HTLoadError(request, 501, buffer);
1.17      timbl     721:            goto clean_up;
                    722:        }
                    723:     
1.19      timbl     724:         /* @@ Bug: The decision of whether or not to cache should also be
1.21      luotonen  725:        ** made contingent on a IP address match or non match.
                    726:        */
1.19      timbl     727:         if (HTCacheHTTP) {
                    728:            target = HTTee(target, HTCacheWriter(request, NULL, format_in,
1.21      luotonen  729:                                                 request->output_format,
                    730:                                                 request->output_stream));
1.19      timbl     731:        }
                    732:        
1.11      timbl     733: /*     Push the data down the stream
1.3       timbl     734: **     We have to remember the end of the first buffer we just read
1.2       timbl     735: */
1.17      timbl     736:        if (format_in == WWW_HTML) {
                    737:            target = HTNetToText(target);       /* Pipe through CR stripper */
                    738:        }
1.21      luotonen  739: 
1.17      timbl     740:        (*target->isa->put_block)(target,
1.21      luotonen  741:                                  isoc->input_pointer,
                    742:                                  isoc->input_limit - isoc->input_pointer);
                    743:        HTInputSocket_free(isoc);
1.17      timbl     744:        HTCopy(s, target);
                    745:            
                    746:        (*target->isa->free)(target);
                    747:        status = HT_LOADED;
1.11      timbl     748:     
1.2       timbl     749: /*     Clean up
1.1       timbl     750: */
1.17      timbl     751:        
                    752: clean_up: 
                    753:        if (TRACE) fprintf(stderr, "HTTP: close socket %d.\n", s);
                    754:        (void) NETCLOSE(s);
                    755:     
                    756:        return status;                  /* Good return */
1.3       timbl     757:     
1.17      timbl     758:     } /* read response */
                    759: } /* load HTTP */
1.1       timbl     760: 
                    761: /*     Protocol descriptor
                    762: */
                    763: 
1.17      timbl     764: GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0, 0 };
1.21      luotonen  765: 

Webmaster