blob: b4581153334138dbc79424d118bc1314a3370315 [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 Vlasenko85c24712008-03-17 09:04:04 +0000191static ALWAYS_INLINE void progressmeter(int flag ATTRIBUTE_UNUSED) { }
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 Vlasenko68404f12008-03-17 09:00:54 +0000389int wget_main(int argc ATTRIBUTE_UNUSED, 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 Vlasenkofe7cd642007-08-18 15:32:12 +0000452 opt = getopt32(argv, "csqO:P:Y:U:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000453 &fname_out, &dir_prefix,
454 &proxy_flag, &user_agent
455 USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
456 );
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000457 if (strcmp(proxy_flag, "off") == 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000458 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000459 use_proxy = 0;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000460 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000461#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000462 if (headers_llist) {
463 int size = 1;
464 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000465 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000466 while (ll) {
467 size += strlen(ll->data) + 2;
468 ll = ll->link;
469 }
470 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000471 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000472 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000473 }
474 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000475#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000476
Eric Andersen79757c92001-04-05 21:45:54 +0000477 parse_url(argv[optind], &target);
478 server.host = target.host;
479 server.port = target.port;
480
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000481 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000482 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000483 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000484 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000485 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000486 } else {
487 use_proxy = 0;
488 }
Robert Griebld7760112002-05-14 23:36:45 +0000489 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000490
Denis Vlasenko818322b2007-09-24 18:27:04 +0000491 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000492 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000493 fname_out = bb_get_last_path_component_nostrip(target.path);
494 /* handle "wget http://kernel.org//" */
495 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000496 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000497 /* -P DIR is considered only if there was no -O FILE */
498 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000499 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000500 } else {
501 if (LONE_DASH(fname_out)) {
502 /* -O - */
503 output_fd = 1;
504 opt &= ~WGET_OPT_CONTINUE;
505 }
Eric Andersen29edd002000-12-09 16:55:35 +0000506 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000507#if ENABLE_FEATURE_WGET_STATUSBAR
508 curfile = bb_get_last_path_component_nostrip(fname_out);
509#endif
510
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000511 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000512 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000513 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
Eric Andersen29edd002000-12-09 16:55:35 +0000514
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000515 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000516 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000517 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000518 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000519 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000520 }
521 /* File doesn't exist. We do not create file here yet.
522 We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000523 }
524
Eric Andersene6dc4392003-10-31 09:31:46 +0000525 /* We want to do exactly _one_ DNS lookup, since some
526 * sites (i.e. ftp.us.debian.org) use round-robin DNS
527 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000528 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000529 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000530 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000531 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000532 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000533 }
534
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000535 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000536 /*
537 * HTTP session
538 */
539 do {
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000540 got_clen = 0;
541 chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000542
Denis Vlasenko3526a132006-09-09 12:20:57 +0000543 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000544 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000545
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000546 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000547 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000548 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000549
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000550 /* Send HTTP request. */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000551 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000552 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000553 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000554 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000555 } else {
Eric Andersen6d7fa432001-04-10 18:17:05 +0000556 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000557 }
Eric Andersen96700832000-09-04 15:15:55 +0000558
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000559 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
560 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000561
Denis Vlasenko9cade082006-11-21 10:43:02 +0000562#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000563 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000564 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
565 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000566 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000567 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000568 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000569 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000570 }
571#endif
572
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000573 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000574 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000575#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000576 if (extra_headers)
577 fputs(extra_headers, sfp);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000578#endif
Denis Vlasenko7534e082006-10-23 23:21:58 +0000579 fprintf(sfp, "Connection: close\r\n\r\n");
Eric Andersen79757c92001-04-05 21:45:54 +0000580
581 /*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000582 * Retrieve HTTP response line and check for "200" status code.
583 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000584 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000585 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000586 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000587
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000588 str = buf;
589 str = skip_non_whitespace(str);
590 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000591 // FIXME: no error check
592 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000593 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000594 switch (status) {
595 case 0:
596 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000597 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000598 /* eat all remaining headers */;
599 goto read_response;
600 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000601/*
602Response 204 doesn't say "null file", it says "metadata
603has changed but data didn't":
604
605"10.2.5 204 No Content
606The server has fulfilled the request but does not need to return
607an entity-body, and might want to return updated metainformation.
608The response MAY include new or updated metainformation in the form
609of entity-headers, which if present SHOULD be associated with
610the requested variant.
611
612If the client is a user agent, it SHOULD NOT change its document
613view from that which caused the request to be sent. This response
614is primarily intended to allow input for actions to take place
615without causing a change to the user agent's active document view,
616although any new or updated metainformation SHOULD be applied
617to the document currently in the user agent's active view.
618
619The 204 response MUST NOT include a message-body, and thus
620is always terminated by the first empty line after the header fields."
621
622However, in real world it was observed that some web servers
623(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
624*/
625 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000626 break;
627 case 300: /* redirection */
628 case 301:
629 case 302:
630 case 303:
631 break;
632 case 206:
633 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000634 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000635 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000636 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000637 /* Show first line only and kill any ESC tricks */
638 buf[strcspn(buf, "\n\r\x1b")] = '\0';
639 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000640 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000641
Eric Andersen79757c92001-04-05 21:45:54 +0000642 /*
643 * Retrieve HTTP headers.
644 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000645 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000646 /* gethdr did already convert the "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000647 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000648 if (key == KEY_content_length) {
649 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000650 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000651 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000652 }
Eric Andersen79757c92001-04-05 21:45:54 +0000653 got_clen = 1;
654 continue;
655 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000656 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000657 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000658 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000659 chunked = got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000660 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000661 if (key == KEY_location) {
662 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000663 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000664 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000665 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000666 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000667 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000668 server.host = target.host;
669 server.port = target.port;
670 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000671 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000672 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000673 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000674 }
675 }
676 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000677 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000678
Eric Andersen79757c92001-04-05 21:45:54 +0000679 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000680
681 } else {
682
Eric Andersen79757c92001-04-05 21:45:54 +0000683 /*
684 * FTP session
685 */
Denis Vlasenko3526a132006-09-09 12:20:57 +0000686 if (!target.user)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000687 target.user = xstrdup("anonymous:busybox@");
Eric Andersen79757c92001-04-05 21:45:54 +0000688
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000689 sfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000690 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000691 bb_error_msg_and_die("%s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000692
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000693 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000694 * Splitting username:password pair,
695 * trying to log in
696 */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000697 str = strchr(target.user, ':');
698 if (str)
699 *(str++) = '\0';
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000700 switch (ftpcmd("USER ", target.user, sfp, buf)) {
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000701 case 230:
702 break;
703 case 331:
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000704 if (ftpcmd("PASS ", str, sfp, buf) == 230)
Eric Andersenb520e082000-10-03 00:21:45 +0000705 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000706 /* fall through (failed login) */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000707 default:
708 bb_error_msg_and_die("ftp login: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000709 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000710
Eric Andersen79757c92001-04-05 21:45:54 +0000711 ftpcmd("TYPE I", NULL, sfp, buf);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000712
Eric Andersen79757c92001-04-05 21:45:54 +0000713 /*
714 * Querying file size
715 */
Rob Landleyaf12cb32006-06-27 18:41:03 +0000716 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
Denis Vlasenkod686a042006-11-27 14:43:21 +0000717 content_len = BB_STRTOOFF(buf+4, NULL, 10);
718 if (errno || content_len < 0) {
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000719 bb_error_msg_and_die("SIZE value is garbage");
Eric Andersen24794452004-03-06 22:11:45 +0000720 }
Eric Andersen96700832000-09-04 15:15:55 +0000721 got_clen = 1;
Eric Andersen96700832000-09-04 15:15:55 +0000722 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000723
Eric Andersen79757c92001-04-05 21:45:54 +0000724 /*
725 * Entering passive mode
726 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000727 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
728 pasv_error:
729 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
730 }
Denis Vlasenkof8c8bb12006-11-21 19:10:26 +0000731 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000732 // Server's IP is N1.N2.N3.N4 (we ignore it)
733 // Server's port for data connection is P1*256+P2
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000734 str = strrchr(buf, ')');
735 if (str) str[0] = '\0';
736 str = strrchr(buf, ',');
737 if (!str) goto pasv_error;
738 port = xatou_range(str+1, 0, 255);
739 *str = '\0';
740 str = strrchr(buf, ',');
741 if (!str) goto pasv_error;
742 port += xatou_range(str+1, 0, 255) * 256;
Denis Vlasenko5d687242007-01-12 20:59:31 +0000743 set_nport(lsa, htons(port));
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000744 dfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000745
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000746 if (beg_range) {
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000747 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000748 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000749 content_len -= beg_range;
Eric Andersen96700832000-09-04 15:15:55 +0000750 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000751
Rob Landleyaf12cb32006-06-27 18:41:03 +0000752 if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
Denis Vlasenko77105632007-09-24 15:04:00 +0000753 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
Eric Andersen96700832000-09-04 15:15:55 +0000754 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000755
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000756 if (opt & WGET_OPT_SPIDER) {
757 if (ENABLE_FEATURE_CLEAN_UP)
758 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000759 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000760 }
Eric Andersen79757c92001-04-05 21:45:54 +0000761
Eric Andersen96700832000-09-04 15:15:55 +0000762 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000763 * Retrieve file
Eric Andersen96700832000-09-04 15:15:55 +0000764 */
Rob Landley19a39402006-06-13 17:10:26 +0000765
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000766 /* Do it before progressmeter (want to have nice error message) */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000767 if (output_fd < 0) {
768 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
769 /* compat with wget: -O FILE can overwrite */
770 if (opt & WGET_OPT_OUTNAME)
771 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
772 output_fd = xopen(fname_out, o_flags);
773 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000774
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000775 if (!(opt & WGET_OPT_QUIET))
Glenn L McGrath1bca5ed2000-12-09 08:12:06 +0000776 progressmeter(-1);
Rob Landley19a39402006-06-13 17:10:26 +0000777
Denis Vlasenko77105632007-09-24 15:04:00 +0000778 if (chunked)
779 goto get_clen;
780
Denis Vlasenko06783a52007-09-24 13:51:54 +0000781 /* Loops only if chunked */
782 while (1) {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000783 while (content_len > 0 || !got_clen) {
Denis Vlasenko06783a52007-09-24 13:51:54 +0000784 int n;
Denis Vlasenko3526a132006-09-09 12:20:57 +0000785 unsigned rdsz = sizeof(buf);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000786
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000787 if (content_len < sizeof(buf) && (chunked || got_clen))
788 rdsz = (unsigned)content_len;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000789 n = safe_fread(buf, rdsz, dfp);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000790 if (n <= 0) {
791 if (ferror(dfp)) {
792 /* perror will not work: ferror doesn't set errno */
793 bb_error_msg_and_die(bb_msg_read_error);
794 }
Denis Vlasenko3526a132006-09-09 12:20:57 +0000795 break;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000796 }
Denis Vlasenko06783a52007-09-24 13:51:54 +0000797 xwrite(output_fd, buf, n);
Denis Vlasenko9cade082006-11-21 10:43:02 +0000798#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +0000799 transferred += n;
Eric Andersenb520e082000-10-03 00:21:45 +0000800#endif
Denis Vlasenko06783a52007-09-24 13:51:54 +0000801 if (got_clen)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000802 content_len -= n;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000803 }
Eric Andersen79757c92001-04-05 21:45:54 +0000804
Denis Vlasenko06783a52007-09-24 13:51:54 +0000805 if (!chunked)
806 break;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000807
Denis Vlasenko06783a52007-09-24 13:51:54 +0000808 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
Denis Vlasenko77105632007-09-24 15:04:00 +0000809 get_clen:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000810 safe_fgets(buf, sizeof(buf), dfp);
811 content_len = STRTOOFF(buf, NULL, 16);
812 /* FIXME: error check? */
813 if (content_len == 0)
814 break; /* all done! */
815 }
Rob Landley19a39402006-06-13 17:10:26 +0000816
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000817 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000818 progressmeter(0);
Rob Landley19a39402006-06-13 17:10:26 +0000819
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000820 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000821 fclose(dfp);
822 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000823 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000824 ftpcmd("QUIT", NULL, sfp, buf);
825 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000826
827 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000828}