blob: 26b62cc58046a9246562d60c740a5f30f4a91341 [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 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +00009#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +000010
Eric Andersen79757c92001-04-05 21:45:54 +000011struct host_info {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +000012 // May be used if we ever will want to free() all xstrdup()s...
13 /* char *allocated; */
Denis Vlasenko818322b2007-09-24 18:27:04 +000014 const char *path;
15 const char *user;
16 char *host;
17 int port;
18 smallint is_ftp;
Eric Andersen79757c92001-04-05 21:45:54 +000019};
20
Denis Vlasenko77105632007-09-24 15:04:00 +000021
22/* Globals (can be accessed from signal handlers) */
23struct globals {
24 off_t content_len; /* Content-length of the file */
25 off_t beg_range; /* Range at which continue begins */
26#if ENABLE_FEATURE_WGET_STATUSBAR
27 off_t lastsize;
28 off_t totalsize;
29 off_t transferred; /* Number of bytes transferred so far */
30 const char *curfile; /* Name of current file being transferred */
31 unsigned lastupdate_sec;
32 unsigned start_sec;
33#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +020034 smallint chunked; /* chunked transfer encoding */
35 smallint got_clen; /* got content-length: from server */
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 )
Denis Vlasenko77105632007-09-24 15:04:00 +000049#define INIT_G() do { } while (0)
50
51
Denis Vlasenko9cade082006-11-21 10:43:02 +000052#if ENABLE_FEATURE_WGET_STATUSBAR
Rob Landley19a39402006-06-13 17:10:26 +000053enum {
Denis Vlasenkof8aa1092006-10-01 10:58:54 +000054 STALLTIME = 5 /* Seconds when xfer considered "stalled" */
Rob Landley19a39402006-06-13 17:10:26 +000055};
Denis Vlasenko47ddd012007-09-24 18:24:17 +000056
Denis Vlasenko00d84172008-11-24 07:34:42 +000057static unsigned int get_tty2_width(void)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000058{
Denis Vlasenko55995022008-05-18 22:28:26 +000059 unsigned width;
Denis Vlasenko00d84172008-11-24 07:34:42 +000060 get_terminal_width_height(2, &width, NULL);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000061 return width;
62}
63
Denis Vlasenko00d84172008-11-24 07:34:42 +000064static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000065{
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000066 /* We can be called from signal handler */
67 int save_errno = errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000068 off_t abbrevsize;
69 unsigned since_last_update, elapsed;
70 unsigned ratio;
71 int barlength, i;
72
Denis Vlasenko00d84172008-11-24 07:34:42 +000073 if (flag == -1) { /* first call to progress_meter */
Denis Vlasenko47ddd012007-09-24 18:24:17 +000074 start_sec = monotonic_sec();
75 lastupdate_sec = start_sec;
76 lastsize = 0;
77 totalsize = content_len + beg_range; /* as content_len changes.. */
78 }
79
80 ratio = 100;
Denys Vlasenko7f432802009-06-28 01:02:24 +020081 if (totalsize != 0 && !G.chunked) {
Denis Vlasenko47ddd012007-09-24 18:24:17 +000082 /* long long helps to have it working even if !LFS */
83 ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
84 if (ratio > 100) ratio = 100;
85 }
86
87 fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
88
Denis Vlasenko00d84172008-11-24 07:34:42 +000089 barlength = get_tty2_width() - 49;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000090 if (barlength > 0) {
91 /* god bless gcc for variable arrays :) */
92 i = barlength * ratio / 100;
93 {
94 char buf[i+1];
95 memset(buf, '*', i);
96 buf[i] = '\0';
97 fprintf(stderr, "|%s%*s|", buf, barlength - i, "");
98 }
99 }
100 i = 0;
101 abbrevsize = transferred + beg_range;
102 while (abbrevsize >= 100000) {
103 i++;
104 abbrevsize >>= 10;
105 }
106 /* see http://en.wikipedia.org/wiki/Tera */
107 fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]);
108
109// Nuts! Ain't it easier to update progress meter ONLY when we transferred++?
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000110
111 elapsed = monotonic_sec();
112 since_last_update = elapsed - lastupdate_sec;
113 if (transferred > lastsize) {
114 lastupdate_sec = elapsed;
115 lastsize = transferred;
116 if (since_last_update >= STALLTIME) {
117 /* We "cut off" these seconds from elapsed time
118 * by adjusting start time */
119 start_sec += since_last_update;
120 }
121 since_last_update = 0; /* we are un-stalled now */
122 }
123 elapsed -= start_sec; /* now it's "elapsed since start" */
124
125 if (since_last_update >= STALLTIME) {
126 fprintf(stderr, " - stalled -");
127 } else {
128 off_t to_download = totalsize - beg_range;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200129 if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || G.chunked) {
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000130 fprintf(stderr, "--:--:-- ETA");
131 } else {
132 /* to_download / (transferred/elapsed) - elapsed: */
133 int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
134 /* (long long helps to have working ETA even if !LFS) */
135 i = eta % 3600;
136 fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
137 }
138 }
139
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000140 if (flag == 0) {
Denis Vlasenko00d84172008-11-24 07:34:42 +0000141 /* last call to progress_meter */
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000142 alarm(0);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000143 transferred = 0;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000144 fputc('\n', stderr);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000145 } else {
Denis Vlasenko00d84172008-11-24 07:34:42 +0000146 if (flag == -1) { /* first call to progress_meter */
147 signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000148 }
149 alarm(1);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000150 }
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +0000151
152 errno = save_errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000153}
154/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
155 * much of which was blatantly stolen from openssh. */
156/*-
157 * Copyright (c) 1992, 1993
158 * The Regents of the University of California. All rights reserved.
159 *
160 * Redistribution and use in source and binary forms, with or without
161 * modification, are permitted provided that the following conditions
162 * are met:
163 * 1. Redistributions of source code must retain the above copyright
164 * notice, this list of conditions and the following disclaimer.
165 * 2. Redistributions in binary form must reproduce the above copyright
166 * notice, this list of conditions and the following disclaimer in the
167 * documentation and/or other materials provided with the distribution.
168 *
169 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
170 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
171 *
172 * 4. Neither the name of the University nor the names of its contributors
173 * may be used to endorse or promote products derived from this software
174 * without specific prior written permission.
175 *
176 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
177 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
180 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
181 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
182 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
183 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
184 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
185 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
186 * SUCH DAMAGE.
187 *
188 */
189#else /* FEATURE_WGET_STATUSBAR */
190
Denis Vlasenko00d84172008-11-24 07:34:42 +0000191static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000192
Eric Andersenb520e082000-10-03 00:21:45 +0000193#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000194
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000195
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200196/* IPv6 knows scoped address types i.e. link and site local addresses. Link
197 * local addresses can have a scope identifier to specify the
198 * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
199 * identifier is only valid on a single node.
200 *
201 * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
202 * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
203 * in the Host header as invalid requests, see
204 * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
205 */
206static void strip_ipv6_scope_id(char *host)
207{
208 char *scope, *cp;
209
210 /* bbox wget actually handles IPv6 addresses without [], like
211 * wget "http://::1/xxx", but this is not standard.
212 * To save code, _here_ we do not support it. */
213
214 if (host[0] != '[')
215 return; /* not IPv6 */
216
217 scope = strchr(host, '%');
218 if (!scope)
219 return;
220
221 /* Remove the IPv6 zone identifier from the host address */
222 cp = strchr(host, ']');
223 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
224 /* malformed address (not "[xx]:nn" or "[xx]") */
225 return;
226 }
227
228 /* cp points to "]...", scope points to "%eth0]..." */
229 overlapping_strcpy(scope, cp);
230}
231
Denis Vlasenko12d21292007-06-27 21:40:07 +0000232/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
233 * and a short count if an eof or non-interrupt error is encountered. */
234static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
Matt Kraai854125f2001-05-09 19:15:46 +0000235{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000236 size_t ret;
237 char *p = (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000238
239 do {
240 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000241 errno = 0;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000242 ret = fread(p, 1, nmemb, stream);
243 p += ret;
244 nmemb -= ret;
245 } while (nmemb && ferror(stream) && errno == EINTR);
Matt Kraai854125f2001-05-09 19:15:46 +0000246
Denis Vlasenko12d21292007-06-27 21:40:07 +0000247 return p - (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000248}
249
Denis Vlasenko12d21292007-06-27 21:40:07 +0000250/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
Matt Kraai854125f2001-05-09 19:15:46 +0000251 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
252static char *safe_fgets(char *s, int size, FILE *stream)
253{
254 char *ret;
255
256 do {
257 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000258 errno = 0;
Matt Kraai854125f2001-05-09 19:15:46 +0000259 ret = fgets(s, size, stream);
260 } while (ret == NULL && ferror(stream) && errno == EINTR);
261
262 return ret;
263}
264
Denis Vlasenko9cade082006-11-21 10:43:02 +0000265#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denis Vlasenko12d21292007-06-27 21:40:07 +0000266/* Base64-encode character string. buf is assumed to be char buf[512]. */
267static char *base64enc_512(char buf[512], const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000268{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000269 unsigned len = strlen(str);
270 if (len > 512/4*3 - 10) /* paranoia */
271 len = 512/4*3 - 10;
272 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
Rob Landley76ef08c2006-06-13 16:44:26 +0000273 return buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000274}
275#endif
276
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200277static char* sanitize_string(char *s)
278{
279 unsigned char *p = (void *) s;
280 while (*p >= ' ')
281 p++;
282 *p = '\0';
283 return s;
284}
285
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000286static FILE *open_socket(len_and_sockaddr *lsa)
287{
288 FILE *fp;
289
290 /* glibc 2.4 seems to try seeking on it - ??! */
291 /* hopefully it understands what ESPIPE means... */
292 fp = fdopen(xconnect_stream(lsa), "r+");
293 if (fp == NULL)
294 bb_perror_msg_and_die("fdopen");
295
296 return fp;
297}
298
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000299static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
300{
301 int result;
302 if (s1) {
303 if (!s2) s2 = "";
304 fprintf(fp, "%s%s\r\n", s1, s2);
305 fflush(fp);
306 }
307
308 do {
309 char *buf_ptr;
310
311 if (fgets(buf, 510, fp) == NULL) {
312 bb_perror_msg_and_die("error getting response");
313 }
314 buf_ptr = strstr(buf, "\r\n");
315 if (buf_ptr) {
316 *buf_ptr = '\0';
317 }
318 } while (!isdigit(buf[0]) || buf[3] != ' ');
319
320 buf[3] = '\0';
321 result = xatoi_u(buf);
322 buf[3] = ' ';
323 return result;
324}
325
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000326static void parse_url(char *src_url, struct host_info *h)
327{
328 char *url, *p, *sp;
329
330 /* h->allocated = */ url = xstrdup(src_url);
331
332 if (strncmp(url, "http://", 7) == 0) {
333 h->port = bb_lookup_port("http", "tcp", 80);
334 h->host = url + 7;
335 h->is_ftp = 0;
336 } else if (strncmp(url, "ftp://", 6) == 0) {
337 h->port = bb_lookup_port("ftp", "tcp", 21);
338 h->host = url + 6;
339 h->is_ftp = 1;
340 } else
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200341 bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000342
343 // FYI:
344 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
345 // 'GET /?var=a/b HTTP 1.0'
346 // and saves 'index.html?var=a%2Fb' (we save 'b')
347 // wget 'http://busybox.net?login=john@doe':
348 // request: 'GET /?login=john@doe HTTP/1.0'
349 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
350 // wget 'http://busybox.net#test/test':
351 // request: 'GET / HTTP/1.0'
352 // saves: 'index.html' (we save 'test')
353 //
354 // We also don't add unique .N suffix if file exists...
355 sp = strchr(h->host, '/');
356 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
357 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
358 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000359 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000360 } else if (*sp == '/') {
361 *sp = '\0';
362 h->path = sp + 1;
363 } else { // '#' or '?'
364 // http://busybox.net?login=john@doe is a valid URL
365 // memmove converts to:
366 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000367 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000368 h->host--;
369 sp[-1] = '\0';
370 h->path = sp;
371 }
372
373 sp = strrchr(h->host, '@');
374 h->user = NULL;
375 if (sp != NULL) {
376 h->user = h->host;
377 *sp = '\0';
378 h->host = sp + 1;
379 }
380
381 sp = h->host;
382}
383
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000384static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
385{
386 char *s, *hdrval;
387 int c;
388
389 /* *istrunc = 0; */
390
391 /* retrieve header line */
392 if (fgets(buf, bufsiz, fp) == NULL)
393 return NULL;
394
395 /* see if we are at the end of the headers */
396 for (s = buf; *s == '\r'; ++s)
397 continue;
398 if (*s == '\n')
399 return NULL;
400
401 /* convert the header name to lower case */
402 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
403 *s = tolower(*s);
404
405 /* verify we are at the end of the header name */
406 if (*s != ':')
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200407 bb_error_msg_and_die("bad header line: %s", sanitize_string(buf));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000408
409 /* locate the start of the header value */
410 *s++ = '\0';
411 hdrval = skip_whitespace(s);
412
413 /* locate the end of header */
414 while (*s && *s != '\r' && *s != '\n')
415 ++s;
416
417 /* end of header found */
418 if (*s) {
419 *s = '\0';
420 return hdrval;
421 }
422
Denys Vlasenko7f432802009-06-28 01:02:24 +0200423 /* Rats! The buffer isn't big enough to hold the entire header value */
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000424 while (c = getc(fp), c != EOF && c != '\n')
425 continue;
426 /* *istrunc = 1; */
427 return hdrval;
428}
429
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000430#if ENABLE_FEATURE_WGET_LONG_OPTIONS
431static char *URL_escape(const char *str)
432{
433 /* URL encode, see RFC 2396 */
434 char *dst;
435 char *res = dst = xmalloc(strlen(str) * 3 + 1);
436 unsigned char c;
437
438 while (1) {
439 c = *str++;
440 if (c == '\0'
441 /* || strchr("!&'()*-.=_~", c) - more code */
442 || c == '!'
443 || c == '&'
444 || c == '\''
445 || c == '('
446 || c == ')'
447 || c == '*'
448 || c == '-'
449 || c == '.'
450 || c == '='
451 || c == '_'
452 || c == '~'
453 || (c >= '0' && c <= '9')
454 || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
455 ) {
456 *dst++ = c;
457 if (c == '\0')
458 return res;
459 } else {
460 *dst++ = '%';
461 *dst++ = bb_hexdigits_upcase[c >> 4];
462 *dst++ = bb_hexdigits_upcase[c & 0xf];
463 }
464 }
465}
466#endif
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000467
Denys Vlasenko7f432802009-06-28 01:02:24 +0200468static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
469{
470 char buf[512];
471 FILE *sfp;
472 char *str;
473 int port;
474
475 if (!target->user)
476 target->user = xstrdup("anonymous:busybox@");
477
478 sfp = open_socket(lsa);
479 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200480 bb_error_msg_and_die("%s", sanitize_string(buf+4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200481
482 /*
483 * Splitting username:password pair,
484 * trying to log in
485 */
486 str = strchr(target->user, ':');
487 if (str)
488 *str++ = '\0';
489 switch (ftpcmd("USER ", target->user, sfp, buf)) {
490 case 230:
491 break;
492 case 331:
493 if (ftpcmd("PASS ", str, sfp, buf) == 230)
494 break;
495 /* fall through (failed login) */
496 default:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200497 bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200498 }
499
500 ftpcmd("TYPE I", NULL, sfp, buf);
501
502 /*
503 * Querying file size
504 */
505 if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) {
506 content_len = BB_STRTOOFF(buf+4, NULL, 10);
507 if (errno || content_len < 0) {
508 bb_error_msg_and_die("SIZE value is garbage");
509 }
510 G.got_clen = 1;
511 }
512
513 /*
514 * Entering passive mode
515 */
516 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
517 pasv_error:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200518 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200519 }
520 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
521 // Server's IP is N1.N2.N3.N4 (we ignore it)
522 // Server's port for data connection is P1*256+P2
523 str = strrchr(buf, ')');
524 if (str) str[0] = '\0';
525 str = strrchr(buf, ',');
526 if (!str) goto pasv_error;
527 port = xatou_range(str+1, 0, 255);
528 *str = '\0';
529 str = strrchr(buf, ',');
530 if (!str) goto pasv_error;
531 port += xatou_range(str+1, 0, 255) * 256;
532 set_nport(lsa, htons(port));
533
534 *dfpp = open_socket(lsa);
535
536 if (beg_range) {
537 sprintf(buf, "REST %"OFF_FMT"d", beg_range);
538 if (ftpcmd(buf, NULL, sfp, buf) == 350)
539 content_len -= beg_range;
540 }
541
542 if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200543 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200544
545 return sfp;
546}
547
548/* Must match option string! */
549enum {
550 WGET_OPT_CONTINUE = (1 << 0),
551 WGET_OPT_SPIDER = (1 << 1),
552 WGET_OPT_QUIET = (1 << 2),
553 WGET_OPT_OUTNAME = (1 << 3),
554 WGET_OPT_PREFIX = (1 << 4),
555 WGET_OPT_PROXY = (1 << 5),
556 WGET_OPT_USER_AGENT = (1 << 6),
557 WGET_OPT_RETRIES = (1 << 7),
558 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
559 WGET_OPT_PASSIVE = (1 << 9),
560 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
561 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
562};
563
564static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
565{
566 char buf[512];
567
568 if (!(option_mask32 & WGET_OPT_QUIET))
569 progress_meter(-1);
570
571 if (G.chunked)
572 goto get_clen;
573
574 /* Loops only if chunked */
575 while (1) {
576 while (content_len > 0 || !G.got_clen) {
577 int n;
578 unsigned rdsz = sizeof(buf);
579
580 if (content_len < sizeof(buf) && (G.chunked || G.got_clen))
581 rdsz = (unsigned)content_len;
582 n = safe_fread(buf, rdsz, dfp);
583 if (n <= 0) {
584 if (ferror(dfp)) {
585 /* perror will not work: ferror doesn't set errno */
586 bb_error_msg_and_die(bb_msg_read_error);
587 }
588 break;
589 }
590 xwrite(output_fd, buf, n);
591#if ENABLE_FEATURE_WGET_STATUSBAR
592 transferred += n;
593#endif
594 if (G.got_clen)
595 content_len -= n;
596 }
597
598 if (!G.chunked)
599 break;
600
601 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
602 get_clen:
603 safe_fgets(buf, sizeof(buf), dfp);
604 content_len = STRTOOFF(buf, NULL, 16);
605 /* FIXME: error check? */
606 if (content_len == 0)
607 break; /* all done! */
608 }
609
610 if (!(option_mask32 & WGET_OPT_QUIET))
611 progress_meter(0);
612}
613
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000614int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000615int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000616{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000617 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000618 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000619 len_and_sockaddr *lsa;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000620 unsigned opt;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200621 int redir_limit;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200622 char *proxy = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000623 char *dir_prefix = NULL;
624#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000625 char *post_data;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000626 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000627 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000628#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200629 FILE *sfp; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000630 FILE *dfp; /* socket to ftp server (data) */
631 char *fname_out; /* where to direct output (-O) */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000632 int output_fd = -1;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200633 bool use_proxy; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000634 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000635 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000636
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000637 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000638 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000639 enum {
640 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
641 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000642#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000643 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000644 /* name, has_arg, val */
645 "continue\0" No_argument "c"
646 "spider\0" No_argument "s"
647 "quiet\0" No_argument "q"
648 "output-document\0" Required_argument "O"
649 "directory-prefix\0" Required_argument "P"
650 "proxy\0" Required_argument "Y"
651 "user-agent\0" Required_argument "U"
Denis Vlasenko50af9262009-03-02 15:08:06 +0000652 /* Ignored: */
653 // "tries\0" Required_argument "t"
654 // "timeout\0" Required_argument "T"
655 /* Ignored (we always use PASV): */
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000656 "passive-ftp\0" No_argument "\xff"
657 "header\0" Required_argument "\xfe"
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000658 "post-data\0" Required_argument "\xfd"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000659 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000660#endif
661
662 INIT_G();
663
664#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000665 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000666#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000667 /* server.allocated = target.allocated = NULL; */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000668 opt_complementary = "-1" IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000669 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000670 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000671 &proxy_flag, &user_agent,
672 NULL, /* -t RETRIES */
673 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000674 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
675 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000676 );
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000677#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000678 if (headers_llist) {
679 int size = 1;
680 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000681 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000682 while (ll) {
683 size += strlen(ll->data) + 2;
684 ll = ll->link;
685 }
686 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000687 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000688 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000689 }
690 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000691#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000692
Denys Vlasenko7f432802009-06-28 01:02:24 +0200693 /* TODO: compat issue: should handle "wget URL1 URL2..." */
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200694
Eric Andersen79757c92001-04-05 21:45:54 +0000695 parse_url(argv[optind], &target);
Eric Andersen79757c92001-04-05 21:45:54 +0000696
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000697 /* Use the proxy if necessary */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200698 use_proxy = (strcmp(proxy_flag, "off") != 0);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000699 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000700 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200701 if (proxy && proxy[0]) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000702 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000703 } else {
704 use_proxy = 0;
705 }
Robert Griebld7760112002-05-14 23:36:45 +0000706 }
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200707 if (!use_proxy) {
708 server.port = target.port;
709 if (ENABLE_FEATURE_IPV6) {
710 server.host = xstrdup(target.host);
711 } else {
712 server.host = target.host;
713 }
714 }
715
716 if (ENABLE_FEATURE_IPV6)
717 strip_ipv6_scope_id(target.host);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000718
Denis Vlasenko818322b2007-09-24 18:27:04 +0000719 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000720 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000721 fname_out = bb_get_last_path_component_nostrip(target.path);
722 /* handle "wget http://kernel.org//" */
723 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000724 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000725 /* -P DIR is considered only if there was no -O FILE */
726 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000727 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000728 } else {
729 if (LONE_DASH(fname_out)) {
730 /* -O - */
731 output_fd = 1;
732 opt &= ~WGET_OPT_CONTINUE;
733 }
Eric Andersen29edd002000-12-09 16:55:35 +0000734 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000735#if ENABLE_FEATURE_WGET_STATUSBAR
736 curfile = bb_get_last_path_component_nostrip(fname_out);
737#endif
738
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000739 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000740 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200741 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
742 */
Eric Andersen29edd002000-12-09 16:55:35 +0000743
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000744 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000745 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000746 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000747 if (output_fd >= 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +0000748 beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000749 }
750 /* File doesn't exist. We do not create file here yet.
Denys Vlasenko7f432802009-06-28 01:02:24 +0200751 * We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000752 }
753
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200754 redir_limit = 5;
755 resolve_lsa:
Denis Vlasenko42823d52007-02-04 02:39:08 +0000756 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000757 if (!(opt & WGET_OPT_QUIET)) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200758 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
759 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
760 free(s);
Eric Andersene6dc4392003-10-31 09:31:46 +0000761 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200762 establish_session:
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000763 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000764 /*
765 * HTTP session
766 */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200767 char *str;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200768 int status;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200769
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200770 /* Open socket to http server */
771 sfp = open_socket(lsa);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200772
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200773 /* Send HTTP request */
774 if (use_proxy) {
775 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
776 target.is_ftp ? "f" : "ht", target.host,
777 target.path);
778 } else {
779 if (opt & WGET_OPT_POST_DATA)
780 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
781 else
782 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
783 }
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000784
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200785 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
786 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000787
Denis Vlasenko9cade082006-11-21 10:43:02 +0000788#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200789 if (target.user) {
790 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
791 base64enc_512(buf, target.user));
792 }
793 if (use_proxy && server.user) {
794 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
795 base64enc_512(buf, server.user));
796 }
Eric Andersen79757c92001-04-05 21:45:54 +0000797#endif
798
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200799 if (beg_range)
800 fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000801#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200802 if (extra_headers)
803 fputs(extra_headers, sfp);
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000804
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200805 if (opt & WGET_OPT_POST_DATA) {
806 char *estr = URL_escape(post_data);
807 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
808 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
809 (int) strlen(estr), estr);
810 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
811 /*fprintf(sfp, "%s\r\n", estr);*/
812 free(estr);
813 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000814#endif
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200815 { /* If "Connection:" is needed, document why */
816 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
817 }
Eric Andersen79757c92001-04-05 21:45:54 +0000818
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200819 /*
820 * Retrieve HTTP response line and check for "200" status code.
821 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000822 read_response:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200823 if (fgets(buf, sizeof(buf), sfp) == NULL)
824 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000825
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200826 str = buf;
827 str = skip_non_whitespace(str);
828 str = skip_whitespace(str);
829 // FIXME: no error check
830 // xatou wouldn't work: "200 OK"
831 status = atoi(str);
832 switch (status) {
833 case 0:
834 case 100:
835 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
836 /* eat all remaining headers */;
837 goto read_response;
838 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000839/*
840Response 204 doesn't say "null file", it says "metadata
841has changed but data didn't":
842
843"10.2.5 204 No Content
844The server has fulfilled the request but does not need to return
845an entity-body, and might want to return updated metainformation.
846The response MAY include new or updated metainformation in the form
847of entity-headers, which if present SHOULD be associated with
848the requested variant.
849
850If the client is a user agent, it SHOULD NOT change its document
851view from that which caused the request to be sent. This response
852is primarily intended to allow input for actions to take place
853without causing a change to the user agent's active document view,
854although any new or updated metainformation SHOULD be applied
855to the document currently in the user agent's active view.
856
857The 204 response MUST NOT include a message-body, and thus
858is always terminated by the first empty line after the header fields."
859
860However, in real world it was observed that some web servers
861(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
862*/
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200863 case 204:
864 break;
865 case 300: /* redirection */
866 case 301:
867 case 302:
868 case 303:
869 break;
870 case 206:
871 if (beg_range)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000872 break;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200873 /* fall through */
874 default:
875 bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
876 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000877
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200878 /*
879 * Retrieve HTTP headers.
880 */
881 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
882 /* gethdr converted "FOO:" string to lowercase */
883 smalluint key = index_in_strings(keywords, buf) + 1;
884 if (key == KEY_content_length) {
885 content_len = BB_STRTOOFF(str, NULL, 10);
886 if (errno || content_len < 0) {
887 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
Eric Andersen79757c92001-04-05 21:45:54 +0000888 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200889 G.got_clen = 1;
890 continue;
891 }
892 if (key == KEY_transfer_encoding) {
893 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
894 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
895 G.chunked = G.got_clen = 1;
896 }
897 if (key == KEY_location && status >= 300) {
898 if (--redir_limit == 0)
899 bb_error_msg_and_die("too many redirections");
900 fclose(sfp);
901 G.got_clen = 0;
902 G.chunked = 0;
903 if (str[0] == '/')
904 /* free(target.allocated); */
905 target.path = /* target.allocated = */ xstrdup(str+1);
906 /* lsa stays the same: it's on the same server */
907 else {
908 parse_url(str, &target);
909 if (!use_proxy) {
910 server.host = target.host;
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200911 /* strip_ipv6_scope_id(target.host); - no! */
912 /* we assume remote never gives us IPv6 addr with scope id */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200913 server.port = target.port;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000914 free(lsa);
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200915 goto resolve_lsa;
916 } /* else: lsa stays the same: we use proxy */
Eric Andersen79757c92001-04-05 21:45:54 +0000917 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200918 goto establish_session;
Eric Andersen79757c92001-04-05 21:45:54 +0000919 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200920 }
921// if (status >= 300)
922// bb_error_msg_and_die("bad redirection (no Location: header from server)");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000923
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200924 /* For HTTP, data is pumped over the same connection */
Eric Andersen79757c92001-04-05 21:45:54 +0000925 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000926
927 } else {
Eric Andersen79757c92001-04-05 21:45:54 +0000928 /*
929 * FTP session
930 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200931 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +0000932 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000933
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000934 if (opt & WGET_OPT_SPIDER) {
935 if (ENABLE_FEATURE_CLEAN_UP)
936 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000937 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000938 }
Eric Andersen79757c92001-04-05 21:45:54 +0000939
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000940 if (output_fd < 0) {
941 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
942 /* compat with wget: -O FILE can overwrite */
943 if (opt & WGET_OPT_OUTNAME)
944 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
945 output_fd = xopen(fname_out, o_flags);
946 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000947
Denys Vlasenko7f432802009-06-28 01:02:24 +0200948 retrieve_file_data(dfp, output_fd);
Rob Landley19a39402006-06-13 17:10:26 +0000949
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200950 if (dfp != sfp) {
951 /* It's ftp. Close it properly */
Eric Andersen79757c92001-04-05 21:45:54 +0000952 fclose(dfp);
953 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200954 bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4));
Eric Andersen79757c92001-04-05 21:45:54 +0000955 ftpcmd("QUIT", NULL, sfp, buf);
956 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000957
958 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000959}