Annotation of libwww/Library/src/HTTP.c, revision 1.25
1.1 timbl 1: /* HyperText Tranfer Protocol - Client implementation HTTP.c
2: ** ==========================
1.2 timbl 3: **
4: ** Bugs:
5: ** Not implemented:
6: ** Forward
7: ** Redirection
8: ** Error handling
1.1 timbl 9: */
10:
11: /* Module parameters:
12: ** -----------------
13: **
14: ** These may be undefined and redefined by syspec.h
15: */
1.2 timbl 16:
17: /* Implements:
18: */
19: #include "HTTP.h"
20:
21: #define HTTP_VERSION "HTTP/1.0"
22: #define HTTP2 /* Version is greater than 0.9 */
23:
24: #define INIT_LINE_SIZE 1024 /* Start with line buffer this big */
25: #define LINE_EXTEND_THRESH 256 /* Minimum read size */
26: #define VERSION_LENGTH 20 /* for returned protocol version */
27:
28: /* Uses:
29: */
1.1 timbl 30: #include "HTParse.h"
31: #include "HTUtils.h"
32: #include "tcp.h"
33: #include "HTTCP.h"
34: #include "HTFormat.h"
1.2 timbl 35: #include <ctype.h>
36: #include "HTAlert.h"
37: #include "HTMIME.h"
1.5 timbl 38: #include "HTML.h" /* SCW */
39: #include "HTInit.h" /* SCW */
1.21 luotonen 40: #include "HTAccess.h" /* HTRequest */
1.14 luotonen 41: #include "HTAABrow.h" /* Access Authorization */
1.20 timbl 42: #include "HTTee.h" /* Tee off a cache stream */
43: #include "HTFWriter.h" /* Write to cache file */
1.1 timbl 44:
1.2 timbl 45: struct _HTStream {
46: HTStreamClass * isa; /* all we need to know */
47: };
48:
1.25 ! luotonen 49: #ifdef NeXT
! 50: #define S_ISDIR(m) (m & S_IFDIR)
! 51: #define S_ISREG(m) (m & S_IFREG)
! 52: #endif
1.2 timbl 53:
1.6 timbl 54: extern char * HTAppName; /* Application name: please supply */
55: extern char * HTAppVersion; /* Application version: please supply */
56:
1.19 timbl 57: PUBLIC BOOL HTCacheHTTP = YES; /* Enable caching of HTTP-retrieved files */
1.24 luotonen 58: PUBLIC char * HTGatewayCacheRoot = NULL;
59: PUBLIC unsigned long HTBytesCached = 0;
1.19 timbl 60:
1.23 luotonen 61: PRIVATE char * gateway_cache_filename ARGS1(CONST char *, name)
62: {
63: char * access;
64: char * host;
65: char * path;
66: char * cache_filename;
67:
1.24 luotonen 68: if (HTGatewayCacheRoot && name && !strchr(name, '?') &&
1.23 luotonen 69: (access = HTParse(name, "", PARSE_ACCESS))) {
70:
71: if (!strcmp(access, "file")) {
72: free(access);
73: return NULL;
74: }
75:
76: host = HTParse(name, "", PARSE_HOST);
77: path = HTParse(name, "", PARSE_PATH | PARSE_PUNCTUATION);
1.24 luotonen 78: cache_filename = (char*)malloc(strlen(HTGatewayCacheRoot) +
79: strlen(access) +
1.23 luotonen 80: (host ? strlen(host) : 0) +
1.24 luotonen 81: (path ? strlen(path) : 0) + 20);
1.23 luotonen 82: if (!cache_filename) outofmem(__FILE__, "cache_filename");
1.24 luotonen 83: sprintf(cache_filename, "%s/%s/%s%s",
84: HTGatewayCacheRoot, access, host, path);
85:
86: if (path[ strlen(path)-1 ] == '/')
87: strcat(cache_filename, "CERNhttpdINDEX");
1.23 luotonen 88:
89: FREE(access); FREE(host); FREE(path);
90: return cache_filename;
91: }
92: return NULL;
93: }
94:
95:
96: PRIVATE FILE * gateway_cache_lookup ARGS1(CONST char *, name)
97: {
98: char * cache_filename = gateway_cache_filename(name);
99:
100: if (cache_filename) {
1.24 luotonen 101: struct stat stat_info;
102:
103: if (stat(cache_filename, &stat_info) != -1) {
104: if (S_ISDIR(stat_info.st_mode)) {
105: StrAllocCat(cache_filename, "/CERNhttpdINDEX");
106: if (stat(cache_filename, &stat_info) == -1) {
107: free(cache_filename);
108: return NULL;
109: }
110: }
111: if (S_ISREG(stat_info.st_mode)) {
112: FILE * cache_file = fopen(cache_filename, "r");
113: if (cache_file && TRACE)
114: fprintf(stderr, "Cache: HIT \"%s\"\n", cache_filename);
115: free(cache_filename);
116: return cache_file;
117: }
118: }
1.23 luotonen 119: free(cache_filename);
120: }
121: return NULL;
122: }
123:
124:
1.24 luotonen 125: PRIVATE FILE * gateway_cache_create ARGS1(char *, cache_filename)
1.23 luotonen 126: {
1.24 luotonen 127: if (HTGatewayCacheRoot && cache_filename) {
128: char * cur = cache_filename + strlen(HTGatewayCacheRoot) + 1;
1.23 luotonen 129: FILE * cache_file;
130: struct stat stat_info;
1.24 luotonen 131: BOOL create = NO;
1.23 luotonen 132:
133: while ((cur = strchr(cur, '/'))) {
134: *cur = 0;
1.24 luotonen 135: if (create || -1 == stat(cache_filename, &stat_info)) {
136: create = YES; /* To avoid doing stat()s in vain */
1.23 luotonen 137: CTRACE(stderr, "Gateway cache: creating cache dir \"%s\"\n",
138: cache_filename);
139: if (-1 == mkdir(cache_filename, 0775)) {
140: CTRACE(stderr, "Gateway cache: can't create dir \"%s\"\n",
141: cache_filename);
142: return NULL;
143: }
144: CTRACE(stderr, "Gateway cache: created directory \"%s\"\n",
145: cache_filename);
146: }
1.24 luotonen 147: else {
148: if (S_ISREG(stat_info.st_mode)) {
149: char * tmp1 = (char*)malloc(strlen(cache_filename)+25);
150: char * tmp2 = (char*)malloc(strlen(cache_filename)+25);
151:
152: sprintf(tmp1, "%s.CERNhttpdINTERNAL", cache_filename);
153: sprintf(tmp2, "%s/CERNhttpdINDEX", cache_filename);
154:
155: CTRACE(stderr, "Moving \"%s\" to \"%s\"\n",
156: cache_filename, tmp1);
157: rename(cache_filename, tmp1);
158:
159: CTRACE(stderr, "Creating dir \"%s\"\n", cache_filename);
160: mkdir(cache_filename, 0775);
161:
162: CTRACE(stderr, "Moving \"%s\" to \"%s\"\n", tmp1, tmp2);
163: rename(tmp1, tmp2);
164:
165: free(tmp1);
166: free(tmp2);
167: }
168: else {
169: CTRACE(stderr,
170: "Gateway cache: dir \"%s\" already exists\n",
171: cache_filename);
172: }
173: }
1.23 luotonen 174: *cur = '/';
175: cur++;
176: }
177: cache_file = fopen(cache_filename, "w");
178: if (!cache_file) {
179: CTRACE(stderr, "Gateway cache: can't create cache file \"%s\"\n",
180: cache_filename);
181: }
182: return cache_file;
183: }
184: return NULL;
185: }
186:
187:
188:
189:
1.21 luotonen 190: PRIVATE void parse_401_headers ARGS2(HTRequest *, req,
191: HTInputSocket *, isoc)
192: {
193: HTAAScheme scheme;
194: char *line;
195: int num_schemes = 0;
196: HTList *valid_schemes = HTList_new();
197: HTAssocList **scheme_specifics = NULL;
198: char *template = NULL;
199:
200: /* Read server reply header lines */
201:
202: if (TRACE)
203: fprintf(stderr, "Server 401 reply header lines:\n");
204:
205: while (NULL != (line = HTInputSocket_getUnfoldedLine(isoc)) &&
206: *line != 0) {
207:
208: if (TRACE) fprintf(stderr, "%s\n", line);
209:
210: if (strchr(line, ':')) { /* Valid header line */
211:
212: char *p = line;
213: char *fieldname = HTNextField(&p);
214: char *arg1 = HTNextField(&p);
215: char *args = p;
216:
217: if (0==strcasecomp(fieldname, "WWW-Authenticate:")) {
218: if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
219: HTList_addObject(valid_schemes, (void*)scheme);
220: if (!scheme_specifics) {
221: int i;
222: scheme_specifics = (HTAssocList**)
223: malloc(HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
224: if (!scheme_specifics)
225: outofmem(__FILE__, "parse_401_headers");
226: for (i=0; i < HTAA_MAX_SCHEMES; i++)
227: scheme_specifics[i] = NULL;
228: }
229: scheme_specifics[scheme] = HTAA_parseArgList(args);
230: num_schemes++;
231: }
232: else if (TRACE) {
233: fprintf(stderr, "Unknown scheme `%s' %s\n",
234: (arg1 ? arg1 : "(null)"),
235: "in WWW-Authenticate: field");
236: }
237: }
238:
239: else if (0==strcasecomp(fieldname, "WWW-Protection-Template:")) {
240: if (TRACE)
241: fprintf(stderr, "Protection template set to `%s'\n", arg1);
242: StrAllocCopy(template, arg1);
243: }
244:
245: } /* if a valid header line */
246: else if (TRACE) {
247: fprintf(stderr, "Invalid header line `%s' ignored\n", line);
248: } /* else invalid header line */
249: } /* while header lines remain */
250:
251: req->valid_schemes = valid_schemes;
252: req->scheme_specifics = scheme_specifics;
253: req->prot_template = template;
254: }
255:
256:
257:
1.1 timbl 258: /* Load Document from HTTP Server HTLoadHTTP()
259: ** ==============================
260: **
261: ** Given a hypertext address, this routine loads a document.
262: **
263: **
264: ** On entry,
265: ** arg is the hypertext reference of the article to be loaded.
266: **
267: ** On exit,
268: ** returns >=0 If no error, a good socket number
269: ** <0 Error.
270: **
271: ** The socket must be closed by the caller after the document has been
272: ** read.
273: **
274: */
1.19 timbl 275: PUBLIC int HTLoadHTTP ARGS1 (HTRequest *, request)
1.1 timbl 276: {
1.22 luotonen 277: CONST char * arg = NULL;
1.1 timbl 278: int s; /* Socket number for returned data */
279: int status; /* tcp return */
1.10 timbl 280: char crlf[3]; /* A CR LF equivalent string */
1.3 timbl 281: HTStream * target = NULL; /* Unconverted data */
282:
1.2 timbl 283: CONST char* gate = 0; /* disable this feature */
1.1 timbl 284: SockA soc_address; /* Binary network address */
285: SockA * sin = &soc_address;
1.2 timbl 286: BOOL extensions = YES; /* Assume good HTTP server */
1.17 timbl 287:
1.22 luotonen 288: if (request->reason == HTAA_OK_GATEWAY) {
289: arg = request->translated;
1.24 luotonen 290: HTBytesCached = 0;
1.23 luotonen 291:
292: /*
293: ** Cache lookup
294: */
1.24 luotonen 295: if (HTGatewayCacheRoot && request->method == METHOD_GET &&
1.23 luotonen 296: !request->authorization && !request->arg_keywords) {
297:
298: FILE * cache_file = gateway_cache_lookup(arg);
299:
300: if (cache_file) {
301: HTFileCopy(cache_file, request->output_stream);
302: return HT_LOADED;
303: }
304:
305: } /* Cache lookup */
306:
1.22 luotonen 307: }
308: else {
309: arg = HTAnchor_physical(request->anchor);
310: StrAllocCopy(request->argument, arg);
311: }
312:
1.1 timbl 313: if (!arg) return -3; /* Bad if no name sepcified */
314: if (!*arg) return -2; /* Bad if name had zero length */
315:
316: /* Set up defaults:
317: */
318: #ifdef DECNET
1.2 timbl 319: sin->sdn_family = AF_DECnet; /* Family = DECnet, host order */
320: sin->sdn_objnum = DNP_OBJ; /* Default: http object number */
1.1 timbl 321: #else /* Internet */
1.2 timbl 322: sin->sin_family = AF_INET; /* Family = internet, host order */
323: sin->sin_port = htons(TCP_PORT); /* Default: http port */
1.1 timbl 324: #endif
325:
1.10 timbl 326: sprintf(crlf, "%c%c", CR, LF); /* To be corect on Mac, VM, etc */
327:
1.1 timbl 328: if (TRACE) {
329: if (gate) fprintf(stderr,
330: "HTTPAccess: Using gateway %s for %s\n", gate, arg);
331: else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg);
332: }
333:
334: /* Get node name and optional port number:
335: */
336: {
337: char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
338: int status = HTParseInet(sin, p1); /* TBL 920622 */
339: free(p1);
340: if (status) return status; /* No such host for example */
341: }
342:
1.15 luotonen 343: /*
344: ** Compose authorization information (this was moved here
345: ** from after the making of the connection so that the connection
346: ** wouldn't have to wait while prompting username and password
347: ** from the user). -- AL 13.10.93
348: */
349: #ifdef ACCESS_AUTH
1.21 luotonen 350: HTAA_composeAuth(request);
351: if (TRACE) {
352: if (request->authorization)
353: fprintf(stderr, "HTTP: Sending Authorization: %s\n",
354: request->authorization);
355: else
356: fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
1.15 luotonen 357: }
358: #endif /* ACCESS_AUTH */
1.1 timbl 359:
1.10 timbl 360: /* Now, let's get a socket set up from the server for the data:
1.1 timbl 361: */
362: #ifdef DECNET
363: s = socket(AF_DECnet, SOCK_STREAM, 0);
364: #else
365: s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
366: #endif
367: status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
368: if (status < 0) {
369: if (TRACE) fprintf(stderr,
370: "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno);
1.17 timbl 371:
1.1 timbl 372: return HTInetStatus("connect");
373: }
374:
375: if (TRACE) fprintf(stderr, "HTTP connected, socket %d\n", s);
376:
1.17 timbl 377:
378: /* Compose and send command
379: ** ------------------------
380: */
381: {
382: char *command; /* The whole command */
383:
1.1 timbl 384: /* Ask that node for the document,
385: ** omitting the host name & anchor if not gatewayed.
386: */
1.17 timbl 387: if (gate) {
388: command = malloc(4 + strlen(arg)+ 2 + 31);
389: if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
390: strcpy(command, "GET ");
391: strcat(command, arg);
392: } else { /* not gatewayed */
393: char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
394: command = malloc(4 + strlen(p1)+ 2 + 31);
395: if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
1.23 luotonen 396: if (request->method != METHOD_INVALID) {
397: strcpy(command, HTMethod_name(request->method));
1.22 luotonen 398: strcat(command, " ");
399: }
400: else {
401: strcpy(command, "GET ");
402: }
1.17 timbl 403: strcat(command, p1);
404: free(p1);
405: }
1.2 timbl 406: #ifdef HTTP2
1.17 timbl 407: if (extensions) {
408: strcat(command, " ");
409: strcat(command, HTTP_VERSION);
410: }
1.2 timbl 411: #endif
1.17 timbl 412:
413: strcat(command, crlf); /* CR LF, as in rfc 977 */
414:
415: if (extensions) {
1.21 luotonen 416:
1.17 timbl 417: int i;
418: HTAtom * present = WWW_PRESENT;
419: char line[256]; /*@@@@ */
1.21 luotonen 420: HTList *conversions[2];
421:
1.22 luotonen 422: if (!HTConversions) {
423: HTConversions = HTList_new();
424: HTFormatInit(HTConversions);
425: }
1.21 luotonen 426: conversions[0] = HTConversions;
427: conversions[1] = request->conversions;
428:
429: for (i=0; i<2; i++) {
430: HTList *cur = conversions[i];
431: HTPresentation *pres;
432:
433: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
434: if (pres->rep_out == present) {
435: if (pres->quality != 1.0) {
436: sprintf(line, "Accept: %s q=%.3f%c%c",
437: HTAtom_name(pres->rep),
438: pres->quality, CR, LF);
439: } else {
440: sprintf(line, "Accept: %s%c%c",
441: HTAtom_name(pres->rep), CR, LF);
442: }
443: StrAllocCat(command, line);
1.17 timbl 444: }
445: }
1.2 timbl 446: }
1.22 luotonen 447:
448: sprintf(line, "User-Agent: %s%s %s/%s libwww/%s%c%c",
449: request->user_agent ? request->user_agent : "",
450: request->user_agent ? " VIA Gateway" : "",
1.17 timbl 451: HTAppName ? HTAppName : "unknown",
452: HTAppVersion ? HTAppVersion : "0.0",
453: HTLibraryVersion, CR, LF);
454: StrAllocCat(command, line);
455:
1.22 luotonen 456: if (request->from) {
457: sprintf(line, "From: %s%c%c", request->from, CR, LF);
458: StrAllocCat(command, line);
459: }
460:
1.14 luotonen 461: #ifdef ACCESS_AUTH
1.21 luotonen 462: if (request->authorization != NULL) {
463: sprintf(line, "Authorization: %s%c%c",
464: request->authorization, CR, LF);
1.17 timbl 465: StrAllocCat(command, line);
466: }
467: #endif /* ACCESS_AUTH */
1.22 luotonen 468:
469: if (request->content_type) {
470: sprintf(line, "Content-Type: %s%c%c",
471: HTAtom_name(request->content_type), CR, LF);
472: StrAllocCat(command, line);
473: }
474:
475: if (request->content_length > 0) {
476: sprintf(line, "Content-Length: %d%c%c",
477: request->content_length, CR, LF);
478: StrAllocCat(command, line);
479: }
480:
481:
1.14 luotonen 482: }
1.17 timbl 483:
484: StrAllocCat(command, crlf); /* Blank line means "end" */
485:
486: if (TRACE) fprintf(stderr, "HTTP Tx: %s\n", command);
487:
488: /* Translate into ASCII if necessary
489: */
1.4 timbl 490: #ifdef NOT_ASCII
1.17 timbl 491: {
492: char * p;
493: for(p = command; *p; p++) {
494: *p = TOASCII(*p);
495: }
1.1 timbl 496: }
1.3 timbl 497: #endif
1.17 timbl 498:
499: status = NETWRITE(s, command, (int)strlen(command));
500: free(command);
501: if (status<0) {
502: if (TRACE) fprintf(stderr,
503: "HTTPAccess: Unable to send command.\n");
1.1 timbl 504: return HTInetStatus("send");
1.17 timbl 505: }
506: } /* compose and send command */
507:
1.2 timbl 508:
1.17 timbl 509: /* Read the response
510: ** -----------------
1.11 timbl 511: **
512: ** HTTP0 servers must return ASCII style text, though it can in
513: ** principle be just text without any markup at all.
514: ** Full HTTP servers must return a response
515: ** line and RFC822 style header. The response must therefore in
516: ** either case have a CRLF somewhere soon.
517: **
518: ** This is the theory. In practice, there are (1993) unfortunately
519: ** many binary documents just served up with HTTP0.9. This
520: ** means we have to preserve the binary buffer (on the assumption that
521: ** conversion from ASCII may lose information) in case it turns
522: ** out that we want the binary original.
1.2 timbl 523: */
1.3 timbl 524:
1.22 luotonen 525: if (request->reason == HTAA_OK_GATEWAY) {
1.24 luotonen 526:
527: char * cache_filename = NULL;
528:
1.22 luotonen 529: /*
530: ** Server as a gateway -- send body of the message
531: ** received from client (if any).
532: */
533: if (request->isoc && request->content_length > 0) {
534: int remain = request->content_length;
535: int i = remain;
536: char * buf;
537:
538: while (remain > 0 &&
539: (buf = HTInputSocket_getBlock(request->isoc, &i))) {
540: int status = NETWRITE(s, buf, i);
541: if (status < 0) {
542: CTRACE(stderr, "HTTPAccess: Unable to forward body\n");
543: return HTInetStatus("send");
544: }
545: remain -= i;
546: i = remain;
547: }
548: }
1.23 luotonen 549:
550: /*
551: ** Cache the document if it a GET request,
552: ** not protected and not a search request.
553: */
1.24 luotonen 554: if (HTGatewayCacheRoot && request->method == METHOD_GET &&
1.23 luotonen 555: !request->authorization && !request->arg_keywords) {
556:
1.24 luotonen 557: cache_filename = gateway_cache_filename(request->translated);
558: if (cache_filename) {
559: FILE * cache_file = gateway_cache_create(cache_filename);
560:
561: if (cache_file) {
562: request->output_stream = HTTee(request->output_stream,
563: HTFWriter_new(cache_file));
564: CTRACE(stderr, "Gateway cache: writing to cache file\n");
565: }
566: else {
567: FREE(cache_filename);
568: CTRACE(stderr,
569: "Gateway cache: couldn't create cache file\n");
570: }
1.23 luotonen 571: }
572: }
573: else CTRACE(stderr, "Gateway cache: not caching\n");
574:
1.22 luotonen 575: /*
576: ** Load results directly to client
577: */
578: HTCopy(s, request->output_stream);
1.25 ! luotonen 579: (*request->output_stream->isa->free)(request->output_stream);
1.24 luotonen 580: if (cache_filename) {
581: struct stat stat_info;
582: if (stat(cache_filename, &stat_info) != -1) {
583: HTBytesCached = stat_info.st_size;
584: CTRACE(stderr, "Cached %lu bytes to \"%s\"\n",
585: HTBytesCached, cache_filename);
586: }
587: else CTRACE(stderr, "Couldn't stat cache file \"%s\"\n",
588: cache_filename);
589: }
1.22 luotonen 590: return HT_LOADED;
591: }
592: else { /* read response */
1.21 luotonen 593:
1.17 timbl 594: HTFormat format_in; /* Format arriving in the message */
1.21 luotonen 595: HTInputSocket *isoc = HTInputSocket_new(s);
596: char * status_line = HTInputSocket_getStatusLine(isoc);
1.2 timbl 597:
1.11 timbl 598: /* Kludge to trap binary responses from illegal HTTP0.9 servers.
599: ** First time we have enough, look at the stub in ASCII
600: ** and get out of here if it doesn't look right.
601: **
602: ** We also check for characters above 128 in the first few bytes, and
603: ** if we find them we forget the html default.
604: **
605: ** Bugs: A HTTP0.9 server returning a document starting "HTTP/"
606: ** will be taken as a HTTP 1.0 server. Failure.
607: ** An HTTP 0.9 server returning a binary document with
608: ** characters < 128 will be read as ASCII.
609: */
1.21 luotonen 610: if (!status_line) { /* HTTP0 response */
611: if (HTInputSocket_seemsBinary(isoc)) {
612: format_in = HTAtom_for("www/unknown");
613: }
614: else {
615: format_in = WWW_HTML;
616: }
617: goto copy;
618: } /* end kludge */
619:
620: if (status_line) { /* Decode full HTTP response */
621: /*
622: ** We now have a terminated server status line, and we have
623: ** checked that it is most probably a legal one. Parse it.
624: */
625: char server_version[VERSION_LENGTH+1];
626: int server_status;
627:
628: if (TRACE)
629: fprintf(stderr, "HTTP Status Line: Rx: %.70s\n", status_line);
1.17 timbl 630:
1.21 luotonen 631: sscanf(status_line, "%20s%d", server_version, &server_status);
1.2 timbl 632:
1.21 luotonen 633: format_in = HTAtom_for("www/mime");
1.7 timbl 634:
1.21 luotonen 635: switch (server_status / 100) {
1.2 timbl 636:
1.21 luotonen 637: default: /* bad number */
638: HTAlert("Unknown status reply from server!");
639: break;
1.17 timbl 640:
1.21 luotonen 641: case 3: /* Various forms of redirection */
642: HTAlert(
1.17 timbl 643: "Redirection response from server is not handled by this client");
1.21 luotonen 644: break;
1.17 timbl 645:
1.21 luotonen 646: case 4: /* Access Authorization problem */
1.14 luotonen 647: #ifdef ACCESS_AUTH
1.21 luotonen 648: switch (server_status) {
649: case 401:
650: parse_401_headers(request, isoc);
651:
652: if (TRACE) fprintf(stderr, "%s %d %s\n",
653: "HTTP: close socket", s,
654: "to retry with Access Authorization");
655: HTInputSocket_free(isoc);
656: (void)NETCLOSE(s);
1.24 luotonen 657: if (HTAA_retryWithAuth(request, HTLoadHTTP)) {
1.21 luotonen 658: status = HT_LOADED;/* @@ THIS ONLY WORKS ON LINEMODE */
659: goto clean_up;
660: }
661: /* else falltrough */
662: default:
1.14 luotonen 663: {
1.21 luotonen 664: char *p1 = HTParse(gate ? gate : arg, "",
665: PARSE_HOST);
666: char * message;
667:
668: if (!(message = (char*)malloc(strlen(status_line) +
669: strlen(p1) + 100)))
670: outofmem(__FILE__, "HTTP 4xx status");
1.14 luotonen 671: sprintf(message,
1.21 luotonen 672: "HTTP server at %s replies:\n%s\n\n%s\n",
673: p1, status_line,
674: ((server_status == 401)
675: ? "Access Authorization package giving up.\n"
676: : ""));
1.22 luotonen 677: status = HTLoadError(request, server_status, message);
1.14 luotonen 678: free(message);
679: free(p1);
680: goto clean_up;
681: }
1.21 luotonen 682: } /* switch */
683: goto clean_up;
684: break;
685: #else
686: /* case 4 without Access Authorization falls through */
687: /* to case 5 (previously "I think I goofed"). -- AL */
688: #endif /* ACCESS_AUTH */
689:
690: case 5: /* I think you goofed */
691: {
692: char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
693: char * message = (char*)malloc(strlen(status_line) +
694: strlen(p1) + 100);
695: if (!message) outofmem(__FILE__, "HTTP 5xx status");
696: sprintf(message,
697: "HTTP server at %s replies:\n%s", p1, status_line);
1.22 luotonen 698: status = HTLoadError(request, server_status, message);
1.21 luotonen 699: free(message);
700: free(p1);
701: goto clean_up;
702: }
703: break;
1.17 timbl 704:
1.21 luotonen 705: case 2: /* Good: Got MIME object */
706: break;
1.17 timbl 707:
1.21 luotonen 708: } /* switch on response code */
1.17 timbl 709:
1.21 luotonen 710: } /* Full HTTP reply */
1.17 timbl 711:
712:
1.3 timbl 713: /* Set up the stream stack to handle the body of the message
714: */
1.21 luotonen 715:
1.13 duns 716: copy:
1.21 luotonen 717:
1.18 timbl 718: target = HTStreamStack(format_in, request);
1.21 luotonen 719:
1.17 timbl 720: if (!target) {
721: char buffer[1024]; /* @@@@@@@@ */
722: sprintf(buffer, "Sorry, no known way of converting %s to %s.",
723: HTAtom_name(format_in), HTAtom_name(request->output_format));
724: fprintf(stderr, "HTTP: %s", buffer);
1.22 luotonen 725: status = HTLoadError(request, 501, buffer);
1.17 timbl 726: goto clean_up;
727: }
728:
1.19 timbl 729: /* @@ Bug: The decision of whether or not to cache should also be
1.21 luotonen 730: ** made contingent on a IP address match or non match.
731: */
1.19 timbl 732: if (HTCacheHTTP) {
733: target = HTTee(target, HTCacheWriter(request, NULL, format_in,
1.21 luotonen 734: request->output_format,
735: request->output_stream));
1.19 timbl 736: }
737:
1.11 timbl 738: /* Push the data down the stream
1.3 timbl 739: ** We have to remember the end of the first buffer we just read
1.2 timbl 740: */
1.17 timbl 741: if (format_in == WWW_HTML) {
742: target = HTNetToText(target); /* Pipe through CR stripper */
743: }
1.21 luotonen 744:
1.17 timbl 745: (*target->isa->put_block)(target,
1.21 luotonen 746: isoc->input_pointer,
747: isoc->input_limit - isoc->input_pointer);
748: HTInputSocket_free(isoc);
1.17 timbl 749: HTCopy(s, target);
750:
751: (*target->isa->free)(target);
752: status = HT_LOADED;
1.11 timbl 753:
1.2 timbl 754: /* Clean up
1.1 timbl 755: */
1.17 timbl 756:
757: clean_up:
758: if (TRACE) fprintf(stderr, "HTTP: close socket %d.\n", s);
759: (void) NETCLOSE(s);
760:
761: return status; /* Good return */
1.3 timbl 762:
1.17 timbl 763: } /* read response */
764: } /* load HTTP */
1.1 timbl 765:
766: /* Protocol descriptor
767: */
768:
1.17 timbl 769: GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0, 0 };
1.21 luotonen 770:
Webmaster