blob: 5e387891a4c4c5a0a834a1b64ed01ff07e1f6c94 [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
Denys Vlasenko7f432802009-06-28 01:02:24 +020035 smallint chunked; /* chunked transfer encoding */
36 smallint got_clen; /* got content-length: from server */
Denis Vlasenko77105632007-09-24 15:04:00 +000037};
38#define G (*(struct globals*)&bb_common_bufsiz1)
39struct BUG_G_too_big {
Denis Vlasenko6b404432008-01-07 16:13:14 +000040 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
Denis Vlasenko77105632007-09-24 15:04:00 +000041};
42#define content_len (G.content_len )
43#define beg_range (G.beg_range )
44#define lastsize (G.lastsize )
45#define totalsize (G.totalsize )
46#define transferred (G.transferred )
47#define curfile (G.curfile )
48#define lastupdate_sec (G.lastupdate_sec )
49#define start_sec (G.start_sec )
Denis Vlasenko77105632007-09-24 15:04:00 +000050#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;
Denys Vlasenko7f432802009-06-28 01:02:24 +020082 if (totalsize != 0 && !G.chunked) {
Denis Vlasenko47ddd012007-09-24 18:24:17 +000083 /* 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;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200130 if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || G.chunked) {
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000131 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 +0000242static 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
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000255static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
256{
257 int result;
258 if (s1) {
259 if (!s2) s2 = "";
260 fprintf(fp, "%s%s\r\n", s1, s2);
261 fflush(fp);
262 }
263
264 do {
265 char *buf_ptr;
266
267 if (fgets(buf, 510, fp) == NULL) {
268 bb_perror_msg_and_die("error getting response");
269 }
270 buf_ptr = strstr(buf, "\r\n");
271 if (buf_ptr) {
272 *buf_ptr = '\0';
273 }
274 } while (!isdigit(buf[0]) || buf[3] != ' ');
275
276 buf[3] = '\0';
277 result = xatoi_u(buf);
278 buf[3] = ' ';
279 return result;
280}
281
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000282static void parse_url(char *src_url, struct host_info *h)
283{
284 char *url, *p, *sp;
285
286 /* h->allocated = */ url = xstrdup(src_url);
287
288 if (strncmp(url, "http://", 7) == 0) {
289 h->port = bb_lookup_port("http", "tcp", 80);
290 h->host = url + 7;
291 h->is_ftp = 0;
292 } else if (strncmp(url, "ftp://", 6) == 0) {
293 h->port = bb_lookup_port("ftp", "tcp", 21);
294 h->host = url + 6;
295 h->is_ftp = 1;
296 } else
297 bb_error_msg_and_die("not an http or ftp url: %s", url);
298
299 // FYI:
300 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
301 // 'GET /?var=a/b HTTP 1.0'
302 // and saves 'index.html?var=a%2Fb' (we save 'b')
303 // wget 'http://busybox.net?login=john@doe':
304 // request: 'GET /?login=john@doe HTTP/1.0'
305 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
306 // wget 'http://busybox.net#test/test':
307 // request: 'GET / HTTP/1.0'
308 // saves: 'index.html' (we save 'test')
309 //
310 // We also don't add unique .N suffix if file exists...
311 sp = strchr(h->host, '/');
312 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
313 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
314 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000315 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000316 } else if (*sp == '/') {
317 *sp = '\0';
318 h->path = sp + 1;
319 } else { // '#' or '?'
320 // http://busybox.net?login=john@doe is a valid URL
321 // memmove converts to:
322 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000323 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000324 h->host--;
325 sp[-1] = '\0';
326 h->path = sp;
327 }
328
329 sp = strrchr(h->host, '@');
330 h->user = NULL;
331 if (sp != NULL) {
332 h->user = h->host;
333 *sp = '\0';
334 h->host = sp + 1;
335 }
336
337 sp = h->host;
338}
339
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000340static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
341{
342 char *s, *hdrval;
343 int c;
344
345 /* *istrunc = 0; */
346
347 /* retrieve header line */
348 if (fgets(buf, bufsiz, fp) == NULL)
349 return NULL;
350
351 /* see if we are at the end of the headers */
352 for (s = buf; *s == '\r'; ++s)
353 continue;
354 if (*s == '\n')
355 return NULL;
356
357 /* convert the header name to lower case */
358 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
359 *s = tolower(*s);
360
361 /* verify we are at the end of the header name */
362 if (*s != ':')
363 bb_error_msg_and_die("bad header line: %s", buf);
364
365 /* locate the start of the header value */
366 *s++ = '\0';
367 hdrval = skip_whitespace(s);
368
369 /* locate the end of header */
370 while (*s && *s != '\r' && *s != '\n')
371 ++s;
372
373 /* end of header found */
374 if (*s) {
375 *s = '\0';
376 return hdrval;
377 }
378
Denys Vlasenko7f432802009-06-28 01:02:24 +0200379 /* Rats! The buffer isn't big enough to hold the entire header value */
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000380 while (c = getc(fp), c != EOF && c != '\n')
381 continue;
382 /* *istrunc = 1; */
383 return hdrval;
384}
385
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000386#if ENABLE_FEATURE_WGET_LONG_OPTIONS
387static char *URL_escape(const char *str)
388{
389 /* URL encode, see RFC 2396 */
390 char *dst;
391 char *res = dst = xmalloc(strlen(str) * 3 + 1);
392 unsigned char c;
393
394 while (1) {
395 c = *str++;
396 if (c == '\0'
397 /* || strchr("!&'()*-.=_~", c) - more code */
398 || c == '!'
399 || c == '&'
400 || c == '\''
401 || c == '('
402 || c == ')'
403 || c == '*'
404 || c == '-'
405 || c == '.'
406 || c == '='
407 || c == '_'
408 || c == '~'
409 || (c >= '0' && c <= '9')
410 || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
411 ) {
412 *dst++ = c;
413 if (c == '\0')
414 return res;
415 } else {
416 *dst++ = '%';
417 *dst++ = bb_hexdigits_upcase[c >> 4];
418 *dst++ = bb_hexdigits_upcase[c & 0xf];
419 }
420 }
421}
422#endif
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000423
Denys Vlasenko7f432802009-06-28 01:02:24 +0200424static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
425{
426 char buf[512];
427 FILE *sfp;
428 char *str;
429 int port;
430
431 if (!target->user)
432 target->user = xstrdup("anonymous:busybox@");
433
434 sfp = open_socket(lsa);
435 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
436 bb_error_msg_and_die("%s", buf+4);
437
438 /*
439 * Splitting username:password pair,
440 * trying to log in
441 */
442 str = strchr(target->user, ':');
443 if (str)
444 *str++ = '\0';
445 switch (ftpcmd("USER ", target->user, sfp, buf)) {
446 case 230:
447 break;
448 case 331:
449 if (ftpcmd("PASS ", str, sfp, buf) == 230)
450 break;
451 /* fall through (failed login) */
452 default:
453 bb_error_msg_and_die("ftp login: %s", buf+4);
454 }
455
456 ftpcmd("TYPE I", NULL, sfp, buf);
457
458 /*
459 * Querying file size
460 */
461 if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) {
462 content_len = BB_STRTOOFF(buf+4, NULL, 10);
463 if (errno || content_len < 0) {
464 bb_error_msg_and_die("SIZE value is garbage");
465 }
466 G.got_clen = 1;
467 }
468
469 /*
470 * Entering passive mode
471 */
472 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
473 pasv_error:
474 bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
475 }
476 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
477 // Server's IP is N1.N2.N3.N4 (we ignore it)
478 // Server's port for data connection is P1*256+P2
479 str = strrchr(buf, ')');
480 if (str) str[0] = '\0';
481 str = strrchr(buf, ',');
482 if (!str) goto pasv_error;
483 port = xatou_range(str+1, 0, 255);
484 *str = '\0';
485 str = strrchr(buf, ',');
486 if (!str) goto pasv_error;
487 port += xatou_range(str+1, 0, 255) * 256;
488 set_nport(lsa, htons(port));
489
490 *dfpp = open_socket(lsa);
491
492 if (beg_range) {
493 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
494 if (ftpcmd(buf, NULL, sfp, buf) == 350)
495 content_len -= beg_range;
496 }
497
498 if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
499 bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
500
501 return sfp;
502}
503
504/* Must match option string! */
505enum {
506 WGET_OPT_CONTINUE = (1 << 0),
507 WGET_OPT_SPIDER = (1 << 1),
508 WGET_OPT_QUIET = (1 << 2),
509 WGET_OPT_OUTNAME = (1 << 3),
510 WGET_OPT_PREFIX = (1 << 4),
511 WGET_OPT_PROXY = (1 << 5),
512 WGET_OPT_USER_AGENT = (1 << 6),
513 WGET_OPT_RETRIES = (1 << 7),
514 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
515 WGET_OPT_PASSIVE = (1 << 9),
516 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
517 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
518};
519
520static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
521{
522 char buf[512];
523
524 if (!(option_mask32 & WGET_OPT_QUIET))
525 progress_meter(-1);
526
527 if (G.chunked)
528 goto get_clen;
529
530 /* Loops only if chunked */
531 while (1) {
532 while (content_len > 0 || !G.got_clen) {
533 int n;
534 unsigned rdsz = sizeof(buf);
535
536 if (content_len < sizeof(buf) && (G.chunked || G.got_clen))
537 rdsz = (unsigned)content_len;
538 n = safe_fread(buf, rdsz, dfp);
539 if (n <= 0) {
540 if (ferror(dfp)) {
541 /* perror will not work: ferror doesn't set errno */
542 bb_error_msg_and_die(bb_msg_read_error);
543 }
544 break;
545 }
546 xwrite(output_fd, buf, n);
547#if ENABLE_FEATURE_WGET_STATUSBAR
548 transferred += n;
549#endif
550 if (G.got_clen)
551 content_len -= n;
552 }
553
554 if (!G.chunked)
555 break;
556
557 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
558 get_clen:
559 safe_fgets(buf, sizeof(buf), dfp);
560 content_len = STRTOOFF(buf, NULL, 16);
561 /* FIXME: error check? */
562 if (content_len == 0)
563 break; /* all done! */
564 }
565
566 if (!(option_mask32 & WGET_OPT_QUIET))
567 progress_meter(0);
568}
569
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000570int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000571int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000572{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000573 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000574 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000575 len_and_sockaddr *lsa;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000576 unsigned opt;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200577 char *proxy = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000578 char *dir_prefix = NULL;
579#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000580 char *post_data;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000581 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000582 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000583#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200584 FILE *sfp; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000585 FILE *dfp; /* socket to ftp server (data) */
586 char *fname_out; /* where to direct output (-O) */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000587 int output_fd = -1;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200588 bool use_proxy; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000589 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000590 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000591
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000592 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000593 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000594 enum {
595 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
596 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000597#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000598 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000599 /* name, has_arg, val */
600 "continue\0" No_argument "c"
601 "spider\0" No_argument "s"
602 "quiet\0" No_argument "q"
603 "output-document\0" Required_argument "O"
604 "directory-prefix\0" Required_argument "P"
605 "proxy\0" Required_argument "Y"
606 "user-agent\0" Required_argument "U"
Denis Vlasenko50af9262009-03-02 15:08:06 +0000607 /* Ignored: */
608 // "tries\0" Required_argument "t"
609 // "timeout\0" Required_argument "T"
610 /* Ignored (we always use PASV): */
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000611 "passive-ftp\0" No_argument "\xff"
612 "header\0" Required_argument "\xfe"
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000613 "post-data\0" Required_argument "\xfd"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000614 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000615#endif
616
617 INIT_G();
618
619#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000620 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000621#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000622 /* server.allocated = target.allocated = NULL; */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000623 opt_complementary = "-1" IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000624 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000625 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000626 &proxy_flag, &user_agent,
627 NULL, /* -t RETRIES */
628 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000629 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
630 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000631 );
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000632#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000633 if (headers_llist) {
634 int size = 1;
635 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000636 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000637 while (ll) {
638 size += strlen(ll->data) + 2;
639 ll = ll->link;
640 }
641 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000642 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000643 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000644 }
645 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000646#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000647
Denys Vlasenko7f432802009-06-28 01:02:24 +0200648 /* TODO: compat issue: should handle "wget URL1 URL2..." */
Eric Andersen79757c92001-04-05 21:45:54 +0000649 parse_url(argv[optind], &target);
650 server.host = target.host;
651 server.port = target.port;
652
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000653 /* Use the proxy if necessary */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200654 use_proxy = (strcmp(proxy_flag, "off") != 0);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000655 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000656 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000657 if (proxy && *proxy) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000658 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000659 } else {
660 use_proxy = 0;
661 }
Robert Griebld7760112002-05-14 23:36:45 +0000662 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000663
Denis Vlasenko818322b2007-09-24 18:27:04 +0000664 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000665 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000666 fname_out = bb_get_last_path_component_nostrip(target.path);
667 /* handle "wget http://kernel.org//" */
668 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000669 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000670 /* -P DIR is considered only if there was no -O FILE */
671 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000672 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000673 } else {
674 if (LONE_DASH(fname_out)) {
675 /* -O - */
676 output_fd = 1;
677 opt &= ~WGET_OPT_CONTINUE;
678 }
Eric Andersen29edd002000-12-09 16:55:35 +0000679 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000680#if ENABLE_FEATURE_WGET_STATUSBAR
681 curfile = bb_get_last_path_component_nostrip(fname_out);
682#endif
683
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000684 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000685 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200686 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
687 */
Eric Andersen29edd002000-12-09 16:55:35 +0000688
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000689 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000690 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000691 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000692 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000693 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000694 }
695 /* File doesn't exist. We do not create file here yet.
Denys Vlasenko7f432802009-06-28 01:02:24 +0200696 * We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000697 }
698
Eric Andersene6dc4392003-10-31 09:31:46 +0000699 /* We want to do exactly _one_ DNS lookup, since some
700 * sites (i.e. ftp.us.debian.org) use round-robin DNS
701 * and we want to connect to only one IP... */
Denis Vlasenko42823d52007-02-04 02:39:08 +0000702 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000703 if (!(opt & WGET_OPT_QUIET)) {
Denis Vlasenko85629f02007-01-22 09:36:41 +0000704 fprintf(stderr, "Connecting to %s (%s)\n", server.host,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000705 xmalloc_sockaddr2dotted(&lsa->u.sa));
Denis Vlasenko85629f02007-01-22 09:36:41 +0000706 /* We leak result of xmalloc_sockaddr2dotted */
Eric Andersene6dc4392003-10-31 09:31:46 +0000707 }
708
Denys Vlasenko7f432802009-06-28 01:02:24 +0200709 /* G.got_clen = 0; - already is */
710 sfp = NULL;
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000711 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000712 /*
713 * HTTP session
714 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200715 int status;
716 int try = 5;
717
Eric Andersen79757c92001-04-05 21:45:54 +0000718 do {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200719 char *str;
720
721 G.got_clen = 0;
722 G.chunked = 0;
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000723
Denis Vlasenko3526a132006-09-09 12:20:57 +0000724 if (!--try)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000725 bb_error_msg_and_die("too many redirections");
Eric Andersen7d697012001-01-24 20:28:35 +0000726
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000727 /* Open socket to http server */
Eric Andersen79757c92001-04-05 21:45:54 +0000728 if (sfp) fclose(sfp);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000729 sfp = open_socket(lsa);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000730
Denys Vlasenko7f432802009-06-28 01:02:24 +0200731 /* Send HTTP request */
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000732 if (use_proxy) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000733 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
Eric Andersen79757c92001-04-05 21:45:54 +0000734 target.is_ftp ? "f" : "ht", target.host,
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000735 target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000736 } else {
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000737 if (opt & WGET_OPT_POST_DATA)
738 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
739 else
740 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
Eric Andersen79757c92001-04-05 21:45:54 +0000741 }
Eric Andersen96700832000-09-04 15:15:55 +0000742
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000743 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
744 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000745
Denis Vlasenko9cade082006-11-21 10:43:02 +0000746#if ENABLE_FEATURE_WGET_AUTHENTICATION
Eric Andersen79757c92001-04-05 21:45:54 +0000747 if (target.user) {
Denis Vlasenko12d21292007-06-27 21:40:07 +0000748 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
749 base64enc_512(buf, target.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000750 }
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000751 if (use_proxy && server.user) {
Eric Andersen79757c92001-04-05 21:45:54 +0000752 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
Denis Vlasenko12d21292007-06-27 21:40:07 +0000753 base64enc_512(buf, server.user));
Eric Andersen79757c92001-04-05 21:45:54 +0000754 }
755#endif
756
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000757 if (beg_range)
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000758 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000759#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000760 if (extra_headers)
761 fputs(extra_headers, sfp);
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000762
763 if (opt & WGET_OPT_POST_DATA) {
764 char *estr = URL_escape(post_data);
765 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
766 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
767 (int) strlen(estr), estr);
768 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
769 /*fprintf(sfp, "%s\r\n", estr);*/
770 free(estr);
771 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000772#endif
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000773 { /* If "Connection:" is needed, document why */
774 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
775 }
Eric Andersen79757c92001-04-05 21:45:54 +0000776
777 /*
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000778 * Retrieve HTTP response line and check for "200" status code.
779 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000780 read_response:
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000781 if (fgets(buf, sizeof(buf), sfp) == NULL)
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000782 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000783
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000784 str = buf;
785 str = skip_non_whitespace(str);
786 str = skip_whitespace(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000787 // FIXME: no error check
788 // xatou wouldn't work: "200 OK"
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000789 status = atoi(str);
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000790 switch (status) {
791 case 0:
792 case 100:
Denis Vlasenko06783a52007-09-24 13:51:54 +0000793 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000794 /* eat all remaining headers */;
795 goto read_response;
796 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000797/*
798Response 204 doesn't say "null file", it says "metadata
799has changed but data didn't":
800
801"10.2.5 204 No Content
802The server has fulfilled the request but does not need to return
803an entity-body, and might want to return updated metainformation.
804The response MAY include new or updated metainformation in the form
805of entity-headers, which if present SHOULD be associated with
806the requested variant.
807
808If the client is a user agent, it SHOULD NOT change its document
809view from that which caused the request to be sent. This response
810is primarily intended to allow input for actions to take place
811without causing a change to the user agent's active document view,
812although any new or updated metainformation SHOULD be applied
813to the document currently in the user agent's active view.
814
815The 204 response MUST NOT include a message-body, and thus
816is always terminated by the first empty line after the header fields."
817
818However, in real world it was observed that some web servers
819(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
820*/
821 case 204:
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000822 break;
823 case 300: /* redirection */
824 case 301:
825 case 302:
826 case 303:
827 break;
828 case 206:
829 if (beg_range)
Eric Andersen79757c92001-04-05 21:45:54 +0000830 break;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000831 /* fall through */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000832 default:
Denis Vlasenko067e3f02006-11-10 23:25:53 +0000833 /* Show first line only and kill any ESC tricks */
834 buf[strcspn(buf, "\n\r\x1b")] = '\0';
835 bb_error_msg_and_die("server returned error: %s", buf);
Eric Andersen79757c92001-04-05 21:45:54 +0000836 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000837
Eric Andersen79757c92001-04-05 21:45:54 +0000838 /*
839 * Retrieve HTTP headers.
840 */
Denis Vlasenko06783a52007-09-24 13:51:54 +0000841 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200842 /* gethdr converted "FOO:" string to lowercase */
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000843 smalluint key = index_in_strings(keywords, *&buf) + 1;
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000844 if (key == KEY_content_length) {
845 content_len = BB_STRTOOFF(str, NULL, 10);
Denis Vlasenkod686a042006-11-27 14:43:21 +0000846 if (errno || content_len < 0) {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000847 bb_error_msg_and_die("content-length %s is garbage", str);
Eric Andersen24794452004-03-06 22:11:45 +0000848 }
Denys Vlasenko7f432802009-06-28 01:02:24 +0200849 G.got_clen = 1;
Eric Andersen79757c92001-04-05 21:45:54 +0000850 continue;
851 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000852 if (key == KEY_transfer_encoding) {
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000853 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
Denis Vlasenko77105632007-09-24 15:04:00 +0000854 bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200855 G.chunked = G.got_clen = 1;
Eric Andersen6d7fa432001-04-10 18:17:05 +0000856 }
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000857 if (key == KEY_location) {
858 if (str[0] == '/')
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000859 /* free(target.allocated); */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000860 target.path = /* target.allocated = */ xstrdup(str+1);
Eric Andersen79757c92001-04-05 21:45:54 +0000861 else {
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000862 parse_url(str, &target);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000863 if (use_proxy == 0) {
Eric Andersen79757c92001-04-05 21:45:54 +0000864 server.host = target.host;
865 server.port = target.port;
866 }
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000867 free(lsa);
Denis Vlasenko42823d52007-02-04 02:39:08 +0000868 lsa = xhost2sockaddr(server.host, server.port);
Glenn L McGrath58a2e0e2004-01-17 23:07:14 +0000869 break;
Eric Andersen79757c92001-04-05 21:45:54 +0000870 }
871 }
872 }
Denis Vlasenko3469c182006-12-16 22:19:47 +0000873 } while (status >= 300);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000874
Eric Andersen79757c92001-04-05 21:45:54 +0000875 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000876
877 } else {
Eric Andersen79757c92001-04-05 21:45:54 +0000878 /*
879 * FTP session
880 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200881 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +0000882 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000883
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000884 if (opt & WGET_OPT_SPIDER) {
885 if (ENABLE_FEATURE_CLEAN_UP)
886 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000887 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000888 }
Eric Andersen79757c92001-04-05 21:45:54 +0000889
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000890 if (output_fd < 0) {
891 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
892 /* compat with wget: -O FILE can overwrite */
893 if (opt & WGET_OPT_OUTNAME)
894 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
895 output_fd = xopen(fname_out, o_flags);
896 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000897
Denys Vlasenko7f432802009-06-28 01:02:24 +0200898 retrieve_file_data(dfp, output_fd);
Rob Landley19a39402006-06-13 17:10:26 +0000899
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000900 if ((use_proxy == 0) && target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000901 fclose(dfp);
902 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000903 bb_error_msg_and_die("ftp error: %s", buf+4);
Eric Andersen79757c92001-04-05 21:45:54 +0000904 ftpcmd("QUIT", NULL, sfp, buf);
905 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000906
907 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000908}