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