blob: b40a1ac15e0ede5e9a5a133b57abd6504fd083fd [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 *
Denis Vlasenkodb12d1d2008-12-07 00:52:58 +00007 * Licensed under GPLv2, see file LICENSE in this tarball for details.
Eric Andersen96700832000-09-04 15:15:55 +00008 */
9
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000010#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +000011
Eric Andersen79757c92001-04-05 21:45:54 +000012struct host_info {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +000013 // May be used if we ever will want to free() all xstrdup()s...
14 /* char *allocated; */
Denis Vlasenko818322b2007-09-24 18:27:04 +000015 const char *path;
16 const char *user;
17 char *host;
18 int port;
19 smallint is_ftp;
Eric Andersen79757c92001-04-05 21:45:54 +000020};
21
Denis Vlasenko77105632007-09-24 15:04:00 +000022
23/* Globals (can be accessed from signal handlers) */
24struct globals {
25 off_t content_len; /* Content-length of the file */
26 off_t beg_range; /* Range at which continue begins */
27#if ENABLE_FEATURE_WGET_STATUSBAR
28 off_t lastsize;
29 off_t totalsize;
30 off_t transferred; /* Number of bytes transferred so far */
31 const char *curfile; /* Name of current file being transferred */
32 unsigned lastupdate_sec;
33 unsigned start_sec;
34#endif
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000035 smallint chunked; /* chunked transfer encoding */
Denis Vlasenko77105632007-09-24 15:04:00 +000036};
37#define G (*(struct globals*)&bb_common_bufsiz1)
38struct BUG_G_too_big {
Denis Vlasenko6b404432008-01-07 16:13:14 +000039 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
Denis Vlasenko77105632007-09-24 15:04:00 +000040};
41#define content_len (G.content_len )
42#define beg_range (G.beg_range )
43#define lastsize (G.lastsize )
44#define totalsize (G.totalsize )
45#define transferred (G.transferred )
46#define curfile (G.curfile )
47#define lastupdate_sec (G.lastupdate_sec )
48#define start_sec (G.start_sec )
49#define chunked (G.chunked )
50#define INIT_G() do { } while (0)
51
52
Denis Vlasenko9cade082006-11-21 10:43:02 +000053#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +000054enum {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +000055 STALLTIME = 5 /* Seconds when xfer considered "stalled" */
Rob Landley19a39402006-06-13 17:10:26 +000056};
Denis Vlasenko47ddd012007-09-24 18:24:17 +000057
Denis Vlasenko00d84172008-11-24 07:34:42 +000058static unsigned int get_tty2_width(void)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000059{
Denis Vlasenko55995022008-05-18 22:28:26 +000060 unsigned width;
Denis Vlasenko00d84172008-11-24 07:34:42 +000061 get_terminal_width_height(2, &width, NULL);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000062 return width;
63}
64
Denis Vlasenko00d84172008-11-24 07:34:42 +000065static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000066{
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000067 /* We can be called from signal handler */
68 int save_errno = errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000069 off_t abbrevsize;
70 unsigned since_last_update, elapsed;
71 unsigned ratio;
72 int barlength, i;
73
Denis Vlasenko00d84172008-11-24 07:34:42 +000074 if (flag == -1) { /* first call to progress_meter */
Denis Vlasenko47ddd012007-09-24 18:24:17 +000075 start_sec = monotonic_sec();
76 lastupdate_sec = start_sec;
77 lastsize = 0;
78 totalsize = content_len + beg_range; /* as content_len changes.. */
79 }
80
81 ratio = 100;
82 if (totalsize != 0 && !chunked) {
83 /* long long helps to have it working even if !LFS */
84 ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
85 if (ratio > 100) ratio = 100;
86 }
87
88 fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
89
Denis Vlasenko00d84172008-11-24 07:34:42 +000090 barlength = get_tty2_width() - 49;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000091 if (barlength > 0) {
92 /* god bless gcc for variable arrays :) */
93 i = barlength * ratio / 100;
94 {
95 char buf[i+1];
96 memset(buf, '*', i);
97 buf[i] = '\0';
98 fprintf(stderr, "|%s%*s|", buf, barlength - i, "");
99 }
100 }
101 i = 0;
102 abbrevsize = transferred + beg_range;
103 while (abbrevsize >= 100000) {
104 i++;
105 abbrevsize >>= 10;
106 }
107 /* see http://en.wikipedia.org/wiki/Tera */
108 fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]);
109
110// Nuts! Ain't it easier to update progress meter ONLY when we transferred++?
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000111
112 elapsed = monotonic_sec();
113 since_last_update = elapsed - lastupdate_sec;
114 if (transferred > lastsize) {
115 lastupdate_sec = elapsed;
116 lastsize = transferred;
117 if (since_last_update >= STALLTIME) {
118 /* We "cut off" these seconds from elapsed time
119 * by adjusting start time */
120 start_sec += since_last_update;
121 }
122 since_last_update = 0; /* we are un-stalled now */
123 }
124 elapsed -= start_sec; /* now it's "elapsed since start" */
125
126 if (since_last_update >= STALLTIME) {
127 fprintf(stderr, " - stalled -");
128 } else {
129 off_t to_download = totalsize - beg_range;
130 if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) {
131 fprintf(stderr, "--:--:-- ETA");
132 } else {
133 /* to_download / (transferred/elapsed) - elapsed: */
134 int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
135 /* (long long helps to have working ETA even if !LFS) */
136 i = eta % 3600;
137 fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
138 }
139 }
140
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000141 if (flag == 0) {
Denis Vlasenko00d84172008-11-24 07:34:42 +0000142 /* last call to progress_meter */
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000143 alarm(0);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000144 transferred = 0;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000145 fputc('\n', stderr);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000146 } else {
Denis Vlasenko00d84172008-11-24 07:34:42 +0000147 if (flag == -1) { /* first call to progress_meter */
148 signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000149 }
150 alarm(1);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000151 }
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000152
153 errno = save_errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000154}
155/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
156 * much of which was blatantly stolen from openssh. */
157/*-
158 * Copyright (c) 1992, 1993
159 * The Regents of the University of California. All rights reserved.
160 *
161 * Redistribution and use in source and binary forms, with or without
162 * modification, are permitted provided that the following conditions
163 * are met:
164 * 1. Redistributions of source code must retain the above copyright
165 * notice, this list of conditions and the following disclaimer.
166 * 2. Redistributions in binary form must reproduce the above copyright
167 * notice, this list of conditions and the following disclaimer in the
168 * documentation and/or other materials provided with the distribution.
169 *
170 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
171 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
172 *
173 * 4. Neither the name of the University nor the names of its contributors
174 * may be used to endorse or promote products derived from this software
175 * without specific prior written permission.
176 *
177 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
178 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
179 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
181 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
182 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
183 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
184 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
185 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
186 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
187 * SUCH DAMAGE.
188 *
189 */
190#else /* FEATURE_WGET_STATUSBAR */
191
Denis Vlasenko00d84172008-11-24 07:34:42 +0000192static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000193
Eric Andersenb520e082000-10-03 00:21:45 +0000194#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000195
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000196
Denis Vlasenko12d21292007-06-27 21:40:07 +0000197/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
198 * and a short count if an eof or non-interrupt error is encountered. */
199static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
Matt Kraai854125f2001-05-09 19:15:46 +0000200{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000201 size_t ret;
202 char *p = (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000203
204 do {
205 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000206 errno = 0;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000207 ret = fread(p, 1, nmemb, stream);
208 p += ret;
209 nmemb -= ret;
210 } while (nmemb && ferror(stream) && errno == EINTR);
Matt Kraai854125f2001-05-09 19:15:46 +0000211
Denis Vlasenko12d21292007-06-27 21:40:07 +0000212 return p - (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000213}
214
Denis Vlasenko12d21292007-06-27 21:40:07 +0000215/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
Matt Kraai854125f2001-05-09 19:15:46 +0000216 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
217static char *safe_fgets(char *s, int size, FILE *stream)
218{
219 char *ret;
220
221 do {
222 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000223 errno = 0;
Matt Kraai854125f2001-05-09 19:15:46 +0000224 ret = fgets(s, size, stream);
225 } while (ret == NULL && ferror(stream) && errno == EINTR);
226
227 return ret;
228}
229
Denis Vlasenko9cade082006-11-21 10:43:02 +0000230#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denis Vlasenko12d21292007-06-27 21:40:07 +0000231/* Base64-encode character string. buf is assumed to be char buf[512]. */
232static char *base64enc_512(char buf[512], const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000233{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000234 unsigned len = strlen(str);
235 if (len > 512/4*3 - 10) /* paranoia */
236 len = 512/4*3 - 10;
237 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
Rob Landley76ef08c2006-06-13 16:44:26 +0000238 return buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000239}
240#endif
241
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000242
243static FILE *open_socket(len_and_sockaddr *lsa)
244{
245 FILE *fp;
246
247 /* glibc 2.4 seems to try seeking on it - ??! */
248 /* hopefully it understands what ESPIPE means... */
249 fp = fdopen(xconnect_stream(lsa), "r+");
250 if (fp == NULL)
251 bb_perror_msg_and_die("fdopen");
252
253 return fp;
254}
255
256
257static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
258{
259 int result;
260 if (s1) {
261 if (!s2) s2 = "";
262 fprintf(fp, "%s%s\r\n", s1, s2);
263 fflush(fp);
264 }
265
266 do {
267 char *buf_ptr;
268
269 if (fgets(buf, 510, fp) == NULL) {
270 bb_perror_msg_and_die("error getting response");
271 }
272 buf_ptr = strstr(buf, "\r\n");
273 if (buf_ptr) {
274 *buf_ptr = '\0';
275 }
276 } while (!isdigit(buf[0]) || buf[3] != ' ');
277
278 buf[3] = '\0';
279 result = xatoi_u(buf);
280 buf[3] = ' ';
281 return result;
282}
283
284
285static void parse_url(char *src_url, struct host_info *h)
286{
287 char *url, *p, *sp;
288
289 /* h->allocated = */ url = xstrdup(src_url);
290
291 if (strncmp(url, "http://", 7) == 0) {
292 h->port = bb_lookup_port("http", "tcp", 80);
293 h->host = url + 7;
294 h->is_ftp = 0;
295 } else if (strncmp(url, "ftp://", 6) == 0) {
296 h->port = bb_lookup_port("ftp", "tcp", 21);
297 h->host = url + 6;
298 h->is_ftp = 1;
299 } else
300 bb_error_msg_and_die("not an http or ftp url: %s", url);
301
302 // FYI:
303 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
304 // 'GET /?var=a/b HTTP 1.0'
305 // and saves 'index.html?var=a%2Fb' (we save 'b')
306 // wget 'http://busybox.net?login=john@doe':
307 // request: 'GET /?login=john@doe HTTP/1.0'
308 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
309 // wget 'http://busybox.net#test/test':
310 // request: 'GET / HTTP/1.0'
311 // saves: 'index.html' (we save 'test')
312 //
313 // We also don't add unique .N suffix if file exists...
314 sp = strchr(h->host, '/');
315 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
316 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
317 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000318 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000319 } else if (*sp == '/') {
320 *sp = '\0';
321 h->path = sp + 1;
322 } else { // '#' or '?'
323 // http://busybox.net?login=john@doe is a valid URL
324 // memmove converts to:
325 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000326 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000327 h->host--;
328 sp[-1] = '\0';
329 h->path = sp;
330 }
331
332 sp = strrchr(h->host, '@');
333 h->user = NULL;
334 if (sp != NULL) {
335 h->user = h->host;
336 *sp = '\0';
337 h->host = sp + 1;
338 }
339
340 sp = h->host;
341}
342
343
344static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
345{
346 char *s, *hdrval;
347 int c;
348
349 /* *istrunc = 0; */
350
351 /* retrieve header line */
352 if (fgets(buf, bufsiz, fp) == NULL)
353 return NULL;
354
355 /* see if we are at the end of the headers */
356 for (s = buf; *s == '\r'; ++s)
357 continue;
358 if (*s == '\n')
359 return NULL;
360
361 /* convert the header name to lower case */
362 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
363 *s = tolower(*s);
364
365 /* verify we are at the end of the header name */
366 if (*s != ':')
367 bb_error_msg_and_die("bad header line: %s", buf);
368
369 /* locate the start of the header value */
370 *s++ = '\0';
371 hdrval = skip_whitespace(s);
372
373 /* locate the end of header */
374 while (*s && *s != '\r' && *s != '\n')
375 ++s;
376
377 /* end of header found */
378 if (*s) {
379 *s = '\0';
380 return hdrval;
381 }
382
383 /* Rats! The buffer isn't big enough to hold the entire header value. */
384 while (c = getc(fp), c != EOF && c != '\n')
385 continue;
386 /* *istrunc = 1; */
387 return hdrval;
388}
389
390
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000391int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000392int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000393{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000394 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000395 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000396 len_and_sockaddr *lsa;
Denis Vlasenko06783a52007-09-24 13:51:54 +0000397 int status;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000398 int port;
399 int try = 5;
400 unsigned opt;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000401 char *str;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000402 char *proxy = 0;
403 char *dir_prefix = NULL;
404#if ENABLE_FEATURE_WGET_LONG_OPTIONS
405 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000406 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000407#endif
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000408 FILE *sfp = NULL; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000409 FILE *dfp; /* socket to ftp server (data) */
410 char *fname_out; /* where to direct output (-O) */
Denis Vlasenko46611172007-08-06 15:43:17 +0000411 bool got_clen = 0; /* got content-length: from server */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000412 int output_fd = -1;
Denis Vlasenko46611172007-08-06 15:43:17 +0000413 bool use_proxy = 1; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000414 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000415 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000416
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000417 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000418 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000419 enum {
420 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
421 };
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000422 enum {
423 WGET_OPT_CONTINUE = 0x1,
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000424 WGET_OPT_SPIDER = 0x2,
425 WGET_OPT_QUIET = 0x4,
426 WGET_OPT_OUTNAME = 0x8,
427 WGET_OPT_PREFIX = 0x10,
428 WGET_OPT_PROXY = 0x20,
429 WGET_OPT_USER_AGENT = 0x40,
430 WGET_OPT_PASSIVE = 0x80,
431 WGET_OPT_HEADER = 0x100,
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000432 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000433#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000434 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000435 /* name, has_arg, val */
436 "continue\0" No_argument "c"
437 "spider\0" No_argument "s"
438 "quiet\0" No_argument "q"
439 "output-document\0" Required_argument "O"
440 "directory-prefix\0" Required_argument "P"
441 "proxy\0" Required_argument "Y"
442 "user-agent\0" Required_argument "U"
443 "passive-ftp\0" No_argument "\xff"
444 "header\0" Required_argument "\xfe"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000445 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000446#endif
447
448 INIT_G();
449
450#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000451 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000452#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000453 /* server.allocated = target.allocated = NULL; */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000454 opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000455 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000456 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000457 &proxy_flag, &user_agent,
458 NULL, /* -t RETRIES */
459 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000460 USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
461 );
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000462 if (strcmp(proxy_flag, "off") == 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000463 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000464 use_proxy = 0;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000465 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000466#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000467 if (headers_llist) {
468 int size = 1;
469 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000470 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000471 while (ll) {
472 size += strlen(ll->data) + 2;
473 ll = ll->link;
474 }
475 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000476 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000477 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000478 }
479 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000480#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000481
Eric Andersen79757c92001-04-05 21:45:54 +0000482 parse_url(argv[optind], &target);
483 server.host = target.host;
484 server.port = target.port;
485
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000486 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000487 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000488 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000489 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000490 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000491 } else {
492 use_proxy = 0;
493 }
Robert Griebld7760112002-05-14 23:36:45 +0000494 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000495
Denis Vlasenko818322b2007-09-24 18:27:04 +0000496 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000497 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000498 fname_out = bb_get_last_path_component_nostrip(target.path);
499 /* handle "wget http://kernel.org//" */
500 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000501 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000502 /* -P DIR is considered only if there was no -O FILE */
503 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000504 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000505 } else {
506 if (LONE_DASH(fname_out)) {
507 /* -O - */
508 output_fd = 1;
509 opt &= ~WGET_OPT_CONTINUE;
510 }
Eric Andersen29edd002000-12-09 16:55:35 +0000511 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000512#if ENABLE_FEATURE_WGET_STATUSBAR
513 curfile = bb_get_last_path_component_nostrip(fname_out);
514#endif
515
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000516 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000517 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000518 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
Eric Andersen29edd002000-12-09 16:55:35 +0000519
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000520 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000521 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000522 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000523 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000524 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000525 }
526 /* File doesn't exist. We do not create file here yet.
527 We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000528 }
529
Eric Andersene6dc4392003-10-31 09:31:46 +0000530 /* We want to do exactly _one_ DNS lookup, since some
531 * sites (i.e. ftp.us.debian.org) use round-robin DNS
532 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000533 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000534 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000535 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000536 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000537 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000538 }
539
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000540 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000541 /*
542 * HTTP session
543 */
544 do {
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000545 got_clen = 0;
546 chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000547
Denis Vlasenko3526a132006-09-09 12:20:57 +0000548 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000549 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000550
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000551 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000552 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000553 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000554
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000555 /* Send HTTP request. */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000556 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000557 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000558 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000559 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000560 } else {
Eric Andersen6d7fa432001-04-10 18:17:05 +0000561 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000562 }
Eric Andersen96700832000-09-04 15:15:55 +0000563
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000564 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
565 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000566
Denis Vlasenko9cade082006-11-21 10:43:02 +0000567#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000568 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000569 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
570 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000571 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000572 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000573 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000574 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000575 }
576#endif
577
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000578 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000579 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000580#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000581 if (extra_headers)
582 fputs(extra_headers, sfp);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000583#endif
Denis Vlasenko7534e082006-10-23 23:21:58 +0000584 fprintf(sfp, "Connection: close\r\n\r\n");
Eric Andersen79757c92001-04-05 21:45:54 +0000585
586 /*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000587 * Retrieve HTTP response line and check for "200" status code.
588 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000589 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000590 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000591 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000592
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000593 str = buf;
594 str = skip_non_whitespace(str);
595 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000596 // FIXME: no error check
597 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000598 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000599 switch (status) {
600 case 0:
601 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000602 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000603 /* eat all remaining headers */;
604 goto read_response;
605 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000606/*
607Response 204 doesn't say "null file", it says "metadata
608has changed but data didn't":
609
610"10.2.5 204 No Content
611The server has fulfilled the request but does not need to return
612an entity-body, and might want to return updated metainformation.
613The response MAY include new or updated metainformation in the form
614of entity-headers, which if present SHOULD be associated with
615the requested variant.
616
617If the client is a user agent, it SHOULD NOT change its document
618view from that which caused the request to be sent. This response
619is primarily intended to allow input for actions to take place
620without causing a change to the user agent's active document view,
621although any new or updated metainformation SHOULD be applied
622to the document currently in the user agent's active view.
623
624The 204 response MUST NOT include a message-body, and thus
625is always terminated by the first empty line after the header fields."
626
627However, in real world it was observed that some web servers
628(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
629*/
630 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000631 break;
632 case 300: /* redirection */
633 case 301:
634 case 302:
635 case 303:
636 break;
637 case 206:
638 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000639 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000640 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000641 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000642 /* Show first line only and kill any ESC tricks */
643 buf[strcspn(buf, "\n\r\x1b")] = '\0';
644 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000645 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000646
Eric Andersen79757c92001-04-05 21:45:54 +0000647 /*
648 * Retrieve HTTP headers.
649 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000650 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000651 /* gethdr did already convert the "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000652 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000653 if (key == KEY_content_length) {
654 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000655 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000656 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000657 }
Eric Andersen79757c92001-04-05 21:45:54 +0000658 got_clen = 1;
659 continue;
660 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000661 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000662 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000663 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000664 chunked = got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000665 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000666 if (key == KEY_location) {
667 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000668 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000669 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000670 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000671 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000672 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000673 server.host = target.host;
674 server.port = target.port;
675 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000676 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000677 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000678 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000679 }
680 }
681 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000682 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000683
Eric Andersen79757c92001-04-05 21:45:54 +0000684 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000685
686 } else {
687
Eric Andersen79757c92001-04-05 21:45:54 +0000688 /*
689 * FTP session
690 */
Denis Vlasenko3526a132006-09-09 12:20:57 +0000691 if (!target.user)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000692 target.user = xstrdup("anonymous:busybox@");
Eric Andersen79757c92001-04-05 21:45:54 +0000693
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000694 sfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000695 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000696 bb_error_msg_and_die("%s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000697
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000698 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000699 * Splitting username:password pair,
700 * trying to log in
701 */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000702 str = strchr(target.user, ':');
703 if (str)
704 *(str++) = '\0';
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000705 switch (ftpcmd("USER ", target.user, sfp, buf)) {
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000706 case 230:
707 break;
708 case 331:
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000709 if (ftpcmd("PASS ", str, sfp, buf) == 230)
Eric Andersenb520e082000-10-03 00:21:45 +0000710 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000711 /* fall through (failed login) */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000712 default:
713 bb_error_msg_and_die("ftp login: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000714 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000715
Eric Andersen79757c92001-04-05 21:45:54 +0000716 ftpcmd("TYPE I", NULL, sfp, buf);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000717
Eric Andersen79757c92001-04-05 21:45:54 +0000718 /*
719 * Querying file size
720 */
Rob Landleyaf12cb32006-06-27 18:41:03 +0000721 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
Denis Vlasenkod686a042006-11-27 14:43:21 +0000722 content_len = BB_STRTOOFF(buf+4, NULL, 10);
723 if (errno || content_len < 0) {
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000724 bb_error_msg_and_die("SIZE value is garbage");
Eric Andersen24794452004-03-06 22:11:45 +0000725 }
Eric Andersen96700832000-09-04 15:15:55 +0000726 got_clen = 1;
Eric Andersen96700832000-09-04 15:15:55 +0000727 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000728
Eric Andersen79757c92001-04-05 21:45:54 +0000729 /*
730 * Entering passive mode
731 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000732 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
733 pasv_error:
734 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
735 }
Denis Vlasenkof8c8bb12006-11-21 19:10:26 +0000736 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000737 // Server's IP is N1.N2.N3.N4 (we ignore it)
738 // Server's port for data connection is P1*256+P2
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000739 str = strrchr(buf, ')');
740 if (str) str[0] = '\0';
741 str = strrchr(buf, ',');
742 if (!str) goto pasv_error;
743 port = xatou_range(str+1, 0, 255);
744 *str = '\0';
745 str = strrchr(buf, ',');
746 if (!str) goto pasv_error;
747 port += xatou_range(str+1, 0, 255) * 256;
Denis Vlasenko5d687242007-01-12 20:59:31 +0000748 set_nport(lsa, htons(port));
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000749 dfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000750
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000751 if (beg_range) {
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000752 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000753 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000754 content_len -= beg_range;
Eric Andersen96700832000-09-04 15:15:55 +0000755 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000756
Rob Landleyaf12cb32006-06-27 18:41:03 +0000757 if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
Denis Vlasenko77105632007-09-24 15:04:00 +0000758 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
Eric Andersen96700832000-09-04 15:15:55 +0000759 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000760
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000761 if (opt & WGET_OPT_SPIDER) {
762 if (ENABLE_FEATURE_CLEAN_UP)
763 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000764 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000765 }
Eric Andersen79757c92001-04-05 21:45:54 +0000766
Eric Andersen96700832000-09-04 15:15:55 +0000767 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000768 * Retrieve file
Eric Andersen96700832000-09-04 15:15:55 +0000769 */
Rob Landley19a39402006-06-13 17:10:26 +0000770
Denis Vlasenko00d84172008-11-24 07:34:42 +0000771 /* Do it before progress_meter (want to have nice error message) */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000772 if (output_fd < 0) {
773 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
774 /* compat with wget: -O FILE can overwrite */
775 if (opt & WGET_OPT_OUTNAME)
776 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
777 output_fd = xopen(fname_out, o_flags);
778 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000779
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000780 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000781 progress_meter(-1);
Rob Landley19a39402006-06-13 17:10:26 +0000782
Denis Vlasenko77105632007-09-24 15:04:00 +0000783 if (chunked)
784 goto get_clen;
785
Denis Vlasenko06783a52007-09-24 13:51:54 +0000786 /* Loops only if chunked */
787 while (1) {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000788 while (content_len > 0 || !got_clen) {
Denis Vlasenko06783a52007-09-24 13:51:54 +0000789 int n;
Denis Vlasenko3526a132006-09-09 12:20:57 +0000790 unsigned rdsz = sizeof(buf);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000791
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000792 if (content_len < sizeof(buf) && (chunked || got_clen))
793 rdsz = (unsigned)content_len;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000794 n = safe_fread(buf, rdsz, dfp);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000795 if (n <= 0) {
796 if (ferror(dfp)) {
797 /* perror will not work: ferror doesn't set errno */
798 bb_error_msg_and_die(bb_msg_read_error);
799 }
Denis Vlasenko3526a132006-09-09 12:20:57 +0000800 break;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000801 }
Denis Vlasenko06783a52007-09-24 13:51:54 +0000802 xwrite(output_fd, buf, n);
Denis Vlasenko9cade082006-11-21 10:43:02 +0000803#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +0000804 transferred += n;
Eric Andersenb520e082000-10-03 00:21:45 +0000805#endif
Denis Vlasenko06783a52007-09-24 13:51:54 +0000806 if (got_clen)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000807 content_len -= n;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000808 }
Eric Andersen79757c92001-04-05 21:45:54 +0000809
Denis Vlasenko06783a52007-09-24 13:51:54 +0000810 if (!chunked)
811 break;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000812
Denis Vlasenko06783a52007-09-24 13:51:54 +0000813 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
Denis Vlasenko77105632007-09-24 15:04:00 +0000814 get_clen:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000815 safe_fgets(buf, sizeof(buf), dfp);
816 content_len = STRTOOFF(buf, NULL, 16);
817 /* FIXME: error check? */
818 if (content_len == 0)
819 break; /* all done! */
820 }
Rob Landley19a39402006-06-13 17:10:26 +0000821
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000822 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000823 progress_meter(0);
Rob Landley19a39402006-06-13 17:10:26 +0000824
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000825 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000826 fclose(dfp);
827 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000828 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000829 ftpcmd("QUIT", NULL, sfp, buf);
830 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000831
832 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000833}