blob: 3f80d615efc15450bebc2ee3d837917a5468808e [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 Vlasenko00d84172008-11-24 07:34:42 +000057static unsigned int get_tty2_width(void)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000058{
Denis Vlasenko55995022008-05-18 22:28:26 +000059 unsigned width;
Denis Vlasenko00d84172008-11-24 07:34:42 +000060 get_terminal_width_height(2, &width, NULL);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000061 return width;
62}
63
Denis Vlasenko00d84172008-11-24 07:34:42 +000064static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000065{
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
Denis Vlasenko00d84172008-11-24 07:34:42 +000073 if (flag == -1) { /* first call to progress_meter */
Denis Vlasenko47ddd012007-09-24 18:24:17 +000074 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
Denis Vlasenko00d84172008-11-24 07:34:42 +000089 barlength = get_tty2_width() - 49;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000090 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) {
Denis Vlasenko00d84172008-11-24 07:34:42 +0000141 /* last call to progress_meter */
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000142 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 Vlasenko00d84172008-11-24 07:34:42 +0000146 if (flag == -1) { /* first call to progress_meter */
147 signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
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 Vlasenko00d84172008-11-24 07:34:42 +0000191static ALWAYS_INLINE void progress_meter(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 Vlasenko00d84172008-11-24 07:34:42 +0000205 errno = 0;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000206 ret = fread(p, 1, nmemb, stream);
207 p += ret;
208 nmemb -= ret;
209 } while (nmemb && ferror(stream) && errno == EINTR);
Matt Kraai854125f2001-05-09 19:15:46 +0000210
Denis Vlasenko12d21292007-06-27 21:40:07 +0000211 return p - (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000212}
213
Denis Vlasenko12d21292007-06-27 21:40:07 +0000214/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
Matt Kraai854125f2001-05-09 19:15:46 +0000215 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
216static char *safe_fgets(char *s, int size, FILE *stream)
217{
218 char *ret;
219
220 do {
221 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000222 errno = 0;
Matt Kraai854125f2001-05-09 19:15:46 +0000223 ret = fgets(s, size, stream);
224 } while (ret == NULL && ferror(stream) && errno == EINTR);
225
226 return ret;
227}
228
Denis Vlasenko9cade082006-11-21 10:43:02 +0000229#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denis Vlasenko12d21292007-06-27 21:40:07 +0000230/* Base64-encode character string. buf is assumed to be char buf[512]. */
231static char *base64enc_512(char buf[512], const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000232{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000233 unsigned len = strlen(str);
234 if (len > 512/4*3 - 10) /* paranoia */
235 len = 512/4*3 - 10;
236 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
Rob Landley76ef08c2006-06-13 16:44:26 +0000237 return buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000238}
239#endif
240
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000241
242static FILE *open_socket(len_and_sockaddr *lsa)
243{
244 FILE *fp;
245
246 /* glibc 2.4 seems to try seeking on it - ??! */
247 /* hopefully it understands what ESPIPE means... */
248 fp = fdopen(xconnect_stream(lsa), "r+");
249 if (fp == NULL)
250 bb_perror_msg_and_die("fdopen");
251
252 return fp;
253}
254
255
256static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
257{
258 int result;
259 if (s1) {
260 if (!s2) s2 = "";
261 fprintf(fp, "%s%s\r\n", s1, s2);
262 fflush(fp);
263 }
264
265 do {
266 char *buf_ptr;
267
268 if (fgets(buf, 510, fp) == NULL) {
269 bb_perror_msg_and_die("error getting response");
270 }
271 buf_ptr = strstr(buf, "\r\n");
272 if (buf_ptr) {
273 *buf_ptr = '\0';
274 }
275 } while (!isdigit(buf[0]) || buf[3] != ' ');
276
277 buf[3] = '\0';
278 result = xatoi_u(buf);
279 buf[3] = ' ';
280 return result;
281}
282
283
284static void parse_url(char *src_url, struct host_info *h)
285{
286 char *url, *p, *sp;
287
288 /* h->allocated = */ url = xstrdup(src_url);
289
290 if (strncmp(url, "http://", 7) == 0) {
291 h->port = bb_lookup_port("http", "tcp", 80);
292 h->host = url + 7;
293 h->is_ftp = 0;
294 } else if (strncmp(url, "ftp://", 6) == 0) {
295 h->port = bb_lookup_port("ftp", "tcp", 21);
296 h->host = url + 6;
297 h->is_ftp = 1;
298 } else
299 bb_error_msg_and_die("not an http or ftp url: %s", url);
300
301 // FYI:
302 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
303 // 'GET /?var=a/b HTTP 1.0'
304 // and saves 'index.html?var=a%2Fb' (we save 'b')
305 // wget 'http://busybox.net?login=john@doe':
306 // request: 'GET /?login=john@doe HTTP/1.0'
307 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
308 // wget 'http://busybox.net#test/test':
309 // request: 'GET / HTTP/1.0'
310 // saves: 'index.html' (we save 'test')
311 //
312 // We also don't add unique .N suffix if file exists...
313 sp = strchr(h->host, '/');
314 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
315 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
316 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000317 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000318 } else if (*sp == '/') {
319 *sp = '\0';
320 h->path = sp + 1;
321 } else { // '#' or '?'
322 // http://busybox.net?login=john@doe is a valid URL
323 // memmove converts to:
324 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000325 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000326 h->host--;
327 sp[-1] = '\0';
328 h->path = sp;
329 }
330
331 sp = strrchr(h->host, '@');
332 h->user = NULL;
333 if (sp != NULL) {
334 h->user = h->host;
335 *sp = '\0';
336 h->host = sp + 1;
337 }
338
339 sp = h->host;
340}
341
342
343static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
344{
345 char *s, *hdrval;
346 int c;
347
348 /* *istrunc = 0; */
349
350 /* retrieve header line */
351 if (fgets(buf, bufsiz, fp) == NULL)
352 return NULL;
353
354 /* see if we are at the end of the headers */
355 for (s = buf; *s == '\r'; ++s)
356 continue;
357 if (*s == '\n')
358 return NULL;
359
360 /* convert the header name to lower case */
361 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
362 *s = tolower(*s);
363
364 /* verify we are at the end of the header name */
365 if (*s != ':')
366 bb_error_msg_and_die("bad header line: %s", buf);
367
368 /* locate the start of the header value */
369 *s++ = '\0';
370 hdrval = skip_whitespace(s);
371
372 /* locate the end of header */
373 while (*s && *s != '\r' && *s != '\n')
374 ++s;
375
376 /* end of header found */
377 if (*s) {
378 *s = '\0';
379 return hdrval;
380 }
381
382 /* Rats! The buffer isn't big enough to hold the entire header value. */
383 while (c = getc(fp), c != EOF && c != '\n')
384 continue;
385 /* *istrunc = 1; */
386 return hdrval;
387}
388
389
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000390int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000391int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000392{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000393 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000394 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000395 len_and_sockaddr *lsa;
Denis Vlasenko06783a52007-09-24 13:51:54 +0000396 int status;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000397 int port;
398 int try = 5;
399 unsigned opt;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000400 char *str;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000401 char *proxy = 0;
402 char *dir_prefix = NULL;
403#if ENABLE_FEATURE_WGET_LONG_OPTIONS
404 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000405 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000406#endif
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000407 FILE *sfp = NULL; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000408 FILE *dfp; /* socket to ftp server (data) */
409 char *fname_out; /* where to direct output (-O) */
Denis Vlasenko46611172007-08-06 15:43:17 +0000410 bool got_clen = 0; /* got content-length: from server */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000411 int output_fd = -1;
Denis Vlasenko46611172007-08-06 15:43:17 +0000412 bool use_proxy = 1; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000413 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000414 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000415
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000416 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000417 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000418 enum {
419 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
420 };
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000421 enum {
422 WGET_OPT_CONTINUE = 0x1,
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000423 WGET_OPT_SPIDER = 0x2,
424 WGET_OPT_QUIET = 0x4,
425 WGET_OPT_OUTNAME = 0x8,
426 WGET_OPT_PREFIX = 0x10,
427 WGET_OPT_PROXY = 0x20,
428 WGET_OPT_USER_AGENT = 0x40,
429 WGET_OPT_PASSIVE = 0x80,
430 WGET_OPT_HEADER = 0x100,
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000431 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000432#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000433 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000434 /* name, has_arg, val */
435 "continue\0" No_argument "c"
436 "spider\0" No_argument "s"
437 "quiet\0" No_argument "q"
438 "output-document\0" Required_argument "O"
439 "directory-prefix\0" Required_argument "P"
440 "proxy\0" Required_argument "Y"
441 "user-agent\0" Required_argument "U"
442 "passive-ftp\0" No_argument "\xff"
443 "header\0" Required_argument "\xfe"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000444 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000445#endif
446
447 INIT_G();
448
449#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000450 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000451#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000452 /* server.allocated = target.allocated = NULL; */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000453 opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000454 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000455 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000456 &proxy_flag, &user_agent,
457 NULL, /* -t RETRIES */
458 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000459 USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
460 );
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000461 if (strcmp(proxy_flag, "off") == 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000462 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000463 use_proxy = 0;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000464 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000465#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000466 if (headers_llist) {
467 int size = 1;
468 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000469 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000470 while (ll) {
471 size += strlen(ll->data) + 2;
472 ll = ll->link;
473 }
474 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000475 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000476 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000477 }
478 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000479#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000480
Eric Andersen79757c92001-04-05 21:45:54 +0000481 parse_url(argv[optind], &target);
482 server.host = target.host;
483 server.port = target.port;
484
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000485 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000486 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000487 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000488 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000489 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000490 } else {
491 use_proxy = 0;
492 }
Robert Griebld7760112002-05-14 23:36:45 +0000493 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000494
Denis Vlasenko818322b2007-09-24 18:27:04 +0000495 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000496 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000497 fname_out = bb_get_last_path_component_nostrip(target.path);
498 /* handle "wget http://kernel.org//" */
499 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000500 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000501 /* -P DIR is considered only if there was no -O FILE */
502 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000503 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000504 } else {
505 if (LONE_DASH(fname_out)) {
506 /* -O - */
507 output_fd = 1;
508 opt &= ~WGET_OPT_CONTINUE;
509 }
Eric Andersen29edd002000-12-09 16:55:35 +0000510 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000511#if ENABLE_FEATURE_WGET_STATUSBAR
512 curfile = bb_get_last_path_component_nostrip(fname_out);
513#endif
514
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000515 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000516 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000517 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
Eric Andersen29edd002000-12-09 16:55:35 +0000518
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000519 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000520 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000521 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000522 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000523 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000524 }
525 /* File doesn't exist. We do not create file here yet.
526 We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000527 }
528
Eric Andersene6dc4392003-10-31 09:31:46 +0000529 /* We want to do exactly _one_ DNS lookup, since some
530 * sites (i.e. ftp.us.debian.org) use round-robin DNS
531 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000532 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000533 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000534 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000535 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000536 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000537 }
538
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000539 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000540 /*
541 * HTTP session
542 */
543 do {
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000544 got_clen = 0;
545 chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000546
Denis Vlasenko3526a132006-09-09 12:20:57 +0000547 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000548 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000549
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000550 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000551 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000552 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000553
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000554 /* Send HTTP request. */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000555 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000556 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000557 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000558 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000559 } else {
Eric Andersen6d7fa432001-04-10 18:17:05 +0000560 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000561 }
Eric Andersen96700832000-09-04 15:15:55 +0000562
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000563 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
564 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000565
Denis Vlasenko9cade082006-11-21 10:43:02 +0000566#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000567 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000568 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
569 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000570 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000571 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000572 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000573 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000574 }
575#endif
576
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000577 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000578 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000579#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000580 if (extra_headers)
581 fputs(extra_headers, sfp);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000582#endif
Denis Vlasenko7534e082006-10-23 23:21:58 +0000583 fprintf(sfp, "Connection: close\r\n\r\n");
Eric Andersen79757c92001-04-05 21:45:54 +0000584
585 /*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000586 * Retrieve HTTP response line and check for "200" status code.
587 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000588 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000589 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000590 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000591
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000592 str = buf;
593 str = skip_non_whitespace(str);
594 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000595 // FIXME: no error check
596 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000597 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000598 switch (status) {
599 case 0:
600 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000601 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000602 /* eat all remaining headers */;
603 goto read_response;
604 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000605/*
606Response 204 doesn't say "null file", it says "metadata
607has changed but data didn't":
608
609"10.2.5 204 No Content
610The server has fulfilled the request but does not need to return
611an entity-body, and might want to return updated metainformation.
612The response MAY include new or updated metainformation in the form
613of entity-headers, which if present SHOULD be associated with
614the requested variant.
615
616If the client is a user agent, it SHOULD NOT change its document
617view from that which caused the request to be sent. This response
618is primarily intended to allow input for actions to take place
619without causing a change to the user agent's active document view,
620although any new or updated metainformation SHOULD be applied
621to the document currently in the user agent's active view.
622
623The 204 response MUST NOT include a message-body, and thus
624is always terminated by the first empty line after the header fields."
625
626However, in real world it was observed that some web servers
627(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
628*/
629 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000630 break;
631 case 300: /* redirection */
632 case 301:
633 case 302:
634 case 303:
635 break;
636 case 206:
637 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000638 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000639 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000640 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000641 /* Show first line only and kill any ESC tricks */
642 buf[strcspn(buf, "\n\r\x1b")] = '\0';
643 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000644 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000645
Eric Andersen79757c92001-04-05 21:45:54 +0000646 /*
647 * Retrieve HTTP headers.
648 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000649 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000650 /* gethdr did already convert the "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000651 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000652 if (key == KEY_content_length) {
653 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000654 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000655 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000656 }
Eric Andersen79757c92001-04-05 21:45:54 +0000657 got_clen = 1;
658 continue;
659 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000660 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000661 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000662 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000663 chunked = got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000664 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000665 if (key == KEY_location) {
666 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000667 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000668 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000669 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000670 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000671 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000672 server.host = target.host;
673 server.port = target.port;
674 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000675 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000676 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000677 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000678 }
679 }
680 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000681 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000682
Eric Andersen79757c92001-04-05 21:45:54 +0000683 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000684
685 } else {
686
Eric Andersen79757c92001-04-05 21:45:54 +0000687 /*
688 * FTP session
689 */
Denis Vlasenko3526a132006-09-09 12:20:57 +0000690 if (!target.user)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000691 target.user = xstrdup("anonymous:busybox@");
Eric Andersen79757c92001-04-05 21:45:54 +0000692
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000693 sfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000694 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000695 bb_error_msg_and_die("%s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000696
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000697 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000698 * Splitting username:password pair,
699 * trying to log in
700 */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000701 str = strchr(target.user, ':');
702 if (str)
703 *(str++) = '\0';
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000704 switch (ftpcmd("USER ", target.user, sfp, buf)) {
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000705 case 230:
706 break;
707 case 331:
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000708 if (ftpcmd("PASS ", str, sfp, buf) == 230)
Eric Andersenb520e082000-10-03 00:21:45 +0000709 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000710 /* fall through (failed login) */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000711 default:
712 bb_error_msg_and_die("ftp login: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000713 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000714
Eric Andersen79757c92001-04-05 21:45:54 +0000715 ftpcmd("TYPE I", NULL, sfp, buf);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000716
Eric Andersen79757c92001-04-05 21:45:54 +0000717 /*
718 * Querying file size
719 */
Rob Landleyaf12cb32006-06-27 18:41:03 +0000720 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
Denis Vlasenkod686a042006-11-27 14:43:21 +0000721 content_len = BB_STRTOOFF(buf+4, NULL, 10);
722 if (errno || content_len < 0) {
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000723 bb_error_msg_and_die("SIZE value is garbage");
Eric Andersen24794452004-03-06 22:11:45 +0000724 }
Eric Andersen96700832000-09-04 15:15:55 +0000725 got_clen = 1;
Eric Andersen96700832000-09-04 15:15:55 +0000726 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000727
Eric Andersen79757c92001-04-05 21:45:54 +0000728 /*
729 * Entering passive mode
730 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000731 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
732 pasv_error:
733 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
734 }
Denis Vlasenkof8c8bb12006-11-21 19:10:26 +0000735 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000736 // Server's IP is N1.N2.N3.N4 (we ignore it)
737 // Server's port for data connection is P1*256+P2
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000738 str = strrchr(buf, ')');
739 if (str) str[0] = '\0';
740 str = strrchr(buf, ',');
741 if (!str) goto pasv_error;
742 port = xatou_range(str+1, 0, 255);
743 *str = '\0';
744 str = strrchr(buf, ',');
745 if (!str) goto pasv_error;
746 port += xatou_range(str+1, 0, 255) * 256;
Denis Vlasenko5d687242007-01-12 20:59:31 +0000747 set_nport(lsa, htons(port));
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000748 dfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000749
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000750 if (beg_range) {
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000751 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000752 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000753 content_len -= beg_range;
Eric Andersen96700832000-09-04 15:15:55 +0000754 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000755
Rob Landleyaf12cb32006-06-27 18:41:03 +0000756 if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
Denis Vlasenko77105632007-09-24 15:04:00 +0000757 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
Eric Andersen96700832000-09-04 15:15:55 +0000758 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000759
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000760 if (opt & WGET_OPT_SPIDER) {
761 if (ENABLE_FEATURE_CLEAN_UP)
762 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000763 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000764 }
Eric Andersen79757c92001-04-05 21:45:54 +0000765
Eric Andersen96700832000-09-04 15:15:55 +0000766 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000767 * Retrieve file
Eric Andersen96700832000-09-04 15:15:55 +0000768 */
Rob Landley19a39402006-06-13 17:10:26 +0000769
Denis Vlasenko00d84172008-11-24 07:34:42 +0000770 /* Do it before progress_meter (want to have nice error message) */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000771 if (output_fd < 0) {
772 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
773 /* compat with wget: -O FILE can overwrite */
774 if (opt & WGET_OPT_OUTNAME)
775 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
776 output_fd = xopen(fname_out, o_flags);
777 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000778
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000779 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000780 progress_meter(-1);
Rob Landley19a39402006-06-13 17:10:26 +0000781
Denis Vlasenko77105632007-09-24 15:04:00 +0000782 if (chunked)
783 goto get_clen;
784
Denis Vlasenko06783a52007-09-24 13:51:54 +0000785 /* Loops only if chunked */
786 while (1) {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000787 while (content_len > 0 || !got_clen) {
Denis Vlasenko06783a52007-09-24 13:51:54 +0000788 int n;
Denis Vlasenko3526a132006-09-09 12:20:57 +0000789 unsigned rdsz = sizeof(buf);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000790
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000791 if (content_len < sizeof(buf) && (chunked || got_clen))
792 rdsz = (unsigned)content_len;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000793 n = safe_fread(buf, rdsz, dfp);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000794 if (n <= 0) {
795 if (ferror(dfp)) {
796 /* perror will not work: ferror doesn't set errno */
797 bb_error_msg_and_die(bb_msg_read_error);
798 }
Denis Vlasenko3526a132006-09-09 12:20:57 +0000799 break;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000800 }
Denis Vlasenko06783a52007-09-24 13:51:54 +0000801 xwrite(output_fd, buf, n);
Denis Vlasenko9cade082006-11-21 10:43:02 +0000802#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +0000803 transferred += n;
Eric Andersenb520e082000-10-03 00:21:45 +0000804#endif
Denis Vlasenko06783a52007-09-24 13:51:54 +0000805 if (got_clen)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000806 content_len -= n;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000807 }
Eric Andersen79757c92001-04-05 21:45:54 +0000808
Denis Vlasenko06783a52007-09-24 13:51:54 +0000809 if (!chunked)
810 break;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000811
Denis Vlasenko06783a52007-09-24 13:51:54 +0000812 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
Denis Vlasenko77105632007-09-24 15:04:00 +0000813 get_clen:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000814 safe_fgets(buf, sizeof(buf), dfp);
815 content_len = STRTOOFF(buf, NULL, 16);
816 /* FIXME: error check? */
817 if (content_len == 0)
818 break; /* all done! */
819 }
Rob Landley19a39402006-06-13 17:10:26 +0000820
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000821 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000822 progress_meter(0);
Rob Landley19a39402006-06-13 17:10:26 +0000823
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000824 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000825 fclose(dfp);
826 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000827 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000828 ftpcmd("QUIT", NULL, sfp, buf);
829 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000830
831 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000832}