blob: ca3acd0855a045023b83b09b7311b15ffed7c75c [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
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000390#if ENABLE_FEATURE_WGET_LONG_OPTIONS
391static char *URL_escape(const char *str)
392{
393 /* URL encode, see RFC 2396 */
394 char *dst;
395 char *res = dst = xmalloc(strlen(str) * 3 + 1);
396 unsigned char c;
397
398 while (1) {
399 c = *str++;
400 if (c == '\0'
401 /* || strchr("!&'()*-.=_~", c) - more code */
402 || c == '!'
403 || c == '&'
404 || c == '\''
405 || c == '('
406 || c == ')'
407 || c == '*'
408 || c == '-'
409 || c == '.'
410 || c == '='
411 || c == '_'
412 || c == '~'
413 || (c >= '0' && c <= '9')
414 || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
415 ) {
416 *dst++ = c;
417 if (c == '\0')
418 return res;
419 } else {
420 *dst++ = '%';
421 *dst++ = bb_hexdigits_upcase[c >> 4];
422 *dst++ = bb_hexdigits_upcase[c & 0xf];
423 }
424 }
425}
426#endif
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000427
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000428int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000429int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000430{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000431 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000432 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000433 len_and_sockaddr *lsa;
Denis Vlasenko06783a52007-09-24 13:51:54 +0000434 int status;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000435 int port;
436 int try = 5;
437 unsigned opt;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000438 char *str;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000439 char *proxy = 0;
440 char *dir_prefix = NULL;
441#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000442 char *post_data;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000443 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000444 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000445#endif
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000446 FILE *sfp = NULL; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000447 FILE *dfp; /* socket to ftp server (data) */
448 char *fname_out; /* where to direct output (-O) */
Denis Vlasenko46611172007-08-06 15:43:17 +0000449 bool got_clen = 0; /* got content-length: from server */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000450 int output_fd = -1;
Denis Vlasenko46611172007-08-06 15:43:17 +0000451 bool use_proxy = 1; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000452 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000453 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000454
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000455 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000456 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000457 enum {
458 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
459 };
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000460 enum {
Denis Vlasenko50af9262009-03-02 15:08:06 +0000461 WGET_OPT_CONTINUE = (1 << 0),
462 WGET_OPT_SPIDER = (1 << 1),
463 WGET_OPT_QUIET = (1 << 2),
464 WGET_OPT_OUTNAME = (1 << 3),
465 WGET_OPT_PREFIX = (1 << 4),
466 WGET_OPT_PROXY = (1 << 5),
467 WGET_OPT_USER_AGENT = (1 << 6),
468 WGET_OPT_RETRIES = (1 << 7),
469 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
470 WGET_OPT_PASSIVE = (1 << 9),
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000471 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
472 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000473 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000474#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000475 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000476 /* name, has_arg, val */
477 "continue\0" No_argument "c"
478 "spider\0" No_argument "s"
479 "quiet\0" No_argument "q"
480 "output-document\0" Required_argument "O"
481 "directory-prefix\0" Required_argument "P"
482 "proxy\0" Required_argument "Y"
483 "user-agent\0" Required_argument "U"
Denis Vlasenko50af9262009-03-02 15:08:06 +0000484 /* Ignored: */
485 // "tries\0" Required_argument "t"
486 // "timeout\0" Required_argument "T"
487 /* Ignored (we always use PASV): */
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000488 "passive-ftp\0" No_argument "\xff"
489 "header\0" Required_argument "\xfe"
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000490 "post-data\0" Required_argument "\xfd"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000491 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000492#endif
493
494 INIT_G();
495
496#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000497 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000498#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000499 /* server.allocated = target.allocated = NULL; */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000500 opt_complementary = "-1" IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000501 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000502 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000503 &proxy_flag, &user_agent,
504 NULL, /* -t RETRIES */
505 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000506 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
507 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000508 );
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000509 if (strcmp(proxy_flag, "off") == 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000510 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000511 use_proxy = 0;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000512 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000513#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000514 if (headers_llist) {
515 int size = 1;
516 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000517 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000518 while (ll) {
519 size += strlen(ll->data) + 2;
520 ll = ll->link;
521 }
522 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000523 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000524 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000525 }
526 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000527#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000528
Eric Andersen79757c92001-04-05 21:45:54 +0000529 parse_url(argv[optind], &target);
530 server.host = target.host;
531 server.port = target.port;
532
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000533 /* Use the proxy if necessary */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000534 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000535 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000536 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000537 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000538 } else {
539 use_proxy = 0;
540 }
Robert Griebld7760112002-05-14 23:36:45 +0000541 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000542
Denis Vlasenko818322b2007-09-24 18:27:04 +0000543 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000544 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000545 fname_out = bb_get_last_path_component_nostrip(target.path);
546 /* handle "wget http://kernel.org//" */
547 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000548 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000549 /* -P DIR is considered only if there was no -O FILE */
550 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000551 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000552 } else {
553 if (LONE_DASH(fname_out)) {
554 /* -O - */
555 output_fd = 1;
556 opt &= ~WGET_OPT_CONTINUE;
557 }
Eric Andersen29edd002000-12-09 16:55:35 +0000558 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000559#if ENABLE_FEATURE_WGET_STATUSBAR
560 curfile = bb_get_last_path_component_nostrip(fname_out);
561#endif
562
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000563 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000564 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000565 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
Eric Andersen29edd002000-12-09 16:55:35 +0000566
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000567 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000568 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000569 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000570 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000571 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000572 }
573 /* File doesn't exist. We do not create file here yet.
574 We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000575 }
576
Eric Andersene6dc4392003-10-31 09:31:46 +0000577 /* We want to do exactly _one_ DNS lookup, since some
578 * sites (i.e. ftp.us.debian.org) use round-robin DNS
579 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000580 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000581 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000582 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000583 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000584 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000585 }
586
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000587 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000588 /*
589 * HTTP session
590 */
591 do {
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000592 got_clen = 0;
593 chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000594
Denis Vlasenko3526a132006-09-09 12:20:57 +0000595 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000596 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000597
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000598 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000599 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000600 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000601
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000602 /* Send HTTP request. */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000603 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000604 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000605 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000606 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000607 } else {
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000608 if (opt & WGET_OPT_POST_DATA)
609 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
610 else
611 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000612 }
Eric Andersen96700832000-09-04 15:15:55 +0000613
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000614 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
615 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000616
Denis Vlasenko9cade082006-11-21 10:43:02 +0000617#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000618 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000619 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
620 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000621 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000622 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000623 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000624 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000625 }
626#endif
627
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000628 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000629 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000630#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000631 if (extra_headers)
632 fputs(extra_headers, sfp);
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000633
634 if (opt & WGET_OPT_POST_DATA) {
635 char *estr = URL_escape(post_data);
636 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
637 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
638 (int) strlen(estr), estr);
639 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
640 /*fprintf(sfp, "%s\r\n", estr);*/
641 free(estr);
642 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000643#endif
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000644 { /* If "Connection:" is needed, document why */
645 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
646 }
Eric Andersen79757c92001-04-05 21:45:54 +0000647
648 /*
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000649 * Retrieve HTTP response line and check for "200" status code.
650 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000651 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000652 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000653 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000654
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000655 str = buf;
656 str = skip_non_whitespace(str);
657 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000658 // FIXME: no error check
659 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000660 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000661 switch (status) {
662 case 0:
663 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000664 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000665 /* eat all remaining headers */;
666 goto read_response;
667 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000668/*
669Response 204 doesn't say "null file", it says "metadata
670has changed but data didn't":
671
672"10.2.5 204 No Content
673The server has fulfilled the request but does not need to return
674an entity-body, and might want to return updated metainformation.
675The response MAY include new or updated metainformation in the form
676of entity-headers, which if present SHOULD be associated with
677the requested variant.
678
679If the client is a user agent, it SHOULD NOT change its document
680view from that which caused the request to be sent. This response
681is primarily intended to allow input for actions to take place
682without causing a change to the user agent's active document view,
683although any new or updated metainformation SHOULD be applied
684to the document currently in the user agent's active view.
685
686The 204 response MUST NOT include a message-body, and thus
687is always terminated by the first empty line after the header fields."
688
689However, in real world it was observed that some web servers
690(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
691*/
692 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000693 break;
694 case 300: /* redirection */
695 case 301:
696 case 302:
697 case 303:
698 break;
699 case 206:
700 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000701 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000702 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000703 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000704 /* Show first line only and kill any ESC tricks */
705 buf[strcspn(buf, "\n\r\x1b")] = '\0';
706 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000707 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000708
Eric Andersen79757c92001-04-05 21:45:54 +0000709 /*
710 * Retrieve HTTP headers.
711 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000712 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000713 /* gethdr did already convert the "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000714 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000715 if (key == KEY_content_length) {
716 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000717 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000718 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000719 }
Eric Andersen79757c92001-04-05 21:45:54 +0000720 got_clen = 1;
721 continue;
722 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000723 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000724 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000725 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000726 chunked = got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000727 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000728 if (key == KEY_location) {
729 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000730 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000731 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000732 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000733 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000734 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000735 server.host = target.host;
736 server.port = target.port;
737 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000738 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000739 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000740 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000741 }
742 }
743 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000744 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000745
Eric Andersen79757c92001-04-05 21:45:54 +0000746 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000747
748 } else {
749
Eric Andersen79757c92001-04-05 21:45:54 +0000750 /*
751 * FTP session
752 */
Denis Vlasenko3526a132006-09-09 12:20:57 +0000753 if (!target.user)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000754 target.user = xstrdup("anonymous:busybox@");
Eric Andersen79757c92001-04-05 21:45:54 +0000755
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000756 sfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000757 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000758 bb_error_msg_and_die("%s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000759
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000760 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000761 * Splitting username:password pair,
762 * trying to log in
763 */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000764 str = strchr(target.user, ':');
765 if (str)
766 *(str++) = '\0';
Denis Vlasenkoc16bd212006-09-27 19:51:06 +0000767 switch (ftpcmd("USER ", target.user, sfp, buf)) {
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000768 case 230:
769 break;
770 case 331:
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000771 if (ftpcmd("PASS ", str, sfp, buf) == 230)
Eric Andersenb520e082000-10-03 00:21:45 +0000772 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000773 /* fall through (failed login) */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000774 default:
775 bb_error_msg_and_die("ftp login: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000776 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000777
Eric Andersen79757c92001-04-05 21:45:54 +0000778 ftpcmd("TYPE I", NULL, sfp, buf);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000779
Eric Andersen79757c92001-04-05 21:45:54 +0000780 /*
781 * Querying file size
782 */
Rob Landleyaf12cb32006-06-27 18:41:03 +0000783 if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
Denis Vlasenkod686a042006-11-27 14:43:21 +0000784 content_len = BB_STRTOOFF(buf+4, NULL, 10);
785 if (errno || content_len < 0) {
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000786 bb_error_msg_and_die("SIZE value is garbage");
Eric Andersen24794452004-03-06 22:11:45 +0000787 }
Eric Andersen96700832000-09-04 15:15:55 +0000788 got_clen = 1;
Eric Andersen96700832000-09-04 15:15:55 +0000789 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000790
Eric Andersen79757c92001-04-05 21:45:54 +0000791 /*
792 * Entering passive mode
793 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000794 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
795 pasv_error:
796 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
797 }
Denis Vlasenkof8c8bb12006-11-21 19:10:26 +0000798 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000799 // Server's IP is N1.N2.N3.N4 (we ignore it)
800 // Server's port for data connection is P1*256+P2
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000801 str = strrchr(buf, ')');
802 if (str) str[0] = '\0';
803 str = strrchr(buf, ',');
804 if (!str) goto pasv_error;
805 port = xatou_range(str+1, 0, 255);
806 *str = '\0';
807 str = strrchr(buf, ',');
808 if (!str) goto pasv_error;
809 port += xatou_range(str+1, 0, 255) * 256;
Denis Vlasenko5d687242007-01-12 20:59:31 +0000810 set_nport(lsa, htons(port));
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000811 dfp = open_socket(lsa);
Eric Andersen79757c92001-04-05 21:45:54 +0000812
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000813 if (beg_range) {
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000814 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000815 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000816 content_len -= beg_range;
Eric Andersen96700832000-09-04 15:15:55 +0000817 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000818
Rob Landleyaf12cb32006-06-27 18:41:03 +0000819 if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
Denis Vlasenko77105632007-09-24 15:04:00 +0000820 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
Eric Andersen96700832000-09-04 15:15:55 +0000821 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000822
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000823 if (opt & WGET_OPT_SPIDER) {
824 if (ENABLE_FEATURE_CLEAN_UP)
825 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000826 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000827 }
Eric Andersen79757c92001-04-05 21:45:54 +0000828
Eric Andersen96700832000-09-04 15:15:55 +0000829 /*
Eric Andersen79757c92001-04-05 21:45:54 +0000830 * Retrieve file
Eric Andersen96700832000-09-04 15:15:55 +0000831 */
Rob Landley19a39402006-06-13 17:10:26 +0000832
Denis Vlasenko00d84172008-11-24 07:34:42 +0000833 /* Do it before progress_meter (want to have nice error message) */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000834 if (output_fd < 0) {
835 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
836 /* compat with wget: -O FILE can overwrite */
837 if (opt & WGET_OPT_OUTNAME)
838 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
839 output_fd = xopen(fname_out, o_flags);
840 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000841
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000842 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000843 progress_meter(-1);
Rob Landley19a39402006-06-13 17:10:26 +0000844
Denis Vlasenko77105632007-09-24 15:04:00 +0000845 if (chunked)
846 goto get_clen;
847
Denis Vlasenko06783a52007-09-24 13:51:54 +0000848 /* Loops only if chunked */
849 while (1) {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000850 while (content_len > 0 || !got_clen) {
Denis Vlasenko06783a52007-09-24 13:51:54 +0000851 int n;
Denis Vlasenko3526a132006-09-09 12:20:57 +0000852 unsigned rdsz = sizeof(buf);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000853
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000854 if (content_len < sizeof(buf) && (chunked || got_clen))
855 rdsz = (unsigned)content_len;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000856 n = safe_fread(buf, rdsz, dfp);
Denis Vlasenko06783a52007-09-24 13:51:54 +0000857 if (n <= 0) {
858 if (ferror(dfp)) {
859 /* perror will not work: ferror doesn't set errno */
860 bb_error_msg_and_die(bb_msg_read_error);
861 }
Denis Vlasenko3526a132006-09-09 12:20:57 +0000862 break;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000863 }
Denis Vlasenko06783a52007-09-24 13:51:54 +0000864 xwrite(output_fd, buf, n);
Denis Vlasenko9cade082006-11-21 10:43:02 +0000865#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +0000866 transferred += n;
Eric Andersenb520e082000-10-03 00:21:45 +0000867#endif
Denis Vlasenko06783a52007-09-24 13:51:54 +0000868 if (got_clen)
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000869 content_len -= n;
Glenn L McGrath83e4a5b2003-08-28 21:55:22 +0000870 }
Eric Andersen79757c92001-04-05 21:45:54 +0000871
Denis Vlasenko06783a52007-09-24 13:51:54 +0000872 if (!chunked)
873 break;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000874
Denis Vlasenko06783a52007-09-24 13:51:54 +0000875 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
Denis Vlasenko77105632007-09-24 15:04:00 +0000876 get_clen:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000877 safe_fgets(buf, sizeof(buf), dfp);
878 content_len = STRTOOFF(buf, NULL, 16);
879 /* FIXME: error check? */
880 if (content_len == 0)
881 break; /* all done! */
882 }
Rob Landley19a39402006-06-13 17:10:26 +0000883
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000884 if (!(opt & WGET_OPT_QUIET))
Denis Vlasenko00d84172008-11-24 07:34:42 +0000885 progress_meter(0);
Rob Landley19a39402006-06-13 17:10:26 +0000886
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000887 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000888 fclose(dfp);
889 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000890 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000891 ftpcmd("QUIT", NULL, sfp, buf);
892 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000893
894 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000895}