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

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

Webmaster