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

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

Webmaster