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 *) ¶, 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