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