blob: 6527538e0eef7bf31dad3bc46d8e77d44adce1fd [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 {
Denis Vlasenko50af9262009-03-02 15:08:06 +0000423 WGET_OPT_CONTINUE = (1 << 0),
424 WGET_OPT_SPIDER = (1 << 1),
425 WGET_OPT_QUIET = (1 << 2),
426 WGET_OPT_OUTNAME = (1 << 3),
427 WGET_OPT_PREFIX = (1 << 4),
428 WGET_OPT_PROXY = (1 << 5),
429 WGET_OPT_USER_AGENT = (1 << 6),
430 WGET_OPT_RETRIES = (1 << 7),
431 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
432 WGET_OPT_PASSIVE = (1 << 9),
433 WGET_OPT_HEADER = (1 << 10),
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000434 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000435#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000436 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000437 /* name, has_arg, val */
438 "continue\0" No_argument "c"
439 "spider\0" No_argument "s"
440 "quiet\0" No_argument "q"
441 "output-document\0" Required_argument "O"
442 "directory-prefix\0" Required_argument "P"
443 "proxy\0" Required_argument "Y"
444 "user-agent\0" Required_argument "U"
Denis Vlasenko50af9262009-03-02 15:08:06 +0000445 /* Ignored: */
446 // "tries\0" Required_argument "t"
447 // "timeout\0" Required_argument "T"
448 /* Ignored (we always use PASV): */
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000449 "passive-ftp\0" No_argument "\xff"
450 "header\0" Required_argument "\xfe"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000451 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000452#endif
453
454 INIT_G();
455
456#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000457 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000458#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000459 /* server.allocated = target.allocated = NULL; */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000460 opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000461 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000462 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000463 &proxy_flag, &user_agent,
464 NULL, /* -t RETRIES */
465 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000466 USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
467 );
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000468 if (strcmp(proxy_flag, "off") == 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000469 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000470 use_proxy = 0;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000471 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000472#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000473 if (headers_llist) {
474 int size = 1;
475 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000476 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000477 while (ll) {
478 size += strlen(ll->data) + 2;
479 ll = ll->link;
480 }
481 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000482 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000483 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000484 }
485 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000486#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000487
Eric Andersen79757c92001-04-05 21:45:54 +0000488 parse_url(argv[optind], &target);
489 server.host = target.host;
490 server.port = target.port;
491
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000492 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000493 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000494 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000495 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000496 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000497 } else {
498 use_proxy = 0;
499 }
Robert Griebld7760112002-05-14 23:36:45 +0000500 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000501
Denis Vlasenko818322b2007-09-24 18:27:04 +0000502 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000503 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000504 fname_out = bb_get_last_path_component_nostrip(target.path);
505 /* handle "wget http://kernel.org//" */
506 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000507 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000508 /* -P DIR is considered only if there was no -O FILE */
509 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000510 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000511 } else {
512 if (LONE_DASH(fname_out)) {
513 /* -O - */
514 output_fd = 1;
515 opt &= ~WGET_OPT_CONTINUE;
516 }
Eric Andersen29edd002000-12-09 16:55:35 +0000517 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000518#if ENABLE_FEATURE_WGET_STATUSBAR
519 curfile = bb_get_last_path_component_nostrip(fname_out);
520#endif
521
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000522 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000523 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000524 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
Eric Andersen29edd002000-12-09 16:55:35 +0000525
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000526 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000527 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000528 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000529 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000530 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000531 }
532 /* File doesn't exist. We do not create file here yet.
533 We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000534 }
535
Eric Andersene6dc4392003-10-31 09:31:46 +0000536 /* We want to do exactly _one_ DNS lookup, since some
537 * sites (i.e. ftp.us.debian.org) use round-robin DNS
538 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000539 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000540 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000541 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000542 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000543 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000544 }
545
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000546 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000547 /*
548 * HTTP session
549 */
550 do {
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000551 got_clen = 0;
552 chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000553
Denis Vlasenko3526a132006-09-09 12:20:57 +0000554 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000555 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000556
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000557 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000558 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000559 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000560
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000561 /* Send HTTP request. */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000562 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000563 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000564 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000565 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000566 } else {
Eric Andersen6d7fa432001-04-10 18:17:05 +0000567 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000568 }
Eric Andersen96700832000-09-04 15:15:55 +0000569
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000570 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
571 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000572
Denis Vlasenko9cade082006-11-21 10:43:02 +0000573#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000574 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000575 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
576 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000577 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000578 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000579 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000580 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000581 }
582#endif
583
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000584 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000585 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000586#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000587 if (extra_headers)
588 fputs(extra_headers, sfp);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000589#endif
Denis Vlasenko7534e082006-10-23 23:21:58 +0000590 fprintf(sfp, "Connection: close\r\n\r\n");
Eric Andersen79757c92001-04-05 21:45:54 +0000591
592 /*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000593 * Retrieve HTTP response line and check for "200" status code.
594 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000595 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000596 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000597 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000598
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000599 str = buf;
600 str = skip_non_whitespace(str);
601 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000602 // FIXME: no error check
603 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000604 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000605 switch (status) {
606 case 0:
607 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000608 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000609 /* eat all remaining headers */;
610 goto read_response;
611 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000612/*
613Response 204 doesn't say "null file", it says "metadata
614has changed but data didn't":
615
616"10.2.5 204 No Content
617The server has fulfilled the request but does not need to return
618an entity-body, and might want to return updated metainformation.
619The response MAY include new or updated metainformation in the form
620of entity-headers, which if present SHOULD be associated with
621the requested variant.
622
623If the client is a user agent, it SHOULD NOT change its document
624view from that which caused the request to be sent. This response
625is primarily intended to allow input for actions to take place
626without causing a change to the user agent's active document view,
627although any new or updated metainformation SHOULD be applied
628to the document currently in the user agent's active view.
629
630The 204 response MUST NOT include a message-body, and thus
631is always terminated by the first empty line after the header fields."
632
633However, in real world it was observed that some web servers
634(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
635*/
636 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000637 break;
638 case 300: /* redirection */
639 case 301:
640 case 302:
641 case 303:
642 break;
643 case 206:
644 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000645 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000646 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000647 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000648 /* Show first line only and kill any ESC tricks */
649 buf[strcspn(buf, "\n\r\x1b")] = '\0';
650 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000651 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000652
Eric Andersen79757c92001-04-05 21:45:54 +0000653 /*
654 * Retrieve HTTP headers.
655 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000656 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000657 /* gethdr did already convert the "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000658 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000659 if (key == KEY_content_length) {
660 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000661 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000662 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000663 }
Eric Andersen79757c92001-04-05 21:45:54 +0000664 got_clen = 1;
665 continue;
666 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000667 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000668 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000669 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000670 chunked = got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000671 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000672 if (key == KEY_location) {
673 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000674 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000675 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000676 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000677 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000678 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000679 server.host = target.host;
680 server.port = target.port;
681 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000682 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000683 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000684 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000685 }
686 }
687 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000688 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000689
Eric Andersen79757c92001-04-05 21:45:54 +0000690 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000691
692 } else {
693
Eric Andersen79757c92001-04-05 21:45:54 +0000694 /*
695 * FTP session
696 */
Denis Vlasenko3526a132006-09-09 12:20:57 +0000697 if (!target.user)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000698 target.user = xstrdup("anonymous:busybox@");
Eric Andersen79757c92001-04-05 21:45:54 +0000699
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000700 sfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000701 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000702 bb_error_msg_and_die("%s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000703
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000704 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000705 * Splitting username:password pair,
706 * trying to log in
707 */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000708 str = strchr(target.user, ':');
709 if (str)
710 *(str++) = '\0';
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000711 switch (ftpcmd("USER ", target.user, sfp, buf)) {
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000712 case 230:
713 break;
714 case 331:
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000715 if (ftpcmd("PASS ", str, sfp, buf) == 230)
Eric Andersenb520e082000-10-03 00:21:45 +0000716 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000717 /* fall through (failed login) */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000718 default:
719 bb_error_msg_and_die("ftp login: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000720 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000721
Eric Andersen79757c92001-04-05 21:45:54 +0000722 ftpcmd("TYPE I", NULL, sfp, buf);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000723
Eric Andersen79757c92001-04-05 21:45:54 +0000724 /*
725 * Querying file size
726 */
Rob Landleyaf12cb32006-06-27 18:41:03 +0000727 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
Denis Vlasenkod686a042006-11-27 14:43:21 +0000728 content_len = BB_STRTOOFF(buf+4, NULL, 10);
729 if (errno || content_len < 0) {
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000730 bb_error_msg_and_die("SIZE value is garbage");
Eric Andersen24794452004-03-06 22:11:45 +0000731 }
Eric Andersen96700832000-09-04 15:15:55 +0000732 got_clen = 1;
Eric Andersen96700832000-09-04 15:15:55 +0000733 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000734
Eric Andersen79757c92001-04-05 21:45:54 +0000735 /*
736 * Entering passive mode
737 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000738 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
739 pasv_error:
740 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
741 }
Denis Vlasenkof8c8bb12006-11-21 19:10:26 +0000742 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000743 // Server's IP is N1.N2.N3.N4 (we ignore it)
744 // Server's port for data connection is P1*256+P2
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000745 str = strrchr(buf, ')');
746 if (str) str[0] = '\0';
747 str = strrchr(buf, ',');
748 if (!str) goto pasv_error;
749 port = xatou_range(str+1, 0, 255);
750 *str = '\0';
751 str = strrchr(buf, ',');
752 if (!str) goto pasv_error;
753 port += xatou_range(str+1, 0, 255) * 256;
Denis Vlasenko5d687242007-01-12 20:59:31 +0000754 set_nport(lsa, htons(port));
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000755 dfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000756
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000757 if (beg_range) {
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000758 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000759 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000760 content_len -= beg_range;
Eric Andersen96700832000-09-04 15:15:55 +0000761 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000762
Rob Landleyaf12cb32006-06-27 18:41:03 +0000763 if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
Denis Vlasenko77105632007-09-24 15:04:00 +0000764 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
Eric Andersen96700832000-09-04 15:15:55 +0000765 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000766
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000767 if (opt & WGET_OPT_SPIDER) {
768 if (ENABLE_FEATURE_CLEAN_UP)
769 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000770 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000771 }
Eric Andersen79757c92001-04-05 21:45:54 +0000772
Eric Andersen96700832000-09-04 15:15:55 +0000773 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000774 * Retrieve file
Eric Andersen96700832000-09-04 15:15:55 +0000775 */
Rob Landley19a39402006-06-13 17:10:26 +0000776
Denis Vlasenko00d84172008-11-24 07:34:42 +0000777 /* Do it before progress_meter (want to have nice error message) */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000778 if (output_fd < 0) {
779 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
780 /* compat with wget: -O FILE can overwrite */
781 if (opt & WGET_OPT_OUTNAME)
782 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
783 output_fd = xopen(fname_out, o_flags);
784 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000785
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000786 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000787 progress_meter(-1);
Rob Landley19a39402006-06-13 17:10:26 +0000788
Denis Vlasenko77105632007-09-24 15:04:00 +0000789 if (chunked)
790 goto get_clen;
791
Denis Vlasenko06783a52007-09-24 13:51:54 +0000792 /* Loops only if chunked */
793 while (1) {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000794 while (content_len > 0 || !got_clen) {
Denis Vlasenko06783a52007-09-24 13:51:54 +0000795 int n;
Denis Vlasenko3526a132006-09-09 12:20:57 +0000796 unsigned rdsz = sizeof(buf);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000797
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000798 if (content_len < sizeof(buf) && (chunked || got_clen))
799 rdsz = (unsigned)content_len;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000800 n = safe_fread(buf, rdsz, dfp);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000801 if (n <= 0) {
802 if (ferror(dfp)) {
803 /* perror will not work: ferror doesn't set errno */
804 bb_error_msg_and_die(bb_msg_read_error);
805 }
Denis Vlasenko3526a132006-09-09 12:20:57 +0000806 break;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000807 }
Denis Vlasenko06783a52007-09-24 13:51:54 +0000808 xwrite(output_fd, buf, n);
Denis Vlasenko9cade082006-11-21 10:43:02 +0000809#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +0000810 transferred += n;
Eric Andersenb520e082000-10-03 00:21:45 +0000811#endif
Denis Vlasenko06783a52007-09-24 13:51:54 +0000812 if (got_clen)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000813 content_len -= n;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000814 }
Eric Andersen79757c92001-04-05 21:45:54 +0000815
Denis Vlasenko06783a52007-09-24 13:51:54 +0000816 if (!chunked)
817 break;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000818
Denis Vlasenko06783a52007-09-24 13:51:54 +0000819 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
Denis Vlasenko77105632007-09-24 15:04:00 +0000820 get_clen:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000821 safe_fgets(buf, sizeof(buf), dfp);
822 content_len = STRTOOFF(buf, NULL, 16);
823 /* FIXME: error check? */
824 if (content_len == 0)
825 break; /* all done! */
826 }
Rob Landley19a39402006-06-13 17:10:26 +0000827
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000828 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000829 progress_meter(0);
Rob Landley19a39402006-06-13 17:10:26 +0000830
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000831 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000832 fclose(dfp);
833 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000834 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000835 ftpcmd("QUIT", NULL, sfp, buf);
836 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000837
838 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000839}