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