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

1.44      frystyk     1: /*     HyperText73 Tranfer Protocol    - Client implementation         HTTP.c
1.1       timbl       2: **     ==========================
1.2       timbl       3: **
1.55      frystyk     4: **     This module implments the HTTP protocol
                      5: **
                      6: ** History:
                      7: **    < May 24 94 ??   Unknown - but obviosly written
1.56      frystyk     8: **     May 24 94 HF    Made reentrent and cleaned up a bit. Implemented
                      9: **                     Forward, redirection, error handling and referer field
1.55      frystyk    10: **
1.1       timbl      11: */
                     12: 
1.2       timbl      13: #define HTTP_VERSION   "HTTP/1.0"
1.55      frystyk    14: #define HTTP2                                /* Version is greater than 0.9 */
                     15: #define VERSION_LENGTH                 20    /* Number of chars in protocol version */
1.2       timbl      16: 
1.55      frystyk    17: /* Uses: */
1.1       timbl      18: #include "HTParse.h"
                     19: #include "HTUtils.h"
                     20: #include "tcp.h"
                     21: #include "HTTCP.h"
                     22: #include "HTFormat.h"
1.2       timbl      23: #include "HTAlert.h"
                     24: #include "HTMIME.h"
1.5       timbl      25: #include "HTML.h"              /* SCW */
                     26: #include "HTInit.h"            /* SCW */
1.21      luotonen   27: #include "HTAccess.h"          /* HTRequest */
1.14      luotonen   28: #include "HTAABrow.h"          /* Access Authorization */
1.20      timbl      29: #include "HTTee.h"             /* Tee off a cache stream */
                     30: #include "HTFWriter.h"         /* Write to cache file */
1.54      luotonen   31: #include "HTError.h"
1.55      frystyk    32: #include "HTChunk.h"
                     33: #include "HTTP.h"                                             /* Implements */
                     34: 
                     35: /* Macros and other defines */
                     36: #define PUTBLOCK(b, l) (*target->isa->put_block)(target, b, l)
                     37: #define PUTS(s)                (*target->isa->put_string)(target, s)
                     38: #define FREE_TARGET    (*target->isa->free)(target)
1.1       timbl      39: 
1.2       timbl      40: struct _HTStream {
                     41:        HTStreamClass * isa;            /* all we need to know */
                     42: };
                     43: 
1.55      frystyk    44: /* Globals */
1.6       timbl      45: extern char * HTAppName;       /* Application name: please supply */
                     46: extern char * HTAppVersion;    /* Application version: please supply */
                     47: 
1.50      luotonen   48: #ifdef OLD_CODE
1.37      luotonen   49: PUBLIC long HTProxyBytes = 0;  /* Number of bytes transferred thru proxy */
1.50      luotonen   50: #endif
                     51: 
1.37      luotonen   52: extern BOOL using_proxy;       /* are we using a proxy gateway? */
                     53: PUBLIC char * HTProxyHeaders = NULL;   /* Headers to pass as-is */
1.23      luotonen   54: 
1.55      frystyk    55: /* ------------------------------------------------------------------------- */
                     56: /* TEMPORARY STUFF  - MOVE TO HTML file */
                     57: 
                     58: typedef struct _http_info {
                     59:     int                         socket;   /* Socket number for communication */
                     60:     HTInputSocket *            isoc;
                     61: } http_info;
                     62: 
                     63: /* ------------------------------------------------------------------------- */
                     64: 
1.21      luotonen   65: PRIVATE void parse_401_headers ARGS2(HTRequest *,      req,
                     66:                                     HTInputSocket *,   isoc)
                     67: {
                     68:     HTAAScheme scheme;
                     69:     char *line;
                     70:     int num_schemes = 0;
                     71:     HTList *valid_schemes = HTList_new();
                     72:     HTAssocList **scheme_specifics = NULL;
                     73:     char *template = NULL;
                     74: 
                     75:     /* Read server reply header lines */
                     76: 
                     77:     if (TRACE)
                     78:        fprintf(stderr, "Server 401 reply header lines:\n");
                     79: 
                     80:     while (NULL != (line = HTInputSocket_getUnfoldedLine(isoc)) &&
                     81:           *line != 0) {
                     82: 
                     83:        if (TRACE) fprintf(stderr, "%s\n", line);
                     84: 
                     85:        if (strchr(line, ':')) {        /* Valid header line */
                     86: 
                     87:            char *p = line;
                     88:            char *fieldname = HTNextField(&p);
                     89:            char *arg1 = HTNextField(&p);
                     90:            char *args = p;
                     91:            
                     92:            if (0==strcasecomp(fieldname, "WWW-Authenticate:")) {
                     93:                if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
                     94:                    HTList_addObject(valid_schemes, (void*)scheme);
                     95:                    if (!scheme_specifics) {
                     96:                        int i;
                     97:                        scheme_specifics = (HTAssocList**)
                     98:                            malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
                     99:                        if (!scheme_specifics)
                    100:                            outofmem(__FILE__, "parse_401_headers");
                    101:                        for (i=0; i < HTAA_MAX_SCHEMES; i++)
                    102:                            scheme_specifics[i] = NULL;
                    103:                    }
                    104:                    scheme_specifics[scheme] = HTAA_parseArgList(args);
                    105:                    num_schemes++;
                    106:                }
                    107:                else if (TRACE) {
                    108:                    fprintf(stderr, "Unknown scheme `%s' %s\n",
                    109:                            (arg1 ? arg1 : "(null)"),
                    110:                            "in WWW-Authenticate: field");
                    111:                }
                    112:            }
                    113: 
                    114:            else if (0==strcasecomp(fieldname, "WWW-Protection-Template:")) {
                    115:                if (TRACE)
                    116:                    fprintf(stderr, "Protection template set to `%s'\n", arg1);
                    117:                StrAllocCopy(template, arg1);
                    118:            }
                    119: 
                    120:        } /* if a valid header line */
                    121:        else if (TRACE) {
                    122:            fprintf(stderr, "Invalid header line `%s' ignored\n", line);
                    123:        } /* else invalid header line */
1.44      frystyk   124:        free(line);
1.21      luotonen  125:     } /* while header lines remain */
1.44      frystyk   126:     FREE(line);
1.21      luotonen  127:     req->valid_schemes = valid_schemes;
                    128:     req->scheme_specifics = scheme_specifics;
                    129:     req->prot_template = template;
                    130: }
                    131: 
                    132: 
1.55      frystyk   133: /*                                                              HTTPCleanup
1.1       timbl     134: **
1.55      frystyk   135: **      This function closes the connection and frees memory.
1.1       timbl     136: **
1.55      frystyk   137: **      Returns 0 on OK, else -1
1.1       timbl     138: */
1.55      frystyk   139: PRIVATE int HTTPCleanup ARGS2(HTRequest *, request, http_info *, http)
1.1       timbl     140: {
1.55      frystyk   141:     int status = 0;
                    142:     if (!request) {
                    143:        if (TRACE) fprintf(stderr, "HTTPCleanup. Bad argument!\n");
                    144:        status = -1;
                    145:     } else {
                    146:        if (http->socket >= 0) {
                    147:            if (TRACE) fprintf(stderr, "HTTP........ Closing socket %d\n",
                    148:                               http->socket);
                    149:            if ((status = NETCLOSE(http->socket)) < 0)
                    150:                HTErrorSysAdd(request, ERR_FATAL, NO, "NETCLOSE");          
                    151:        }       
                    152:     }  
                    153:     free(http);
                    154:     return status;
                    155: }
1.36      frystyk   156: 
1.23      luotonen  157: 
1.55      frystyk   158: /*                                                              HTTPSendRequest
                    159: **
                    160: **      This function composes and sends a request to the connected server
                    161: **      specified.
                    162: **
                    163: **      Returns 0 on OK, else -1 but does NOT close the connection
1.1       timbl     164: */
1.55      frystyk   165: PRIVATE int HTTPSendRequest ARGS3(HTRequest *, request,
                    166:                                  http_info *, http, char *, url)
                    167: {
                    168:     int status = 0;
                    169:     BOOL extensions = YES;                       /* Assume good HTTP server */
                    170:     HTChunk *command = HTChunkCreate(2048);            /* The whole command */
                    171:     if (request->method != METHOD_INVALID) {
                    172:        HTChunkPuts(command, HTMethod_name(request->method));
                    173:        HTChunkPutc(command, ' ');
                    174:     }
                    175:     else
                    176:        HTChunkPuts(command, "GET ");
1.1       timbl     177: 
1.55      frystyk   178:     /* if we are using a proxy gateway don't copy in the first slash
                    179:      ** of say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj
                    180:      ** so that just gohper://.... is sent. */
1.1       timbl     181:     {
1.55      frystyk   182:        char *p1 = HTParse(url, "", PARSE_PATH|PARSE_PUNCTUATION);
                    183:        if (using_proxy)
                    184:            HTChunkPuts(command, p1+1);
1.21      luotonen  185:        else
1.55      frystyk   186:            HTChunkPuts(command, p1);
                    187:        free(p1);
1.15      luotonen  188:     }
1.1       timbl     189: 
1.2       timbl     190: #ifdef HTTP2
1.55      frystyk   191:     if (extensions) {
                    192:        HTChunkPutc(command, ' ');
                    193:        HTChunkPuts(command, HTTP_VERSION);
                    194:     }
1.2       timbl     195: #endif
1.55      frystyk   196:     HTChunkPutc(command, CR);                       /* CR LF, as in rfc 977 */
                    197:     HTChunkPutc(command, LF);
1.17      timbl     198:     
1.55      frystyk   199:     if (extensions && HTImProxy && HTProxyHeaders) {
                    200:        HTChunkPuts(command, HTProxyHeaders);
                    201:     } else if (extensions) {
1.56      frystyk   202:        char line[256];    /*@@@@ */
1.55      frystyk   203: 
                    204:        /* If no conversion list, then put it up, but leave initialization
                    205:           to the client */
                    206:        if (!HTConversions)
                    207:            HTConversions = HTList_new();
1.21      luotonen  208: 
1.55      frystyk   209:        /* Run through both lists and generate `accept' lines */
                    210:        {
1.17      timbl     211:            int i;
1.21      luotonen  212:            HTList *conversions[2];
                    213:            conversions[0] = HTConversions;
                    214:            conversions[1] = request->conversions;
1.34      frystyk   215:            
1.21      luotonen  216:            for (i=0; i<2; i++) {
                    217:                HTList *cur = conversions[i];
                    218:                HTPresentation *pres;
1.55      frystyk   219:                while ((pres = (HTPresentation *) HTList_nextObject(cur))) {
                    220:                    if (pres->rep_out == WWW_PRESENT) {
1.21      luotonen  221:                        if (pres->quality != 1.0) {
1.35      frystyk   222:                            sprintf(line, "Accept: %s; q=%.3f%c%c",
1.21      luotonen  223:                                    HTAtom_name(pres->rep),
                    224:                                    pres->quality, CR, LF);
                    225:                        } else {
                    226:                            sprintf(line, "Accept: %s%c%c",
                    227:                                    HTAtom_name(pres->rep), CR, LF);
                    228:                        }
1.55      frystyk   229:                        HTChunkPuts(command, line);
1.17      timbl     230:                    }
                    231:                }
1.2       timbl     232:            }
1.55      frystyk   233:        }
1.22      luotonen  234: 
1.56      frystyk   235:        /* Put out referer field if any parent */
1.58    ! frystyk   236:        if (request->parentAnchor) {
1.56      frystyk   237:            char *this = HTAnchor_address((HTAnchor *) request->anchor);
1.58    ! frystyk   238:            char *parent = HTAnchor_address((HTAnchor *)request->parentAnchor);
1.56      frystyk   239:            char *relative = HTParse(parent, this,
                    240:                 PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    241:            if (relative && *relative) {
                    242:                sprintf(line, "Referer: %s%c%c", parent, CR, LF);
                    243:                HTChunkPuts(command, line);
                    244:            }
                    245:            free(this);
                    246:            free(parent);
                    247:            free(relative);
                    248:        }
1.58    ! frystyk   249: 
1.55      frystyk   250:        /* Put out user-agent */
1.56      frystyk   251:        sprintf(line, "User-Agent: %s/%s  libwww/%s%c%c",
                    252:                HTAppName ? HTAppName : "unknown",
                    253:                HTAppVersion ? HTAppVersion : "0.0",
                    254:                HTLibraryVersion, CR, LF);
                    255:        HTChunkPuts(command, line);
1.45      luotonen  256: 
1.55      frystyk   257:        /* Put out authorization */
                    258:        if (request->authorization != NULL) {
                    259:            HTChunkPuts(command, "Authorization: ");
                    260:            HTChunkPuts(command, request->authorization);
                    261:            HTChunkPutc(command, CR);
                    262:            HTChunkPutc(command, LF);
1.37      luotonen  263:        }
1.55      frystyk   264:     }
                    265:     HTChunkPutc(command, CR);                     /* Blank line means "end" */
                    266:     HTChunkPutc(command, LF);
                    267:     HTChunkTerminate(command);
                    268:     if (TRACE) fprintf(stderr, "HTTP Tx..... %s", command->data);
1.17      timbl     269:     
1.55      frystyk   270:     /* Translate into ASCII if necessary */
1.4       timbl     271: #ifdef NOT_ASCII
1.55      frystyk   272:     {
                    273:        char * p;
                    274:        for(p = command; *p; p++) {
                    275:            *p = TOASCII(*p);
1.1       timbl     276:        }
1.55      frystyk   277:     }
1.3       timbl     278: #endif
1.17      timbl     279:     
1.55      frystyk   280:     /* Now, we are ready for sending the request */
                    281:     if ((status = NETWRITE(http->socket, command->data, command->size)) < 0) {
                    282:        if (TRACE) fprintf(stderr, "HTTP Tx..... Error sending command\n");
                    283:        HTErrorSysAdd(request, ERR_FATAL, NO, "NETWRITE");
                    284:        if (status != HT_INTERRUPTED) {
                    285:            char *unescaped = NULL;
                    286:            StrAllocCopy(unescaped, url);
                    287:            HTUnEscape(unescaped);
                    288:            HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
                    289:                       (void *) unescaped, (int) strlen(unescaped),
                    290:                       "HTTPSendRequest");
                    291:            free(unescaped);
                    292:        }
                    293:     }
                    294:     HTChunkFree(command);
                    295:     return status;
                    296: }
                    297: 
                    298: 
                    299: /*                                                              HTTPGetBody
                    300: **
                    301: **      Put up a streamstack and read the body from the socket.
                    302: **     In the special case of user asking for source and the message
                    303: **     being in MIME, we force the MIME decoding to occur, as it is really
                    304: **     HTTP decoding.  If the user really wants the HTTP headers, he
                    305: **     can ask for them as www/mime.
                    306: **
                    307: **     Returns < 0 on error, else HT_LOADED
                    308: */
                    309: PRIVATE int HTTPGetBody ARGS5(HTRequest *, request, http_info *, http,
                    310:                              HTInputSocket *, isoc, HTFormat, format_in,
                    311:                              BOOL, use_cache)
                    312: {
                    313:     int status = -1;
                    314:     HTStream  *target = NULL;                           /* Unconverted data */
                    315:     if (format_in == WWW_MIME && request->output_format == WWW_SOURCE) {
                    316:        target = HTMIMEConvert(request, NULL, format_in,
                    317:                               request->output_format,
                    318:                               request->output_stream);
                    319:     } else
                    320:        target = HTStreamStack(format_in, request, NO);
                    321:     if (target) {
                    322:        
                    323:        /* @@ Bug: The decision of whether or not to cache should also
                    324:           be made contingent on a IP address match or non match. */
                    325:        
                    326:        if (HTCacheDir && use_cache) {
                    327:            target = HTTee(target,
                    328:                           HTCacheWriter(request, NULL, format_in,
                    329:                                         request->output_format,
                    330:                                         request->output_stream));
1.17      timbl     331:        }
1.55      frystyk   332:        
                    333:        /* Push the data down the stream remembering the end of the
                    334:           first buffer we just read */
                    335:        if (format_in == WWW_HTML)
                    336:            target = HTNetToText(target);/* Pipe through CR stripper */
                    337:        
                    338:        PUTBLOCK(isoc->input_pointer,
                    339:                 isoc->input_limit-isoc->input_pointer);
                    340:        HTCopy(http->socket, target);                /* USE RETURN AS STATUS */
                    341:        FREE_TARGET;
                    342:        status = HT_LOADED;
                    343:     }
                    344:     return status;
                    345: }
                    346: 
                    347: 
1.56      frystyk   348: /*                                                              HTTPRedirect
                    349: **
                    350: **      Reads the response from a 3xx server status code. Only the first line
                    351: **     is read. The format expected is
                    352: **
                    353: **     Location: <url> String CrLf
                    354: **
                    355: **     The comment string is ignored!
                    356: **
                    357: **     NOTE: THIS IS NOT IN CORRESPONDANCE WITH THE SPECS!!!
                    358: **
                    359: **     Returns new anchor on success else NULL
                    360: */
                    361: PRIVATE HTAnchor *HTTPRedirect ARGS3(HTRequest *, request,
                    362:                                     HTInputSocket *, isoc, int, code)
                    363: {
                    364:     BOOL found = NO;
                    365:     HTAnchor *anchor = NULL;                                  /* New anchor */
                    366:     char *line;
                    367:     if (TRACE)
                    368:        fprintf(stderr, "Redirection. Looking for URL's\n");
                    369:     while ((line = HTInputSocket_getUnfoldedLine(isoc)) != NULL) {
                    370:        char *strptr = line;
                    371:        if (*strptr) {
                    372:            while (*strptr && *strptr == ' ')         /* Skip leading spaces */
                    373:                strptr++;
                    374:            if (!strncasecomp(strptr, "location:", 9)) {
                    375:                char *url = strchr(strptr, ' ');
                    376:                char *comment;
                    377:                while (*url && *url == ' ')           /* Skip leading spaces */
                    378:                    url++;
                    379:                if ((comment = strchr(url, ' ')) != NULL)
                    380:                    *comment = '\0';                    /* Skip any comments */
                    381:                if (code == 301)
1.57      frystyk   382:                    HTErrorAdd(request, ERR_INFO, NO, HTERR_MOVED,
1.56      frystyk   383:                               (void *) url, (int) strlen(url),
                    384:                               "HTTPRedirect");
                    385:                else if (code == 302)
1.57      frystyk   386:                    HTErrorAdd(request, ERR_INFO, NO, HTERR_FOUND,
1.56      frystyk   387:                               (void *) url, (int) strlen(url),
                    388:                               "HTTPRedirect");
                    389:                else {
                    390:                    if (TRACE)
                    391:                        fprintf(stderr,
                    392:                                "Redirection. Weird, should never happen\n");
                    393:                }
                    394: 
                    395:                /* Now use the new anchor instead of the old one */
                    396:                anchor = HTAnchor_findAddress(url);
                    397:                found = YES;
                    398:                FREE(line);
                    399:                break;
                    400:            }
                    401:        }
                    402:        free(line);
                    403:     }
                    404: 
                    405:     if (!found) {
                    406:        int length = (int) strlen(line);
                    407:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
                    408:                   (void *) line, length < 50 ? length : 50, "HTTPRedirect");
                    409:     }
                    410:     return anchor;
                    411: }
                    412: 
                    413: 
1.55      frystyk   414: /*             Load Document from HTTP Server                  HTLoadHTTP()
                    415: **             ==============================
                    416: **
                    417: **     Given a hypertext address, this routine loads a document.
                    418: **
                    419: ** On entry,
                    420: **      request                This is the request structure
                    421: ** On exit,
                    422: **     returns         <0              Error has occured
1.58    ! frystyk   423: **                     HT_LOADED       if return status 200 OK
        !           424: **                     HT_NO_DATA      if return status 204 No Response
1.55      frystyk   425: */
                    426: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
                    427: {
                    428:     char *url;
                    429:     int status = -1;                                          /* tcp return */
                    430:     http_info *http;                       /* Specific protocol information */
                    431: 
                    432:     if (!request || !request->anchor) {
                    433:         if (TRACE) fprintf(stderr, "HTLoadHTTP.. Bad argument\n");
                    434:         return -1;
                    435:     }
                    436:     url = HTAnchor_physical(request->anchor);
                    437:     HTSimplify(url);
                    438:     if (TRACE) fprintf(stderr, "HTTP........ Looking for `%s\'\n", url);
                    439: 
                    440:     /* Initiate a new http structure */
                    441:     if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL)
                    442:         outofmem(__FILE__, "HTLoadHTTP");
                    443:     http->socket = -1;
1.17      timbl     444:     
1.55      frystyk   445: /*
                    446: ** Compose authorization information (this was moved here
                    447: ** from after the making of the connection so that the connection
                    448: ** wouldn't have to wait while prompting username and password
                    449: ** from the user).                             -- AL 13.10.93
                    450: */
                    451:     HTAA_composeAuth(request);
                    452:     if (TRACE) {
                    453:        if (request->authorization)
                    454:            fprintf(stderr, "HTTP........ Sending Authorization: %s\n",
                    455:                    request->authorization);
                    456:        else
                    457:            fprintf(stderr, "HTTP........ Not sending authorization (yet)\n");
                    458:     }
                    459: 
                    460:     /* Now let's set up a connection */
                    461:     if ((status = HTDoConnect(request, url, TCP_PORT,
                    462:                              &http->socket, NULL)) < 0) {
                    463:         if (TRACE)
                    464:             fprintf(stderr, "HTTP........ Connection not established!\n");
                    465:        if (status != HT_INTERRUPTED) {
                    466:            char *unescaped = NULL;
                    467:            StrAllocCopy(unescaped, url);
                    468:            HTUnEscape(unescaped);
                    469:            HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
                    470:                       (void *) unescaped, (int) strlen(unescaped),
                    471:                       "HTLoadHTTP");
                    472:            free(unescaped);
                    473:        }
                    474:        HTTPCleanup(request, http);
                    475:        return status;
                    476:     }
                    477:     if (TRACE) fprintf(stderr, "HTTP........ Connected, socket %d\n",
                    478:                       http->socket);
                    479: 
                    480:     /* Compose the request and send it over the net */
                    481:     if ((status = HTTPSendRequest(request, http, url)) < 0) {
                    482:        HTTPCleanup(request, http);
                    483:        return status;
                    484:     }    
1.2       timbl     485: 
1.17      timbl     486: /*     Read the response
                    487: **     -----------------
1.11      timbl     488: **
                    489: **     HTTP0 servers must return ASCII style text, though it can in
                    490: **     principle be just text without any markup at all.
                    491: **     Full HTTP servers must return a response
                    492: **     line and RFC822 style header.  The response must therefore in
                    493: **     either case have a CRLF somewhere soon.
                    494: **
                    495: **     This is the theory.  In practice, there are (1993) unfortunately
                    496: **     many binary documents just served up with HTTP0.9.  This
                    497: **     means we have to preserve the binary buffer (on the assumption that
                    498: **     conversion from ASCII may lose information) in case it turns
                    499: **     out that we want the binary original.
1.2       timbl     500: */
1.53      luotonen  501: 
                    502:     CTRACE(stderr, "Waiting..... for response\n");
                    503: 
1.37      luotonen  504:     if (HTImProxy) {
1.24      luotonen  505: 
1.22      luotonen  506:        /*
                    507:        ** Server as a gateway -- send body of the message
                    508:        ** received from client (if any).
                    509:        */
                    510:        if (request->isoc && request->content_length > 0) {
                    511:            int remain = request->content_length;
                    512:            int i = remain;
                    513:            char * buf;
                    514: 
                    515:            while (remain > 0  &&
                    516:                   (buf = HTInputSocket_getBlock(request->isoc, &i))) {
1.55      frystyk   517:                int status = NETWRITE(http->socket, buf, i);
1.22      luotonen  518:                if (status < 0) {
1.27      luotonen  519:                    CTRACE(stderr, "HTTPAccess.. Unable to forward body\n");
1.55      frystyk   520:                    HTErrorSysAdd(request, ERR_FATAL, NO, "NETWRITE");
                    521:                    HTTPCleanup(request, http);
                    522:                    return status;
1.22      luotonen  523:                }
                    524:                remain -= i;
                    525:                i = remain;
                    526:            }
                    527:        }
1.23      luotonen  528: 
                    529:        /*
1.22      luotonen  530:        ** Load results directly to client
                    531:        */
1.55      frystyk   532:        HTCopy(http->socket, request->output_stream);
1.25      luotonen  533:        (*request->output_stream->isa->free)(request->output_stream);
1.55      frystyk   534:        HTTPCleanup(request, http);
1.22      luotonen  535:        return HT_LOADED;
1.55      frystyk   536:     } else {                                               /* read response */
1.21      luotonen  537: 
1.17      timbl     538:        HTFormat format_in;             /* Format arriving in the message */
1.55      frystyk   539:        HTInputSocket *isoc = HTInputSocket_new(http->socket);
                    540:        char *status_line = HTInputSocket_getStatusLine(isoc);
1.2       timbl     541: 
1.11      timbl     542: /* Kludge to trap binary responses from illegal HTTP0.9 servers.
                    543: ** First time we have enough, look at the stub in ASCII
                    544: ** and get out of here if it doesn't look right.
                    545: **
                    546: ** We also check for characters above 128 in the first few bytes, and
                    547: ** if we find them we forget the html default.
                    548: **
                    549: ** Bugs: A HTTP0.9 server returning a document starting "HTTP/"
                    550: **     will be taken as a HTTP 1.0 server.  Failure.
                    551: **     An HTTP 0.9 server returning a binary document with
                    552: **     characters < 128 will be read as ASCII.
                    553: */
1.36      frystyk   554:        /* If HTTP 0 response, then DO NOT CACHE (Henrik 14/02-94) */
                    555:        if (!status_line) {     
1.21      luotonen  556:            if (HTInputSocket_seemsBinary(isoc)) {
                    557:                format_in = HTAtom_for("www/unknown");
1.55      frystyk   558:            } else {
1.21      luotonen  559:                format_in = WWW_HTML;
                    560:            }
1.55      frystyk   561:            status = HTTPGetBody(request, http, isoc, format_in, NO);
                    562:        } else {
1.21      luotonen  563:            /*
                    564:            ** We now have a terminated server status line, and we have
                    565:            ** checked that it is most probably a legal one.  Parse it.
                    566:            */
                    567:            char server_version[VERSION_LENGTH+1];
                    568:            int server_status;
                    569: 
                    570:            if (TRACE)
1.55      frystyk   571:                fprintf(stderr, "HTTP Rx..... `%.70s\'\n", status_line);
                    572:            {
                    573:                char formatstr[20];
                    574:                sprintf(formatstr, "%%%ds%%d", VERSION_LENGTH);
                    575:                if (sscanf(status_line, formatstr, server_version,
                    576:                           &server_status) < 2) {
                    577:                    int length = (int) strlen(status_line);
                    578:                    HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
                    579:                               (void *) status_line, length < 50 ? length : 50,
                    580:                               "HTLoadHTTP");
                    581:                    HTInputSocket_free(isoc);       
                    582:                    free(http);
                    583:                    free(status_line);
                    584:                    return -1;                               /* Bad response */
                    585:                }
                    586:                *(server_version+VERSION_LENGTH) = '\0';
                    587:            }
1.21      luotonen  588:            format_in = HTAtom_for("www/mime");
1.7       timbl     589:     
1.55      frystyk   590:            /* Big switch for all response codes */
                    591:            switch (server_status/100) {
1.2       timbl     592: 
1.55      frystyk   593:              case 2:           /* Good: Got MIME object */
1.58    ! frystyk   594:                switch (server_status) {
        !           595:                  case 204:                                   /* No response */
        !           596:                    HTErrorAdd(request, ERR_INFO, NO, HTERR_NO_RESPONSE,
        !           597:                               NULL, 0, "HTLoadHTTP");
        !           598:                    status = HT_NO_DATA;
        !           599:                    break;                             /* Don't get any body */
        !           600:                  case 203:                                       /* Partial */
        !           601:                    HTErrorAdd(request, ERR_INFO, NO, HTERR_PARTIAL,
        !           602:                               NULL, 0, "HTLoadHTTP");
        !           603:                    /* Drop through to 200 as we still have to get the body */
        !           604:                  case 200:
        !           605:                    status = HTTPGetBody(request, http, isoc, format_in, YES);
        !           606:                    break;
        !           607:                  default:
        !           608:                    {
        !           609:                        int length = (int) strlen(status_line);
        !           610:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
        !           611:                                   (void *) status_line, length < 50 ?
        !           612:                                   length : 50, "HTLoadHTTP");
        !           613:                    }
        !           614:                    status = -1;
        !           615:                    break;
        !           616:                }
1.21      luotonen  617:                break;
1.58    ! frystyk   618: 
1.21      luotonen  619:              case 3:           /* Various forms of redirection */
1.55      frystyk   620:                switch (server_status) {
                    621:                  case 301:                                         /* Moved */
                    622:                  case 302:                                         /* Found */
1.56      frystyk   623:                    {
                    624:                        HTParentAnchor *anchor;
                    625:                        if ((anchor = (HTParentAnchor *)
                    626:                             HTTPRedirect(request, isoc,
                    627:                                          server_status)) != NULL) {
                    628:                            free(status_line);
                    629:                            HTInputSocket_free(isoc);
                    630:                            HTTPCleanup(request, http);
1.58    ! frystyk   631: 
        !           632:                            /* Now do a recursive call but keep error stack */
        !           633:                            if (HTLoadAnchorRecursive((HTAnchor *) anchor,
        !           634:                                                      request) == YES)
1.56      frystyk   635:                                return HT_LOADED;
                    636:                            else
                    637:                                return -1;
                    638:                        }
                    639:                    }
                    640:                    break;
1.55      frystyk   641:                  case 303:                                        /* Method */
1.56      frystyk   642:                    HTAlert("This client doesn't support automatic redirection of type `Method'");
                    643:                    status = -1;
1.55      frystyk   644:                    break;
                    645:                  case 304:                            /* Not modified Since */
                    646:                    {
                    647:                        char *unescaped = NULL;
                    648:                        StrAllocCopy(unescaped, url);
                    649:                        HTUnEscape(unescaped);
1.57      frystyk   650:                        HTErrorAdd(request, ERR_INFO, NO,
1.55      frystyk   651:                                   HTERR_NOT_MODIFIED, (void *) unescaped,
                    652:                                   (int) strlen(unescaped), "HTLoadHTTP");
                    653:                        free(unescaped);
                    654:                    }
                    655:                    status = HT_LOADED;
                    656:                    break;
                    657: 
                    658:                  default:
                    659:                    {
                    660:                        int length = (int) strlen(status_line);
                    661:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
                    662:                                   (void *) status_line, length < 50 ?
                    663:                                   length : 50, "HTLoadHTTP");
                    664:                    }
                    665:                    status = -1;
                    666:                    break;
                    667:                }
1.21      luotonen  668:                break;
1.17      timbl     669:                    
1.21      luotonen  670:              case 4:           /* Access Authorization problem */
                    671:                switch (server_status) {
                    672:                  case 401:
                    673:                    parse_401_headers(request, isoc);
                    674: 
                    675:                    if (TRACE) fprintf(stderr, "%s %d %s\n",
1.55      frystyk   676:                                       "HTTP: close socket", http->socket,
1.21      luotonen  677:                                       "to retry with Access Authorization");
1.24      luotonen  678:                    if (HTAA_retryWithAuth(request, HTLoadHTTP)) {
1.21      luotonen  679:                        status = HT_LOADED;/* @@ THIS ONLY WORKS ON LINEMODE */
1.55      frystyk   680:                        break;
1.21      luotonen  681:                    }
                    682:                    /* else falltrough */
                    683:                  default:
1.14      luotonen  684:                    {
1.55      frystyk   685:                        char *unescaped = NULL;
                    686:                        StrAllocCopy(unescaped, url);
                    687:                        HTUnEscape(unescaped);
                    688:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
                    689:                                   (void *) unescaped,
                    690:                                   (int) strlen(unescaped), "HTLoadHTTP");
                    691:                        free(unescaped);
                    692: #ifdef OLD_CODE
                    693:                        char *p1 = HTParse(url, "", PARSE_HOST);
1.21      luotonen  694:                        char * message;
                    695: 
                    696:                        if (!(message = (char*)malloc(strlen(status_line) +
                    697:                                                      strlen(p1) + 100)))
                    698:                            outofmem(__FILE__, "HTTP 4xx status");
1.14      luotonen  699:                        sprintf(message,
1.21      luotonen  700:                                "HTTP server at %s replies:\n%s\n\n%s\n",
                    701:                                p1, status_line,
                    702:                                ((server_status == 401) 
                    703:                                 ? "Access Authorization package giving up.\n"
                    704:                                 : ""));
1.22      luotonen  705:                        status = HTLoadError(request, server_status, message);
1.14      luotonen  706:                        free(message);
                    707:                        free(p1);
1.55      frystyk   708: #endif /* OLD_CODE */
1.14      luotonen  709:                    }
1.55      frystyk   710:                    status = -1;
                    711:                    break;
                    712:                }
1.21      luotonen  713:                break;
                    714: 
                    715:              case 5:           /* I think you goofed */
                    716:                {
1.55      frystyk   717:                    char *unescaped = NULL;
                    718:                    StrAllocCopy(unescaped, url);
                    719:                    HTUnEscape(unescaped);
                    720:                    HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
                    721:                               (void *) unescaped, (int) strlen(unescaped),
                    722:                               "HTLoadHTTP");
                    723:                    free(unescaped);
                    724: #ifdef OLD_CODE
                    725:                    char *p1 = HTParse(url, "", PARSE_HOST);
1.21      luotonen  726:                    char * message = (char*)malloc(strlen(status_line) + 
                    727:                                                   strlen(p1) + 100);
                    728:                    if (!message) outofmem(__FILE__, "HTTP 5xx status");
                    729:                    sprintf(message,
                    730:                            "HTTP server at %s replies:\n%s", p1, status_line);
1.22      luotonen  731:                    status = HTLoadError(request, server_status, message);
1.21      luotonen  732:                    free(message);
                    733:                    free(p1);
1.55      frystyk   734: #endif
1.21      luotonen  735:                }
1.55      frystyk   736:                status = -1;
1.21      luotonen  737:                break;
1.55      frystyk   738: 
                    739:              default:                                         /* bad number */
                    740:                {
                    741:                    int length = (int) strlen(status_line);
                    742:                    HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
                    743:                               (void *) status_line, length < 50 ? length : 50,
                    744:                               "HTLoadHTTP");
                    745:                }
                    746:                status = -1;
1.21      luotonen  747:                break;
1.55      frystyk   748:            }
                    749:            FREE(status_line);                   /* Leak fix Henrik 18/02-94 */
1.17      timbl     750:        }
1.48      timbl     751: 
1.55      frystyk   752:        /* Close the socket and free memory */
                    753:        HTInputSocket_free(isoc);           
                    754:        HTTPCleanup(request, http);
                    755:        return status;                  /* Good return  */    
                    756:     }
                    757: }
1.1       timbl     758: 
                    759: /*     Protocol descriptor
                    760: */
                    761: 
1.17      timbl     762: GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0, 0 };
1.55      frystyk   763: 
                    764: 
1.21      luotonen  765: 

Webmaster