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