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

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

Webmaster