xref: /aosp_15_r20/external/toybox/toys/net/wget.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* wget.c - Simple downloader to get the resource file from a HTTP server
2*cf5a6c84SAndroid Build Coastguard Worker  *
3*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2016 Lipi C.H. Lee <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2021 Eric Molitor <[email protected]>
5*cf5a6c84SAndroid Build Coastguard Worker  *
6*cf5a6c84SAndroid Build Coastguard Worker  * Relevant sources of information
7*cf5a6c84SAndroid Build Coastguard Worker  * -------------------------------
8*cf5a6c84SAndroid Build Coastguard Worker  * HTTP 1.1: https://www.rfc-editor.org/rfc/rfc7230
9*cf5a6c84SAndroid Build Coastguard Worker  * Chunked Encoding: https://www.rfc-editor.org/rfc/rfc7230#section-4.1
10*cf5a6c84SAndroid Build Coastguard Worker  * UTF-8 Encoded Header Values https://www.rfc-editor.org/rfc/rfc5987
11*cf5a6c84SAndroid Build Coastguard Worker  *
12*cf5a6c84SAndroid Build Coastguard Worker  * Test URLs
13*cf5a6c84SAndroid Build Coastguard Worker  * ---------
14*cf5a6c84SAndroid Build Coastguard Worker  * Chunked Encoding: https://jigsaw.w3.org/HTTP/ChunkedScript
15*cf5a6c84SAndroid Build Coastguard Worker  * Redirect 301: https://jigsaw.w3.org/HTTP/300/301.html
16*cf5a6c84SAndroid Build Coastguard Worker  * Redirect 302: https://jigsaw.w3.org/HTTP/300/302.html
17*cf5a6c84SAndroid Build Coastguard Worker  * TLS 1.0: https://tls-v1-0.badssl.com:1010/
18*cf5a6c84SAndroid Build Coastguard Worker  * TLS 1.1: https://tls-v1-1.badssl.com:1011/
19*cf5a6c84SAndroid Build Coastguard Worker  * TLS 1.2: https://tls-v1-2.badssl.com:1012/
20*cf5a6c84SAndroid Build Coastguard Worker  * TLS 1.3: https://tls13.1d.pw/
21*cf5a6c84SAndroid Build Coastguard Worker  * Transfer Encoding [gzip|deflate]: https://jigsaw.w3.org/HTTP/TE/bar.txt
22*cf5a6c84SAndroid Build Coastguard Worker  *
23*cf5a6c84SAndroid Build Coastguard Worker  *
24*cf5a6c84SAndroid Build Coastguard Worker  * TODO: Add support for configurable TLS versions
25*cf5a6c84SAndroid Build Coastguard Worker  * TODO: Add support for ftp
26*cf5a6c84SAndroid Build Coastguard Worker  * TODO: Add support for Transfer Encoding (gzip|deflate)
27*cf5a6c84SAndroid Build Coastguard Worker  * TODO: Add support for RFC5987
28*cf5a6c84SAndroid Build Coastguard Worker 
29*cf5a6c84SAndroid Build Coastguard Worker USE_WGET(NEWTOY(wget, "<1>1(max-redirect)#<0=20d(debug)O(output-document):p(post-data):", TOYFLAG_USR|TOYFLAG_BIN))
30*cf5a6c84SAndroid Build Coastguard Worker 
31*cf5a6c84SAndroid Build Coastguard Worker config WGET
32*cf5a6c84SAndroid Build Coastguard Worker   bool "wget"
33*cf5a6c84SAndroid Build Coastguard Worker   default y
34*cf5a6c84SAndroid Build Coastguard Worker   help
35*cf5a6c84SAndroid Build Coastguard Worker     usage: wget [OPTIONS]... [URL]
36*cf5a6c84SAndroid Build Coastguard Worker         --max-redirect          maximum redirections allowed
37*cf5a6c84SAndroid Build Coastguard Worker     -d, --debug                 print lots of debugging information
38*cf5a6c84SAndroid Build Coastguard Worker     -O, --output-document=FILE  specify output filename
39*cf5a6c84SAndroid Build Coastguard Worker     -p, --post-data=DATA        send data in body of POST request
40*cf5a6c84SAndroid Build Coastguard Worker 
41*cf5a6c84SAndroid Build Coastguard Worker     examples:
42*cf5a6c84SAndroid Build Coastguard Worker       wget http://www.example.com
43*cf5a6c84SAndroid Build Coastguard Worker 
44*cf5a6c84SAndroid Build Coastguard Worker config WGET_LIBTLS
45*cf5a6c84SAndroid Build Coastguard Worker   bool "Enable HTTPS support for wget via LibTLS"
46*cf5a6c84SAndroid Build Coastguard Worker   default n
47*cf5a6c84SAndroid Build Coastguard Worker   depends on WGET && !TOYBOX_LIBCRYPTO
48*cf5a6c84SAndroid Build Coastguard Worker   help
49*cf5a6c84SAndroid Build Coastguard Worker     Enable HTTPS support for wget by linking to LibTLS.
50*cf5a6c84SAndroid Build Coastguard Worker     Supports using libtls, libretls or libtls-bearssl.
51*cf5a6c84SAndroid Build Coastguard Worker 
52*cf5a6c84SAndroid Build Coastguard Worker     Use TOYBOX_LIBCRYPTO to enable HTTPS support via OpenSSL.
53*cf5a6c84SAndroid Build Coastguard Worker */
54*cf5a6c84SAndroid Build Coastguard Worker 
55*cf5a6c84SAndroid Build Coastguard Worker #define FOR_wget
56*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
57*cf5a6c84SAndroid Build Coastguard Worker 
58*cf5a6c84SAndroid Build Coastguard Worker #if CFG_WGET_LIBTLS
59*cf5a6c84SAndroid Build Coastguard Worker #define WGET_SSL 1
60*cf5a6c84SAndroid Build Coastguard Worker #include <tls.h>
61*cf5a6c84SAndroid Build Coastguard Worker #elif CFG_TOYBOX_LIBCRYPTO
62*cf5a6c84SAndroid Build Coastguard Worker #define WGET_SSL 1
63*cf5a6c84SAndroid Build Coastguard Worker #include <openssl/crypto.h>
64*cf5a6c84SAndroid Build Coastguard Worker #include <openssl/ssl.h>
65*cf5a6c84SAndroid Build Coastguard Worker #include <openssl/err.h>
66*cf5a6c84SAndroid Build Coastguard Worker #else
67*cf5a6c84SAndroid Build Coastguard Worker #define WGET_SSL 0
68*cf5a6c84SAndroid Build Coastguard Worker #endif
69*cf5a6c84SAndroid Build Coastguard Worker #define HTTPS (WGET_SSL && TT.https)
70*cf5a6c84SAndroid Build Coastguard Worker 
71*cf5a6c84SAndroid Build Coastguard Worker 
72*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
73*cf5a6c84SAndroid Build Coastguard Worker   char *p, *O;
74*cf5a6c84SAndroid Build Coastguard Worker   long max_redirect;
75*cf5a6c84SAndroid Build Coastguard Worker 
76*cf5a6c84SAndroid Build Coastguard Worker   int sock, https;
77*cf5a6c84SAndroid Build Coastguard Worker   char *url;
78*cf5a6c84SAndroid Build Coastguard Worker #if CFG_WGET_LIBTLS
79*cf5a6c84SAndroid Build Coastguard Worker   struct tls *tls;
80*cf5a6c84SAndroid Build Coastguard Worker #elif CFG_TOYBOX_LIBCRYPTO
81*cf5a6c84SAndroid Build Coastguard Worker   struct ssl_ctx_st *ctx;
82*cf5a6c84SAndroid Build Coastguard Worker   struct ssl_st *ssl;
83*cf5a6c84SAndroid Build Coastguard Worker #endif
84*cf5a6c84SAndroid Build Coastguard Worker )
85*cf5a6c84SAndroid Build Coastguard Worker 
86*cf5a6c84SAndroid Build Coastguard Worker // get http info in URL
wget_info(char * url,char ** host,char ** port,char ** path)87*cf5a6c84SAndroid Build Coastguard Worker static void wget_info(char *url, char **host, char **port, char **path)
88*cf5a6c84SAndroid Build Coastguard Worker {
89*cf5a6c84SAndroid Build Coastguard Worker   char *ss = url;
90*cf5a6c84SAndroid Build Coastguard Worker 
91*cf5a6c84SAndroid Build Coastguard Worker   // Must start with case insensitive http:// or https://
92*cf5a6c84SAndroid Build Coastguard Worker   if (strncasecmp(url, "http", 4)) url = 0;
93*cf5a6c84SAndroid Build Coastguard Worker   else {
94*cf5a6c84SAndroid Build Coastguard Worker     url += 4;
95*cf5a6c84SAndroid Build Coastguard Worker     if ((TT.https = WGET_SSL && toupper(*url=='s'))) url++;
96*cf5a6c84SAndroid Build Coastguard Worker     if (!strstart(&url, "://")) url = 0;
97*cf5a6c84SAndroid Build Coastguard Worker   }
98*cf5a6c84SAndroid Build Coastguard Worker   if (!url) error_exit("unsupported protocol: %s", ss);
99*cf5a6c84SAndroid Build Coastguard Worker   if ((*path = strchr(*host = url, '/'))) *((*path)++) = 0;
100*cf5a6c84SAndroid Build Coastguard Worker   else *path = "";
101*cf5a6c84SAndroid Build Coastguard Worker 
102*cf5a6c84SAndroid Build Coastguard Worker   // Get port number and trim literal IPv6 addresses
103*cf5a6c84SAndroid Build Coastguard Worker   if (**host=='[' && (ss = strchr(++*host, ']'))) {
104*cf5a6c84SAndroid Build Coastguard Worker     *ss++ = 0;
105*cf5a6c84SAndroid Build Coastguard Worker     *port = (*ss==':') ? ++ss : 0;
106*cf5a6c84SAndroid Build Coastguard Worker   } else if ((*port = strchr(*host, ':'))) *((*port)++) = 0;
107*cf5a6c84SAndroid Build Coastguard Worker   if (!*port) *port = HTTPS ? "443" : "80";
108*cf5a6c84SAndroid Build Coastguard Worker }
109*cf5a6c84SAndroid Build Coastguard Worker 
wget_connect(char * host,char * port)110*cf5a6c84SAndroid Build Coastguard Worker static void wget_connect(char *host, char *port)
111*cf5a6c84SAndroid Build Coastguard Worker {
112*cf5a6c84SAndroid Build Coastguard Worker   if (!HTTPS)
113*cf5a6c84SAndroid Build Coastguard Worker     TT.sock = xconnectany(xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0));
114*cf5a6c84SAndroid Build Coastguard Worker   else {
115*cf5a6c84SAndroid Build Coastguard Worker #if CFG_WGET_LIBTLS
116*cf5a6c84SAndroid Build Coastguard Worker     struct tls_config *cfg = NULL;
117*cf5a6c84SAndroid Build Coastguard Worker     uint32_t protocols;
118*cf5a6c84SAndroid Build Coastguard Worker     if (!(TT.tls = tls_client()))
119*cf5a6c84SAndroid Build Coastguard Worker       error_exit("tls_client: %s", tls_error(TT.tls));
120*cf5a6c84SAndroid Build Coastguard Worker     if (!(cfg = tls_config_new()))
121*cf5a6c84SAndroid Build Coastguard Worker       error_exit("tls_config_new: %s", tls_config_error(cfg));
122*cf5a6c84SAndroid Build Coastguard Worker     if (tls_config_parse_protocols(&protocols, "tlsv1.2"))
123*cf5a6c84SAndroid Build Coastguard Worker       error_exit("tls_config_parse_protocols");
124*cf5a6c84SAndroid Build Coastguard Worker     if (tls_config_set_protocols(cfg, protocols))
125*cf5a6c84SAndroid Build Coastguard Worker       error_exit("tls_config_set_protocols: %s", tls_config_error(cfg));
126*cf5a6c84SAndroid Build Coastguard Worker     if (tls_configure(TT.tls, cfg))
127*cf5a6c84SAndroid Build Coastguard Worker       error_exit("tls_configure: %s", tls_error(TT.tls));
128*cf5a6c84SAndroid Build Coastguard Worker     tls_config_free(cfg);
129*cf5a6c84SAndroid Build Coastguard Worker 
130*cf5a6c84SAndroid Build Coastguard Worker     if (tls_connect(TT.tls, host, port))
131*cf5a6c84SAndroid Build Coastguard Worker       error_exit("tls_connect: %s", tls_error(TT.tls));
132*cf5a6c84SAndroid Build Coastguard Worker #elif CFG_TOYBOX_LIBCRYPTO
133*cf5a6c84SAndroid Build Coastguard Worker     SSL_library_init();
134*cf5a6c84SAndroid Build Coastguard Worker     OpenSSL_add_all_algorithms();
135*cf5a6c84SAndroid Build Coastguard Worker     SSL_load_error_strings();
136*cf5a6c84SAndroid Build Coastguard Worker     ERR_load_crypto_strings();
137*cf5a6c84SAndroid Build Coastguard Worker 
138*cf5a6c84SAndroid Build Coastguard Worker     TT.ctx = SSL_CTX_new(TLS_client_method());
139*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.ctx) error_exit("SSL_CTX_new");
140*cf5a6c84SAndroid Build Coastguard Worker 
141*cf5a6c84SAndroid Build Coastguard Worker     TT.sock = xconnectany(xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0));
142*cf5a6c84SAndroid Build Coastguard Worker 
143*cf5a6c84SAndroid Build Coastguard Worker     TT.ssl = SSL_new(TT.ctx);
144*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.ssl)
145*cf5a6c84SAndroid Build Coastguard Worker       error_exit("SSL_new: %s", ERR_error_string(ERR_get_error(), NULL));
146*cf5a6c84SAndroid Build Coastguard Worker 
147*cf5a6c84SAndroid Build Coastguard Worker     if (!SSL_set_tlsext_host_name(TT.ssl, host))
148*cf5a6c84SAndroid Build Coastguard Worker       error_exit("SSL_set_tlsext_host_name: %s",
149*cf5a6c84SAndroid Build Coastguard Worker                  ERR_error_string(ERR_get_error(), NULL));
150*cf5a6c84SAndroid Build Coastguard Worker 
151*cf5a6c84SAndroid Build Coastguard Worker     SSL_set_fd(TT.ssl, TT.sock);
152*cf5a6c84SAndroid Build Coastguard Worker     if (SSL_connect(TT.ssl) == -1)
153*cf5a6c84SAndroid Build Coastguard Worker       error_exit("SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL));
154*cf5a6c84SAndroid Build Coastguard Worker 
155*cf5a6c84SAndroid Build Coastguard Worker     if (FLAG(d)) printf("TLS: %s\n", SSL_get_cipher(TT.ssl));
156*cf5a6c84SAndroid Build Coastguard Worker #endif
157*cf5a6c84SAndroid Build Coastguard Worker   }
158*cf5a6c84SAndroid Build Coastguard Worker }
159*cf5a6c84SAndroid Build Coastguard Worker 
wget_read(void * buf,size_t len)160*cf5a6c84SAndroid Build Coastguard Worker static size_t wget_read(void *buf, size_t len)
161*cf5a6c84SAndroid Build Coastguard Worker {
162*cf5a6c84SAndroid Build Coastguard Worker   if (!HTTPS) return xread(TT.sock, buf, len);
163*cf5a6c84SAndroid Build Coastguard Worker   else {
164*cf5a6c84SAndroid Build Coastguard Worker     char *err = 0;
165*cf5a6c84SAndroid Build Coastguard Worker     int ret;
166*cf5a6c84SAndroid Build Coastguard Worker 
167*cf5a6c84SAndroid Build Coastguard Worker #if CFG_WGET_LIBTLS
168*cf5a6c84SAndroid Build Coastguard Worker     if ((ret = tls_read(TT.tls, buf, len))<0) err = tls_error(TT.tls);
169*cf5a6c84SAndroid Build Coastguard Worker #elif CFG_TOYBOX_LIBCRYPTO
170*cf5a6c84SAndroid Build Coastguard Worker     if ((ret = SSL_read(TT.ssl, buf, len))<0)
171*cf5a6c84SAndroid Build Coastguard Worker       err = ERR_error_string(ERR_get_error(), 0);
172*cf5a6c84SAndroid Build Coastguard Worker #endif
173*cf5a6c84SAndroid Build Coastguard Worker     if (err) error_exit("https read: %s", err);
174*cf5a6c84SAndroid Build Coastguard Worker 
175*cf5a6c84SAndroid Build Coastguard Worker     return ret;
176*cf5a6c84SAndroid Build Coastguard Worker   }
177*cf5a6c84SAndroid Build Coastguard Worker }
178*cf5a6c84SAndroid Build Coastguard Worker 
wget_write(void * buf,size_t len)179*cf5a6c84SAndroid Build Coastguard Worker static void wget_write(void *buf, size_t len)
180*cf5a6c84SAndroid Build Coastguard Worker {
181*cf5a6c84SAndroid Build Coastguard Worker   if (!HTTPS) xwrite(TT.sock, buf, len);
182*cf5a6c84SAndroid Build Coastguard Worker   else {
183*cf5a6c84SAndroid Build Coastguard Worker     char *err = 0;
184*cf5a6c84SAndroid Build Coastguard Worker 
185*cf5a6c84SAndroid Build Coastguard Worker #if CFG_WGET_LIBTLS
186*cf5a6c84SAndroid Build Coastguard Worker     if (len != tls_write(TT.tls, buf, len)) err = tls_error(TT.tls);
187*cf5a6c84SAndroid Build Coastguard Worker #elif CFG_TOYBOX_LIBCRYPTO
188*cf5a6c84SAndroid Build Coastguard Worker     if (len != SSL_write(TT.ssl, buf, len))
189*cf5a6c84SAndroid Build Coastguard Worker       err = ERR_error_string(ERR_get_error(), 0);
190*cf5a6c84SAndroid Build Coastguard Worker #endif
191*cf5a6c84SAndroid Build Coastguard Worker     if (err) error_exit("https write: %s", err);
192*cf5a6c84SAndroid Build Coastguard Worker   }
193*cf5a6c84SAndroid Build Coastguard Worker }
194*cf5a6c84SAndroid Build Coastguard Worker 
wget_close()195*cf5a6c84SAndroid Build Coastguard Worker static void wget_close()
196*cf5a6c84SAndroid Build Coastguard Worker {
197*cf5a6c84SAndroid Build Coastguard Worker   if (TT.sock) {
198*cf5a6c84SAndroid Build Coastguard Worker       xclose(TT.sock);
199*cf5a6c84SAndroid Build Coastguard Worker       TT.sock = 0;
200*cf5a6c84SAndroid Build Coastguard Worker   }
201*cf5a6c84SAndroid Build Coastguard Worker 
202*cf5a6c84SAndroid Build Coastguard Worker #if CFG_WGET_LIBTLS
203*cf5a6c84SAndroid Build Coastguard Worker   if (TT.tls) {
204*cf5a6c84SAndroid Build Coastguard Worker     tls_close(TT.tls);
205*cf5a6c84SAndroid Build Coastguard Worker     tls_free(TT.tls);
206*cf5a6c84SAndroid Build Coastguard Worker     TT.tls = 0;
207*cf5a6c84SAndroid Build Coastguard Worker   }
208*cf5a6c84SAndroid Build Coastguard Worker #elif CFG_TOYBOX_LIBCRYPTO
209*cf5a6c84SAndroid Build Coastguard Worker   if (TT.ssl) {
210*cf5a6c84SAndroid Build Coastguard Worker     SSL_shutdown(TT.ssl);
211*cf5a6c84SAndroid Build Coastguard Worker     SSL_free(TT.ssl);
212*cf5a6c84SAndroid Build Coastguard Worker     TT.ssl = 0;
213*cf5a6c84SAndroid Build Coastguard Worker   }
214*cf5a6c84SAndroid Build Coastguard Worker 
215*cf5a6c84SAndroid Build Coastguard Worker   if (TT.ctx) {
216*cf5a6c84SAndroid Build Coastguard Worker     SSL_CTX_free(TT.ctx);
217*cf5a6c84SAndroid Build Coastguard Worker     TT.ctx = 0;
218*cf5a6c84SAndroid Build Coastguard Worker   }
219*cf5a6c84SAndroid Build Coastguard Worker #endif
220*cf5a6c84SAndroid Build Coastguard Worker }
221*cf5a6c84SAndroid Build Coastguard Worker 
wget_find_header(char * header,char * val)222*cf5a6c84SAndroid Build Coastguard Worker static char *wget_find_header(char *header, char *val)
223*cf5a6c84SAndroid Build Coastguard Worker {
224*cf5a6c84SAndroid Build Coastguard Worker   if (!(header = strcasestr(header, val))) return 0;
225*cf5a6c84SAndroid Build Coastguard Worker   header += strlen(val);
226*cf5a6c84SAndroid Build Coastguard Worker 
227*cf5a6c84SAndroid Build Coastguard Worker   return xstrndup(header, strcspn(header, "\r\n"));
228*cf5a6c84SAndroid Build Coastguard Worker }
229*cf5a6c84SAndroid Build Coastguard Worker 
wget_main(void)230*cf5a6c84SAndroid Build Coastguard Worker void wget_main(void)
231*cf5a6c84SAndroid Build Coastguard Worker {
232*cf5a6c84SAndroid Build Coastguard Worker   long status = 0;
233*cf5a6c84SAndroid Build Coastguard Worker   size_t len, c_len = 0;
234*cf5a6c84SAndroid Build Coastguard Worker   int fd = 0, ii;
235*cf5a6c84SAndroid Build Coastguard Worker   char *body, *index, *host, *port, *path = 0, *chunked, *ss;
236*cf5a6c84SAndroid Build Coastguard Worker   char agent[] = "toybox wget/" TOYBOX_VERSION;
237*cf5a6c84SAndroid Build Coastguard Worker 
238*cf5a6c84SAndroid Build Coastguard Worker   TT.url = escape_url(*toys.optargs, 0);
239*cf5a6c84SAndroid Build Coastguard Worker 
240*cf5a6c84SAndroid Build Coastguard Worker   // Ask server for URL, following redirects until success
241*cf5a6c84SAndroid Build Coastguard Worker   while (status != 200) {
242*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.max_redirect--) error_exit("Too many redirects");
243*cf5a6c84SAndroid Build Coastguard Worker 
244*cf5a6c84SAndroid Build Coastguard Worker     // Connect and write request
245*cf5a6c84SAndroid Build Coastguard Worker     wget_info(TT.url, &host, &port, &path);
246*cf5a6c84SAndroid Build Coastguard Worker     if (TT.p) sprintf(toybuf, "Content-Length: %ld\r\n", (long)strlen(TT.p));
247*cf5a6c84SAndroid Build Coastguard Worker     ss = xmprintf("%s /%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\n"
248*cf5a6c84SAndroid Build Coastguard Worker                   "Connection: close\r\n%s\r\n%s", FLAG(p) ? "POST" : "GET",
249*cf5a6c84SAndroid Build Coastguard Worker                   path, host, agent, TT.p ? toybuf : "", TT.p ? : "");
250*cf5a6c84SAndroid Build Coastguard Worker     if (FLAG(d)) printf("--- Request\n%s", ss);
251*cf5a6c84SAndroid Build Coastguard Worker     wget_connect(host, port);
252*cf5a6c84SAndroid Build Coastguard Worker     wget_write(ss, strlen(ss));
253*cf5a6c84SAndroid Build Coastguard Worker     free(ss);
254*cf5a6c84SAndroid Build Coastguard Worker 
255*cf5a6c84SAndroid Build Coastguard Worker     // Read HTTP response into toybuf (probably with some body at end)
256*cf5a6c84SAndroid Build Coastguard Worker     for (index = toybuf;
257*cf5a6c84SAndroid Build Coastguard Worker       (len = wget_read(index, sizeof(toybuf)-(index-toybuf)))>0; index += len);
258*cf5a6c84SAndroid Build Coastguard Worker 
259*cf5a6c84SAndroid Build Coastguard Worker     // Split response into header and body, and null terminate header.
260*cf5a6c84SAndroid Build Coastguard Worker     // (RFC7230 says header cannot contain NUL.)
261*cf5a6c84SAndroid Build Coastguard Worker     if (!(body = memmem(ss = toybuf, index-toybuf, "\r\n\r\n", 4)))
262*cf5a6c84SAndroid Build Coastguard Worker       error_exit("response header too large");
263*cf5a6c84SAndroid Build Coastguard Worker     *body = 0;
264*cf5a6c84SAndroid Build Coastguard Worker     body += 4;
265*cf5a6c84SAndroid Build Coastguard Worker     len = index-body;
266*cf5a6c84SAndroid Build Coastguard Worker     if (FLAG(d)) printf("--- Response\n%s\n\n", toybuf);
267*cf5a6c84SAndroid Build Coastguard Worker 
268*cf5a6c84SAndroid Build Coastguard Worker     status = strstart(&ss, "HTTP/1.1 ") ? strtol(ss, 0, 10) : 0;
269*cf5a6c84SAndroid Build Coastguard Worker     if ((status == 301) || (status == 302)) {
270*cf5a6c84SAndroid Build Coastguard Worker       if (!(ss = wget_find_header(toybuf, "Location: ")))
271*cf5a6c84SAndroid Build Coastguard Worker         error_exit("bad redirect");
272*cf5a6c84SAndroid Build Coastguard Worker       free(TT.url);
273*cf5a6c84SAndroid Build Coastguard Worker       TT.url = ss;
274*cf5a6c84SAndroid Build Coastguard Worker       wget_close();
275*cf5a6c84SAndroid Build Coastguard Worker     } else if (status != 200) error_exit("response %ld", status);
276*cf5a6c84SAndroid Build Coastguard Worker   }
277*cf5a6c84SAndroid Build Coastguard Worker 
278*cf5a6c84SAndroid Build Coastguard Worker   // Open output file
279*cf5a6c84SAndroid Build Coastguard Worker   if (TT.O && !strcmp(TT.O, "-")) fd = 1;
280*cf5a6c84SAndroid Build Coastguard Worker   else if (!TT.O) {
281*cf5a6c84SAndroid Build Coastguard Worker     ss = wget_find_header(toybuf, "Content-Disposition: attachment; filename=");
282*cf5a6c84SAndroid Build Coastguard Worker     if (ss) {
283*cf5a6c84SAndroid Build Coastguard Worker       unescape_url(ss, 1);
284*cf5a6c84SAndroid Build Coastguard Worker       for (ii = strlen(ss); ii; ii--) {
285*cf5a6c84SAndroid Build Coastguard Worker         if (ss[ii]=='/') memmove(ss, ss+ii, strlen(ss+ii));
286*cf5a6c84SAndroid Build Coastguard Worker         break;
287*cf5a6c84SAndroid Build Coastguard Worker       }
288*cf5a6c84SAndroid Build Coastguard Worker       if (!*ss) {
289*cf5a6c84SAndroid Build Coastguard Worker         free(ss);
290*cf5a6c84SAndroid Build Coastguard Worker         ss = 0;
291*cf5a6c84SAndroid Build Coastguard Worker       }
292*cf5a6c84SAndroid Build Coastguard Worker     }
293*cf5a6c84SAndroid Build Coastguard Worker     if (!ss) {
294*cf5a6c84SAndroid Build Coastguard Worker       path = 0;
295*cf5a6c84SAndroid Build Coastguard Worker       for (ii = 0, ss = *toys.optargs; *ss && *ss!='?' && *ss!='#'; ss++)
296*cf5a6c84SAndroid Build Coastguard Worker         if (*ss=='/' && ++ii>2) path = ss+1;
297*cf5a6c84SAndroid Build Coastguard Worker       ss = (path && ss>path) ? xstrndup(path, ss-path) : 0;
298*cf5a6c84SAndroid Build Coastguard Worker       // TODO: handle %20 style escapes
299*cf5a6c84SAndroid Build Coastguard Worker     }
300*cf5a6c84SAndroid Build Coastguard Worker     if (!ss) ss = "index.html";
301*cf5a6c84SAndroid Build Coastguard Worker     if (!access((TT.O = ss), F_OK)) error_exit("%s already exists", TT.O);
302*cf5a6c84SAndroid Build Coastguard Worker   }
303*cf5a6c84SAndroid Build Coastguard Worker   // TODO: don't allow header/basename to write to stdout
304*cf5a6c84SAndroid Build Coastguard Worker   if (!fd) fd = xcreate(TT.O, (O_WRONLY|O_CREAT|O_TRUNC), 0644);
305*cf5a6c84SAndroid Build Coastguard Worker 
306*cf5a6c84SAndroid Build Coastguard Worker   // If chunked we offset the first buffer by 2 character, meaning it is
307*cf5a6c84SAndroid Build Coastguard Worker   // pointing at half of the header boundary, aka '\r\n'. This simplifies
308*cf5a6c84SAndroid Build Coastguard Worker   // parsing of the first c_len length by allowing the do while loop to fall
309*cf5a6c84SAndroid Build Coastguard Worker   // through on the first iteration and parse the first c_len size.
310*cf5a6c84SAndroid Build Coastguard Worker   chunked = wget_find_header(toybuf, "transfer-encoding: chunked");
311*cf5a6c84SAndroid Build Coastguard Worker   if (chunked) memmove(toybuf, body-2, len += 2);
312*cf5a6c84SAndroid Build Coastguard Worker   else memmove(toybuf, body, len);
313*cf5a6c84SAndroid Build Coastguard Worker 
314*cf5a6c84SAndroid Build Coastguard Worker   // len is the size remaining in toybuf
315*cf5a6c84SAndroid Build Coastguard Worker   // c_len is the size of the remaining bytes in the current chunk
316*cf5a6c84SAndroid Build Coastguard Worker   do {
317*cf5a6c84SAndroid Build Coastguard Worker     if (chunked) {
318*cf5a6c84SAndroid Build Coastguard Worker       if (c_len > 0) { // We have an incomplete c_len to write
319*cf5a6c84SAndroid Build Coastguard Worker         if (len <= c_len) { // Buffer is less than the c_len so full write
320*cf5a6c84SAndroid Build Coastguard Worker           xwrite(fd, toybuf, len);
321*cf5a6c84SAndroid Build Coastguard Worker           c_len = c_len - len;
322*cf5a6c84SAndroid Build Coastguard Worker           len = 0;
323*cf5a6c84SAndroid Build Coastguard Worker         } else { // Buffer is larger than the c_len so partial write
324*cf5a6c84SAndroid Build Coastguard Worker           xwrite(fd, toybuf, c_len);
325*cf5a6c84SAndroid Build Coastguard Worker           len = len - c_len;
326*cf5a6c84SAndroid Build Coastguard Worker           memmove(toybuf, toybuf + c_len, len);
327*cf5a6c84SAndroid Build Coastguard Worker           c_len = 0;
328*cf5a6c84SAndroid Build Coastguard Worker         }
329*cf5a6c84SAndroid Build Coastguard Worker       }
330*cf5a6c84SAndroid Build Coastguard Worker 
331*cf5a6c84SAndroid Build Coastguard Worker       // If len is less than 2 we can't validate the chunk boundary so fall
332*cf5a6c84SAndroid Build Coastguard Worker       // through and go read more into toybuf.
333*cf5a6c84SAndroid Build Coastguard Worker       if (!c_len && (len > 2)) {
334*cf5a6c84SAndroid Build Coastguard Worker         char *c;
335*cf5a6c84SAndroid Build Coastguard Worker         if (strncmp(toybuf, "\r\n", 2) != 0) error_exit("chunk boundary");
336*cf5a6c84SAndroid Build Coastguard Worker 
337*cf5a6c84SAndroid Build Coastguard Worker         // If we can't find the end of the new chunk signature fall through and
338*cf5a6c84SAndroid Build Coastguard Worker         // read more into toybuf.
339*cf5a6c84SAndroid Build Coastguard Worker         c = memmem(toybuf + 2, len - 2, "\r\n",2);
340*cf5a6c84SAndroid Build Coastguard Worker         if (c) {
341*cf5a6c84SAndroid Build Coastguard Worker           c_len = strtol(toybuf + 2, NULL, 16);
342*cf5a6c84SAndroid Build Coastguard Worker           if (!c_len) break; // A c_len of zero means we are complete
343*cf5a6c84SAndroid Build Coastguard Worker           len = len - (c - toybuf) - 2;
344*cf5a6c84SAndroid Build Coastguard Worker           memmove(toybuf, c + 2, len);
345*cf5a6c84SAndroid Build Coastguard Worker         }
346*cf5a6c84SAndroid Build Coastguard Worker       }
347*cf5a6c84SAndroid Build Coastguard Worker 
348*cf5a6c84SAndroid Build Coastguard Worker       if (len == sizeof(toybuf)) error_exit("chunk overflow");
349*cf5a6c84SAndroid Build Coastguard Worker     } else {
350*cf5a6c84SAndroid Build Coastguard Worker       xwrite(fd, toybuf, len);
351*cf5a6c84SAndroid Build Coastguard Worker       len = 0;
352*cf5a6c84SAndroid Build Coastguard Worker     }
353*cf5a6c84SAndroid Build Coastguard Worker   } while ((len += wget_read(toybuf + len, sizeof(toybuf) - len)) > 0);
354*cf5a6c84SAndroid Build Coastguard Worker 
355*cf5a6c84SAndroid Build Coastguard Worker   wget_close();
356*cf5a6c84SAndroid Build Coastguard Worker   free(TT.url);
357*cf5a6c84SAndroid Build Coastguard Worker }
358