1 /*
2  * $LynxId: HTTP.c,v 1.175 2018/05/04 20:07:43 Elliot.Thomas Exp $
3  *
4  * HyperText Tranfer Protocol	- Client implementation		HTTP.c
5  * ==========================
6  * Modified:
7  * 27 Jan 1994	PDM  Added Ari Luotonen's Fix for Reload when using proxy
8  *		     servers.
9  * 28 Apr 1997	AJL,FM Do Proxy Authorisation.
10  */
11 
12 #include <HTUtils.h>
13 #include <HTTP.h>
14 #include <LYUtils.h>
15 
16 #ifdef USE_SSL
17 #include <HTNews.h>
18 #endif
19 
20 #define HTTP_PORT   80
21 #define HTTPS_PORT  443
22 #define SNEWS_PORT  563
23 
24 #define INIT_LINE_SIZE		1536	/* 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 #include <HTParse.h>
29 #include <HTTCP.h>
30 #include <HTFormat.h>
31 #include <HTFile.h>
32 #include <HTAlert.h>
33 #include <HTMIME.h>
34 #include <HTML.h>
35 #include <HTInit.h>
36 #include <HTAABrow.h>
37 #include <HTAccess.h>		/* Are we using an HTTP gateway? */
38 
39 #include <LYCookie.h>
40 #include <LYGlobalDefs.h>
41 #include <GridText.h>
42 #include <LYStrings.h>
43 #include <LYUtils.h>
44 #include <LYrcFile.h>
45 #include <LYLeaks.h>
46 #include <LYCurses.h>
47 
48 #ifdef USE_SSL
49 
50 #ifdef USE_OPENSSL_INCL
51 #include <openssl/x509v3.h>
52 #endif
53 
54 #if defined(LIBRESSL_VERSION_NUMBER)
55 /* OpenSSL and LibreSSL version numbers do not correspond */
56 
57 #if LIBRESSL_VERSION_NUMBER >= 0x2060100fL
58 #define SSL_set_no_TLSV1()		SSL_set_min_proto_version(handle, TLS1_1_VERSION)
59 #endif
60 
61 #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
62 
63 #define SSLEAY_VERSION_NUMBER		OPENSSL_VERSION_NUMBER
64 #undef  SSL_load_error_strings
65 #undef  SSLeay_add_ssl_algorithms
66 #define ASN1_STRING_data		ASN1_STRING_get0_data
67 #define TLS_client_method()		SSLv23_client_method()
68 #define SSL_load_error_strings()	/* nothing */
69 #define SSLeay_add_ssl_algorithms()	/* nothing */
70 #define SSL_set_no_TLSV1()		SSL_set_min_proto_version(handle, TLS1_1_VERSION)
71 
72 #elif defined(SSLEAY_VERSION_NUMBER)
73 
74 #define TLS_client_method()		SSLv23_client_method()
75 
76 #endif
77 
78 #ifndef SSL_set_no_TLSV1
79 #define SSL_set_no_TLSV1()		SSL_set_options(handle, SSL_OP_NO_TLSv1)
80 #endif
81 
82 #ifdef USE_GNUTLS_INCL
83 #include <gnutls/x509.h>
84 #endif
85 
86 #endif /* USE_SSL */
87 
88 BOOLEAN reloading = FALSE;	/* Reloading => send no-cache pragma to proxy */
89 char *redirecting_url = NULL;	/* Location: value. */
90 BOOL permanent_redirection = FALSE;	/* Got 301 status? */
91 BOOL redirect_post_content = FALSE;	/* Don't convert to GET? */
92 
93 #ifdef USE_SSL
94 SSL_CTX *ssl_ctx = NULL;	/* SSL ctx */
95 SSL *SSL_handle = NULL;
96 static int ssl_okay;
97 
free_ssl_ctx(void)98 static void free_ssl_ctx(void)
99 {
100     if (ssl_ctx != NULL)
101 	SSL_CTX_free(ssl_ctx);
102 }
103 
needs_limit(const char * actual)104 static BOOL needs_limit(const char *actual)
105 {
106     return ((int) strlen(actual) > LYcols - 7) ? TRUE : FALSE;
107 }
108 
limited_string(const char * source,const char * actual)109 static char *limited_string(const char *source, const char *actual)
110 {
111     int limit = ((int) strlen(source)
112 		 - ((int) strlen(actual) - (LYcols - 10)));
113     char *temp = NULL;
114 
115     StrAllocCopy(temp, source);
116     if (limit < 0)
117 	limit = 0;
118     strcpy(temp + limit, "...");
119     return temp;
120 }
121 
122 /*
123  * If the error message is too long to fit in the line, truncate that to fit
124  * within the limits for prompting.
125  */
SSL_single_prompt(char ** target,const char * source)126 static void SSL_single_prompt(char **target, const char *source)
127 {
128     HTSprintf0(target, SSL_FORCED_PROMPT, source);
129     if (needs_limit(*target)) {
130 	char *temp = limited_string(source, *target);
131 
132 	*target = NULL;
133 	HTSprintf0(target, SSL_FORCED_PROMPT, temp);
134 	free(temp);
135     }
136 }
137 
SSL_double_prompt(char ** target,const char * format,const char * arg1,const char * arg2)138 static void SSL_double_prompt(char **target, const char *format, const char
139 			      *arg1, const char *arg2)
140 {
141     HTSprintf0(target, format, arg1, arg2);
142     if (needs_limit(*target)) {
143 	char *parg2 = limited_string(arg2, *target);
144 
145 	*target = NULL;
146 	HTSprintf0(target, format, arg1, parg2);
147 	if (needs_limit(*target)) {
148 	    char *parg1 = limited_string(arg1, *target);
149 
150 	    *target = NULL;
151 	    HTSprintf0(target, format, parg1, parg2);
152 	    free(parg1);
153 	}
154 	free(parg2);
155     }
156 }
157 
HTSSLCallback(int preverify_ok,X509_STORE_CTX * x509_ctx GCC_UNUSED)158 static int HTSSLCallback(int preverify_ok, X509_STORE_CTX * x509_ctx GCC_UNUSED)
159 {
160     char *msg = NULL;
161     int result = 1;
162 
163 #ifdef USE_X509_SUPPORT
164     HTSprintf0(&msg,
165 	       gettext("SSL callback:%s, preverify_ok=%d, ssl_okay=%d"),
166 	       X509_verify_cert_error_string((long) X509_STORE_CTX_get_error(x509_ctx)),
167 	       preverify_ok, ssl_okay);
168     _HTProgress(msg);
169     FREE(msg);
170 #endif
171 
172 #ifndef USE_NSS_COMPAT_INCL
173     if (!(preverify_ok || ssl_okay || ssl_noprompt)) {
174 #ifdef USE_X509_SUPPORT
175 	SSL_single_prompt(&msg,
176 			  X509_verify_cert_error_string((long)
177 							X509_STORE_CTX_get_error(x509_ctx)));
178 	if (HTForcedPrompt(ssl_noprompt, msg, NO))
179 	    ssl_okay = 1;
180 	else
181 	    result = 0;
182 #endif
183 
184 	FREE(msg);
185     }
186 #endif
187     return result;
188 }
189 
HTGetSSLHandle(void)190 SSL *HTGetSSLHandle(void)
191 {
192 #ifdef USE_GNUTLS_INCL
193     static char *certfile = NULL;
194 #endif
195     static char *client_keyfile = NULL;
196     static char *client_certfile = NULL;
197 
198     if (ssl_ctx == NULL) {
199 	/*
200 	 * First time only.
201 	 */
202 #if SSLEAY_VERSION_NUMBER < 0x0800
203 	if ((ssl_ctx = SSL_CTX_new()) != NULL) {
204 	    X509_set_default_verify_paths(ssl_ctx->cert);
205 	}
206 #else
207 	SSLeay_add_ssl_algorithms();
208 	if ((ssl_ctx = SSL_CTX_new(TLS_client_method())) != NULL) {
209 		/* Always disable SSLv2 & SSLv3 to "mitigate POODLE vulnerability". */
210 	    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
211 #ifdef SSL_OP_NO_COMPRESSION
212 	    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_COMPRESSION);
213 #endif
214 #ifdef SSL_MODE_AUTO_RETRY
215 	    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
216 #endif
217 #ifdef SSL_MODE_RELEASE_BUFFERS
218 	    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
219 #endif
220 	    SSL_CTX_set_default_verify_paths(ssl_ctx);
221 	    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, HTSSLCallback);
222 	}
223 #endif /* SSLEAY_VERSION_NUMBER < 0x0800 */
224 #if defined(USE_PROGRAM_DIR) & !defined(USE_GNUTLS_INCL)
225 	if (ssl_ctx != NULL) {
226 	    X509_LOOKUP *lookup;
227 
228 	    lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
229 					   X509_LOOKUP_file());
230 	    if (lookup != NULL) {
231 		char *certfile = NULL;
232 
233 		HTSprintf0(&certfile, "%s\\cert.pem", program_dir);
234 		X509_LOOKUP_load_file(lookup, certfile, X509_FILETYPE_PEM);
235 		FREE(certfile);
236 	    }
237 	}
238 #endif
239 #ifdef USE_GNUTLS_INCL
240 	if ((certfile = LYGetEnv("SSL_CERT_FILE")) != NULL) {
241 	    CTRACE((tfp,
242 		    "HTGetSSLHandle: certfile is set to %s by SSL_CERT_FILE\n",
243 		    certfile));
244 	} else {
245 	    if (non_empty(SSL_cert_file)) {
246 		certfile = SSL_cert_file;
247 		CTRACE((tfp,
248 			"HTGetSSLHandle: certfile is set to %s by config SSL_CERT_FILE\n",
249 			certfile));
250 	    }
251 #if defined(USE_PROGRAM_DIR)
252 	    else {
253 		HTSprintf0(&(certfile), "%s\\cert.pem", program_dir);
254 		CTRACE((tfp,
255 			"HTGetSSLHandle: certfile is set to %s by installed directory\n", certfile));
256 	    }
257 #endif
258 	}
259 #endif
260 	atexit(free_ssl_ctx);
261     }
262 
263     if (non_empty(SSL_client_key_file)) {
264 	client_keyfile = SSL_client_key_file;
265 	CTRACE((tfp,
266 		"HTGetSSLHandle: client key file is set to %s by config SSL_CLIENT_KEY_FILE\n",
267 		client_keyfile));
268     }
269 
270     if (non_empty(SSL_client_cert_file)) {
271 	client_certfile = SSL_client_cert_file;
272 	CTRACE((tfp,
273 		"HTGetSSLHandle: client cert file is set to %s by config SSL_CLIENT_CERT_FILE\n",
274 		client_certfile));
275     }
276 #ifdef USE_GNUTLS_INCL
277     ssl_ctx->certfile = certfile;
278     ssl_ctx->certfile_type = GNUTLS_X509_FMT_PEM;
279     ssl_ctx->client_keyfile = client_keyfile;
280     ssl_ctx->client_keyfile_type = GNUTLS_X509_FMT_PEM;
281     ssl_ctx->client_certfile = client_certfile;
282     ssl_ctx->client_certfile_type = GNUTLS_X509_FMT_PEM;
283 #elif SSLEAY_VERSION_NUMBER >= 0x0930
284     if (client_certfile != NULL) {
285 	if (client_keyfile == NULL) {
286 	    client_keyfile = client_certfile;
287 	}
288 	SSL_CTX_use_certificate_chain_file(ssl_ctx, client_certfile);
289 	SSL_CTX_use_PrivateKey_file(ssl_ctx, client_keyfile, SSL_FILETYPE_PEM);
290     }
291 #endif
292     ssl_okay = 0;
293     return (SSL_new(ssl_ctx));
294 }
295 
HTSSLInitPRNG(void)296 void HTSSLInitPRNG(void)
297 {
298 #if SSLEAY_VERSION_NUMBER >= 0x00905100
299     if (RAND_status() == 0) {
300 	char rand_file[256];
301 	time_t t;
302 	long l, seed;
303 
304 #ifndef _WINDOWS
305 	pid_t pid;
306 
307 #else
308 	DWORD pid;
309 #endif
310 
311 	t = time(NULL);
312 
313 #ifndef _WINDOWS
314 	pid = getpid();
315 #else
316 	pid = GetCurrentThreadId();
317 #endif
318 
319 	RAND_file_name(rand_file, 256L);
320 	CTRACE((tfp, "HTTP: Seeding PRNG\n"));
321 	/* Seed as much as 1024 bytes from RAND_file_name */
322 	RAND_load_file(rand_file, 1024L);
323 	/* Seed in time (mod_ssl does this) */
324 	RAND_seed((unsigned char *) &t, (int) sizeof(time_t));
325 
326 	/* Seed in pid (mod_ssl does this) */
327 	RAND_seed((unsigned char *) &pid, (int) sizeof(pid));
328 	/* Initialize system's random number generator */
329 	RAND_bytes((unsigned char *) &seed, (int) sizeof(long));
330 
331 	lynx_srand((unsigned) seed);
332 	while (RAND_status() == 0) {
333 	    /* Repeatedly seed the PRNG using the system's random number generator until it has been seeded with enough data */
334 	    l = lynx_rand();
335 	    RAND_seed((unsigned char *) &l, (int) sizeof(long));
336 	}
337 	/* Write a rand_file */
338 	RAND_write_file(rand_file);
339     }
340 #endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
341     return;
342 }
343 
344 #define HTTP_NETREAD(sock, buff, size, handle) \
345 	(handle \
346 	 ? SSL_read(handle, buff, size) \
347 	 : NETREAD(sock, buff, size))
348 
349 #define HTTP_NETWRITE(sock, buff, size, handle) \
350 	(handle \
351 	 ? SSL_write(handle, buff, size) \
352 	 : NETWRITE(sock, buff, size))
353 
354 #define HTTP_NETCLOSE(sock, handle)  \
355 	{ (void)NETCLOSE(sock); \
356 	  if (handle) \
357 	      SSL_free(handle); \
358 	  SSL_handle = handle = NULL; \
359 	}
360 
361 #else
362 #define HTTP_NETREAD(a, b, c, d)   NETREAD(a, b, c)
363 #define HTTP_NETWRITE(a, b, c, d)  NETWRITE(a, b, c)
364 #define HTTP_NETCLOSE(a, b)  (void)NETCLOSE(a)
365 #endif /* USE_SSL */
366 
367 #ifdef _WINDOWS			/* 1997/11/06 (Thu) 13:00:08 */
368 
369 #define	BOX_TITLE	"Lynx " __FILE__
370 #define	BOX_FLAG	(MB_ICONINFORMATION | MB_SETFOREGROUND)
371 
372 typedef struct {
373     int fd;
374     char *buf;
375     int len;
376 } recv_data_t;
377 
378 int ws_read_per_sec = 0;
379 static int ws_errno = 0;
380 
381 static DWORD g_total_times = 0;
382 static DWORD g_total_bytes = 0;
383 
384 /* The same like read, but takes care of EINTR and uses select to
385    timeout the stale connections.  */
386 
ws_read(int fd,char * buf,int len)387 static int ws_read(int fd, char *buf, int len)
388 {
389     int res;
390     int retry = 3;
391 
392     do {
393 	res = recv(fd, buf, len, 0);
394 	if (WSAEWOULDBLOCK == WSAGetLastError()) {
395 	    Sleep(100);
396 	    if (retry-- > 0)
397 		continue;
398 	}
399     } while (res == SOCKET_ERROR && SOCKET_ERRNO == EINTR);
400 
401     return res;
402 }
403 
404 #define DWORD_ERR ((DWORD)-1)
405 
_thread_func(void * p)406 static DWORD __stdcall _thread_func(void *p)
407 {
408     DWORD result;
409     int i, val;
410     recv_data_t *q = (recv_data_t *) p;
411 
412     i = 0;
413     i++;
414     val = ws_read(q->fd, q->buf, q->len);
415 
416     if (val == SOCKET_ERROR) {
417 	ws_errno = WSAGetLastError();
418 #if 0
419 	char buff[256];
420 
421 	sprintf(buff, "Thread read: %d, error (%ld), fd = %d, len = %d",
422 		i, ws_errno, q->fd, q->len);
423 	MessageBox(NULL, buff, BOX_TITLE, BOX_FLAG);
424 #endif
425 	result = DWORD_ERR;
426     } else {
427 	result = val;
428     }
429 
430     return result;
431 }
432 
433 /* The same like read, but takes care of EINTR and uses select to
434    timeout the stale connections.  */
435 
ws_netread(int fd,char * buf,int len)436 int ws_netread(int fd, char *buf, int len)
437 {
438     int i;
439     char buff[256];
440 
441     /* 1998/03/30 (Mon) 09:01:21 */
442     HANDLE hThread;
443     DWORD dwThreadID;
444     DWORD exitcode = 0;
445     DWORD ret_val = DWORD_ERR;
446     DWORD val, process_time, now_TickCount, save_TickCount;
447 
448     static recv_data_t para;
449 
450 #define TICK	5
451 #define STACK_SIZE	0x2000uL
452 
453     EnterCriticalSection(&critSec_READ);
454 
455     para.fd = fd;
456     para.buf = buf;
457     para.len = len;
458 
459     ws_read_per_sec = 0;
460     save_TickCount = GetTickCount();
461 
462     hThread = CreateThread(NULL, STACK_SIZE,
463 			   _thread_func,
464 			   (void *) &para, 0UL, &dwThreadID);
465 
466     if (hThread == 0) {
467 	HTInfoMsg("CreateThread Failed (read)");
468 	goto read_exit;
469     }
470 
471     i = 0;
472     while (1) {
473 	val = WaitForSingleObject(hThread, 1000 / TICK);
474 	i++;
475 	if (val == WAIT_FAILED) {
476 	    HTInfoMsg("Wait Failed");
477 	    ret_val = DWORD_ERR;
478 	    break;
479 	} else if (val == WAIT_TIMEOUT) {
480 	    i++;
481 	    if (i / TICK > (AlertSecs + 2)) {
482 		sprintf(buff, "Read Waiting (%2d.%01d) for %d Bytes",
483 			i / TICK, (i % TICK) * 10 / TICK, len);
484 		SetConsoleTitle(buff);
485 	    }
486 	    if (win32_check_interrupt() || ((i / TICK) > lynx_timeout)) {
487 		if (CloseHandle(hThread) == FALSE) {
488 		    HTInfoMsg("Thread terminate Failed");
489 		}
490 		WSASetLastError(ETIMEDOUT);
491 		ret_val = HT_INTERRUPTED;
492 		break;
493 	    }
494 	} else if (val == WAIT_OBJECT_0) {
495 	    if (GetExitCodeThread(hThread, &exitcode) == FALSE) {
496 		exitcode = DWORD_ERR;
497 	    }
498 	    if (CloseHandle(hThread) == FALSE) {
499 		HTInfoMsg("Thread terminate Failed");
500 	    }
501 	    now_TickCount = GetTickCount();
502 	    if (now_TickCount >= save_TickCount)
503 		process_time = now_TickCount - save_TickCount;
504 	    else
505 		process_time = now_TickCount + (0xffffffff - save_TickCount);
506 
507 	    if (process_time == 0)
508 		process_time = 1;
509 	    g_total_times += process_time;
510 
511 	    /*
512 	     * DWORD is unsigned, and could be an error code which is signed.
513 	     */
514 	    if ((long) exitcode > 0)
515 		g_total_bytes += exitcode;
516 
517 	    ws_read_per_sec = g_total_bytes;
518 	    if (ws_read_per_sec > 2000000) {
519 		if (g_total_times > 1000)
520 		    ws_read_per_sec /= (g_total_times / 1000);
521 	    } else {
522 		ws_read_per_sec *= 1000;
523 		ws_read_per_sec /= g_total_times;
524 	    }
525 
526 	    ret_val = exitcode;
527 	    break;
528 	}
529     }				/* end while(1) */
530 
531   read_exit:
532     LeaveCriticalSection(&critSec_READ);
533     return ret_val;
534 }
535 #endif /* _WINDOWS */
536 
537 /*
538  * RFC-1738 says we can have user/password using these ASCII characters
539  *    safe           = "$" | "-" | "_" | "." | "+"
540  *    extra          = "!" | "*" | "'" | "(" | ")" | ","
541  *    hex            = digit | "A" | "B" | "C" | "D" | "E" | "F" |
542  *                             "a" | "b" | "c" | "d" | "e" | "f"
543  *    escape         = "%" hex hex
544  *    unreserved     = alpha | digit | safe | extra
545  *    uchar          = unreserved | escape
546  *    user           = *[ uchar | ";" | "?" | "&" | "=" ]
547  *    password       = *[ uchar | ";" | "?" | "&" | "=" ]
548  * and we cannot have a password without user, i.e., no leading ":"
549  * and ":", "@", "/" must be encoded, i.e., will not appear as such.
550  *
551  * However, in a URL
552  *    //<user>:<password>@<host>:<port>/<url-path>
553  * valid characters in the host are different, not allowing most of those
554  * punctuation characters.
555  *
556  * RFC-3986 amends this, using
557  *     userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
558  *     unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
559  *     reserved      = gen-delims / sub-delims
560  *     gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
561  *     sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
562  *                     / "*" / "+" / "," / ";" / "="
563  * and
564  *     host          = IP-literal / IPv4address / reg-name
565  *     reg-name      = *( unreserved / pct-encoded / sub-delims )
566  */
HTSkipToAt(char * host,int * gen_delims)567 char *HTSkipToAt(char *host, int *gen_delims)
568 {
569     char *result = 0;
570     char *s = host;
571     int pass = 0;
572     int ch;
573     int last = -1;
574 
575     *gen_delims = 0;
576     while ((ch = UCH(*s)) != '\0') {
577 	if (ch == ':') {
578 	    if (pass++)
579 		break;
580 	} else if (ch == '@') {
581 	    if (s != host && last != ':')
582 		result = s;
583 	    break;
584 	} else if (RFC_3986_GEN_DELIMS(ch)) {
585 	    *gen_delims += 1;
586 	    if (!RFC_3986_GEN_DELIMS(s[1]))
587 		break;
588 	} else if (ch == '%') {
589 	    if (!(isxdigit(UCH(s[1])) && isxdigit(UCH(s[2]))))
590 		break;
591 	} else if (!(RFC_3986_UNRESERVED(ch) ||
592 		     RFC_3986_SUB_DELIMS(ch))) {
593 	    break;
594 	}
595 	++s;
596 	last = ch;
597     }
598     return result;
599 }
600 
fake_hostname(char * auth)601 static char *fake_hostname(char *auth)
602 {
603     char *result = NULL;
604     char *colon = NULL;
605 
606     StrAllocCopy(result, auth);
607     if ((colon = strchr(result, ':')) != 0)
608 	*colon = '\0';
609     if (strchr(result, '.') == 0)
610 	FREE(result);
611     return result;
612 }
613 
614 /*
615  * Strip any username from the given string so we retain only the host.
616  */
strip_userid(char * host,int parse_only)617 void strip_userid(char *host, int parse_only)
618 {
619     int gen_delims = 0;
620     char *p1 = host;
621     char *p2 = HTSkipToAt(host, &gen_delims);
622 
623     if (p2 != 0) {
624 	char *msg = NULL;
625 	char *auth = NULL;
626 	char *fake = NULL;
627 	char *p3 = p2;
628 	int sub_delims = 0;
629 	int my_delimit = UCH(*p2);
630 	int do_trimming = (my_delimit == '@');
631 
632 	*p2++ = '\0';
633 
634 	StrAllocCopy(auth, host);
635 
636 	/*
637 	 * Trailing "gen-delims" demonstrates that there is no user/password.
638 	 */
639 	while ((p3 != host) && RFC_3986_GEN_DELIMS(p3[-1])) {
640 	    *(--p3) = '\0';
641 	}
642 	/*
643 	 * While legal, punctuation-only user/password is questionable.
644 	 */
645 	while ((p3 != host) && RFC_3986_SUB_DELIMS(p3[-1])) {
646 	    ++sub_delims;
647 	    *(--p3) = '\0';
648 	}
649 	/*
650 	 * Trim trailing "gen-delims" from the real hostname.
651 	 */
652 	for (p3 = p2; *p3 != '\0'; ++p3) {
653 	    if (RFC_3986_GEN_DELIMS(*p3)) {
654 		*p3 = '\0';
655 		break;
656 	    }
657 	}
658 	CTRACE((tfp, "trim auth:    result:`%s'\n", host));
659 
660 	if (gen_delims || strcmp(host, auth)) {
661 	    do_trimming = !gen_delims;
662 	}
663 	if (*host == '\0' && sub_delims) {
664 	    HTSprintf0(&msg,
665 		       gettext("User/password contains only punctuation: %s"),
666 		       auth);
667 	} else if ((fake = fake_hostname(host)) != NULL) {
668 	    HTSprintf0(&msg,
669 		       gettext("User/password may be confused with hostname: '%s' (e.g, '%s')"),
670 		       auth, fake);
671 	}
672 	if (msg != 0 && !parse_only)
673 	    HTAlert(msg);
674 	if (do_trimming) {
675 	    while ((*p1++ = *p2++) != '\0') {
676 		;
677 	    }
678 	    CTRACE((tfp, "trim host:    result:`%s'\n", host));
679 	}
680 	FREE(fake);
681 	FREE(auth);
682 	FREE(msg);
683     }
684 }
685 
686 /*
687  * Check if the user's options specified to use the given encoding.  Normally
688  * all encodings with compiled-in support are specified (encodingALL).
689  */
acceptEncoding(int code)690 static BOOL acceptEncoding(int code)
691 {
692     BOOL result = FALSE;
693 
694     if ((code & LYAcceptEncoding) != 0) {
695 	const char *program = 0;
696 
697 	switch (code) {
698 	case encodingGZIP:
699 	    program = HTGetProgramPath(ppGZIP);
700 	    break;
701 	case encodingDEFLATE:
702 	    program = HTGetProgramPath(ppINFLATE);
703 	    break;
704 	case encodingCOMPRESS:
705 	    program = HTGetProgramPath(ppCOMPRESS);
706 	    break;
707 	case encodingBZIP2:
708 	    program = HTGetProgramPath(ppBZIP2);
709 	    break;
710 	default:
711 	    break;
712 	}
713 	/*
714 	 * FIXME:  if lynx did not rely upon external programs to decompress
715 	 * files for external viewers, this check could be relaxed.
716 	 */
717 	result = (BOOL) (program != 0);
718     }
719     return result;
720 }
721 
722 #ifdef USE_SSL
show_cert_issuer(X509 * peer_cert GCC_UNUSED)723 static void show_cert_issuer(X509 * peer_cert GCC_UNUSED)
724 {
725 #if defined(USE_OPENSSL_INCL) || defined(USE_GNUTLS_FUNCS)
726     char ssl_dn[1024];
727     char *msg = NULL;
728 
729     X509_NAME_oneline(X509_get_issuer_name(peer_cert), ssl_dn, (int) sizeof(ssl_dn));
730     HTSprintf0(&msg, gettext("Certificate issued by: %s"), ssl_dn);
731     _HTProgress(msg);
732     FREE(msg);
733 #elif defined(USE_GNUTLS_INCL)
734     /* the OpenSSL "compat" code compiles but dumps core with GNU TLS */
735 #endif
736 }
737 #endif
738 
739 /*
740  * Remove IPv6 brackets (and any port-number) from the given host-string.
741  */
742 #ifdef USE_SSL
StripIpv6Brackets(char * host)743 static char *StripIpv6Brackets(char *host)
744 {
745     int port_number;
746     char *p;
747 
748     if ((p = HTParsePort(host, &port_number)) != 0)
749 	*p = '\0';
750 
751     if (*host == '[') {
752 	p = host + strlen(host) - 1;
753 	if (*p == ']') {
754 	    *p = '\0';
755 	    ++host;
756 	}
757     }
758     return host;
759 }
760 #endif
761 
762 /*		Load Document from HTTP Server			HTLoadHTTP()
763  *		==============================
764  *
765  *	Given a hypertext address, this routine loads a document.
766  *
767  *
768  *  On entry,
769  *	arg	is the hypertext reference of the article to be loaded.
770  *
771  *  On exit,
772  *	returns >=0	If no error, a good socket number
773  *		<0	Error.
774  *
775  *	The socket must be closed by the caller after the document has been
776  *	read.
777  *
778  */
HTLoadHTTP(const char * arg,HTParentAnchor * anAnchor,HTFormat format_out,HTStream * sink)779 static int HTLoadHTTP(const char *arg,
780 		      HTParentAnchor *anAnchor,
781 		      HTFormat format_out,
782 		      HTStream *sink)
783 {
784     static char empty[1];
785     int s;			/* Socket number for returned data */
786     const char *url = arg;	/* The URL which get_physical() returned */
787     bstring *command = NULL;	/* The whole command */
788     char *eol;			/* End of line if found */
789     char *start_of_data;	/* Start of body of reply */
790     int status;			/* tcp return */
791     off_t bytes_already_read;
792     char crlf[3];		/* A CR LF equivalent string */
793     HTStream *target;		/* Unconverted data */
794     HTFormat format_in;		/* Format arriving in the message */
795     BOOL do_head = FALSE;	/* Whether or not we should do a head */
796     BOOL do_post = FALSE;	/* ARE WE posting ? */
797     const char *METHOD;
798 
799     char *line_buffer = NULL;
800     char *line_kept_clean = NULL;
801 
802 #ifdef SH_EX			/* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
803     int real_length_of_line = 0;
804 #endif
805     BOOL extensions;		/* Assume good HTTP server */
806     char *linebuf = NULL;
807     char temp[80];
808     BOOL first_Accept = TRUE;
809     BOOL show_401 = FALSE;
810     BOOL show_407 = FALSE;
811     BOOL auth_proxy = NO;	/* Generate a proxy authorization. - AJL */
812 
813     int length, rawlength, rv;
814     int server_status = 0;
815     BOOL doing_redirect, already_retrying = FALSE;
816     int len = 0;
817 
818 #ifdef USE_SSL
819     unsigned long SSLerror;
820     BOOL do_connect = FALSE;	/* ARE WE going to use a proxy tunnel ? */
821     BOOL did_connect = FALSE;	/* ARE WE actually using a proxy tunnel ? */
822     const char *connect_url = NULL;	/* The URL being proxied */
823     char *connect_host = NULL;	/* The host being proxied */
824     SSL *handle = NULL;		/* The SSL handle */
825     X509 *peer_cert;		/* The peer certificate */
826     char ssl_dn[1024];
827     char *cert_host;
828     char *ssl_host;
829     char *p;
830     char *msg = NULL;
831     int status_sslcertcheck;
832     char *ssl_dn_start;
833     char *ssl_all_cns = NULL;
834 
835 #ifdef USE_GNUTLS_INCL
836     int ret;
837     unsigned tls_status;
838 #endif
839 
840 #if (SSLEAY_VERSION_NUMBER >= 0x0900) && !defined(USE_GNUTLS_FUNCS)
841     BOOL try_tls = TRUE;
842 #endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
843     SSL_handle = NULL;
844 #else
845     void *handle = NULL;
846 #endif /* USE_SSL */
847 
848     if (anAnchor->isHEAD)
849 	do_head = TRUE;
850     else if (anAnchor->post_data)
851 	do_post = TRUE;
852 
853     if (!url) {
854 	status = -3;
855 	_HTProgress(BAD_REQUEST);
856 	goto done;
857     }
858     if (!*url) {
859 	status = -2;
860 	_HTProgress(BAD_REQUEST);
861 	goto done;
862     }
863 #ifdef USE_SSL
864     if (using_proxy && !StrNCmp(url, "http://", 7)) {
865 	int portnumber;
866 
867 	if ((connect_url = strstr((url + 7), "https://"))) {
868 	    do_connect = TRUE;
869 	    connect_host = HTParse(connect_url, "https", PARSE_HOST);
870 	    if (!HTParsePort(connect_host, &portnumber)) {
871 		sprintf(temp, ":%d", HTTPS_PORT);
872 		StrAllocCat(connect_host, temp);
873 	    }
874 	    CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url));
875 	    CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host));
876 	} else if ((connect_url = strstr((url + 7), "snews://"))) {
877 	    do_connect = TRUE;
878 	    connect_host = HTParse(connect_url, "snews", PARSE_HOST);
879 	    if (!HTParsePort(connect_host, &portnumber)) {
880 		sprintf(temp, ":%d", SNEWS_PORT);
881 		StrAllocCat(connect_host, temp);
882 	    }
883 	    CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url));
884 	    CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host));
885 	}
886     }
887 #endif /* USE_SSL */
888 
889     sprintf(crlf, "%c%c", CR, LF);
890 
891     /*
892      * At this point, we're talking HTTP/1.0.
893      */
894     extensions = YES;
895 
896   try_again:
897     /*
898      * All initializations are moved down here from up above, so we can start
899      * over here...
900      */
901     eol = 0;
902     length = 0;
903     doing_redirect = FALSE;
904     permanent_redirection = FALSE;
905     redirect_post_content = FALSE;
906     target = NULL;
907     line_buffer = NULL;
908     line_kept_clean = NULL;
909 
910 #ifdef USE_SSL
911     if (!StrNCmp(url, "https", 5))
912 	status = HTDoConnect(url, "HTTPS", HTTPS_PORT, &s);
913     else
914 	status = HTDoConnect(url, "HTTP", HTTP_PORT, &s);
915 #else
916     if (!StrNCmp(url, "https", 5)) {
917 	HTAlert(gettext("This client does not contain support for HTTPS URLs."));
918 	status = HT_NOT_LOADED;
919 	goto done;
920     }
921     status = HTDoConnect(arg, "HTTP", HTTP_PORT, &s);
922 #endif /* USE_SSL */
923     if (status == HT_INTERRUPTED) {
924 	/*
925 	 * Interrupt cleanly.
926 	 */
927 	CTRACE((tfp, "HTTP: Interrupted on connect; recovering cleanly.\n"));
928 	_HTProgress(CONNECTION_INTERRUPTED);
929 	status = HT_NOT_LOADED;
930 	goto done;
931     }
932     if (status < 0) {
933 #ifdef _WINDOWS
934 	CTRACE((tfp, "HTTP: Unable to connect to remote host for `%s'\n"
935 		" (status = %d, sock_errno = %d).\n",
936 		url, status, SOCKET_ERRNO));
937 #else
938 	CTRACE((tfp,
939 		"HTTP: Unable to connect to remote host for `%s' (errno = %d).\n",
940 		url, SOCKET_ERRNO));
941 #endif
942 	HTAlert(gettext("Unable to connect to remote host."));
943 	status = HT_NOT_LOADED;
944 	goto done;
945     }
946 #ifdef USE_SSL
947   use_tunnel:
948     /*
949      * If this is an https document, then do the SSL stuff here.
950      */
951     if (did_connect || !StrNCmp(url, "https", 5)) {
952 	SSL_handle = handle = HTGetSSLHandle();
953 	SSL_set_fd(handle, s);
954 	/* get host we're connecting to */
955 	ssl_host = HTParse(url, "", PARSE_HOST);
956 	ssl_host = StripIpv6Brackets(ssl_host);
957 #if defined(USE_GNUTLS_FUNCS)
958 	ret = gnutls_server_name_set(handle->gnutls_state,
959 				     GNUTLS_NAME_DNS,
960 				     ssl_host, strlen(ssl_host));
961 	CTRACE((tfp, "...called gnutls_server_name_set(%s) ->%d\n", ssl_host, ret));
962 #elif SSLEAY_VERSION_NUMBER >= 0x0900
963 #ifndef USE_NSS_COMPAT_INCL
964 	if (!try_tls) {
965 	    SSL_set_no_TLSV1();
966 	    CTRACE((tfp, "...adding SSL_OP_NO_TLSv1\n"));
967 	}
968 #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
969 	else {
970 	    int ret = (int) SSL_set_tlsext_host_name(handle, ssl_host);
971 
972 	    CTRACE((tfp, "...called SSL_set_tlsext_host_name(%s) ->%d\n",
973 		    ssl_host, ret));
974 	}
975 #endif
976 #endif
977 #endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
978 	HTSSLInitPRNG();
979 	status = SSL_connect(handle);
980 
981 	if (status <= 0) {
982 #if (SSLEAY_VERSION_NUMBER >= 0x0900)
983 #if !defined(USE_GNUTLS_FUNCS)
984 	    if (try_tls) {
985 		_HTProgress(gettext("Retrying connection without TLS."));
986 		try_tls = FALSE;
987 		if (did_connect)
988 		    HTTP_NETCLOSE(s, handle);
989 		goto try_again;
990 	    } else
991 #endif
992 	    {
993 		CTRACE((tfp,
994 			"HTTP: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
995 			url, status));
996 		SSL_load_error_strings();
997 		while ((SSLerror = ERR_get_error()) != 0) {
998 		    CTRACE((tfp, "HTTP: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
999 		}
1000 		HTAlert("Unable to make secure connection to remote host.");
1001 		if (did_connect)
1002 		    HTTP_NETCLOSE(s, handle);
1003 		status = HT_NOT_LOADED;
1004 		goto done;
1005 	    }
1006 #else
1007 	    unsigned long SSLerror;
1008 
1009 	    CTRACE((tfp,
1010 		    "HTTP: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
1011 		    url, status));
1012 	    SSL_load_error_strings();
1013 	    while ((SSLerror = ERR_get_error()) != 0) {
1014 		CTRACE((tfp, "HTTP: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
1015 	    }
1016 	    HTAlert("Unable to make secure connection to remote host.");
1017 	    if (did_connect)
1018 		HTTP_NETCLOSE(s, handle);
1019 	    status = HT_NOT_LOADED;
1020 	    goto done;
1021 #endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
1022 	}
1023 #ifdef USE_GNUTLS_INCL
1024 	gnutls_certificate_set_verify_flags(handle->gnutls_cred,
1025 					    GNUTLS_VERIFY_DO_NOT_ALLOW_SAME |
1026 					    GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
1027 	ret = gnutls_certificate_verify_peers2(handle->gnutls_state, &tls_status);
1028 	if (ret < 0 || tls_status != 0) {
1029 	    int flag_continue = 1;
1030 
1031 #if GNUTLS_VERSION_NUMBER >= 0x030104
1032 	    int type;
1033 	    gnutls_datum_t out;
1034 
1035 	    if (ret < 0) {
1036 		SSL_single_prompt(&msg,
1037 				  gettext("GnuTLS error when trying to verify certificate."));
1038 	    } else {
1039 		type = gnutls_certificate_type_get(handle->gnutls_state);
1040 		(void) gnutls_certificate_verification_status_print(tls_status,
1041 								    type,
1042 								    &out, 0);
1043 		SSL_single_prompt(&msg, (const char *) out.data);
1044 		gnutls_free(out.data);
1045 	    }
1046 #else
1047 	    char *msg2;
1048 
1049 	    if (ret == 0 && tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1050 		msg2 = gettext("the certificate has no known issuer");
1051 	    } else if (tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1052 		msg2 = gettext("no issuer was found");
1053 	    } else if (tls_status & GNUTLS_CERT_SIGNER_NOT_CA) {
1054 		msg2 = gettext("issuer is not a CA");
1055 	    } else if (tls_status & GNUTLS_CERT_REVOKED) {
1056 		msg2 = gettext("the certificate has been revoked");
1057 	    } else {
1058 		msg2 = gettext("the certificate is not trusted");
1059 	    }
1060 	    SSL_single_prompt(&msg, msg2);
1061 #endif
1062 	    CTRACE((tfp, "HTLoadHTTP: %s\n", msg));
1063 	    if (!ssl_noprompt) {
1064 		if (!HTForcedPrompt(ssl_noprompt, msg, NO)) {
1065 		    flag_continue = 0;
1066 		}
1067 	    } else if (ssl_noprompt == FORCE_PROMPT_NO) {
1068 		flag_continue = 0;
1069 	    }
1070 	    FREE(msg);
1071 	    if (flag_continue == 0) {
1072 		status = HT_NOT_LOADED;
1073 		FREE(msg);
1074 		goto done;
1075 	    }
1076 	}
1077 #endif
1078 
1079 	peer_cert = (X509 *) SSL_get_peer_certificate(handle);
1080 #if defined(USE_OPENSSL_INCL) || defined(USE_GNUTLS_FUNCS)
1081 	X509_NAME_oneline(X509_get_subject_name(peer_cert),
1082 			  ssl_dn, (int) sizeof(ssl_dn));
1083 #elif defined(USE_GNUTLS_INCL)
1084 	X509_NAME_oneline(X509_get_subject_name(peer_cert),
1085 			  ssl_dn + 1, (int) sizeof(ssl_dn) - 1);
1086 
1087 	/* Iterate over DN in incompatible GnuTLS format to bring it into OpenSSL format */
1088 	ssl_dn[0] = '/';
1089 	ssl_dn_start = ssl_dn;
1090 	while (*ssl_dn_start) {
1091 	    if ((*ssl_dn_start == ',') && (*(ssl_dn_start + 1) == ' ')) {
1092 		*ssl_dn_start++ = '/';
1093 		if (*(p = ssl_dn_start) != 0) {
1094 		    while ((p[0] = p[1]) != 0)
1095 			++p;
1096 		}
1097 	    } else {
1098 		ssl_dn_start++;
1099 	    }
1100 	}
1101 #endif
1102 
1103 	/*
1104 	 * X.509 DN validation taking ALL CN fields into account
1105 	 * (c) 2006 Thorsten Glaser <tg@mirbsd.de>
1106 	 */
1107 
1108 	/* initialise status information */
1109 	status_sslcertcheck = 0;	/* 0 = no CN found in DN */
1110 	ssl_dn_start = ssl_dn;
1111 
1112 	/* validate all CNs found in DN */
1113 	CTRACE((tfp, "Validating CNs in '%s'\n", ssl_dn_start));
1114 	while ((cert_host = strstr(ssl_dn_start, "/CN=")) != NULL) {
1115 	    status_sslcertcheck = 1;	/* 1 = could not verify CN */
1116 	    /* start of CommonName */
1117 	    cert_host += 4;
1118 	    /* find next part of DistinguishedName */
1119 	    if ((p = StrChr(cert_host, '/')) != NULL) {
1120 		*p = '\0';
1121 		ssl_dn_start = p;	/* yes this points to the NUL byte */
1122 	    } else
1123 		ssl_dn_start = NULL;
1124 	    cert_host = StripIpv6Brackets(cert_host);
1125 
1126 	    /* verify this CN */
1127 	    CTRACE((tfp, "Matching\n\tssl_host  '%s'\n\tcert_host '%s'\n",
1128 		    ssl_host, cert_host));
1129 	    if (!strcasecomp_asterisk(ssl_host, cert_host)) {
1130 		status_sslcertcheck = 2;	/* 2 = verified peer */
1131 		/* I think this is cool to have in the logs -TG */
1132 		HTSprintf0(&msg,
1133 			   gettext("Verified connection to %s (cert=%s)"),
1134 			   ssl_host, cert_host);
1135 		_HTProgress(msg);
1136 		FREE(msg);
1137 		/* no need to continue the verification loop */
1138 		break;
1139 	    }
1140 
1141 	    /* add this CN to list of failed CNs */
1142 	    if (ssl_all_cns == NULL)
1143 		StrAllocCopy(ssl_all_cns, "CN<");
1144 	    else
1145 		StrAllocCat(ssl_all_cns, ":CN<");
1146 	    StrAllocCat(ssl_all_cns, cert_host);
1147 	    StrAllocCat(ssl_all_cns, ">");
1148 	    /* if we cannot retry, don't try it */
1149 	    if (ssl_dn_start == NULL)
1150 		break;
1151 	    /* now retry next CN found in DN */
1152 	    *ssl_dn_start = '/';	/* formerly NUL byte */
1153 	}
1154 
1155 	/* check the X.509v3 Subject Alternative Name */
1156 #ifdef USE_GNUTLS_INCL
1157 	if (status_sslcertcheck < 2) {
1158 	    int i;
1159 	    size_t size;
1160 	    gnutls_x509_crt_t cert;
1161 	    static char buf[2048];
1162 
1163 	    /* import the certificate to the x509_crt format */
1164 	    if (gnutls_x509_crt_init(&cert) == 0) {
1165 
1166 		if (gnutls_x509_crt_import(cert, peer_cert,
1167 					   GNUTLS_X509_FMT_DER) < 0) {
1168 		    gnutls_x509_crt_deinit(cert);
1169 		    goto done;
1170 		}
1171 
1172 		ret = 0;
1173 		for (i = 0; !(ret < 0); i++) {
1174 		    size = sizeof(buf);
1175 		    ret = gnutls_x509_crt_get_subject_alt_name(cert,
1176 							       (unsigned) i,
1177 							       buf, &size,
1178 							       NULL);
1179 
1180 		    if (strcasecomp_asterisk(ssl_host, buf) == 0) {
1181 			status_sslcertcheck = 2;
1182 			HTSprintf0(&msg,
1183 				   gettext("Verified connection to %s (subj=%s)"),
1184 				   ssl_host, buf);
1185 			_HTProgress(msg);
1186 			FREE(msg);
1187 			break;
1188 		    }
1189 
1190 		}
1191 	    }
1192 	}
1193 #endif
1194 #ifdef USE_OPENSSL_INCL
1195 	if (status_sslcertcheck < 2) {
1196 	    STACK_OF(GENERAL_NAME) * gens;
1197 	    int i, numalts;
1198 	    const GENERAL_NAME *gn;
1199 
1200 	    gens = (STACK_OF(GENERAL_NAME) *)
1201 		X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL);
1202 
1203 	    if (gens != NULL) {
1204 		numalts = sk_GENERAL_NAME_num(gens);
1205 		for (i = 0; i < numalts; ++i) {
1206 		    gn = sk_GENERAL_NAME_value(gens, i);
1207 		    if (gn->type == GEN_DNS)
1208 			cert_host = (char *) ASN1_STRING_data(gn->d.ia5);
1209 		    else if (gn->type == GEN_IPADD) {
1210 			/* XXX untested -TG */
1211 			size_t j = (size_t) ASN1_STRING_length(gn->d.ia5);
1212 
1213 			cert_host = (char *) malloc(j + 1);
1214 			MemCpy(cert_host, ASN1_STRING_data(gn->d.ia5), j);
1215 			cert_host[j] = '\0';
1216 		    } else
1217 			continue;
1218 		    status_sslcertcheck = 1;	/* got at least one */
1219 		    /* verify this SubjectAltName (see above) */
1220 		    cert_host = StripIpv6Brackets(cert_host);
1221 		    if (!(gn->type == GEN_IPADD ? strcasecomp :
1222 			  strcasecomp_asterisk) (ssl_host, cert_host)) {
1223 			status_sslcertcheck = 2;
1224 			HTSprintf0(&msg,
1225 				   gettext("Verified connection to %s (subj=%s)"),
1226 				   ssl_host, cert_host);
1227 			_HTProgress(msg);
1228 			FREE(msg);
1229 			if (gn->type == GEN_IPADD)
1230 			    free(cert_host);
1231 			break;
1232 		    }
1233 		    /* add to list of failed CNs */
1234 		    if (ssl_all_cns == NULL)
1235 			StrAllocCopy(ssl_all_cns, "SAN<");
1236 		    else
1237 			StrAllocCat(ssl_all_cns, ":SAN<");
1238 		    if (gn->type == GEN_DNS)
1239 			StrAllocCat(ssl_all_cns, "DNS=");
1240 		    else if (gn->type == GEN_IPADD)
1241 			StrAllocCat(ssl_all_cns, "IP=");
1242 		    StrAllocCat(ssl_all_cns, cert_host);
1243 		    StrAllocCat(ssl_all_cns, ">");
1244 		    if (gn->type == GEN_IPADD)
1245 			free(cert_host);
1246 		}
1247 		sk_GENERAL_NAME_free(gens);
1248 	    }
1249 	}
1250 #endif /* USE_OPENSSL_INCL */
1251 
1252 	/* if an error occurred, format the appropriate message */
1253 	if (status_sslcertcheck == 0) {
1254 	    SSL_single_prompt(&msg,
1255 			      gettext("Can't find common name in certificate"));
1256 	} else if (status_sslcertcheck == 1) {
1257 	    SSL_double_prompt(&msg,
1258 			      gettext("SSL error:host(%s)!=cert(%s)-Continue?"),
1259 			      ssl_host, ssl_all_cns);
1260 	}
1261 
1262 	/* if an error occurred, let the user decide how much he trusts */
1263 	if (status_sslcertcheck < 2) {
1264 	    if (msg == NULL)
1265 		StrAllocCopy(msg, gettext("SSL error"));
1266 	    if (!HTForcedPrompt(ssl_noprompt, msg, NO)) {
1267 		status = HT_NOT_LOADED;
1268 		FREE(msg);
1269 		FREE(ssl_all_cns);
1270 		goto done;
1271 	    }
1272 	    SSL_double_prompt(&msg,
1273 			      gettext("UNVERIFIED connection to %s (cert=%s)"),
1274 			      ssl_host, ssl_all_cns ? ssl_all_cns : "NONE");
1275 	    _HTProgress(msg);
1276 	    FREE(msg);
1277 	}
1278 
1279 	show_cert_issuer(peer_cert);
1280 
1281 	HTSprintf0(&msg,
1282 		   gettext("Secure %d-bit %s (%s) HTTP connection"),
1283 		   SSL_get_cipher_bits(handle, NULL),
1284 		   SSL_get_cipher_version(handle),
1285 		   SSL_get_cipher(handle));
1286 	_HTProgress(msg);
1287 	FREE(msg);
1288 	FREE(ssl_all_cns);
1289 	FREE(ssl_host);
1290     }
1291 #endif /* USE_SSL */
1292 
1293     /* Ask that node for the document, omitting the host name & anchor
1294      */
1295     {
1296 	char *p1 = (HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION));
1297 
1298 #ifdef USE_SSL
1299 	if (do_connect) {
1300 	    METHOD = "CONNECT";
1301 	    BStrCopy0(command, "CONNECT ");
1302 	} else
1303 #endif /* USE_SSL */
1304 	if (do_post) {
1305 	    METHOD = "POST";
1306 	    BStrCopy0(command, "POST ");
1307 	} else if (do_head) {
1308 	    METHOD = "HEAD";
1309 	    BStrCopy0(command, "HEAD ");
1310 	} else {
1311 	    METHOD = "GET";
1312 	    BStrCopy0(command, "GET ");
1313 	}
1314 
1315 	/*
1316 	 * If we are using a proxy gateway don't copy in the first slash of
1317 	 * say:  /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj so that just
1318 	 * gopher://....  is sent.
1319 	 */
1320 #ifdef USE_SSL
1321 	if (using_proxy && !did_connect) {
1322 	    if (do_connect)
1323 		BStrCat0(command, connect_host);
1324 	    else
1325 		BStrCat0(command, p1 + 1);
1326 	}
1327 #else
1328 	if (using_proxy)
1329 	    BStrCat0(command, p1 + 1);
1330 #endif /* USE_SSL */
1331 	else
1332 	    BStrCat0(command, p1);
1333 	FREE(p1);
1334     }
1335     if (extensions) {
1336 	BStrCat0(command, " ");
1337 	BStrCat0(command, ((HTprotocolLevel == HTTP_1_0)
1338 			   ? "HTTP/1.0"
1339 			   : "HTTP/1.1"));
1340     }
1341 
1342     BStrCat0(command, crlf);	/* CR LF, as in rfc 977 */
1343 
1344     if (extensions) {
1345 	int n, i;
1346 	char *host = NULL;
1347 
1348 	if ((host = HTParse(anAnchor->address, "", PARSE_HOST)) != NULL) {
1349 	    strip_userid(host, TRUE);
1350 	    HTBprintf(&command, "Host: %s%c%c", host, CR, LF);
1351 	    FREE(host);
1352 	}
1353 	if (HTprotocolLevel >= HTTP_1_1) {
1354 	    HTBprintf(&command, "Connection: close%c%c", CR, LF);
1355 	}
1356 
1357 	if (!HTPresentations)
1358 	    HTFormatInit();
1359 	n = HTList_count(HTPresentations);
1360 
1361 	first_Accept = TRUE;
1362 	len = 0;
1363 	for (i = 0; i < n; i++) {
1364 	    HTPresentation *pres =
1365 	    (HTPresentation *) HTList_objectAt(HTPresentations, i);
1366 
1367 	    if (pres->get_accept) {
1368 		if (pres->quality < 1.0) {
1369 		    if (pres->maxbytes > 0) {
1370 			sprintf(temp, ";q=%4.3f;mxb=%" PRI_off_t "",
1371 				pres->quality, CAST_off_t (pres->maxbytes));
1372 		    } else {
1373 			sprintf(temp, ";q=%4.3f", pres->quality);
1374 		    }
1375 		} else if (pres->maxbytes > 0) {
1376 		    sprintf(temp, ";mxb=%" PRI_off_t "", CAST_off_t (pres->maxbytes));
1377 		} else {
1378 		    temp[0] = '\0';
1379 		}
1380 		HTSprintf0(&linebuf, "%s%s%s",
1381 			   (first_Accept ?
1382 			    "Accept: " : ", "),
1383 			   HTAtom_name(pres->rep),
1384 			   temp);
1385 		len += (int) strlen(linebuf);
1386 		if (len > 252 && !first_Accept) {
1387 		    BStrCat0(command, crlf);
1388 		    HTSprintf0(&linebuf, "Accept: %s%s",
1389 			       HTAtom_name(pres->rep),
1390 			       temp);
1391 		    len = (int) strlen(linebuf);
1392 		}
1393 		BStrCat0(command, linebuf);
1394 		first_Accept = FALSE;
1395 	    }
1396 	}
1397 	HTBprintf(&command, "%s*/*;q=0.01%c%c",
1398 		  (first_Accept ?
1399 		   "Accept: " : ", "), CR, LF);
1400 
1401 	/*
1402 	 * FIXME:  suppressing the "Accept-Encoding" in this case is done to
1403 	 * work around limitations of the presentation logic used for the
1404 	 * command-line "-base" option.  The remote site may transmit the
1405 	 * document gzip'd, but the ensuing logic in HTSaveToFile() would see
1406 	 * the mime-type as gzip rather than text/html, and not prepend the
1407 	 * base URL.  This is less efficient than accepting the compressed data
1408 	 * and uncompressing it, adding the base URL but is simpler than
1409 	 * augmenting the dump's presentation logic -TD
1410 	 */
1411 	if (LYPrependBaseToSource && dump_output_immediately) {
1412 	    CTRACE((tfp,
1413 		    "omit Accept-Encoding to work-around interaction with -source\n"));
1414 	} else {
1415 	    char *list = 0;
1416 	    int j, k;
1417 
1418 	    for (j = 1; j < encodingALL; j <<= 1) {
1419 		if (acceptEncoding(j)) {
1420 		    for (k = 0; tbl_preferred_encoding[k].name != 0; ++k) {
1421 			if (tbl_preferred_encoding[k].value == j) {
1422 			    if (list != 0)
1423 				StrAllocCat(list, ", ");
1424 			    StrAllocCat(list, tbl_preferred_encoding[k].name);
1425 			    break;
1426 			}
1427 		    }
1428 		}
1429 	    }
1430 
1431 	    if (list != 0) {
1432 		HTBprintf(&command, "Accept-Encoding: %s%c%c", list, CR, LF);
1433 		free(list);
1434 	    }
1435 	}
1436 
1437 	if (non_empty(language)) {
1438 	    HTBprintf(&command, "Accept-Language: %s%c%c", language, CR, LF);
1439 	}
1440 
1441 	if (non_empty(pref_charset)) {
1442 	    BStrCat0(command, "Accept-Charset: ");
1443 	    StrAllocCopy(linebuf, pref_charset);
1444 	    if (linebuf[strlen(linebuf) - 1] == ',')
1445 		linebuf[strlen(linebuf) - 1] = '\0';
1446 	    LYLowerCase(linebuf);
1447 	    if (strstr(linebuf, "iso-8859-1") == NULL)
1448 		StrAllocCat(linebuf, ", iso-8859-1;q=0.01");
1449 	    if (strstr(linebuf, "us-ascii") == NULL)
1450 		StrAllocCat(linebuf, ", us-ascii;q=0.01");
1451 	    BStrCat0(command, linebuf);
1452 	    HTBprintf(&command, "%c%c", CR, LF);
1453 	}
1454 #if 0
1455 	/*
1456 	 * Promote 300 (Multiple Choices) replies, if supported, over 406 (Not
1457 	 * Acceptable) replies.  - FM
1458 	 *
1459 	 * This used to be done in versions 2.7 and 2.8*, but violates the
1460 	 * specs for transparent content negotiation and has the effect that
1461 	 * servers supporting those specs will send 300 (Multiple Choices)
1462 	 * instead of a normal response (e.g.  200 OK), since they will assume
1463 	 * that the client wants to make the choice.  It is not clear whether
1464 	 * there are any servers or sites for which sending this header really
1465 	 * improves anything.
1466 	 *
1467 	 * If there ever is a need to send "Negotiate:  trans" and really mean
1468 	 * it, we should send "Negotiate:  trans,trans" or similar, since that
1469 	 * is semantically equivalent and some servers may ignore "Negotiate:
1470 	 * trans" as a special case when it comes from Lynx (to work around the
1471 	 * old faulty behavior).  - kw
1472 	 *
1473 	 * References:
1474 	 * RFC 2295 (see also RFC 2296), and mail to lynx-dev and
1475 	 * new-httpd@apache.org from Koen Holtman, Jan 1999.
1476 	 */
1477 	if (!do_post) {
1478 	    HTBprintf(&command, "Negotiate: trans%c%c", CR, LF);
1479 	}
1480 #endif /* 0 */
1481 
1482 	/*
1483 	 * When reloading give no-cache pragma to proxy server to make it
1484 	 * refresh its cache.  -- Ari L.  <luotonen@dxcern.cern.ch>
1485 	 *
1486 	 * Also send it as a Cache-Control header for HTTP/1.1.  - FM
1487 	 */
1488 	if (reloading) {
1489 	    HTBprintf(&command, "Pragma: no-cache%c%c", CR, LF);
1490 	    HTBprintf(&command, "Cache-Control: no-cache%c%c", CR, LF);
1491 	}
1492 
1493 	if (LYSendUserAgent || no_useragent) {
1494 	    if (non_empty(LYUserAgent)) {
1495 		char *cp = LYSkipBlanks(LYUserAgent);
1496 
1497 		/* Won't send it at all if all blank - kw */
1498 		if (*cp != '\0')
1499 		    HTBprintf(&command, "User-Agent: %.*s%c%c",
1500 			      INIT_LINE_SIZE - 15, LYUserAgent, CR, LF);
1501 	    } else {
1502 		HTBprintf(&command, "User-Agent: %s/%s  libwww-FM/%s%c%c",
1503 			  HTAppName ? HTAppName : "unknown",
1504 			  HTAppVersion ? HTAppVersion : "0.0",
1505 			  HTLibraryVersion, CR, LF);
1506 	    }
1507 	}
1508 
1509 	if (non_empty(personal_mail_address) && !LYNoFromHeader) {
1510 	    HTBprintf(&command, "From: %s%c%c", personal_mail_address, CR, LF);
1511 	}
1512 
1513 	if (!(LYUserSpecifiedURL ||
1514 	      LYNoRefererHeader || LYNoRefererForThis) &&
1515 	    strcmp(HTLoadedDocumentURL(), "")) {
1516 	    const char *cp = LYRequestReferer;
1517 
1518 	    if (!cp)
1519 		cp = HTLoadedDocumentURL();	/* @@@ Try both? - kw */
1520 	    BStrCat0(command, "Referer: ");
1521 	    if (isLYNXIMGMAP(cp)) {
1522 		char *pound = findPoundSelector(cp);
1523 		int nn = (pound ? (int) (pound - cp) : (int) strlen(cp));
1524 
1525 		HTSABCat(&command, cp + LEN_LYNXIMGMAP, nn);
1526 	    } else {
1527 		BStrCat0(command, cp);
1528 	    }
1529 	    HTBprintf(&command, "%c%c", CR, LF);
1530 	} {
1531 	    char *abspath;
1532 	    char *docname;
1533 	    char *hostname;
1534 	    char *colon;
1535 	    int portnumber;
1536 	    char *auth, *cookie = NULL;
1537 	    BOOL secure = (BOOL) (StrNCmp(anAnchor->address, "https", 5)
1538 				  ? FALSE
1539 				  : TRUE);
1540 
1541 	    abspath = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
1542 	    docname = HTParse(arg, "", PARSE_PATH);
1543 	    hostname = HTParse(arg, "", PARSE_HOST);
1544 	    if (hostname &&
1545 		NULL != (colon = HTParsePort(hostname, &portnumber))) {
1546 		*colon = '\0';	/* Chop off port number */
1547 	    } else if (!StrNCmp(arg, "https", 5)) {
1548 		portnumber = HTTPS_PORT;
1549 	    } else {
1550 		portnumber = HTTP_PORT;
1551 	    }
1552 
1553 	    /*
1554 	     * Add Authorization, Proxy-Authorization, and/or Cookie headers,
1555 	     * if applicable.
1556 	     */
1557 	    if (using_proxy) {
1558 		/*
1559 		 * If we are using a proxy, first determine if we should
1560 		 * include an Authorization header and/or Cookie header for the
1561 		 * ultimate target of this request.  - FM & AJL
1562 		 */
1563 		char *host2 = NULL, *path2 = NULL;
1564 		int port2 = (StrNCmp(docname, "https", 5) ?
1565 			     HTTP_PORT : HTTPS_PORT);
1566 
1567 		host2 = HTParse(docname, "", PARSE_HOST);
1568 		path2 = HTParse(docname, "", PARSE_PATH | PARSE_PUNCTUATION);
1569 		if ((colon = HTParsePort(host2, &port2)) != NULL) {
1570 		    /* Use non-default port number */
1571 		    *colon = '\0';
1572 		}
1573 
1574 		/*
1575 		 * This composeAuth() does file access, i.e., for the ultimate
1576 		 * target of the request.  - AJL
1577 		 */
1578 		auth_proxy = NO;
1579 		auth = HTAA_composeAuth(host2, port2, path2, auth_proxy);
1580 		if (auth == NULL) {
1581 		    CTRACE((tfp, "HTTP: Not sending authorization (yet).\n"));
1582 		} else if (*auth != '\0') {
1583 		    /*
1584 		     * We have an Authorization header to be included.
1585 		     */
1586 		    HTBprintf(&command, "%s%c%c", auth, CR, LF);
1587 		    CTRACE((tfp, "HTTP: Sending authorization: %s\n", auth));
1588 		} else {
1589 		    /*
1590 		     * The user either cancelled or made a mistake with the
1591 		     * username and password prompt.
1592 		     */
1593 		    if (!(traversal || dump_output_immediately) &&
1594 			HTConfirm(CONFIRM_WO_PASSWORD)) {
1595 			show_401 = TRUE;
1596 		    } else {
1597 			if (traversal || dump_output_immediately)
1598 			    HTAlert(FAILED_NEED_PASSWD);
1599 #ifdef USE_SSL
1600 			if (did_connect)
1601 			    HTTP_NETCLOSE(s, handle);
1602 #endif /* USE_SSL */
1603 			BStrFree(command);
1604 			FREE(hostname);
1605 			FREE(docname);
1606 			FREE(abspath);
1607 			FREE(host2);
1608 			FREE(path2);
1609 			status = HT_NOT_LOADED;
1610 			goto done;
1611 		    }
1612 		}
1613 		/*
1614 		 * Add 'Cookie:' header, if it's HTTP or HTTPS document being
1615 		 * proxied.
1616 		 */
1617 		if (!StrNCmp(docname, "http", 4)) {
1618 		    cookie = LYAddCookieHeader(host2, path2, port2, secure);
1619 		}
1620 		FREE(host2);
1621 		FREE(path2);
1622 		/*
1623 		 * The next composeAuth() will be for the proxy.  - AJL
1624 		 */
1625 		auth_proxy = YES;
1626 	    } else {
1627 		/*
1628 		 * Add cookie for a non-proxied request.  - FM
1629 		 */
1630 		cookie = LYAddCookieHeader(hostname, abspath, portnumber, secure);
1631 		auth_proxy = NO;
1632 	    }
1633 	    /*
1634 	     * If we do have a cookie set, add it to the request buffer.  - FM
1635 	     */
1636 	    if (cookie != NULL) {
1637 		if (*cookie != '$') {
1638 		    /*
1639 		     * It's a historical cookie, so signal to the server that
1640 		     * we support modern cookies.  - FM
1641 		     */
1642 		    BStrCat0(command, "Cookie2: $Version=\"1\"");
1643 		    BStrCat0(command, crlf);
1644 		    CTRACE((tfp, "HTTP: Sending Cookie2: $Version =\"1\"\n"));
1645 		}
1646 		if (*cookie != '\0') {
1647 		    /*
1648 		     * It's not a zero-length string, so add the header.  Note
1649 		     * that any folding of long strings has been done already
1650 		     * in LYCookie.c.  - FM
1651 		     */
1652 		    BStrCat0(command, "Cookie: ");
1653 		    BStrCat0(command, cookie);
1654 		    BStrCat0(command, crlf);
1655 		    CTRACE((tfp, "HTTP: Sending Cookie: %s\n", cookie));
1656 		}
1657 		FREE(cookie);
1658 	    }
1659 	    FREE(abspath);
1660 
1661 	    /*
1662 	     * If we are using a proxy, auth_proxy should be YES, and we check
1663 	     * here whether we want a Proxy-Authorization header for it.  If we
1664 	     * are not using a proxy, auth_proxy should still be NO, and we
1665 	     * check here for whether we want an Authorization header.  - FM &
1666 	     * AJL
1667 	     */
1668 	    if ((auth = HTAA_composeAuth(hostname,
1669 					 portnumber,
1670 					 docname,
1671 					 auth_proxy)) != NULL &&
1672 		*auth != '\0') {
1673 		/*
1674 		 * If auth is not NULL nor zero-length, it's an Authorization
1675 		 * or Proxy-Authorization header to be included.  - FM
1676 		 */
1677 		HTBprintf(&command, "%s%c%c", auth, CR, LF);
1678 		CTRACE((tfp, (auth_proxy ?
1679 			      "HTTP: Sending proxy authorization: %s\n" :
1680 			      "HTTP: Sending authorization: %s\n"),
1681 			auth));
1682 	    } else if (auth && *auth == '\0') {
1683 		/*
1684 		 * If auth is a zero-length string, the user either cancelled
1685 		 * or goofed at the username and password prompt.  - FM
1686 		 */
1687 		if (!(traversal || dump_output_immediately) && HTConfirm(CONFIRM_WO_PASSWORD)) {
1688 		    if (auth_proxy == TRUE) {
1689 			show_407 = TRUE;
1690 		    } else {
1691 			show_401 = TRUE;
1692 		    }
1693 		} else {
1694 		    if (traversal || dump_output_immediately)
1695 			HTAlert(FAILED_NEED_PASSWD);
1696 		    BStrFree(command);
1697 		    FREE(hostname);
1698 		    FREE(docname);
1699 		    status = HT_NOT_LOADED;
1700 		    goto done;
1701 		}
1702 	    } else {
1703 		CTRACE((tfp, (auth_proxy ?
1704 			      "HTTP: Not sending proxy authorization (yet).\n" :
1705 			      "HTTP: Not sending authorization (yet).\n")));
1706 	    }
1707 	    FREE(hostname);
1708 	    FREE(docname);
1709 	}
1710     }
1711 
1712     if (
1713 #ifdef USE_SSL
1714 	   !do_connect &&
1715 #endif /* USE_SSL */
1716 	   do_post) {
1717 	CTRACE((tfp, "HTTP: Doing post, content-type '%s'\n",
1718 		anAnchor->post_content_type
1719 		? anAnchor->post_content_type
1720 		: "lose"));
1721 	HTBprintf(&command, "Content-type: %s%c%c",
1722 		  anAnchor->post_content_type
1723 		  ? anAnchor->post_content_type
1724 		  : "lose",
1725 		  CR, LF);
1726 
1727 	HTBprintf(&command, "Content-length: %d%c%c",
1728 		  !isBEmpty(anAnchor->post_data)
1729 		  ? BStrLen(anAnchor->post_data)
1730 		  : 0,
1731 		  CR, LF);
1732 
1733 	BStrCat0(command, crlf);	/* Blank line means "end" of headers */
1734 
1735 	BStrCat(command, anAnchor->post_data);
1736     } else
1737 	BStrCat0(command, crlf);	/* Blank line means "end" of headers */
1738 
1739     if (TRACE) {
1740 	CTRACE((tfp, "Writing:\n"));
1741 	trace_bstring(command);
1742 #ifdef USE_SSL
1743 	CTRACE((tfp, "%s",
1744 		(anAnchor->post_data && !do_connect ? crlf : "")));
1745 #else
1746 	CTRACE((tfp, "%s",
1747 		(anAnchor->post_data ? crlf : "")));
1748 #endif /* USE_SSL */
1749 	CTRACE((tfp, "----------------------------------\n"));
1750     }
1751 
1752     _HTProgress(gettext("Sending HTTP request."));
1753 
1754 #ifdef    NOT_ASCII		/* S/390 -- gil -- 0548 */
1755     {
1756 	char *p2;
1757 
1758 	for (p2 = BStrData(command);
1759 	     p2 < BStrData(command) + BStrLen(command);
1760 	     p2++)
1761 	    *p2 = TOASCII(*p2);
1762     }
1763 #endif /* NOT_ASCII */
1764     status = (int) HTTP_NETWRITE(s,
1765 				 BStrData(command),
1766 				 BStrLen(command),
1767 				 handle);
1768     BStrFree(command);
1769     FREE(linebuf);
1770     if (status <= 0) {
1771 	if (status == 0) {
1772 	    CTRACE((tfp, "HTTP: Got status 0 in initial write\n"));
1773 	    /* Do nothing. */
1774 	} else if ((SOCKET_ERRNO == ENOTCONN ||
1775 		    SOCKET_ERRNO == ECONNRESET ||
1776 		    SOCKET_ERRNO == EPIPE) &&
1777 		   !already_retrying &&
1778 	    /* Don't retry if we're posting. */ !do_post) {
1779 	    /*
1780 	     * Arrrrgh, HTTP 0/1 compatibility problem, maybe.
1781 	     */
1782 	    CTRACE((tfp,
1783 		    "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n"));
1784 	    _HTProgress(RETRYING_AS_HTTP0);
1785 	    HTTP_NETCLOSE(s, handle);
1786 	    extensions = NO;
1787 	    already_retrying = TRUE;
1788 	    goto try_again;
1789 	} else {
1790 	    CTRACE((tfp,
1791 		    "HTTP: Hit unexpected network WRITE error; aborting connection.\n"));
1792 	    HTTP_NETCLOSE(s, handle);
1793 	    status = -1;
1794 	    HTAlert(gettext("Unexpected network write error; connection aborted."));
1795 	    goto done;
1796 	}
1797     }
1798 
1799     CTRACE((tfp, "HTTP: WRITE delivered OK\n"));
1800     _HTProgress(gettext("HTTP request sent; waiting for response."));
1801 
1802     /*    Read the first line of the response
1803      * -----------------------------------
1804      */
1805     {
1806 	/* Get numeric status etc */
1807 	BOOL end_of_file = NO;
1808 	int buffer_length = INIT_LINE_SIZE;
1809 
1810 	line_buffer = typecallocn(char, (size_t) buffer_length);
1811 
1812 	if (line_buffer == NULL)
1813 	    outofmem(__FILE__, "HTLoadHTTP");
1814 
1815 	HTReadProgress(bytes_already_read = 0, (off_t) 0);
1816 	do {			/* Loop to read in the first line */
1817 	    /*
1818 	     * Extend line buffer if necessary for those crazy WAIS URLs ;-)
1819 	     */
1820 	    if (buffer_length - length < LINE_EXTEND_THRESH) {
1821 		buffer_length = buffer_length + buffer_length;
1822 		line_buffer =
1823 		    (char *) realloc(line_buffer, ((unsigned) buffer_length *
1824 						   sizeof(char)));
1825 
1826 		if (line_buffer == NULL)
1827 		    outofmem(__FILE__, "HTLoadHTTP");
1828 	    }
1829 	    CTRACE((tfp, "HTTP: Trying to read %d\n", buffer_length - length - 1));
1830 	    status = HTTP_NETREAD(s,
1831 				  line_buffer + length,
1832 				  (buffer_length - length - 1),
1833 				  handle);
1834 	    CTRACE((tfp, "HTTP: Read %d\n", status));
1835 	    if (status <= 0) {
1836 		/*
1837 		 * Retry if we get nothing back too.
1838 		 * Bomb out if we get nothing twice.
1839 		 */
1840 		if (status == HT_INTERRUPTED) {
1841 		    CTRACE((tfp, "HTTP: Interrupted initial read.\n"));
1842 		    _HTProgress(CONNECTION_INTERRUPTED);
1843 		    HTTP_NETCLOSE(s, handle);
1844 		    status = HT_NO_DATA;
1845 		    goto clean_up;
1846 		} else if (status < 0 &&
1847 			   (SOCKET_ERRNO == ENOTCONN ||
1848 #ifdef _WINDOWS			/* 1997/11/09 (Sun) 16:59:58 */
1849 			    SOCKET_ERRNO == ETIMEDOUT ||
1850 #endif
1851 			    SOCKET_ERRNO == ECONNRESET ||
1852 			    SOCKET_ERRNO == EPIPE) &&
1853 			   !already_retrying && !do_post) {
1854 		    /*
1855 		     * Arrrrgh, HTTP 0/1 compability problem, maybe.
1856 		     */
1857 		    CTRACE((tfp,
1858 			    "HTTP: BONZO Trying again with HTTP0 request.\n"));
1859 		    HTTP_NETCLOSE(s, handle);
1860 		    FREE(line_buffer);
1861 		    FREE(line_kept_clean);
1862 
1863 		    extensions = NO;
1864 		    already_retrying = TRUE;
1865 		    _HTProgress(RETRYING_AS_HTTP0);
1866 		    goto try_again;
1867 		}
1868 #ifdef USE_SSL
1869 		else if ((SSLerror = ERR_get_error()) != 0) {
1870 		    CTRACE((tfp,
1871 			    "HTTP: Hit unexpected network read error; aborting connection; status %d:%s.\n",
1872 			    status, ERR_error_string(SSLerror, NULL)));
1873 		    HTAlert(gettext("Unexpected network read error; connection aborted."));
1874 		    HTTP_NETCLOSE(s, handle);
1875 		    status = -1;
1876 		    goto clean_up;
1877 		}
1878 #endif
1879 		else {
1880 		    CTRACE((tfp,
1881 			    "HTTP: Hit unexpected network read error; aborting connection; status %d.\n",
1882 			    status));
1883 		    HTAlert(gettext("Unexpected network read error; connection aborted."));
1884 		    HTTP_NETCLOSE(s, handle);
1885 		    status = -1;
1886 		    goto clean_up;
1887 		}
1888 	    }
1889 #ifdef    NOT_ASCII		/* S/390 -- gil -- 0564 */
1890 	    {
1891 		char *p2;
1892 
1893 		for (p2 = line_buffer + length;
1894 		     p2 < line_buffer + length + status;
1895 		     p2++)
1896 		    *p2 = FROMASCII(*p2);
1897 	    }
1898 #endif /* NOT_ASCII */
1899 
1900 	    bytes_already_read += status;
1901 	    HTReadProgress(bytes_already_read, (off_t) 0);
1902 
1903 #ifdef UCX			/* UCX returns -1 on EOF */
1904 	    if (status == 0 || status == -1)
1905 #else
1906 	    if (status == 0)
1907 #endif
1908 	    {
1909 		break;
1910 	    }
1911 	    line_buffer[length + status] = 0;
1912 
1913 	    if (line_buffer) {
1914 		FREE(line_kept_clean);
1915 		line_kept_clean = (char *) malloc((unsigned) buffer_length *
1916 						  sizeof(char));
1917 
1918 		if (line_kept_clean == NULL)
1919 		    outofmem(__FILE__, "HTLoadHTTP");
1920 		MemCpy(line_kept_clean, line_buffer, buffer_length);
1921 #ifdef SH_EX			/* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
1922 		real_length_of_line = length + status;
1923 #endif
1924 	    }
1925 
1926 	    eol = StrChr(line_buffer + length, LF);
1927 	    /* Do we *really* want to do this? */
1928 	    if (eol && eol != line_buffer && *(eol - 1) == CR)
1929 		*(eol - 1) = ' ';
1930 
1931 	    length = length + status;
1932 
1933 	    /* Do we really want to do *this*? */
1934 	    if (eol)
1935 		*eol = 0;	/* Terminate the line */
1936 	}
1937 	/* All we need is the first line of the response.  If it's a HTTP/1.0
1938 	 * response, then the first line will be absurdly short and therefore
1939 	 * we can safely gate the number of bytes read through this code (as
1940 	 * opposed to below) to ~1000.
1941 	 *
1942 	 * Well, let's try 100.
1943 	 */
1944 	while (!eol && !end_of_file && bytes_already_read < 100);
1945     }				/* Scope of loop variables */
1946 
1947     /* save total length, in case we decide later to show it all - kw */
1948     rawlength = length;
1949 
1950     /*    We now have a terminated unfolded line.  Parse it.
1951      * --------------------------------------------------
1952      */
1953     CTRACE((tfp, "HTTP: Rx: %s\n", line_buffer));
1954 
1955     /*
1956      * Kludge to work with old buggy servers and the VMS Help gateway.  They
1957      * can't handle the third word, so we try again without it.
1958      */
1959     if (extensions &&		/* Old buggy server or Help gateway? */
1960 	(0 == StrNCmp(line_buffer, "<TITLE>Bad File Request</TITLE>", 31) ||
1961 	 0 == StrNCmp(line_buffer, "Address should begin with", 25) ||
1962 	 0 == StrNCmp(line_buffer, "<TITLE>Help ", 12) ||
1963 	 0 == strcmp(line_buffer,
1964 		     "Document address invalid or access not authorised"))) {
1965 	FREE(line_buffer);
1966 	FREE(line_kept_clean);
1967 	extensions = NO;
1968 	already_retrying = TRUE;
1969 	CTRACE((tfp, "HTTP: close socket %d to retry with HTTP0\n", s));
1970 	HTTP_NETCLOSE(s, handle);
1971 	/* print a progress message */
1972 	_HTProgress(RETRYING_AS_HTTP0);
1973 	goto try_again;
1974     } {
1975 	int fields;
1976 	char server_version[VERSION_LENGTH + 1];
1977 
1978 	server_version[0] = 0;
1979 
1980 	fields = sscanf(line_buffer, "%20s %d",
1981 			server_version,
1982 			&server_status);
1983 
1984 	CTRACE((tfp, "HTTP: Scanned %d fields from line_buffer\n", fields));
1985 
1986 	if (non_empty(http_error_file)) {
1987 	    /* Make the status code externally available */
1988 	    FILE *error_file;
1989 
1990 #ifdef SERVER_STATUS_ONLY
1991 	    error_file = fopen(http_error_file, TXT_W);
1992 	    if (error_file) {	/* Managed to open the file */
1993 		fprintf(error_file, "error=%d\n", server_status);
1994 		fclose(error_file);
1995 	    }
1996 #else
1997 	    error_file = fopen(http_error_file, TXT_A);
1998 	    if (error_file) {	/* Managed to open the file */
1999 		fprintf(error_file, "   URL=%s (%s)\n", url, METHOD);
2000 		fprintf(error_file, "STATUS=%s\n", line_buffer);
2001 		fclose(error_file);
2002 	    }
2003 #endif /* SERVER_STATUS_ONLY */
2004 	}
2005 
2006 	/*
2007 	 * Rule out a non-HTTP/1.n reply as best we can.
2008 	 */
2009 	if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
2010 	    server_version[1] != 'T' || server_version[2] != 'T' ||
2011 	    server_version[3] != 'P' || server_version[4] != '/' ||
2012 	    server_version[6] != '.') {
2013 	    /*
2014 	     * Ugh!  An HTTP0 reply,
2015 	     */
2016 	    HTAtom *encoding;
2017 
2018 	    CTRACE((tfp, "--- Talking HTTP0.\n"));
2019 
2020 	    format_in = HTFileFormat(url, &encoding, NULL);
2021 	    /*
2022 	     * Treat all plain text as HTML.  This sucks but its the only
2023 	     * solution without without looking at content.
2024 	     */
2025 	    if (!StrNCmp(HTAtom_name(format_in), STR_PLAINTEXT, 10)) {
2026 		CTRACE((tfp, "HTTP: format_in being changed to text/HTML\n"));
2027 		format_in = WWW_HTML;
2028 	    }
2029 	    if (!IsUnityEnc(encoding)) {
2030 		/*
2031 		 * Change the format to that for "www/compressed".
2032 		 */
2033 		CTRACE((tfp, "HTTP: format_in is '%s',\n", HTAtom_name(format_in)));
2034 		StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in));
2035 		StrAllocCopy(anAnchor->content_encoding, HTAtom_name(encoding));
2036 		format_in = HTAtom_for("www/compressed");
2037 		CTRACE((tfp, "        Treating as '%s' with encoding '%s'\n",
2038 			"www/compressed", HTAtom_name(encoding)));
2039 	    }
2040 
2041 	    start_of_data = line_kept_clean;
2042 	} else {
2043 	    /*
2044 	     * Set up to decode full HTTP/1.n response.  - FM
2045 	     */
2046 	    format_in = HTAtom_for("www/mime");
2047 	    CTRACE((tfp, "--- Talking HTTP1.\n"));
2048 
2049 	    /*
2050 	     * We set start_of_data to "" when !eol here because there will be
2051 	     * a put_block done below; we do *not* use the value of
2052 	     * start_of_data (as a pointer) in the computation of length (or
2053 	     * anything else) when !eol.  Otherwise, set the value of length to
2054 	     * what we have beyond eol (i.e., beyond the status line).  - FM
2055 	     */
2056 	    if (eol != 0) {
2057 		start_of_data = (eol + 1);
2058 	    } else {
2059 		start_of_data = empty;
2060 	    }
2061 	    length = (eol
2062 		      ? length - (int) (start_of_data - line_buffer)
2063 		      : 0);
2064 
2065 	    /*
2066 	     * Trim trailing spaces in line_buffer so that we can use it in
2067 	     * messages which include the status line.  - FM
2068 	     */
2069 	    while (line_buffer[strlen(line_buffer) - 1] == ' ')
2070 		line_buffer[strlen(line_buffer) - 1] = '\0';
2071 
2072 	    /*
2073 	     * Take appropriate actions based on the status.  - FM
2074 	     */
2075 	    switch (server_status / 100) {
2076 	    case 1:
2077 		/*
2078 		 * HTTP/1.1 Informational statuses.
2079 		 * 100 Continue.
2080 		 * 101 Switching Protocols.
2081 		 * > 101 is unknown.
2082 		 * We should never get these, and they have only the status
2083 		 * line and possibly other headers, so we'll deal with them by
2084 		 * showing the full header to the user as text/plain.  - FM
2085 		 */
2086 		HTAlert(gettext("Got unexpected Informational Status."));
2087 		do_head = TRUE;
2088 		break;
2089 
2090 	    case 2:
2091 		/*
2092 		 * Good:  Got MIME object!  (Successful) - FM
2093 		 */
2094 		if (do_head) {
2095 		    /*
2096 		     * If HEAD was requested, show headers (and possibly bogus
2097 		     * body) for all 2xx status codes as text/plain - KW
2098 		     */
2099 		    HTProgress(line_buffer);
2100 		    break;
2101 		}
2102 		switch (server_status) {
2103 		case 204:
2104 		    /*
2105 		     * No Content.
2106 		     */
2107 		    HTAlert(line_buffer);
2108 		    HTTP_NETCLOSE(s, handle);
2109 		    HTNoDataOK = 1;
2110 		    status = HT_NO_DATA;
2111 		    goto clean_up;
2112 
2113 		case 205:
2114 		    /*
2115 		     * Reset Content.  The server has fulfilled the request but
2116 		     * nothing is returned and we should reset any form
2117 		     * content.  We'll instruct the user to do that, and
2118 		     * restore the current document.  - FM
2119 		     */
2120 		    HTAlert(gettext("Request fulfilled.  Reset Content."));
2121 		    HTTP_NETCLOSE(s, handle);
2122 		    status = HT_NO_DATA;
2123 		    goto clean_up;
2124 
2125 		case 206:
2126 		    /*
2127 		     * Partial Content.  We didn't send a Range so something
2128 		     * went wrong somewhere.  Show the status message and
2129 		     * restore the current document.  - FM
2130 		     */
2131 		    HTAlert(line_buffer);
2132 		    HTTP_NETCLOSE(s, handle);
2133 		    status = HT_NO_DATA;
2134 		    goto clean_up;
2135 
2136 		default:
2137 		    /*
2138 		     * 200 OK.
2139 		     * 201 Created.
2140 		     * 202 Accepted.
2141 		     * 203 Non-Authoritative Information.
2142 		     * > 206 is unknown.
2143 		     * All should return something to display.
2144 		     */
2145 #if defined(USE_SSL)		/* && !defined(DISABLE_NEWS)  _H */
2146 		    if (do_connect) {
2147 			CTRACE((tfp,
2148 				"HTTP: Proxy tunnel to '%s' established.\n",
2149 				connect_host));
2150 			do_connect = FALSE;
2151 			url = connect_url;
2152 			FREE(line_buffer);
2153 			FREE(line_kept_clean);
2154 #ifndef DISABLE_NEWS
2155 			if (!StrNCmp(connect_url, "snews", 5)) {
2156 			    CTRACE((tfp,
2157 				    "      Will attempt handshake and snews connection.\n"));
2158 			    status = HTNewsProxyConnect(s, url, anAnchor,
2159 							format_out, sink);
2160 			    goto done;
2161 			}
2162 #endif /* DISABLE_NEWS */
2163 			did_connect = TRUE;
2164 			already_retrying = TRUE;
2165 			eol = 0;
2166 			length = 0;
2167 			doing_redirect = FALSE;
2168 			permanent_redirection = FALSE;
2169 			target = NULL;
2170 			CTRACE((tfp,
2171 				"      Will attempt handshake and resubmit headers.\n"));
2172 			goto use_tunnel;
2173 		    }
2174 #endif /* USE_SSL */
2175 		    HTProgress(line_buffer);
2176 		}		/* case 2 switch */
2177 		break;
2178 
2179 	    case 3:
2180 		/*
2181 		 * Various forms of Redirection.  - FM
2182 		 * 300 Multiple Choices.
2183 		 * 301 Moved Permanently.
2184 		 * 302 Found (temporary; we can, and do, use GET).
2185 		 * 303 See Other (temporary; always use GET).
2186 		 * 304 Not Modified.
2187 		 * 305 Use Proxy.
2188 		 * 306 Set Proxy.
2189 		 * 307 Temporary Redirect with method retained.
2190 		 * > 308 is unknown.
2191 		 */
2192 		if (no_url_redirection || do_head || keep_mime_headers) {
2193 		    /*
2194 		     * If any of these flags are set, we do not redirect, but
2195 		     * instead show what was returned to the user as
2196 		     * text/plain.  - FM
2197 		     */
2198 		    HTProgress(line_buffer);
2199 		    break;
2200 		}
2201 
2202 		if (server_status == 300) {	/* Multiple Choices */
2203 		    /*
2204 		     * For client driven content negotiation.  The server
2205 		     * should be sending some way for the user-agent to make a
2206 		     * selection, so we'll show the user whatever the server
2207 		     * returns.  There might be a Location:  header with the
2208 		     * server's preference present, but the choice should be up
2209 		     * to the user, someday based on an Alternates:  header,
2210 		     * and a body always should be present with descriptions
2211 		     * and links for the choices (i.e., we use the latter, for
2212 		     * now).  - FM
2213 		     */
2214 		    HTAlert(line_buffer);
2215 		    if (traversal) {
2216 			HTTP_NETCLOSE(s, handle);
2217 			status = -1;
2218 			goto clean_up;
2219 		    }
2220 		    if (!dump_output_immediately &&
2221 			format_out == HTAtom_for("www/download")) {
2222 			/*
2223 			 * Convert a download request to a presentation request
2224 			 * for interactive users.  - FM
2225 			 */
2226 			format_out = WWW_PRESENT;
2227 		    }
2228 		    break;
2229 		}
2230 
2231 		if (server_status == 304) {	/* Not Modified */
2232 		    /*
2233 		     * We didn't send an "If-Modified-Since" header, so this
2234 		     * status is inappropriate.  We'll deal with it by showing
2235 		     * the full header to the user as text/plain.  - FM
2236 		     */
2237 		    HTAlert(gettext("Got unexpected 304 Not Modified status."));
2238 		    do_head = TRUE;
2239 		    break;
2240 		}
2241 
2242 		if (server_status == 305 ||
2243 		    server_status == 306 ||
2244 		    server_status > 307) {
2245 		    /*
2246 		     * Show user the content, if any, for 305, 306, or unknown
2247 		     * status.  - FM
2248 		     */
2249 		    HTAlert(line_buffer);
2250 		    if (traversal) {
2251 			HTTP_NETCLOSE(s, handle);
2252 			status = -1;
2253 			goto clean_up;
2254 		    }
2255 		    if (!dump_output_immediately &&
2256 			format_out == HTAtom_for("www/download")) {
2257 			/*
2258 			 * Convert a download request to a presentation request
2259 			 * for interactive users.  - FM
2260 			 */
2261 			format_out = WWW_PRESENT;
2262 		    }
2263 		    break;
2264 		}
2265 
2266 		/*
2267 		 * We do not load the file, but read the headers for the
2268 		 * "Location:", check out that redirecting_url and if it's
2269 		 * acceptible (e.g., not a telnet URL when we have that
2270 		 * disabled), initiate a new fetch.  If that's another
2271 		 * redirecting_url, we'll repeat the checks, and fetch
2272 		 * initiations if acceptible, until we reach the actual URL, or
2273 		 * the redirection limit set in HTAccess.c is exceeded.  If the
2274 		 * status was 301 indicating that the relocation is permanent,
2275 		 * we set the permanent_redirection flag to make it permanent
2276 		 * for the current anchor tree (i.e., will persist until the
2277 		 * tree is freed or the client exits).  If the redirection
2278 		 * would include POST content, we seek confirmation from an
2279 		 * interactive user, with option to use 303 for 301 (but not
2280 		 * for 307), and otherwise refuse the redirection.  We also
2281 		 * don't allow permanent redirection if we keep POST content.
2282 		 * If we don't find the Location header or it's value is
2283 		 * zero-length, we display whatever the server returned, and
2284 		 * the user should RELOAD that to try again, or make a
2285 		 * selection from it if it contains links, or Left-Arrow to the
2286 		 * previous document.  - FM
2287 		 */
2288 		{
2289 		    if ((dump_output_immediately || traversal) &&
2290 			do_post &&
2291 			server_status != 303 &&
2292 			server_status != 302 &&
2293 			server_status != 301) {
2294 			/*
2295 			 * Don't redirect POST content without approval from an
2296 			 * interactive user.  - FM
2297 			 */
2298 			HTTP_NETCLOSE(s, handle);
2299 			status = -1;
2300 			HTAlert(gettext("Redirection of POST content requires user approval."));
2301 			if (traversal)
2302 			    HTProgress(line_buffer);
2303 			goto clean_up;
2304 		    }
2305 
2306 		    HTProgress(line_buffer);
2307 		    if (server_status == 301) {		/* Moved Permanently */
2308 			if (do_post) {
2309 			    /*
2310 			     * Don't make the redirection permanent if we have
2311 			     * POST content.  - FM
2312 			     */
2313 			    CTRACE((tfp,
2314 				    "HTTP: Have POST content.  Treating 301 (Permanent) as Temporary.\n"));
2315 			    HTAlert(gettext("Have POST content.  Treating Permanent Redirection as Temporary.\n"));
2316 			} else {
2317 			    permanent_redirection = TRUE;
2318 			}
2319 		    }
2320 		    doing_redirect = TRUE;
2321 
2322 		    break;
2323 		}
2324 
2325 	    case 4:
2326 		/*
2327 		 * "I think I goofed!" (Client Error) - FM
2328 		 */
2329 		switch (server_status) {
2330 		case 401:	/* Unauthorized */
2331 		    /*
2332 		     * Authorization for origin server required.  If show_401
2333 		     * is set, proceed to showing the 401 body.  Otherwise, if
2334 		     * we can set up authorization based on the
2335 		     * WWW-Authenticate header, and the user provides a
2336 		     * username and password, try again.  Otherwise, check
2337 		     * whether to show the 401 body or restore the current
2338 		     * document - FM
2339 		     */
2340 		    if (show_401)
2341 			break;
2342 		    if (HTAA_shouldRetryWithAuth(start_of_data, (size_t)
2343 						 length, s, NO)) {
2344 
2345 			HTTP_NETCLOSE(s, handle);
2346 			if (dump_output_immediately &&
2347 			    !HTAA_HaveUserinfo(HTParse(arg, "", PARSE_HOST)) &&
2348 			    !authentication_info[0]) {
2349 			    fprintf(stderr,
2350 				    "HTTP: Access authorization required.\n");
2351 			    fprintf(stderr,
2352 				    "       Use the -auth=id:pw parameter.\n");
2353 			    status = HT_NO_DATA;
2354 			    goto clean_up;
2355 			}
2356 
2357 			CTRACE((tfp, "%s %d %s\n",
2358 				"HTTP: close socket", s,
2359 				"to retry with Access Authorization"));
2360 
2361 			_HTProgress(gettext("Retrying with access authorization information."));
2362 			FREE(line_buffer);
2363 			FREE(line_kept_clean);
2364 #ifdef USE_SSL
2365 			if (using_proxy && !StrNCmp(url, "https://", 8)) {
2366 			    url = arg;
2367 			    do_connect = TRUE;
2368 			    did_connect = FALSE;
2369 			}
2370 #endif /* USE_SSL */
2371 			goto try_again;
2372 		    } else if (!(traversal || dump_output_immediately) &&
2373 			       HTConfirm(gettext("Show the 401 message body?"))) {
2374 			break;
2375 		    } else {
2376 			if (traversal || dump_output_immediately)
2377 			    HTAlert(FAILED_RETRY_WITH_AUTH);
2378 			HTTP_NETCLOSE(s, handle);
2379 			status = -1;
2380 			goto clean_up;
2381 		    }
2382 
2383 		case 407:
2384 		    /*
2385 		     * Authorization for proxy server required.  If we are not
2386 		     * in fact using a proxy, or show_407 is set, proceed to
2387 		     * showing the 407 body.  Otherwise, if we can set up
2388 		     * authorization based on the Proxy-Authenticate header,
2389 		     * and the user provides a username and password, try
2390 		     * again.  Otherwise, check whether to show the 401 body or
2391 		     * restore the current document.  - FM & AJL
2392 		     */
2393 		    if (!using_proxy || show_407)
2394 			break;
2395 		    if (HTAA_shouldRetryWithAuth(start_of_data, (size_t)
2396 						 length, s, YES)) {
2397 
2398 			HTTP_NETCLOSE(s, handle);
2399 			if (dump_output_immediately && !proxyauth_info[0]) {
2400 			    fprintf(stderr,
2401 				    "HTTP: Proxy authorization required.\n");
2402 			    fprintf(stderr,
2403 				    "       Use the -pauth=id:pw parameter.\n");
2404 			    status = HT_NO_DATA;
2405 			    goto clean_up;
2406 			}
2407 
2408 			CTRACE((tfp, "%s %d %s\n",
2409 				"HTTP: close socket", s,
2410 				"to retry with Proxy Authorization"));
2411 
2412 			_HTProgress(HTTP_RETRY_WITH_PROXY);
2413 			FREE(line_buffer);
2414 			FREE(line_kept_clean);
2415 			goto try_again;
2416 		    } else if (!(traversal || dump_output_immediately) &&
2417 			       HTConfirm(gettext("Show the 407 message body?"))) {
2418 			if (!dump_output_immediately &&
2419 			    format_out == HTAtom_for("www/download")) {
2420 			    /*
2421 			     * Convert a download request to a presentation
2422 			     * request for interactive users.  - FM
2423 			     */
2424 			    format_out = WWW_PRESENT;
2425 			}
2426 			break;
2427 		    } else {
2428 			if (traversal || dump_output_immediately)
2429 			    HTAlert(FAILED_RETRY_WITH_PROXY);
2430 			HTTP_NETCLOSE(s, handle);
2431 			status = -1;
2432 			goto clean_up;
2433 		    }
2434 
2435 		case 408:
2436 		    /*
2437 		     * Request Timeout.  Show the status message and restore
2438 		     * the current document.  - FM
2439 		     */
2440 		    HTAlert(line_buffer);
2441 		    HTTP_NETCLOSE(s, handle);
2442 		    status = HT_NO_DATA;
2443 		    goto clean_up;
2444 
2445 		default:
2446 		    /*
2447 		     * 400 Bad Request.
2448 		     * 402 Payment Required.
2449 		     * 403 Forbidden.
2450 		     * 404 Not Found.
2451 		     * 405 Method Not Allowed.
2452 		     * 406 Not Acceptable.
2453 		     * 409 Conflict.
2454 		     * 410 Gone.
2455 		     * 411 Length Required.
2456 		     * 412 Precondition Failed.
2457 		     * 413 Request Entity Too Large.
2458 		     * 414 Request-URI Too Long.
2459 		     * 415 Unsupported Media Type.
2460 		     * 416 List Response (for content negotiation).
2461 		     * > 416 is unknown.
2462 		     * Show the status message, and display the returned text
2463 		     * if we are not doing a traversal.  - FM
2464 		     */
2465 		    HTAlert(line_buffer);
2466 		    if (traversal) {
2467 			HTTP_NETCLOSE(s, handle);
2468 			status = -1;
2469 			goto clean_up;
2470 		    }
2471 		    if (!dump_output_immediately &&
2472 			format_out == HTAtom_for("www/download")) {
2473 			/*
2474 			 * Convert a download request to a presentation request
2475 			 * for interactive users.  - FM
2476 			 */
2477 			format_out = WWW_PRESENT;
2478 		    }
2479 		    break;
2480 		}		/* case 4 switch */
2481 		break;
2482 
2483 	    case 5:
2484 		/*
2485 		 * "I think YOU goofed!" (server error)
2486 		 * 500 Internal Server Error
2487 		 * 501 Not Implemented
2488 		 * 502 Bad Gateway
2489 		 * 503 Service Unavailable
2490 		 * 504 Gateway Timeout
2491 		 * 505 HTTP Version Not Supported
2492 		 * > 505 is unknown.
2493 		 * Should always include a message, which we always should
2494 		 * display.  - FM
2495 		 */
2496 		HTAlert(line_buffer);
2497 		if (traversal) {
2498 		    HTTP_NETCLOSE(s, handle);
2499 		    status = -1;
2500 		    goto clean_up;
2501 		}
2502 		if (!dump_output_immediately &&
2503 		    format_out == HTAtom_for("www/download")) {
2504 		    /*
2505 		     * Convert a download request to a presentation request for
2506 		     * interactive users.  - FM
2507 		     */
2508 		    format_out = WWW_PRESENT;
2509 		}
2510 		break;
2511 
2512 	    default:
2513 		/*
2514 		 * Bad or unknown server_status number.  Take a chance and hope
2515 		 * there is something to display.  - FM
2516 		 */
2517 		HTAlert(gettext("Unknown status reply from server!"));
2518 		HTAlert(line_buffer);
2519 		if (traversal) {
2520 		    HTTP_NETCLOSE(s, handle);
2521 		    status = -1;
2522 		    goto clean_up;
2523 		}
2524 		if (!dump_output_immediately &&
2525 		    format_out == HTAtom_for("www/download")) {
2526 		    /*
2527 		     * Convert a download request to a presentation request for
2528 		     * interactive users.  - FM
2529 		     */
2530 		    format_out = WWW_PRESENT;
2531 		}
2532 		break;
2533 	    }			/* Switch on server_status/100 */
2534 
2535 	}			/* Full HTTP reply */
2536     }				/* scope of fields */
2537 
2538     /*
2539      * The user may have pressed the 'z'ap key during the pause caused by one
2540      * of the HTAlerts above if the server reported an error, to skip loading
2541      * of the error response page.  Checking here before setting up the stream
2542      * stack and feeding it data avoids doing unnecessary work, it also can
2543      * avoid unnecessarily pushing a loaded document out of the cache to make
2544      * room for the unwanted error page.  - kw
2545      */
2546     if (HTCheckForInterrupt()) {
2547 	HTTP_NETCLOSE(s, handle);
2548 	if (doing_redirect) {
2549 	    /*
2550 	     * Impatient user.  - FM
2551 	     */
2552 	    CTRACE((tfp, "HTTP: Interrupted followup read.\n"));
2553 	    _HTProgress(CONNECTION_INTERRUPTED);
2554 	}
2555 	status = HT_INTERRUPTED;
2556 	goto clean_up;
2557     }
2558     /*
2559      * Set up the stream stack to handle the body of the message.
2560      */
2561     if (do_head || keep_mime_headers) {
2562 	/*
2563 	 * It was a HEAD request, or we want the headers and source.
2564 	 */
2565 	start_of_data = line_kept_clean;
2566 #ifdef SH_EX			/* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
2567 /* GIF file contains \0, so strlen does not return the data length */
2568 	length = real_length_of_line;
2569 #else
2570 	length = rawlength;
2571 #endif
2572 	format_in = HTAtom_for(STR_PLAINTEXT);
2573 
2574     } else if (doing_redirect) {
2575 
2576 	format_in = HTAtom_for("message/x-http-redirection");
2577 	StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in));
2578 	if (traversal) {
2579 	    format_out = WWW_DEBUG;
2580 	    if (!sink)
2581 		sink = HTErrorStream();
2582 	} else if (!dump_output_immediately &&
2583 		   format_out == HTAtom_for("www/download")) {
2584 	    /*
2585 	     * Convert a download request to a presentation request for
2586 	     * interactive users.  - FM
2587 	     */
2588 	    format_out = WWW_PRESENT;
2589 	}
2590     }
2591 
2592     target = HTStreamStack(format_in,
2593 			   format_out,
2594 			   sink, anAnchor);
2595 
2596     if (target == NULL) {
2597 	char *buffer = NULL;
2598 
2599 	HTTP_NETCLOSE(s, handle);
2600 	HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
2601 		   HTAtom_name(format_in), HTAtom_name(format_out));
2602 	_HTProgress(buffer);
2603 	FREE(buffer);
2604 	status = -1;
2605 	goto clean_up;
2606     }
2607 
2608     /*
2609      * Recycle the first chunk of data, in all cases.
2610      */
2611     (*target->isa->put_block) (target, start_of_data, length);
2612 
2613     /*
2614      * Go pull the bulk of the data down.
2615      */
2616     rv = HTCopy(anAnchor, s, (void *) handle, target);
2617 
2618     /*
2619      * If we get here with doing_redirect set, it means that we were looking
2620      * for a Location header.  We either have got it now in redirecting_url -
2621      * in that case the stream should not have loaded any data.  Or we didn't
2622      * get it, in that case the stream may have presented the message body
2623      * normally.  - kw
2624      */
2625 
2626     if (rv == -1) {
2627 	/*
2628 	 * Intentional interrupt before data were received, not an error
2629 	 */
2630 	if (doing_redirect && traversal)
2631 	    status = -1;
2632 	else
2633 	    status = HT_INTERRUPTED;
2634 	HTTP_NETCLOSE(s, handle);
2635 	goto clean_up;
2636     }
2637 
2638     if (rv == -2) {
2639 	/*
2640 	 * Aw hell, a REAL error, maybe cuz it's a dumb HTTP0 server
2641 	 */
2642 	(*target->isa->_abort) (target, NULL);
2643 	if (doing_redirect && redirecting_url) {
2644 	    /*
2645 	     * Got a location before the error occurred?  Then consider it an
2646 	     * interrupt but proceed below as normal.  - kw
2647 	     */
2648 	    /* do nothing here */
2649 	} else {
2650 	    HTTP_NETCLOSE(s, handle);
2651 	    if (!doing_redirect && !already_retrying && !do_post) {
2652 		CTRACE((tfp, "HTTP: Trying again with HTTP0 request.\n"));
2653 		/*
2654 		 * May as well consider it an interrupt -- right?
2655 		 */
2656 		FREE(line_buffer);
2657 		FREE(line_kept_clean);
2658 		extensions = NO;
2659 		already_retrying = TRUE;
2660 		_HTProgress(RETRYING_AS_HTTP0);
2661 		goto try_again;
2662 	    } else {
2663 		status = HT_NOT_LOADED;
2664 		goto clean_up;
2665 	    }
2666 	}
2667     }
2668 
2669     /*
2670      * Free if complete transmission (socket was closed before return).  Close
2671      * socket if partial transmission (was freed on abort).
2672      */
2673     if (rv != HT_INTERRUPTED && rv != -2) {
2674 	(*target->isa->_free) (target);
2675     } else {
2676 	HTTP_NETCLOSE(s, handle);
2677     }
2678 
2679     if (doing_redirect) {
2680 	if (redirecting_url) {
2681 	    /*
2682 	     * Set up for checking redirecting_url in LYGetFile.c for
2683 	     * restrictions before we seek the document at that Location.  - FM
2684 	     */
2685 	    CTRACE((tfp, "HTTP: Picked up location '%s'\n",
2686 		    redirecting_url));
2687 	    if (rv == HT_INTERRUPTED) {
2688 		/*
2689 		 * Intentional interrupt after data were received, not an error
2690 		 * (probably).  We take it as a user request to abandon the
2691 		 * redirection chain.
2692 		 *
2693 		 * This could reasonably be changed (by just removing this
2694 		 * block), it would make sense if there are redirecting
2695 		 * resources that "hang" after sending the headers.  - kw
2696 		 */
2697 		FREE(redirecting_url);
2698 		CTRACE((tfp, "HTTP: Interrupted followup read.\n"));
2699 		status = HT_INTERRUPTED;
2700 		goto clean_up;
2701 	    }
2702 	    HTProgress(line_buffer);
2703 	    if (server_status == 305) {		/* Use Proxy */
2704 		/*
2705 		 * Make sure the proxy field ends with a slash.  - FM
2706 		 */
2707 		if (redirecting_url[strlen(redirecting_url) - 1]
2708 		    != '/')
2709 		    StrAllocCat(redirecting_url, "/");
2710 		/*
2711 		 * Append our URL.  - FM
2712 		 */
2713 		StrAllocCat(redirecting_url, anAnchor->address);
2714 		CTRACE((tfp, "HTTP: Proxy URL is '%s'\n",
2715 			redirecting_url));
2716 	    }
2717 	    if (!do_post ||
2718 		server_status == 303 ||
2719 		server_status == 302) {
2720 		/*
2721 		 * We don't have POST content (nor support PUT or DELETE), or
2722 		 * the status is "See Other" or "General Redirection" and we
2723 		 * can convert to GET, so go back and check out the new URL.  -
2724 		 * FM
2725 		 */
2726 		status = HT_REDIRECTING;
2727 		goto clean_up;
2728 	    }
2729 	    /*
2730 	     * Make sure the user wants to redirect the POST content, or treat
2731 	     * as GET - FM & DK
2732 	     */
2733 	    switch (HTConfirmPostRedirect(redirecting_url,
2734 					  server_status)) {
2735 		/*
2736 		 * User failed to confirm.  Abort the fetch.
2737 		 */
2738 	    case 0:
2739 		FREE(redirecting_url);
2740 		status = HT_NO_DATA;
2741 		goto clean_up;
2742 
2743 		/*
2744 		 * User wants to treat as GET with no content.  Go back to
2745 		 * check out the URL.
2746 		 */
2747 	    case 303:
2748 		break;
2749 
2750 		/*
2751 		 * Set the flag to retain the POST content and go back to check
2752 		 * out the URL.  - FM
2753 		 */
2754 	    default:
2755 		redirect_post_content = TRUE;
2756 	    }
2757 
2758 	    /* Lou's old comment:  - FM */
2759 	    /* OK, now we've got the redirection URL temporarily stored
2760 	       in external variable redirecting_url, exported from HTMIME.c,
2761 	       since there's no straightforward way to do this in the library
2762 	       currently.  Do the right thing. */
2763 
2764 	    status = HT_REDIRECTING;
2765 
2766 	} else {
2767 	    status = traversal ? -1 : HT_LOADED;
2768 	}
2769 
2770     } else {
2771 	/*
2772 	 * If any data were received, treat as a complete transmission
2773 	 */
2774 	status = HT_LOADED;
2775     }
2776 
2777     /*
2778      * Clean up
2779      */
2780   clean_up:
2781     FREE(line_buffer);
2782     FREE(line_kept_clean);
2783 
2784   done:
2785     /*
2786      * Clear out on exit, just in case.
2787      */
2788     reloading = FALSE;
2789 #ifdef USE_SSL
2790     FREE(connect_host);
2791     if (handle) {
2792 	SSL_free(handle);
2793 	SSL_handle = handle = NULL;
2794     }
2795 #endif /* USE_SSL */
2796     dump_server_status = server_status;
2797     return status;
2798 }
2799 
2800 /*	Protocol descriptor
2801 */
2802 #ifdef GLOBALDEF_IS_MACRO
2803 #define _HTTP_C_GLOBALDEF_1_INIT { "http", HTLoadHTTP, 0}
2804 GLOBALDEF(HTProtocol, HTTP, _HTTP_C_GLOBALDEF_1_INIT);
2805 #define _HTTP_C_GLOBALDEF_2_INIT { "https", HTLoadHTTP, 0}
2806 GLOBALDEF(HTProtocol, HTTPS, _HTTP_C_GLOBALDEF_2_INIT);
2807 #else
2808 GLOBALDEF HTProtocol HTTP =
2809 {"http", HTLoadHTTP, 0};
2810 GLOBALDEF HTProtocol HTTPS =
2811 {"https", HTLoadHTTP, 0};
2812 #endif /* GLOBALDEF_IS_MACRO */
2813