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

1.74      frystyk     1: /*                                                                      HTTP.c
                      2: **     MULTITHREADED IMPLEMENTATION OF HTTP CLIENT
1.2       timbl       3: **
1.74      frystyk     4: **     (c) COPYRIGHT CERN 1994.
                      5: **     Please first read the full copyright statement in the file COPYRIGH.
                      6: **
                      7: **     This module implments the HTTP protocol as a state machine
1.55      frystyk     8: **
                      9: ** History:
1.59      frystyk    10: **    < May 24 94 ??   Unknown - but obviously written
1.56      frystyk    11: **     May 24 94 HF    Made reentrent and cleaned up a bit. Implemented
                     12: **                     Forward, redirection, error handling and referer field
1.67      duns       13: **      8 Jul 94  FM   Insulate free() from _free structure element.
1.71      frystyk    14: **     Jul 94 HFN      Written on top of HTTP.c, Henrik Frystyk
1.55      frystyk    15: **
1.1       timbl      16: */
                     17: 
1.71      frystyk    18: /* Platform dependent stuff */
1.1       timbl      19: #include "HTUtils.h"
                     20: #include "tcp.h"
1.68      frystyk    21: 
1.71      frystyk    22: /* Libray Includes */
                     23: #include "HTParse.h"
1.1       timbl      24: #include "HTTCP.h"
                     25: #include "HTFormat.h"
1.2       timbl      26: #include "HTAlert.h"
                     27: #include "HTMIME.h"
1.21      luotonen   28: #include "HTAccess.h"          /* HTRequest */
1.14      luotonen   29: #include "HTAABrow.h"          /* Access Authorization */
1.20      timbl      30: #include "HTTee.h"             /* Tee off a cache stream */
                     31: #include "HTFWriter.h"         /* Write to cache file */
1.54      luotonen   32: #include "HTError.h"
1.55      frystyk    33: #include "HTChunk.h"
1.71      frystyk    34: #include "HTGuess.h"
                     35: #include "HTThread.h"
1.55      frystyk    36: #include "HTTP.h"                                             /* Implements */
                     37: 
                     38: /* Macros and other defines */
1.71      frystyk    39: #define HTTP_VERSION   "HTTP/1.0"
                     40: #define PUTC(c)                (*me->target->isa->put_character)(me->target, c)
                     41: #define PUTS(s)                (*me->target->isa->put_string)(me->target, s)
                     42: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
                     43: #define FREE_TARGET    (*me->target->isa->_free)(me->target)
1.74      frystyk    44: #define ABORT_TARGET   (*me->target->isa->abort)(me->target, e)
1.2       timbl      45: 
1.55      frystyk    46: /* Globals */
1.59      frystyk    47: extern char * HTAppName;                 /* Application name: please supply */
                     48: extern char * HTAppVersion;           /* Application version: please supply */
1.72      frystyk    49: extern BOOL using_proxy;                   /* are we using a proxy gateway? */
1.71      frystyk    50: 
1.64      frystyk    51: PUBLIC int  HTMaxRedirections = 10;           /* Max number of redirections */
                     52: PUBLIC BOOL HTEnableFrom = NO;                       /* Enable From header? */
1.71      frystyk    53: PUBLIC char * HTProxyHeaders = NULL;               /* Headers to pass as-is */
1.23      luotonen   54: 
1.59      frystyk    55: /* Type definitions and global variables etc. local to this module */
                     56: /* This is the local definition of HTRequest->net_info */
                     57: typedef enum _HTTPState {
1.71      frystyk    58:     HTTP_ERROR         = -3,
                     59:     HTTP_NO_DATA       = -2,
                     60:     HTTP_GOT_DATA      = -1,
                     61:     HTTP_BEGIN         = 0,
                     62:     HTTP_NEED_CONNECTION,
                     63:     HTTP_NEED_REQUEST,
                     64:     HTTP_SENT_REQUEST,
                     65:     HTTP_NEED_BODY,
                     66:     HTTP_REDIRECTION,
                     67:     HTTP_AA
1.59      frystyk    68: } HTTPState;
1.55      frystyk    69: 
                     70: typedef struct _http_info {
1.71      frystyk    71:     int                        sockfd;                         /* Socket descripter */
                     72:     SockA              sock_addr;              /* SockA is defined in tcp.h */
1.68      frystyk    73:     HTInputSocket *    isoc;                                /* Input buffer */
1.71      frystyk    74:     HTStream *         target;                             /* Output stream */
                     75:     HTChunk *          transmit;                         /* Line to be send */
                     76:     int                addressCount;        /* Attempts if multi-homed host */
                     77:     time_t             connecttime;             /* Used on multihomed hosts */
                     78:     struct _HTRequest *        request;           /* Link back to request structure */
1.68      frystyk    79: 
                     80:     HTTPState          state;                    /* State of the connection */
1.55      frystyk    81: } http_info;
                     82: 
1.71      frystyk    83: #define MAX_STATUS_LEN         150        /* Max number of chars to look at */
1.55      frystyk    84: 
1.71      frystyk    85: struct _HTStream {
                     86:     CONST HTStreamClass *      isa;
                     87:     HTStream *                 target;
                     88:     HTRequest *                        request;
                     89:     http_info *                        http;
                     90:     int                                cnt;
                     91:     HTSocketEOL                        state;
                     92:     BOOL                       transparent;
                     93:     float                      version;
                     94:     int                                status;
                     95:     char                       buffer[MAX_STATUS_LEN+1];
                     96:     char *                     bufptr;
                     97: };
1.21      luotonen   98: 
1.71      frystyk    99: /* ------------------------------------------------------------------------- */
                    100: /*                               Help Functions                             */
                    101: /* ------------------------------------------------------------------------- */
1.21      luotonen  102: 
1.71      frystyk   103: /*                                                                  HTTPCleanup
1.1       timbl     104: **
1.55      frystyk   105: **      This function closes the connection and frees memory.
1.1       timbl     106: **
1.55      frystyk   107: **      Returns 0 on OK, else -1
1.1       timbl     108: */
1.71      frystyk   109: PRIVATE int HTTPCleanup ARGS1(HTRequest *, request)
1.1       timbl     110: {
1.71      frystyk   111:     http_info *http;
1.55      frystyk   112:     int status = 0;
1.71      frystyk   113:     if (!request || !request->net_info) {
                    114:        if (PROT_TRACE) fprintf(stderr, "HTTPCleanup. Bad argument!\n");
1.55      frystyk   115:        status = -1;
                    116:     } else {
1.71      frystyk   117:        http = (http_info *) request->net_info;
1.59      frystyk   118:        if (http->sockfd >= 0) {
1.71      frystyk   119:            if (PROT_TRACE) fprintf(stderr, "HTTP........ Closing socket %d\n",
1.59      frystyk   120:                               http->sockfd);
                    121:            if ((status = NETCLOSE(http->sockfd)) < 0)
                    122:                HTErrorSysAdd(http->request, ERR_FATAL, NO, "NETCLOSE");
1.71      frystyk   123:            HTThreadState(http->sockfd, THD_CLOSE);
                    124:            HTThread_clear((HTNetInfo *) http);
                    125:            http->sockfd = -1;
                    126:        }
                    127:        if (http->isoc)
                    128:            HTInputSocket_free(http->isoc);
                    129:        if (http->transmit)
                    130:            HTChunkFree(http->transmit);
1.55      frystyk   131:     }  
                    132:     free(http);
1.71      frystyk   133:     request->net_info = NULL;
1.55      frystyk   134:     return status;
                    135: }
1.36      frystyk   136: 
1.23      luotonen  137: 
1.55      frystyk   138: /*                                                              HTTPSendRequest
                    139: **
                    140: **      This function composes and sends a request to the connected server
                    141: **      specified.
                    142: **
1.71      frystyk   143: **     Returns         <0              Error has occured or interrupted
                    144: **                     HT_WOULD_BLOCK  if operation would have blocked
                    145: **                     HT_INTERRUPTED  if interrupted
                    146: **
                    147: **      Note: The function does NEVER close the connection
1.1       timbl     148: */
1.55      frystyk   149: PRIVATE int HTTPSendRequest ARGS3(HTRequest *, request,
                    150:                                  http_info *, http, char *, url)
                    151: {
                    152:     int status = 0;
1.1       timbl     153: 
1.71      frystyk   154:     /* If first time through then generate HTTP request */
                    155:     if (!http->transmit) {
                    156:        HTChunk *command = HTChunkCreate(2048);
                    157:        http->transmit = command;
                    158:        if (request->method != METHOD_INVALID) {
                    159:            HTChunkPuts(command, HTMethod_name(request->method));
                    160:            HTChunkPutc(command, ' ');
                    161:        }
1.21      luotonen  162:        else
1.71      frystyk   163:            HTChunkPuts(command, "GET ");
                    164:        
                    165:        /* if we are using a proxy gateway don't copy in the first slash
                    166:         ** of say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj
                    167:         ** so that just gohper://.... is sent. */
                    168:        {
                    169:            char *p1 = HTParse(url, "", PARSE_PATH|PARSE_PUNCTUATION);
                    170:            if (using_proxy)
                    171:                HTChunkPuts(command, p1+1);
                    172:            else
                    173:                HTChunkPuts(command, p1);
                    174:            free(p1);
                    175:        }
1.55      frystyk   176:        HTChunkPutc(command, ' ');
                    177:        HTChunkPuts(command, HTTP_VERSION);
1.71      frystyk   178:        HTChunkPutc(command, CR);                    /* CR LF, as in rfc 977 */
                    179:        HTChunkPutc(command, LF);
                    180:        
                    181:        if (HTImProxy && HTProxyHeaders) {
                    182:            HTChunkPuts(command, HTProxyHeaders);
                    183:        } else {
                    184:            char line[256];    /*@@@@ */
                    185:            
                    186:            /* If no conversion list, then put it up, but leave initialization
                    187:               to the client */
                    188:            if (!HTConversions)
                    189:                HTConversions = HTList_new();
1.34      frystyk   190:            
1.71      frystyk   191:            /* Run through both lists and generate `accept' lines */
                    192:            {
                    193:                int i;
                    194:                HTList *conversions[2];
                    195:                conversions[0] = HTConversions;
                    196:                conversions[1] = request->conversions;
                    197:                
                    198:                for (i=0; i<2; i++) {
                    199:                    HTList *cur = conversions[i];
                    200:                    HTPresentation *pres;
                    201:                    while ((pres =(HTPresentation *) HTList_nextObject(cur))) {
                    202:                        if (pres->rep_out == WWW_PRESENT) {
                    203:                            if (pres->quality != 1.0) {
                    204:                                sprintf(line, "Accept: %s; q=%.3f%c%c",
                    205:                                        HTAtom_name(pres->rep),
                    206:                                        pres->quality, CR, LF);
                    207:                            } else {
                    208:                                sprintf(line, "Accept: %s%c%c",
                    209:                                        HTAtom_name(pres->rep), CR, LF);
                    210:                            }
                    211:                            HTChunkPuts(command, line);
1.21      luotonen  212:                        }
1.17      timbl     213:                    }
                    214:                }
1.2       timbl     215:            }
1.71      frystyk   216:            
                    217:            /* Put out referer field if any parent */
                    218:            if (request->parentAnchor) {
                    219:                char *me = HTAnchor_address((HTAnchor *) request->anchor);
                    220:                char *parent = HTAnchor_address((HTAnchor *)
                    221:                                                request->parentAnchor);
                    222:                char *relative = HTParse(parent, me,
                    223:                                         PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
                    224:                if (relative && *relative) {
                    225:                    sprintf(line, "Referer: %s%c%c", parent, CR, LF);
                    226:                    HTChunkPuts(command, line);
                    227:                }
                    228:                free(me);
                    229:                free(parent);
                    230:                free(relative);
                    231:            }
                    232:            
                    233:            /* Put out from field if enabled by client */
                    234:            if (HTEnableFrom) {
                    235:                CONST char *mailaddress = HTGetMailAddress();
                    236:                if (mailaddress != NULL) {
                    237:                    sprintf(line, "From: %s%c%c", mailaddress, CR, LF);
                    238:                    HTChunkPuts(command, line);
                    239:                }
                    240:            }
                    241:            
                    242:            /* Put out user-agent */
                    243:            sprintf(line, "User-Agent: %s/%s  libwww/%s%c%c",
                    244:                    HTAppName ? HTAppName : "unknown",
                    245:                    HTAppVersion ? HTAppVersion : "0.0",
                    246:                    HTLibraryVersion, CR, LF);
                    247:            HTChunkPuts(command, line);
                    248:            
                    249:            /* Put out authorization */
                    250:            if (request->authorization != NULL) {
                    251:                HTChunkPuts(command, "Authorization: ");
                    252:                HTChunkPuts(command, request->authorization);
                    253:                HTChunkPutc(command, CR);
                    254:                HTChunkPutc(command, LF);
1.63      frystyk   255:            }
                    256:        }
1.71      frystyk   257:        HTChunkPutc(command, CR);                  /* Blank line means "end" */
                    258:        HTChunkPutc(command, LF);
                    259:        HTChunkTerminate(command);
                    260:        if (PROT_TRACE) fprintf(stderr, "HTTP Tx..... %s", command->data);
                    261:        
                    262:        /* Translate into ASCII if necessary */
1.4       timbl     263: #ifdef NOT_ASCII
1.71      frystyk   264:        {
                    265:            char * p;
                    266:            for(p = command->data; *p; p++) {
                    267:                *p = TOASCII(*p);
                    268:            }
1.1       timbl     269:        }
1.71      frystyk   270: #endif
1.55      frystyk   271:     }
1.71      frystyk   272: 
                    273:     /* Now, we are ready for sending the request */
                    274:     status = NETWRITE(http->sockfd, http->transmit->data,
                    275:                      http->transmit->size-1);
                    276:     if (status < 0) {
                    277: #ifdef EAGAIN
                    278:        if (errno == EAGAIN || errno == EWOULDBLOCK) 
                    279: #else
                    280:        if (errno == EWOULDBLOCK)
1.3       timbl     281: #endif
1.71      frystyk   282:        {
                    283:            if (PROT_TRACE)
                    284:                fprintf(stderr, "HTTP Tx..... Write operation would block\n");
                    285:            HTThreadState(http->sockfd, THD_SET_WRITE);
                    286:            return HT_WOULD_BLOCK;
                    287:        } else {                                 /* A real error has occured */
1.55      frystyk   288:            char *unescaped = NULL;
1.71      frystyk   289:            HTErrorSysAdd(request, ERR_FATAL, NO, "NETWRITE");
1.55      frystyk   290:            StrAllocCopy(unescaped, url);
                    291:            HTUnEscape(unescaped);
                    292:            HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
                    293:                       (void *) unescaped, (int) strlen(unescaped),
                    294:                       "HTTPSendRequest");
                    295:            free(unescaped);
1.71      frystyk   296:            return -1;
1.55      frystyk   297:        }
                    298:     }
1.71      frystyk   299:     HTThreadState(http->sockfd, THD_CLR_WRITE);                        /* Write OK */
1.55      frystyk   300:     return status;
                    301: }
                    302: 
                    303: 
1.71      frystyk   304: PRIVATE BOOL HTTPAuthentication ARGS1(HTRequest *, request)
                    305: {
                    306:     HTAAScheme scheme;
                    307:     HTList *valid_schemes = HTList_new();
                    308:     HTAssocList **scheme_specifics = NULL;
1.76      frystyk   309:     char *tmplate = NULL;
1.71      frystyk   310: 
                    311:     if (request->WWWAAScheme) {
                    312:        if ((scheme = HTAAScheme_enum(request->WWWAAScheme)) != HTAA_UNKNOWN) {
                    313:            HTList_addObject(valid_schemes, (void *) scheme);
                    314:            if (!scheme_specifics) {
                    315:                int i;
                    316:                scheme_specifics = (HTAssocList**)
                    317:                    malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
                    318:                if (!scheme_specifics)
                    319:                    outofmem(__FILE__, "HTTPAuthentication");
                    320:                for (i=0; i < HTAA_MAX_SCHEMES; i++)
                    321:                    scheme_specifics[i] = NULL;
                    322:            }
                    323:            scheme_specifics[scheme] = HTAA_parseArgList(request->WWWAARealm);
                    324:        } else if (PROT_TRACE) {
                    325:            HTErrorAdd(request, ERR_INFO, NO, HTERR_UNKNOWN_AA,
                    326:                       (void *) request->WWWAAScheme, 0, "HTTPAuthentication");
                    327:            return NO;
                    328:        }
                    329:     }
                    330:     if (request->WWWprotection) {
                    331:        if (PROT_TRACE)
                    332:            fprintf(stderr, "Protection template set to `%s'\n",
                    333:                    request->WWWprotection);
1.76      frystyk   334:        StrAllocCopy(tmplate, request->WWWprotection);
1.71      frystyk   335:     }
                    336:     request->valid_schemes = valid_schemes;
                    337:     request->scheme_specifics = scheme_specifics;
1.76      frystyk   338:     request->prot_template = tmplate;
1.71      frystyk   339:     return YES;
                    340: }
                    341: 
                    342: 
                    343: /*
                    344: **     This is a big switch handling all HTTP return codes. It puts in any
                    345: **     appropiate error message and decides whether we should expect data
1.76.2.1  frystyk   346: **     or not.
1.55      frystyk   347: */
1.71      frystyk   348: PRIVATE void HTTPResponse ARGS1(HTStream *, me)
1.55      frystyk   349: {
1.71      frystyk   350:     switch (me->status) {
                    351: 
                    352:       case 200:
1.76.2.1  frystyk   353:       case 201:
                    354:       case 202:
                    355:       case 203:
                    356:       case 205:
                    357:       case 206:
1.71      frystyk   358:        break;
1.76.2.1  frystyk   359: 
                    360:       case 204:                                                      /* No Response */
                    361:        me->http->state = HTTP_NO_DATA;
                    362:        break;
                    363: 
1.71      frystyk   364:       case 301:                                                            /* Moved */
                    365:       case 302:                                                            /* Found */
                    366:        me->http->state = HTTP_REDIRECTION;
                    367:        break;
1.55      frystyk   368:        
1.71      frystyk   369:       case 303:                                                           /* Method */
                    370:        HTAlert("This client doesn't support automatic redirection of type `Method'");
                    371:        me->http->state = HTTP_ERROR;
                    372:        break;
1.55      frystyk   373:        
1.76.2.1  frystyk   374:       case 400:                                                      /* Bad Request */
                    375:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_REQUEST,
                    376:                   NULL, 0, "HTLoadHTTP");
1.71      frystyk   377:        me->http->state = HTTP_ERROR;
                    378:        break;
1.70      howcome   379: 
1.71      frystyk   380:       case 401:
1.76.2.1  frystyk   381:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
                    382:                   NULL, 0, "HTLoadHTTP");
1.71      frystyk   383:        me->http->state = HTTP_AA;
                    384:        break;
                    385:        
                    386:       case 402:                                                 /* Payment required */
1.76.2.1  frystyk   387:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_PAYMENT_REQUIRED,
                    388:                   NULL, 0, "HTLoadHTTP");
                    389:        me->http->state = HTTP_ERROR;
1.71      frystyk   390:        break;
1.55      frystyk   391:        
1.71      frystyk   392:       case 403:                                                        /* Forbidden */
1.76.2.1  frystyk   393:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_FORBIDDEN,
                    394:                   NULL, 0, "HTLoadHTTP");
1.71      frystyk   395:        me->http->state = HTTP_ERROR;
                    396:        break;
1.55      frystyk   397:        
1.71      frystyk   398:       case 404:                                                        /* Not Found */
1.76.2.1  frystyk   399:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_FOUND,
                    400:                   NULL, 0, "HTLoadHTTP");
1.71      frystyk   401:        me->http->state = HTTP_ERROR;
                    402:        break;
                    403:        
1.76.2.1  frystyk   404:       case 405:                                                      /* Not Allowed */
                    405:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
                    406:                   NULL, 0, "HTLoadHTTP");
                    407:        me->http->state = HTTP_ERROR;
                    408:        break;
                    409: 
                    410:       case 406:                                                  /* None Acceptable */
                    411:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NONE_ACCEPTABLE,
                    412:                   NULL, 0, "HTLoadHTTP");
                    413:        me->http->state = HTTP_ERROR;
                    414:        break;
                    415: 
                    416:       case 407:                                    /* Proxy Authentication Required */
                    417:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_PROXY,
                    418:                   NULL, 0, "HTLoadHTTP");
                    419:        me->http->state = HTTP_ERROR;
                    420:        break;
                    421: 
                    422:       case 408:                                                  /* Request Timeout */
                    423:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_TIMEOUT,
                    424:                   NULL, 0, "HTLoadHTTP");
                    425:        me->http->state = HTTP_ERROR;
                    426:        break;
                    427: 
1.71      frystyk   428:       case 500:
1.76.2.1  frystyk   429:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_INTERNAL,
                    430:                   NULL, 0, "HTLoadHTTP");
1.71      frystyk   431:        me->http->state = HTTP_ERROR;
                    432:        break;
                    433:        
1.76.2.1  frystyk   434:       case 501:
                    435:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_NOT_IMPLEMENTED,
                    436:                   NULL, 0, "HTLoadHTTP");
                    437:        me->http->state = HTTP_ERROR;
                    438:        break;
                    439: 
                    440:       case 502:
                    441:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_GATE,
                    442:                   NULL, 0, "HTLoadHTTP");
                    443:        me->http->state = HTTP_ERROR;
                    444:        break;
                    445: 
                    446:       case 503:
                    447:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_DOWN,
                    448:                   NULL, 0, "HTLoadHTTP");
                    449:        me->http->state = HTTP_ERROR;
                    450:        break;
                    451: 
                    452:       case 504:
                    453:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_GATE_TIMEOUT,
                    454:                   NULL, 0, "HTLoadHTTP");
                    455:        me->http->state = HTTP_ERROR;
                    456:        break;
                    457: 
1.71      frystyk   458:       default:                                                /* bad number */
                    459:        HTErrorAdd(me->request, ERR_FATAL, NO, HTERR_BAD_REPLY,
                    460:                   (void *) me->buffer, me->cnt, "HTLoadHTTP");
                    461:        me->http->state = HTTP_ERROR;
                    462:        break;
1.55      frystyk   463:     }
                    464: }
                    465: 
1.71      frystyk   466: /* ------------------------------------------------------------------------- */
                    467: /*                         HTTP Status Line Stream                          */
                    468: /* ------------------------------------------------------------------------- */
1.55      frystyk   469: 
1.71      frystyk   470: /*
                    471: **     Analyse the string we have read. If it is a HTTP 1.0 or higher
                    472: **     then create a MIME-stream, else create a Guess stream to find out
                    473: **     what the 0.9 server is sending. We need to copy the buffer as we don't
                    474: **     know if we can modify the contents or not.
1.76.2.1  frystyk   475: **
                    476: **     Stream handling is a function of the status code returned from the 
                    477: **     server:
                    478: **             200:     Use `output_stream' in HTRequest structure
                    479: **             else:    Use `output_flush' in HTRequest structure
1.56      frystyk   480: */
1.71      frystyk   481: PRIVATE void flush ARGS1(HTStream *, me)
1.56      frystyk   482: {
1.71      frystyk   483:     HTRequest *req = me->request;
                    484:     me->transparent = YES;                             /* Only do this once */
                    485:     if (me->state == EOL_FLF) {
                    486:        if (strncasecomp(me->buffer, "http/", 5) ||
                    487:            sscanf(me->buffer+5, "%f %d", &me->version, &me->status) < 2) {
                    488:            HTErrorAdd(req, ERR_INFO, NO, HTERR_HTTP09,
                    489:                       (void *) me->buffer, me->cnt, "HTTPStatusStream");
                    490:            me->target = HTGuess_new(req, NULL, WWW_UNKNOWN,
                    491:                                     req->output_format, req->output_stream);
                    492:            PUTBLOCK(me->buffer, me->cnt);
                    493:        } else {
                    494:            if (req->output_format == WWW_SOURCE) {
                    495:                me->target = HTMIMEConvert(req, NULL, WWW_MIME,
                    496:                                           req->output_format,
                    497:                                           req->output_stream);
1.76.2.1  frystyk   498:            } else if (me->status==200 && req->method==METHOD_GET) {
1.76.2.3! howcome   499:                HTStream *s;
        !           500: 
1.71      frystyk   501:                me->target = HTStreamStack(WWW_MIME,req->output_format,
1.76.2.1  frystyk   502:                                           req->output_stream, req, NO);
1.76.2.3! howcome   503: 
        !           504:                /* Cache HTTP 1.0 responses */
        !           505:                /* howcome added test for return value from HTCacheWriter 12/1/95 */
        !           506: 
        !           507:                if (HTCacheDir && (s = HTCacheWriter(req, NULL, WWW_MIME,
1.76.2.1  frystyk   508:                                                        req->output_format,
1.76.2.3! howcome   509:                                                        req->output_stream)))
        !           510:                    {
        !           511:                        me->target = HTTee(me->target, s);
        !           512:                    }
1.71      frystyk   513:            } else {
                    514:                me->target = HTMIMEConvert(req, NULL, WWW_MIME,
1.76.2.1  frystyk   515:                                           WWW_SOURCE, req->output_flush ?
                    516:                                           req->output_flush : HTBlackHole());
1.56      frystyk   517:            }
1.76.2.1  frystyk   518:            if (!me->target)
1.71      frystyk   519:                me->target = HTBlackHole();                     /* What else */
1.56      frystyk   520:        }
1.71      frystyk   521:     } else {
                    522:        me->target = HTGuess_new(req, NULL, WWW_UNKNOWN, req->output_format,
                    523:                                 req->output_stream);
                    524:        PUTBLOCK(me->buffer, me->cnt);
1.56      frystyk   525:     }
1.71      frystyk   526: }
1.56      frystyk   527: 
1.71      frystyk   528: PRIVATE void HTTPStatus_put_character ARGS2(HTStream *, me, char, c)
                    529: {
                    530:     if (me->transparent)
                    531:        PUTC(c);
                    532:     else {
                    533:        if (me->state == EOL_FCR) {
                    534:            if (c == LF) {
                    535:                me->state = EOL_FLF;                           /* Line found */
                    536:                flush(me);
                    537:            } else {
                    538:                me->state = EOL_BEGIN;
                    539:                me->cnt += 2;
                    540:                *me->bufptr++ = CR;                   /* Need to put it back */
                    541:                *me->bufptr++ = c;
                    542:            }
                    543:        } else if (c == CR) {
                    544:            me->state = EOL_FCR;
                    545:        } else if (c == LF) {
                    546:            me->state = EOL_FLF;                               /* Line found */
                    547:            me->cnt++;
                    548:            *me->bufptr++ = LF;
                    549:            flush(me);
                    550:        } else {
                    551:            me->cnt++;
                    552:            *me->bufptr++ = c;
                    553:            if (me->cnt >= MAX_STATUS_LEN)
                    554:                flush(me);
                    555:        }
1.56      frystyk   556:     }
                    557: }
                    558: 
1.71      frystyk   559: PRIVATE void HTTPStatus_put_string ARGS2(HTStream *, me, CONST char*, s)
                    560: {
                    561:     while (!me->transparent && *s)
                    562:        HTTPStatus_put_character(me, *s++);
                    563:     if (*s) PUTS(s);
                    564: }
1.56      frystyk   565: 
1.71      frystyk   566: PRIVATE void HTTPStatus_put_block ARGS3(HTStream *, me, CONST char*, b, int, l)
                    567: {
                    568:     while (!me->transparent && l-- > 0)
                    569:        HTTPStatus_put_character(me, *b++);
                    570:     if (l > 0) PUTBLOCK(b, l);
                    571: }
                    572: 
                    573: PRIVATE int HTTPStatus_free ARGS1(HTStream *, me)
                    574: {
                    575:     int status = me->status;
1.76.2.1  frystyk   576:     HTTPResponse(me);                                     /* Get next state */
1.71      frystyk   577:     if (!me->transparent)
                    578:        flush(me);
                    579:     if (me->target)
                    580:        FREE_TARGET;
                    581:     free(me);
                    582:     return status;          /* Return the HTTP status value - 0 if HTTP 0.9 */
                    583: }
                    584: 
                    585: PRIVATE int HTTPStatus_abort ARGS2(HTStream *, me, HTError, e)
                    586: {
                    587:     if (me->target)
1.74      frystyk   588:        ABORT_TARGET;
1.71      frystyk   589:     free(me);
1.74      frystyk   590:     if (PROT_TRACE)
                    591:        fprintf(stderr, "HTTPStatus.. ABORTING LOAD...\n");
1.71      frystyk   592:     return EOF;
                    593: }
                    594: 
                    595: /*     HTTPStatus Stream
                    596: **     -----------------
                    597: */
                    598: PRIVATE CONST HTStreamClass HTTPStatusClass =
                    599: {              
                    600:     "HTTPStatus",
                    601:     HTTPStatus_free,
                    602:     HTTPStatus_abort,
                    603:     HTTPStatus_put_character,
                    604:     HTTPStatus_put_string,
                    605:     HTTPStatus_put_block
                    606: };
                    607: 
                    608: PUBLIC HTStream * HTTPStatus_new ARGS2(HTRequest *, request,
                    609:                                       http_info *, http)
                    610: {
                    611:     HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
                    612:     if (!me) outofmem(__FILE__, "HTTPStatus_new");
                    613:     me->isa = &HTTPStatusClass;
                    614:     me->request = request;
                    615:     me->http = http;
                    616:     me->bufptr = me->buffer;
                    617:     me->state = EOL_BEGIN;
                    618:     return me;
                    619: }
                    620: 
                    621: /* ------------------------------------------------------------------------- */
                    622: 
                    623: /*             Load Document from HTTP Server                       HTLoadHTTP
1.55      frystyk   624: **             ==============================
                    625: **
                    626: **     Given a hypertext address, this routine loads a document.
                    627: **
                    628: ** On entry,
                    629: **      request                This is the request structure
                    630: ** On exit,
1.71      frystyk   631: **     returns         <0              Error has occured or interrupted
                    632: **                     HT_WOULD_BLOCK  if operation would have blocked
1.58      frystyk   633: **                     HT_LOADED       if return status 200 OK
                    634: **                     HT_NO_DATA      if return status 204 No Response
1.55      frystyk   635: */
                    636: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
                    637: {
1.71      frystyk   638:     int status = HT_ERROR;
                    639:     char *url;                           /* Gets initialized on every entry */
1.55      frystyk   640:     http_info *http;                       /* Specific protocol information */
                    641: 
                    642:     if (!request || !request->anchor) {
1.71      frystyk   643:         if (PROT_TRACE) fprintf(stderr, "HTLoadHTTP.. Bad argument\n");
                    644:         return HT_ERROR;
1.55      frystyk   645:     }
                    646:     url = HTAnchor_physical(request->anchor);
1.17      timbl     647:     
1.71      frystyk   648:     /* Only do the setup first time through. This is actually state HTTP_BEGIN
                    649:        but it can't be in the state machine as we need the structure first */
                    650:     if (!request->net_info) {
1.22      luotonen  651:        /*
1.71      frystyk   652:        ** Initiate a new http structure and bind to request structure
                    653:        ** This is actually state HTTP_BEGIN, but it can't be in the state
                    654:        ** machine as we need the structure first.
1.22      luotonen  655:        */
1.71      frystyk   656:        if (PROT_TRACE) fprintf(stderr, "HTTP........ Looking for `%s\'\n", url);
                    657:        if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL)
                    658:            outofmem(__FILE__, "HTLoadHTTP");
                    659:        http->sockfd = -1;                          /* Invalid socket number */
                    660:        http->request = request;
                    661:        http->state = HTTP_BEGIN;
                    662:        request->net_info = (HTNetInfo *) http;
                    663:        HTThread_new((HTNetInfo *) http);
                    664:     } else
                    665:        http = (http_info *) request->net_info;         /* Get existing copy */
                    666:  
                    667:     /* Now jump into the machine. We know the state from the previous run */
                    668:     while (1) {
                    669:        switch (http->state) {
                    670:          case HTTP_BEGIN:
                    671:            /*
                    672:             ** Compose authorization information (this was moved here
                    673:             ** from after the making of the connection so that the connection
                    674:             ** wouldn't have to wait while prompting username and password
                    675:             ** from the user).                         -- AL 13.10.93
                    676:             */
                    677:            HTAA_composeAuth(request);
                    678:            if (PROT_TRACE) {
                    679:                if (request->authorization)
                    680:                    fprintf(stderr, "HTTP........ Sending Authorization: %s\n",
                    681:                            request->authorization);
                    682:                else
                    683:                    fprintf(stderr,
                    684:                            "HTTP........ Not sending authorization (yet)\n");
                    685:            }
                    686:            http->state = HTTP_NEED_CONNECTION;
                    687:            break;
                    688:            
                    689:          case HTTP_NEED_CONNECTION:        /* Now let's set up a connection */
                    690:            status = HTDoConnect((HTNetInfo *) http, url, TCP_PORT,
                    691:                                 NULL, NO);
                    692:            if (!status) {
                    693:                if (PROT_TRACE)
                    694:                    fprintf(stderr, "HTTP........ Connected, socket %d\n",
                    695:                            http->sockfd);
                    696:                http->isoc = HTInputSocket_new(http->sockfd);
                    697:                http->state = HTTP_NEED_REQUEST;
                    698:            } else if (status == HT_WOULD_BLOCK)
                    699:                return status;
                    700:            else
                    701:                http->state = HTTP_ERROR;              /* Error or interrupt */
                    702:            break;
                    703: 
                    704:          case HTTP_NEED_REQUEST:         /* Compose the request and send it */
                    705:            if ((status = HTTPSendRequest(request, http, url)) < 0) {
                    706:                if (status == HT_WOULD_BLOCK)
1.55      frystyk   707:                    return status;
1.71      frystyk   708:                else
                    709:                    http->state = HTTP_ERROR;
1.55      frystyk   710:            } else {
1.71      frystyk   711:                http->state = HTTP_SENT_REQUEST;            
1.21      luotonen  712:            }
1.71      frystyk   713:            break;
1.21      luotonen  714: 
1.76.2.1  frystyk   715:          case HTTP_SENT_REQUEST:                           /* Put up stream */
                    716:            http->target = HTImProxy ?
                    717:                request->output_stream : HTTPStatus_new(request, http);
                    718:            http->state = HTTP_NEED_BODY;
                    719:            break;
                    720: 
                    721:          case HTTP_NEED_BODY:                          /* Read the response */
1.74      frystyk   722:            status = HTInputSocket_read(http->isoc, http->target);
                    723:            if (status == HT_WOULD_BLOCK)
1.71      frystyk   724:                return HT_WOULD_BLOCK;
1.75      frystyk   725:            else if (status == HT_INTERRUPTED) {
                    726:                (*http->target->isa->abort)(http->target, NULL);
1.74      frystyk   727:                http->state = HTTP_ERROR;
1.76.2.1  frystyk   728:            } else if (status == HT_LOADED) {
1.75      frystyk   729:                (*http->target->isa->_free)(http->target);
                    730:                if (http->state == HTTP_NEED_BODY)
                    731:                    http->state = HTTP_GOT_DATA;
1.76.2.1  frystyk   732:            } else {
                    733:                (*http->target->isa->_free)(http->target);
                    734:                http->state = HTTP_ERROR;
1.75      frystyk   735:            }
1.71      frystyk   736:            break;
                    737: 
                    738:           case HTTP_REDIRECTION:
                    739:            if (request->redirect) {
                    740:                HTAnchor *anchor;
                    741:                if (status == 301) {
                    742:                    HTErrorAdd(request, ERR_INFO, NO, HTERR_MOVED,
                    743:                               (void *) request->redirect,
                    744:                               (int) strlen(request->redirect), "HTLoadHTTP");
                    745:                } else if (status == 302) {
                    746:                    HTErrorAdd(request, ERR_INFO, NO, HTERR_FOUND,
                    747:                               (void *) request->redirect,
                    748:                               (int) strlen(request->redirect), "HTLoadHTTP");
1.55      frystyk   749:                }
1.71      frystyk   750:                anchor = HTAnchor_findAddress(request->redirect);
                    751:                if (++request->redirections < HTMaxRedirections) {
                    752:                    HTTPCleanup(request);
                    753:                    return HTLoadAnchorRecursive((HTAnchor *) anchor, request);
                    754:                } else {
                    755:                    HTErrorAdd(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT,
1.58      frystyk   756:                               NULL, 0, "HTLoadHTTP");
1.71      frystyk   757:                    http->state = HTTP_ERROR;
1.58      frystyk   758:                }
1.71      frystyk   759:            } else {
                    760:                HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
                    761:                           NULL, 0, "HTLoadHTTP");
                    762:                http->state = HTTP_ERROR;
                    763:            }
                    764:            break;
                    765: 
                    766:          case HTTP_AA:
                    767:            if (HTTPAuthentication(request) == YES &&
                    768:                HTAA_retryWithAuth(request) == YES) {
1.76.2.2  frystyk   769:                HTTPCleanup(request);
1.71      frystyk   770:                return HTLoadAnchor((HTAnchor *) request->anchor, request);
                    771:            } else {
                    772:                char *unescaped = NULL;
                    773:                StrAllocCopy(unescaped, url);
                    774:                HTUnEscape(unescaped);
                    775:                HTErrorAdd(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
                    776:                           (void *) unescaped,
                    777:                           (int) strlen(unescaped), "HTLoadHTTP");
                    778:                free(unescaped);
                    779:                http->state = HTTP_ERROR;
                    780:            }
                    781:            break;
                    782: 
                    783:          case HTTP_GOT_DATA:
1.74      frystyk   784:            HTTPCleanup(request);
1.71      frystyk   785:            return HT_LOADED;
                    786:            break;
                    787:            
                    788:          case HTTP_NO_DATA:
                    789:            HTTPCleanup(request);
                    790:            return HT_NO_DATA;
                    791:            break;
                    792: 
                    793:          case HTTP_ERROR:
                    794:            HTTPCleanup(request);
                    795:            return HT_ERROR;
                    796:            break;
                    797:        }
                    798:     } /* End of while(1) */
                    799: }    
                    800: 
                    801: /* Protocol descriptor */
                    802: 
                    803: GLOBALDEF PUBLIC HTProtocol HTTP = {
                    804:     "http", SOC_NON_BLOCK, HTLoadHTTP, NULL, NULL
                    805: };
1.21      luotonen  806: 

Webmaster