blob: d782cc4fea9c5179b4d7864aa5dfa8f1ebc19a07 [file] [log] [blame]
Eric Andersen96700832000-09-04 15:15:55 +00001/* vi: set sw=4 ts=4: */
2/*
Eric Andersen79757c92001-04-05 21:45:54 +00003 * wget - retrieve a file using HTTP or FTP
Eric Andersen96700832000-09-04 15:15:55 +00004 *
Eric Andersen4e573f42000-11-14 23:29:24 +00005 * Chip Rosenthal Covad Communications <chip@laserlink.net>
Eric Andersenb520e082000-10-03 00:21:45 +00006 *
Eric Andersen96700832000-09-04 15:15:55 +00007 */
8
Denis Vlasenkob6adbf12007-05-26 19:00:18 +00009#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +000010
Eric Andersen79757c92001-04-05 21:45:54 +000011struct host_info {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +000012 // May be used if we ever will want to free() all xstrdup()s...
13 /* char *allocated; */
Denis Vlasenko818322b2007-09-24 18:27:04 +000014 const char *path;
15 const char *user;
16 char *host;
17 int port;
18 smallint is_ftp;
Eric Andersen79757c92001-04-05 21:45:54 +000019};
20
Denis Vlasenko77105632007-09-24 15:04:00 +000021
22/* Globals (can be accessed from signal handlers) */
23struct globals {
24 off_t content_len; /* Content-length of the file */
25 off_t beg_range; /* Range at which continue begins */
26#if ENABLE_FEATURE_WGET_STATUSBAR
27 off_t lastsize;
28 off_t totalsize;
29 off_t transferred; /* Number of bytes transferred so far */
30 const char *curfile; /* Name of current file being transferred */
31 unsigned lastupdate_sec;
32 unsigned start_sec;
33#endif
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000034 smallint chunked; /* chunked transfer encoding */
Denis Vlasenko77105632007-09-24 15:04:00 +000035};
36#define G (*(struct globals*)&bb_common_bufsiz1)
37struct BUG_G_too_big {
Denis Vlasenko6b404432008-01-07 16:13:14 +000038 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
Denis Vlasenko77105632007-09-24 15:04:00 +000039};
40#define content_len (G.content_len )
41#define beg_range (G.beg_range )
42#define lastsize (G.lastsize )
43#define totalsize (G.totalsize )
44#define transferred (G.transferred )
45#define curfile (G.curfile )
46#define lastupdate_sec (G.lastupdate_sec )
47#define start_sec (G.start_sec )
48#define chunked (G.chunked )
49#define INIT_G() do { } while (0)
50
51
Denis Vlasenko9cade082006-11-21 10:43:02 +000052#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +000053enum {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +000054 STALLTIME = 5 /* Seconds when xfer considered "stalled" */
Rob Landley19a39402006-06-13 17:10:26 +000055};
Denis Vlasenko47ddd012007-09-24 18:24:17 +000056
Denis Vlasenko55995022008-05-18 22:28:26 +000057static unsigned int getttywidth(void)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000058{
Denis Vlasenko55995022008-05-18 22:28:26 +000059 unsigned width;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000060 get_terminal_width_height(0, &width, NULL);
61 return width;
62}
63
Denis Vlasenko47ddd012007-09-24 18:24:17 +000064static void progressmeter(int flag)
65{
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000066 /* We can be called from signal handler */
67 int save_errno = errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000068 off_t abbrevsize;
69 unsigned since_last_update, elapsed;
70 unsigned ratio;
71 int barlength, i;
72
73 if (flag == -1) { /* first call to progressmeter */
74 start_sec = monotonic_sec();
75 lastupdate_sec = start_sec;
76 lastsize = 0;
77 totalsize = content_len + beg_range; /* as content_len changes.. */
78 }
79
80 ratio = 100;
81 if (totalsize != 0 && !chunked) {
82 /* long long helps to have it working even if !LFS */
83 ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
84 if (ratio > 100) ratio = 100;
85 }
86
87 fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
88
89 barlength = getttywidth() - 49;
90 if (barlength > 0) {
91 /* god bless gcc for variable arrays :) */
92 i = barlength * ratio / 100;
93 {
94 char buf[i+1];
95 memset(buf, '*', i);
96 buf[i] = '\0';
97 fprintf(stderr, "|%s%*s|", buf, barlength - i, "");
98 }
99 }
100 i = 0;
101 abbrevsize = transferred + beg_range;
102 while (abbrevsize >= 100000) {
103 i++;
104 abbrevsize >>= 10;
105 }
106 /* see http://en.wikipedia.org/wiki/Tera */
107 fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]);
108
109// Nuts! Ain't it easier to update progress meter ONLY when we transferred++?
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000110
111 elapsed = monotonic_sec();
112 since_last_update = elapsed - lastupdate_sec;
113 if (transferred > lastsize) {
114 lastupdate_sec = elapsed;
115 lastsize = transferred;
116 if (since_last_update >= STALLTIME) {
117 /* We "cut off" these seconds from elapsed time
118 * by adjusting start time */
119 start_sec += since_last_update;
120 }
121 since_last_update = 0; /* we are un-stalled now */
122 }
123 elapsed -= start_sec; /* now it's "elapsed since start" */
124
125 if (since_last_update >= STALLTIME) {
126 fprintf(stderr, " - stalled -");
127 } else {
128 off_t to_download = totalsize - beg_range;
129 if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) {
130 fprintf(stderr, "--:--:-- ETA");
131 } else {
132 /* to_download / (transferred/elapsed) - elapsed: */
133 int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
134 /* (long long helps to have working ETA even if !LFS) */
135 i = eta % 3600;
136 fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
137 }
138 }
139
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000140 if (flag == 0) {
141 /* last call to progressmeter */
142 alarm(0);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000143 transferred = 0;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000144 fputc('\n', stderr);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000145 } else {
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +0000146 if (flag == -1) { /* first call to progressmeter */
147 signal_SA_RESTART_empty_mask(SIGALRM, progressmeter);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000148 }
149 alarm(1);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000150 }
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000151
152 errno = save_errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000153}
154/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
155 * much of which was blatantly stolen from openssh. */
156/*-
157 * Copyright (c) 1992, 1993
158 * The Regents of the University of California. All rights reserved.
159 *
160 * Redistribution and use in source and binary forms, with or without
161 * modification, are permitted provided that the following conditions
162 * are met:
163 * 1. Redistributions of source code must retain the above copyright
164 * notice, this list of conditions and the following disclaimer.
165 * 2. Redistributions in binary form must reproduce the above copyright
166 * notice, this list of conditions and the following disclaimer in the
167 * documentation and/or other materials provided with the distribution.
168 *
169 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
170 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
171 *
172 * 4. Neither the name of the University nor the names of its contributors
173 * may be used to endorse or promote products derived from this software
174 * without specific prior written permission.
175 *
176 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
177 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
180 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
181 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
182 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
183 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
184 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
185 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
186 * SUCH DAMAGE.
187 *
188 */
189#else /* FEATURE_WGET_STATUSBAR */
190
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000191static ALWAYS_INLINE void progressmeter(int flag UNUSED_PARAM) { }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000192
Eric Andersenb520e082000-10-03 00:21:45 +0000193#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000194
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000195
Denis Vlasenko12d21292007-06-27 21:40:07 +0000196/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
197 * and a short count if an eof or non-interrupt error is encountered. */
198static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
Matt Kraai854125f2001-05-09 19:15:46 +0000199{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000200 size_t ret;
201 char *p = (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000202
203 do {
204 clearerr(stream);
Denis Vlasenko12d21292007-06-27 21:40:07 +0000205 ret = fread(p, 1, nmemb, stream);
206 p += ret;
207 nmemb -= ret;
208 } while (nmemb && ferror(stream) && errno == EINTR);
Matt Kraai854125f2001-05-09 19:15:46 +0000209
Denis Vlasenko12d21292007-06-27 21:40:07 +0000210 return p - (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000211}
212
Denis Vlasenko12d21292007-06-27 21:40:07 +0000213/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
Matt Kraai854125f2001-05-09 19:15:46 +0000214 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
215static char *safe_fgets(char *s, int size, FILE *stream)
216{
217 char *ret;
218
219 do {
220 clearerr(stream);
221 ret = fgets(s, size, stream);
222 } while (ret == NULL && ferror(stream) && errno == EINTR);
223
224 return ret;
225}
226
Denis Vlasenko9cade082006-11-21 10:43:02 +0000227#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denis Vlasenko12d21292007-06-27 21:40:07 +0000228/* Base64-encode character string. buf is assumed to be char buf[512]. */
229static char *base64enc_512(char buf[512], const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000230{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000231 unsigned len = strlen(str);
232 if (len > 512/4*3 - 10) /* paranoia */
233 len = 512/4*3 - 10;
234 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
Rob Landley76ef08c2006-06-13 16:44:26 +0000235 return buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000236}
237#endif
238
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000239
240static FILE *open_socket(len_and_sockaddr *lsa)
241{
242 FILE *fp;
243
244 /* glibc 2.4 seems to try seeking on it - ??! */
245 /* hopefully it understands what ESPIPE means... */
246 fp = fdopen(xconnect_stream(lsa), "r+");
247 if (fp == NULL)
248 bb_perror_msg_and_die("fdopen");
249
250 return fp;
251}
252
253
254static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
255{
256 int result;
257 if (s1) {
258 if (!s2) s2 = "";
259 fprintf(fp, "%s%s\r\n", s1, s2);
260 fflush(fp);
261 }
262
263 do {
264 char *buf_ptr;
265
266 if (fgets(buf, 510, fp) == NULL) {
267 bb_perror_msg_and_die("error getting response");
268 }
269 buf_ptr = strstr(buf, "\r\n");
270 if (buf_ptr) {
271 *buf_ptr = '\0';
272 }
273 } while (!isdigit(buf[0]) || buf[3] != ' ');
274
275 buf[3] = '\0';
276 result = xatoi_u(buf);
277 buf[3] = ' ';
278 return result;
279}
280
281
282static void parse_url(char *src_url, struct host_info *h)
283{
284 char *url, *p, *sp;
285
286 /* h->allocated = */ url = xstrdup(src_url);
287
288 if (strncmp(url, "http://", 7) == 0) {
289 h->port = bb_lookup_port("http", "tcp", 80);
290 h->host = url + 7;
291 h->is_ftp = 0;
292 } else if (strncmp(url, "ftp://", 6) == 0) {
293 h->port = bb_lookup_port("ftp", "tcp", 21);
294 h->host = url + 6;
295 h->is_ftp = 1;
296 } else
297 bb_error_msg_and_die("not an http or ftp url: %s", url);
298
299 // FYI:
300 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
301 // 'GET /?var=a/b HTTP 1.0'
302 // and saves 'index.html?var=a%2Fb' (we save 'b')
303 // wget 'http://busybox.net?login=john@doe':
304 // request: 'GET /?login=john@doe HTTP/1.0'
305 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
306 // wget 'http://busybox.net#test/test':
307 // request: 'GET / HTTP/1.0'
308 // saves: 'index.html' (we save 'test')
309 //
310 // We also don't add unique .N suffix if file exists...
311 sp = strchr(h->host, '/');
312 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
313 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
314 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000315 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000316 } else if (*sp == '/') {
317 *sp = '\0';
318 h->path = sp + 1;
319 } else { // '#' or '?'
320 // http://busybox.net?login=john@doe is a valid URL
321 // memmove converts to:
322 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000323 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000324 h->host--;
325 sp[-1] = '\0';
326 h->path = sp;
327 }
328
329 sp = strrchr(h->host, '@');
330 h->user = NULL;
331 if (sp != NULL) {
332 h->user = h->host;
333 *sp = '\0';
334 h->host = sp + 1;
335 }
336
337 sp = h->host;
338}
339
340
341static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
342{
343 char *s, *hdrval;
344 int c;
345
346 /* *istrunc = 0; */
347
348 /* retrieve header line */
349 if (fgets(buf, bufsiz, fp) == NULL)
350 return NULL;
351
352 /* see if we are at the end of the headers */
353 for (s = buf; *s == '\r'; ++s)
354 continue;
355 if (*s == '\n')
356 return NULL;
357
358 /* convert the header name to lower case */
359 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
360 *s = tolower(*s);
361
362 /* verify we are at the end of the header name */
363 if (*s != ':')
364 bb_error_msg_and_die("bad header line: %s", buf);
365
366 /* locate the start of the header value */
367 *s++ = '\0';
368 hdrval = skip_whitespace(s);
369
370 /* locate the end of header */
371 while (*s && *s != '\r' && *s != '\n')
372 ++s;
373
374 /* end of header found */
375 if (*s) {
376 *s = '\0';
377 return hdrval;
378 }
379
380 /* Rats! The buffer isn't big enough to hold the entire header value. */
381 while (c = getc(fp), c != EOF && c != '\n')
382 continue;
383 /* *istrunc = 1; */
384 return hdrval;
385}
386
387
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000388int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000389int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000390{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000391 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000392 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000393 len_and_sockaddr *lsa;
Denis Vlasenko06783a52007-09-24 13:51:54 +0000394 int status;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000395 int port;
396 int try = 5;
397 unsigned opt;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000398 char *str;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000399 char *proxy = 0;
400 char *dir_prefix = NULL;
401#if ENABLE_FEATURE_WGET_LONG_OPTIONS
402 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000403 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000404#endif
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000405 FILE *sfp = NULL; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000406 FILE *dfp; /* socket to ftp server (data) */
407 char *fname_out; /* where to direct output (-O) */
Denis Vlasenko46611172007-08-06 15:43:17 +0000408 bool got_clen = 0; /* got content-length: from server */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000409 int output_fd = -1;
Denis Vlasenko46611172007-08-06 15:43:17 +0000410 bool use_proxy = 1; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000411 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000412 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000413
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000414 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000415 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000416 enum {
417 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
418 };
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000419 enum {
420 WGET_OPT_CONTINUE = 0x1,
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000421 WGET_OPT_SPIDER = 0x2,
422 WGET_OPT_QUIET = 0x4,
423 WGET_OPT_OUTNAME = 0x8,
424 WGET_OPT_PREFIX = 0x10,
425 WGET_OPT_PROXY = 0x20,
426 WGET_OPT_USER_AGENT = 0x40,
427 WGET_OPT_PASSIVE = 0x80,
428 WGET_OPT_HEADER = 0x100,
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000429 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000430#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000431 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000432 /* name, has_arg, val */
433 "continue\0" No_argument "c"
434 "spider\0" No_argument "s"
435 "quiet\0" No_argument "q"
436 "output-document\0" Required_argument "O"
437 "directory-prefix\0" Required_argument "P"
438 "proxy\0" Required_argument "Y"
439 "user-agent\0" Required_argument "U"
440 "passive-ftp\0" No_argument "\xff"
441 "header\0" Required_argument "\xfe"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000442 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000443#endif
444
445 INIT_G();
446
447#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000448 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000449#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000450 /* server.allocated = target.allocated = NULL; */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000451 opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000452 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000453 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000454 &proxy_flag, &user_agent,
455 NULL, /* -t RETRIES */
456 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000457 USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
458 );
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000459 if (strcmp(proxy_flag, "off") == 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000460 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000461 use_proxy = 0;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000462 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000463#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000464 if (headers_llist) {
465 int size = 1;
466 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000467 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000468 while (ll) {
469 size += strlen(ll->data) + 2;
470 ll = ll->link;
471 }
472 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000473 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000474 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000475 }
476 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000477#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000478
Eric Andersen79757c92001-04-05 21:45:54 +0000479 parse_url(argv[optind], &target);
480 server.host = target.host;
481 server.port = target.port;
482
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000483 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000484 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000485 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000486 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000487 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000488 } else {
489 use_proxy = 0;
490 }
Robert Griebld7760112002-05-14 23:36:45 +0000491 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000492
Denis Vlasenko818322b2007-09-24 18:27:04 +0000493 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000494 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000495 fname_out = bb_get_last_path_component_nostrip(target.path);
496 /* handle "wget http://kernel.org//" */
497 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000498 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000499 /* -P DIR is considered only if there was no -O FILE */
500 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000501 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000502 } else {
503 if (LONE_DASH(fname_out)) {
504 /* -O - */
505 output_fd = 1;
506 opt &= ~WGET_OPT_CONTINUE;
507 }
Eric Andersen29edd002000-12-09 16:55:35 +0000508 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000509#if ENABLE_FEATURE_WGET_STATUSBAR
510 curfile = bb_get_last_path_component_nostrip(fname_out);
511#endif
512
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000513 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000514 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000515 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
Eric Andersen29edd002000-12-09 16:55:35 +0000516
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000517 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000518 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000519 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000520 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000521 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000522 }
523 /* File doesn't exist. We do not create file here yet.
524 We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000525 }
526
Eric Andersene6dc4392003-10-31 09:31:46 +0000527 /* We want to do exactly _one_ DNS lookup, since some
528 * sites (i.e. ftp.us.debian.org) use round-robin DNS
529 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000530 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000531 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000532 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000533 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000534 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000535 }
536
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000537 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000538 /*
539 * HTTP session
540 */
541 do {
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000542 got_clen = 0;
543 chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000544
Denis Vlasenko3526a132006-09-09 12:20:57 +0000545 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000546 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000547
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000548 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000549 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000550 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000551
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000552 /* Send HTTP request. */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000553 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000554 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000555 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000556 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000557 } else {
Eric Andersen6d7fa432001-04-10 18:17:05 +0000558 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000559 }
Eric Andersen96700832000-09-04 15:15:55 +0000560
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000561 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
562 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000563
Denis Vlasenko9cade082006-11-21 10:43:02 +0000564#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000565 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000566 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
567 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000568 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000569 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000570 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000571 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000572 }
573#endif
574
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000575 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000576 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000577#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000578 if (extra_headers)
579 fputs(extra_headers, sfp);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000580#endif
Denis Vlasenko7534e082006-10-23 23:21:58 +0000581 fprintf(sfp, "Connection: close\r\n\r\n");
Eric Andersen79757c92001-04-05 21:45:54 +0000582
583 /*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000584 * Retrieve HTTP response line and check for "200" status code.
585 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000586 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000587 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000588 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000589
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000590 str = buf;
591 str = skip_non_whitespace(str);
592 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000593 // FIXME: no error check
594 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000595 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000596 switch (status) {
597 case 0:
598 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000599 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000600 /* eat all remaining headers */;
601 goto read_response;
602 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000603/*
604Response 204 doesn't say "null file", it says "metadata
605has changed but data didn't":
606
607"10.2.5 204 No Content
608The server has fulfilled the request but does not need to return
609an entity-body, and might want to return updated metainformation.
610The response MAY include new or updated metainformation in the form
611of entity-headers, which if present SHOULD be associated with
612the requested variant.
613
614If the client is a user agent, it SHOULD NOT change its document
615view from that which caused the request to be sent. This response
616is primarily intended to allow input for actions to take place
617without causing a change to the user agent's active document view,
618although any new or updated metainformation SHOULD be applied
619to the document currently in the user agent's active view.
620
621The 204 response MUST NOT include a message-body, and thus
622is always terminated by the first empty line after the header fields."
623
624However, in real world it was observed that some web servers
625(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
626*/
627 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000628 break;
629 case 300: /* redirection */
630 case 301:
631 case 302:
632 case 303:
633 break;
634 case 206:
635 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000636 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000637 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000638 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000639 /* Show first line only and kill any ESC tricks */
640 buf[strcspn(buf, "\n\r\x1b")] = '\0';
641 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000642 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000643
Eric Andersen79757c92001-04-05 21:45:54 +0000644 /*
645 * Retrieve HTTP headers.
646 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000647 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000648 /* gethdr did already convert the "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000649 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000650 if (key == KEY_content_length) {
651 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000652 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000653 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000654 }
Eric Andersen79757c92001-04-05 21:45:54 +0000655 got_clen = 1;
656 continue;
657 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000658 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000659 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000660 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000661 chunked = got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000662 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000663 if (key == KEY_location) {
664 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000665 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000666 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000667 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000668 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000669 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000670 server.host = target.host;
671 server.port = target.port;
672 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000673 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000674 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000675 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000676 }
677 }
678 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000679 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000680
Eric Andersen79757c92001-04-05 21:45:54 +0000681 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000682
683 } else {
684
Eric Andersen79757c92001-04-05 21:45:54 +0000685 /*
686 * FTP session
687 */
Denis Vlasenko3526a132006-09-09 12:20:57 +0000688 if (!target.user)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000689 target.user = xstrdup("anonymous:busybox@");
Eric Andersen79757c92001-04-05 21:45:54 +0000690
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000691 sfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000692 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000693 bb_error_msg_and_die("%s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000694
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000695 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000696 * Splitting username:password pair,
697 * trying to log in
698 */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000699 str = strchr(target.user, ':');
700 if (str)
701 *(str++) = '\0';
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000702 switch (ftpcmd("USER ", target.user, sfp, buf)) {
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000703 case 230:
704 break;
705 case 331:
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000706 if (ftpcmd("PASS ", str, sfp, buf) == 230)
Eric Andersenb520e082000-10-03 00:21:45 +0000707 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000708 /* fall through (failed login) */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000709 default:
710 bb_error_msg_and_die("ftp login: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000711 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000712
Eric Andersen79757c92001-04-05 21:45:54 +0000713 ftpcmd("TYPE I", NULL, sfp, buf);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000714
Eric Andersen79757c92001-04-05 21:45:54 +0000715 /*
716 * Querying file size
717 */
Rob Landleyaf12cb32006-06-27 18:41:03 +0000718 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
Denis Vlasenkod686a042006-11-27 14:43:21 +0000719 content_len = BB_STRTOOFF(buf+4, NULL, 10);
720 if (errno || content_len < 0) {
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000721 bb_error_msg_and_die("SIZE value is garbage");
Eric Andersen24794452004-03-06 22:11:45 +0000722 }
Eric Andersen96700832000-09-04 15:15:55 +0000723 got_clen = 1;
Eric Andersen96700832000-09-04 15:15:55 +0000724 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000725
Eric Andersen79757c92001-04-05 21:45:54 +0000726 /*
727 * Entering passive mode
728 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000729 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
730 pasv_error:
731 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
732 }
Denis Vlasenkof8c8bb12006-11-21 19:10:26 +0000733 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000734 // Server's IP is N1.N2.N3.N4 (we ignore it)
735 // Server's port for data connection is P1*256+P2
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000736 str = strrchr(buf, ')');
737 if (str) str[0] = '\0';
738 str = strrchr(buf, ',');
739 if (!str) goto pasv_error;
740 port = xatou_range(str+1, 0, 255);
741 *str = '\0';
742 str = strrchr(buf, ',');
743 if (!str) goto pasv_error;
744 port += xatou_range(str+1, 0, 255) * 256;
Denis Vlasenko5d687242007-01-12 20:59:31 +0000745 set_nport(lsa, htons(port));
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000746 dfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000747
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000748 if (beg_range) {
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000749 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000750 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000751 content_len -= beg_range;
Eric Andersen96700832000-09-04 15:15:55 +0000752 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000753
Rob Landleyaf12cb32006-06-27 18:41:03 +0000754 if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
Denis Vlasenko77105632007-09-24 15:04:00 +0000755 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
Eric Andersen96700832000-09-04 15:15:55 +0000756 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000757
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000758 if (opt & WGET_OPT_SPIDER) {
759 if (ENABLE_FEATURE_CLEAN_UP)
760 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000761 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000762 }
Eric Andersen79757c92001-04-05 21:45:54 +0000763
Eric Andersen96700832000-09-04 15:15:55 +0000764 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000765 * Retrieve file
Eric Andersen96700832000-09-04 15:15:55 +0000766 */
Rob Landley19a39402006-06-13 17:10:26 +0000767
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000768 /* Do it before progressmeter (want to have nice error message) */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000769 if (output_fd < 0) {
770 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
771 /* compat with wget: -O FILE can overwrite */
772 if (opt & WGET_OPT_OUTNAME)
773 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
774 output_fd = xopen(fname_out, o_flags);
775 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000776
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000777 if (!(opt & WGET_OPT_QUIET))
Glenn L McGrath1bca5ed2000-12-09 08:12:06 +0000778 progressmeter(-1);
Rob Landley19a39402006-06-13 17:10:26 +0000779
Denis Vlasenko77105632007-09-24 15:04:00 +0000780 if (chunked)
781 goto get_clen;
782
Denis Vlasenko06783a52007-09-24 13:51:54 +0000783 /* Loops only if chunked */
784 while (1) {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000785 while (content_len > 0 || !got_clen) {
Denis Vlasenko06783a52007-09-24 13:51:54 +0000786 int n;
Denis Vlasenko3526a132006-09-09 12:20:57 +0000787 unsigned rdsz = sizeof(buf);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000788
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000789 if (content_len < sizeof(buf) && (chunked || got_clen))
790 rdsz = (unsigned)content_len;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000791 n = safe_fread(buf, rdsz, dfp);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000792 if (n <= 0) {
793 if (ferror(dfp)) {
794 /* perror will not work: ferror doesn't set errno */
795 bb_error_msg_and_die(bb_msg_read_error);
796 }
Denis Vlasenko3526a132006-09-09 12:20:57 +0000797 break;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000798 }
Denis Vlasenko06783a52007-09-24 13:51:54 +0000799 xwrite(output_fd, buf, n);
Denis Vlasenko9cade082006-11-21 10:43:02 +0000800#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +0000801 transferred += n;
Eric Andersenb520e082000-10-03 00:21:45 +0000802#endif
Denis Vlasenko06783a52007-09-24 13:51:54 +0000803 if (got_clen)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000804 content_len -= n;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000805 }
Eric Andersen79757c92001-04-05 21:45:54 +0000806
Denis Vlasenko06783a52007-09-24 13:51:54 +0000807 if (!chunked)
808 break;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000809
Denis Vlasenko06783a52007-09-24 13:51:54 +0000810 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
Denis Vlasenko77105632007-09-24 15:04:00 +0000811 get_clen:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000812 safe_fgets(buf, sizeof(buf), dfp);
813 content_len = STRTOOFF(buf, NULL, 16);
814 /* FIXME: error check? */
815 if (content_len == 0)
816 break; /* all done! */
817 }
Rob Landley19a39402006-06-13 17:10:26 +0000818
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000819 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000820 progressmeter(0);
Rob Landley19a39402006-06-13 17:10:26 +0000821
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000822 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000823 fclose(dfp);
824 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000825 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000826 ftpcmd("QUIT", NULL, sfp, buf);
827 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000828
829 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000830}