Annotation of libwww/Library/src/HTTP.c, revision 1.70
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 */
1.70 ! howcome 345: HTStream *s; /* HWL */
! 346:
1.55 frystyk 347: if (format_in == WWW_MIME && request->output_format == WWW_SOURCE) {
348: target = HTMIMEConvert(request, NULL, format_in,
349: request->output_format,
350: request->output_stream);
351: } else
352: target = HTStreamStack(format_in, request, NO);
353: if (target) {
354:
355: /* @@ Bug: The decision of whether or not to cache should also
356: be made contingent on a IP address match or non match. */
357:
358: if (HTCacheDir && use_cache) {
1.70 ! howcome 359: /*
! 360: target = HTTee(target,
! 361: HTCacheWriter(request, NULL, format_in,
! 362: request->output_format,
! 363: request->output_stream));
! 364: */
! 365: /* HWL added check for s */
! 366: s = HTCacheWriter(request, NULL, format_in,
! 367: request->output_format,
! 368: request->output_stream);
! 369: if (s != NULL) {
! 370: target = HTTee(target, s);
! 371: }
! 372:
1.17 timbl 373: }
1.55 frystyk 374:
375: /* Push the data down the stream remembering the end of the
376: first buffer we just read */
377: if (format_in == WWW_HTML)
378: target = HTNetToText(target);/* Pipe through CR stripper */
379:
1.59 frystyk 380: PUTBLOCK(http->isoc->input_pointer,
381: http->isoc->input_limit - http->isoc->input_pointer);
382: HTCopy(http->sockfd, target); /* USE RETURN AS STATUS */
1.55 frystyk 383: FREE_TARGET;
384: status = HT_LOADED;
385: }
386: return status;
387: }
388:
389:
1.56 frystyk 390: /* HTTPRedirect
391: **
392: ** Reads the response from a 3xx server status code. Only the first line
393: ** is read. The format expected is
394: **
1.68 frystyk 395: ** Location: <url> String CrLf OR URI: <url> String CrLf
1.56 frystyk 396: **
397: ** The comment string is ignored!
398: **
399: ** NOTE: THIS IS NOT IN CORRESPONDANCE WITH THE SPECS!!!
400: **
401: ** Returns new anchor on success else NULL
402: */
403: PRIVATE HTAnchor *HTTPRedirect ARGS3(HTRequest *, request,
1.59 frystyk 404: http_info *, http, int, code)
1.56 frystyk 405: {
406: BOOL found = NO;
407: HTAnchor *anchor = NULL; /* New anchor */
408: char *line;
409: if (TRACE)
410: fprintf(stderr, "Redirection. Looking for URL's\n");
1.59 frystyk 411: while ((line = HTInputSocket_getUnfoldedLine(http->isoc)) != NULL) {
1.56 frystyk 412: char *strptr = line;
413: if (*strptr) {
414: while (*strptr && *strptr == ' ') /* Skip leading spaces */
415: strptr++;
1.68 frystyk 416: if (!strncasecomp(strptr, "location:", 9) ||
417: !strncasecomp(strptr, "uri:", 4)) {
1.56 frystyk 418: char *url = strchr(strptr, ' ');
419: char *comment;
420: while (*url && *url == ' ') /* Skip leading spaces */
421: url++;
422: if ((comment = strchr(url, ' ')) != NULL)
423: *comment = '\0'; /* Skip any comments */
424: if (code == 301)
1.57 frystyk 425: HTErrorAdd(request, ERR_INFO, NO, HTERR_MOVED,
1.56 frystyk 426: (void *) url, (int) strlen(url),
427: "HTTPRedirect");
428: else if (code == 302)
1.57 frystyk 429: HTErrorAdd(request, ERR_INFO, NO, HTERR_FOUND,
1.56 frystyk 430: (void *) url, (int) strlen(url),
431: "HTTPRedirect");
432: else {
433: if (TRACE)
434: fprintf(stderr,
435: "Redirection. Weird, should never happen\n");
436: }
437:
438: /* Now use the new anchor instead of the old one */
439: anchor = HTAnchor_findAddress(url);
440: found = YES;
441: FREE(line);
442: break;
443: }
444: }
445: free(line);
446: }
447:
448: if (!found) {
449: int length = (int) strlen(line);
450: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
451: (void *) line, length < 50 ? length : 50, "HTTPRedirect");
452: }
453: return anchor;
454: }
455:
456:
1.55 frystyk 457: /* Load Document from HTTP Server HTLoadHTTP()
458: ** ==============================
459: **
460: ** Given a hypertext address, this routine loads a document.
461: **
462: ** On entry,
463: ** request This is the request structure
464: ** On exit,
465: ** returns <0 Error has occured
1.58 frystyk 466: ** HT_LOADED if return status 200 OK
467: ** HT_NO_DATA if return status 204 No Response
1.55 frystyk 468: */
469: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
470: {
471: char *url;
1.59 frystyk 472: int status = -1; /* The current status */
1.55 frystyk 473: http_info *http; /* Specific protocol information */
474:
475: if (!request || !request->anchor) {
476: if (TRACE) fprintf(stderr, "HTLoadHTTP.. Bad argument\n");
477: return -1;
478: }
479: url = HTAnchor_physical(request->anchor);
480: if (TRACE) fprintf(stderr, "HTTP........ Looking for `%s\'\n", url);
481:
1.59 frystyk 482: /* Initiate a new http structure and bind to request structure */
483: /* this is actually state HTTP_BEGIN, but it can't be in the state */
484: /* machine as we need the structure first. */
1.55 frystyk 485: if ((http = (http_info *) calloc(1, sizeof(http_info))) == NULL)
1.59 frystyk 486: outofmem(__FILE__, "HTLoadHTTP");
487: http->sockfd = -1; /* Invalid socket number */
488: http->request = request;
489: http->state = HTTP_IDLE;
490: request->net_info = (HTNetInfo *) http;
1.17 timbl 491:
1.59 frystyk 492: /*
493: ** Compose authorization information (this was moved here
494: ** from after the making of the connection so that the connection
495: ** wouldn't have to wait while prompting username and password
496: ** from the user). -- AL 13.10.93
497: */
1.55 frystyk 498: HTAA_composeAuth(request);
499: if (TRACE) {
500: if (request->authorization)
501: fprintf(stderr, "HTTP........ Sending Authorization: %s\n",
502: request->authorization);
503: else
504: fprintf(stderr, "HTTP........ Not sending authorization (yet)\n");
505: }
506:
1.64 frystyk 507: /* Now let's set up a connection and input buffer */
1.68 frystyk 508: if ((status = HTDoConnect((HTNetInfo *) http, url, TCP_PORT,
509: NULL, NO)) < 0) {
1.59 frystyk 510: if (TRACE)
511: fprintf(stderr, "HTTP........ Connection not established\n");
1.55 frystyk 512: if (status != HT_INTERRUPTED) {
513: char *unescaped = NULL;
514: StrAllocCopy(unescaped, url);
515: HTUnEscape(unescaped);
516: HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
517: (void *) unescaped, (int) strlen(unescaped),
518: "HTLoadHTTP");
519: free(unescaped);
520: }
1.59 frystyk 521: HTTPCleanup(http);
1.55 frystyk 522: return status;
523: }
524: if (TRACE) fprintf(stderr, "HTTP........ Connected, socket %d\n",
1.59 frystyk 525: http->sockfd);
1.55 frystyk 526:
1.59 frystyk 527: /* Compose the request and send it */
1.55 frystyk 528: if ((status = HTTPSendRequest(request, http, url)) < 0) {
1.59 frystyk 529: HTTPCleanup(http);
1.55 frystyk 530: return status;
531: }
1.2 timbl 532:
1.17 timbl 533: /* Read the response
534: ** -----------------
1.11 timbl 535: **
536: ** HTTP0 servers must return ASCII style text, though it can in
537: ** principle be just text without any markup at all.
538: ** Full HTTP servers must return a response
539: ** line and RFC822 style header. The response must therefore in
540: ** either case have a CRLF somewhere soon.
541: **
542: ** This is the theory. In practice, there are (1993) unfortunately
543: ** many binary documents just served up with HTTP0.9. This
544: ** means we have to preserve the binary buffer (on the assumption that
545: ** conversion from ASCII may lose information) in case it turns
546: ** out that we want the binary original.
1.2 timbl 547: */
1.53 luotonen 548:
549: CTRACE(stderr, "Waiting..... for response\n");
550:
1.37 luotonen 551: if (HTImProxy) {
1.24 luotonen 552:
1.22 luotonen 553: /*
554: ** Server as a gateway -- send body of the message
555: ** received from client (if any).
556: */
557: if (request->isoc && request->content_length > 0) {
558: int remain = request->content_length;
559: int i = remain;
560: char * buf;
561:
562: while (remain > 0 &&
563: (buf = HTInputSocket_getBlock(request->isoc, &i))) {
1.59 frystyk 564: int status = NETWRITE(http->sockfd, buf, i);
1.22 luotonen 565: if (status < 0) {
1.27 luotonen 566: CTRACE(stderr, "HTTPAccess.. Unable to forward body\n");
1.55 frystyk 567: HTErrorSysAdd(request, ERR_FATAL, NO, "NETWRITE");
1.59 frystyk 568: HTTPCleanup(http);
1.55 frystyk 569: return status;
1.22 luotonen 570: }
571: remain -= i;
572: i = remain;
573: }
574: }
1.23 luotonen 575:
576: /*
1.22 luotonen 577: ** Load results directly to client
578: */
1.59 frystyk 579: HTCopy(http->sockfd, request->output_stream);
1.67 duns 580: (*request->output_stream->isa->_free)(request->output_stream);
1.59 frystyk 581: HTTPCleanup(http);
1.22 luotonen 582: return HT_LOADED;
1.55 frystyk 583: } else { /* read response */
1.21 luotonen 584:
1.17 timbl 585: HTFormat format_in; /* Format arriving in the message */
1.59 frystyk 586: char *status_line = NULL;
1.68 frystyk 587: http->isoc = HTInputSocket_new(http->sockfd);
1.59 frystyk 588: status_line = HTInputSocket_getStatusLine(http->isoc);
1.2 timbl 589:
1.11 timbl 590: /* Kludge to trap binary responses from illegal HTTP0.9 servers.
591: ** First time we have enough, look at the stub in ASCII
592: ** and get out of here if it doesn't look right.
593: **
594: ** We also check for characters above 128 in the first few bytes, and
595: ** if we find them we forget the html default.
596: **
597: ** Bugs: A HTTP0.9 server returning a document starting "HTTP/"
598: ** will be taken as a HTTP 1.0 server. Failure.
599: ** An HTTP 0.9 server returning a binary document with
600: ** characters < 128 will be read as ASCII.
601: */
1.36 frystyk 602: /* If HTTP 0 response, then DO NOT CACHE (Henrik 14/02-94) */
1.68 frystyk 603: if (!status_line) {
604: if (PROT_TRACE)
605: fprintf(stderr,
606: "HTTP........ This seems like a HTTP 0.9 server\n");
1.59 frystyk 607: if (HTInputSocket_seemsBinary(http->isoc)) {
1.21 luotonen 608: format_in = HTAtom_for("www/unknown");
1.55 frystyk 609: } else {
1.21 luotonen 610: format_in = WWW_HTML;
611: }
1.59 frystyk 612: status = HTTPGetBody(request, http, format_in, NO);
1.55 frystyk 613: } else {
1.21 luotonen 614: /*
615: ** We now have a terminated server status line, and we have
616: ** checked that it is most probably a legal one. Parse it.
617: */
618: char server_version[VERSION_LENGTH+1];
619: int server_status;
620:
621: if (TRACE)
1.55 frystyk 622: fprintf(stderr, "HTTP Rx..... `%.70s\'\n", status_line);
623: {
624: char formatstr[20];
625: sprintf(formatstr, "%%%ds%%d", VERSION_LENGTH);
626: if (sscanf(status_line, formatstr, server_version,
627: &server_status) < 2) {
628: int length = (int) strlen(status_line);
629: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
630: (void *) status_line, length < 50 ? length : 50,
631: "HTLoadHTTP");
1.59 frystyk 632: HTInputSocket_free(http->isoc);
1.55 frystyk 633: free(http);
634: free(status_line);
635: return -1; /* Bad response */
636: }
637: *(server_version+VERSION_LENGTH) = '\0';
638: }
1.21 luotonen 639: format_in = HTAtom_for("www/mime");
1.7 timbl 640:
1.55 frystyk 641: /* Big switch for all response codes */
642: switch (server_status/100) {
1.2 timbl 643:
1.55 frystyk 644: case 2: /* Good: Got MIME object */
1.58 frystyk 645: switch (server_status) {
646: case 204: /* No response */
647: HTErrorAdd(request, ERR_INFO, NO, HTERR_NO_RESPONSE,
648: NULL, 0, "HTLoadHTTP");
649: status = HT_NO_DATA;
650: break; /* Don't get any body */
651: case 203: /* Partial */
652: HTErrorAdd(request, ERR_INFO, NO, HTERR_PARTIAL,
653: NULL, 0, "HTLoadHTTP");
654: /* Drop through to 200 as we still have to get the body */
655: case 200:
1.59 frystyk 656: status = HTTPGetBody(request, http, format_in, YES);
1.58 frystyk 657: break;
658: default:
659: {
660: int length = (int) strlen(status_line);
661: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
662: (void *) status_line, length < 50 ?
663: length : 50, "HTLoadHTTP");
664: }
665: status = -1;
666: break;
667: }
1.21 luotonen 668: break;
1.58 frystyk 669:
1.21 luotonen 670: case 3: /* Various forms of redirection */
1.55 frystyk 671: switch (server_status) {
672: case 301: /* Moved */
673: case 302: /* Found */
1.56 frystyk 674: {
675: HTParentAnchor *anchor;
676: if ((anchor = (HTParentAnchor *)
1.59 frystyk 677: HTTPRedirect(request, http,
1.56 frystyk 678: server_status)) != NULL) {
679: free(status_line);
1.59 frystyk 680: HTInputSocket_free(http->isoc);
681: HTTPCleanup(http);
1.68 frystyk 682: request->redirections++;
1.58 frystyk 683:
1.59 frystyk 684: /* If we have not reached the roof for redirects
685: then call HTLoadAnchor recursively but keep
686: the error_stack so that the user knows what
687: is going on */
1.68 frystyk 688: if (request->redirections < HTMaxRedirections) {
1.59 frystyk 689: if (HTLoadAnchorRecursive((HTAnchor *) anchor,
690: request) == YES)
691: return HT_LOADED;
692: else
693: return -1;
694: } else {
695: HTErrorAdd(request, ERR_FATAL, NO,
696: HTERR_MAX_REDIRECT, NULL, 0,
697: "HTTLoadHTTP");
1.56 frystyk 698: return -1;
1.59 frystyk 699: }
1.56 frystyk 700: }
701: }
702: break;
1.55 frystyk 703: case 303: /* Method */
1.56 frystyk 704: HTAlert("This client doesn't support automatic redirection of type `Method'");
705: status = -1;
1.55 frystyk 706: break;
707: case 304: /* Not modified Since */
708: {
709: char *unescaped = NULL;
710: StrAllocCopy(unescaped, url);
711: HTUnEscape(unescaped);
1.57 frystyk 712: HTErrorAdd(request, ERR_INFO, NO,
1.55 frystyk 713: HTERR_NOT_MODIFIED, (void *) unescaped,
714: (int) strlen(unescaped), "HTLoadHTTP");
715: free(unescaped);
716: }
717: status = HT_LOADED;
718: break;
719:
720: default:
721: {
722: int length = (int) strlen(status_line);
723: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
724: (void *) status_line, length < 50 ?
725: length : 50, "HTLoadHTTP");
726: }
727: status = -1;
728: break;
729: }
1.21 luotonen 730: break;
1.17 timbl 731:
1.21 luotonen 732: case 4: /* Access Authorization problem */
733: switch (server_status) {
1.68 frystyk 734: case 400:
735: {
736: char *unescaped = NULL;
737: StrAllocCopy(unescaped, url);
738: HTUnEscape(unescaped);
739: HTErrorAdd(request, ERR_FATAL, NO,
740: HTERR_BAD_REQUEST, (void *) unescaped,
741: (int) strlen(unescaped), "HTLoadHTTP");
742: free(unescaped);
743: }
744: status = -1;
745: break;
1.21 luotonen 746: case 401:
1.59 frystyk 747: parse_401_headers(request, http->isoc);
1.68 frystyk 748: if (TRACE) fprintf(stderr, "HTTP........ Closing socket %d to retry with Access Autorization\n", http->sockfd);
749: if ((status = NETCLOSE(http->sockfd)) < 0) {
750: HTErrorSysAdd(http->request, ERR_FATAL, NO,"NETCLOSE");
751: break;
752: }
753: http->sockfd = -1;
1.24 luotonen 754: if (HTAA_retryWithAuth(request, HTLoadHTTP)) {
1.21 luotonen 755: status = HT_LOADED;/* @@ THIS ONLY WORKS ON LINEMODE */
1.55 frystyk 756: break;
1.68 frystyk 757: } else {
758: char *unescaped = NULL;
759: StrAllocCopy(unescaped, url);
760: HTUnEscape(unescaped);
761: HTErrorAdd(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
762: (void *) unescaped,
763: (int) strlen(unescaped), "HTLoadHTTP");
764: free(unescaped);
765: status = -1;
1.21 luotonen 766: }
1.68 frystyk 767: break;
768: case 402:
769: {
770: char *unescaped = NULL;
771: StrAllocCopy(unescaped, url);
772: HTUnEscape(unescaped);
773: HTErrorAdd(request, ERR_FATAL, NO,
774: HTERR_PAYMENT_REQUIRED, (void *) unescaped,
775: (int) strlen(unescaped), "HTLoadHTTP");
776: free(unescaped);
777: }
778: status = -1;
779: break;
780: case 403:
781: {
782: char *unescaped = NULL;
783: StrAllocCopy(unescaped, url);
784: HTUnEscape(unescaped);
785: HTErrorAdd(request, ERR_FATAL, NO,
786: HTERR_FORBIDDEN, (void *) unescaped,
787: (int) strlen(unescaped), "HTLoadHTTP");
788: free(unescaped);
789: }
790: status = -1;
791: break;
792: case 404:
793: {
794: char *unescaped = NULL;
795: StrAllocCopy(unescaped, url);
796: HTUnEscape(unescaped);
797: HTErrorAdd(request, ERR_FATAL, NO,
798: HTERR_NOT_FOUND, (void *) unescaped,
799: (int) strlen(unescaped), "HTLoadHTTP");
800: free(unescaped);
801: }
802: status = -1;
803: break;
1.21 luotonen 804: default:
1.14 luotonen 805: {
1.55 frystyk 806: char *unescaped = NULL;
807: StrAllocCopy(unescaped, url);
808: HTUnEscape(unescaped);
1.68 frystyk 809: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
1.55 frystyk 810: (void *) unescaped,
811: (int) strlen(unescaped), "HTLoadHTTP");
812: free(unescaped);
813: #ifdef OLD_CODE
814: char *p1 = HTParse(url, "", PARSE_HOST);
1.21 luotonen 815: char * message;
816:
817: if (!(message = (char*)malloc(strlen(status_line) +
818: strlen(p1) + 100)))
819: outofmem(__FILE__, "HTTP 4xx status");
1.14 luotonen 820: sprintf(message,
1.21 luotonen 821: "HTTP server at %s replies:\n%s\n\n%s\n",
822: p1, status_line,
823: ((server_status == 401)
824: ? "Access Authorization package giving up.\n"
825: : ""));
1.22 luotonen 826: status = HTLoadError(request, server_status, message);
1.14 luotonen 827: free(message);
828: free(p1);
1.55 frystyk 829: #endif /* OLD_CODE */
1.14 luotonen 830: }
1.55 frystyk 831: status = -1;
832: break;
833: }
1.21 luotonen 834: break;
835:
836: case 5: /* I think you goofed */
837: {
1.55 frystyk 838: char *unescaped = NULL;
839: StrAllocCopy(unescaped, url);
840: HTUnEscape(unescaped);
841: HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL,
842: (void *) unescaped, (int) strlen(unescaped),
843: "HTLoadHTTP");
844: free(unescaped);
845: #ifdef OLD_CODE
846: char *p1 = HTParse(url, "", PARSE_HOST);
1.21 luotonen 847: char * message = (char*)malloc(strlen(status_line) +
848: strlen(p1) + 100);
849: if (!message) outofmem(__FILE__, "HTTP 5xx status");
850: sprintf(message,
851: "HTTP server at %s replies:\n%s", p1, status_line);
1.22 luotonen 852: status = HTLoadError(request, server_status, message);
1.21 luotonen 853: free(message);
854: free(p1);
1.55 frystyk 855: #endif
1.21 luotonen 856: }
1.55 frystyk 857: status = -1;
1.21 luotonen 858: break;
1.55 frystyk 859:
860: default: /* bad number */
861: {
862: int length = (int) strlen(status_line);
863: HTErrorAdd(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
864: (void *) status_line, length < 50 ? length : 50,
865: "HTLoadHTTP");
866: }
867: status = -1;
1.21 luotonen 868: break;
1.55 frystyk 869: }
870: FREE(status_line); /* Leak fix Henrik 18/02-94 */
1.17 timbl 871: }
1.48 timbl 872:
1.55 frystyk 873: /* Close the socket and free memory */
1.59 frystyk 874: HTInputSocket_free(http->isoc);
875: HTTPCleanup(http);
1.55 frystyk 876: return status; /* Good return */
877: }
878: }
1.1 timbl 879:
880: /* Protocol descriptor
881: */
882:
1.17 timbl 883: GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0, 0 };
1.55 frystyk 884:
885:
1.21 luotonen 886:
Webmaster