mount: add --rbind option support
[oweals/busybox.git] / networking / wget.c
index 76bd5e2609769e940d51df632062ecc2d64b788d..c22a76b977b1a59f2393bc255cc3536868455ef4 100644 (file)
@@ -8,6 +8,32 @@
  * Copyright (C) 2010 Bradley M. Kuhn <bkuhn@ebb.org>
  * Kuhn's copyrights are licensed GPLv2-or-later.  File as a whole remains GPLv2.
  */
+
+//usage:#define wget_trivial_usage
+//usage:       IF_FEATURE_WGET_LONG_OPTIONS(
+//usage:       "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n"
+//usage:       "       [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n"
+//usage:       "       [--no-check-certificate] [-U|--user-agent AGENT]"
+//usage:                       IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
+//usage:       )
+//usage:       IF_NOT_FEATURE_WGET_LONG_OPTIONS(
+//usage:       "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]"
+//usage:                       IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
+//usage:       )
+//usage:#define wget_full_usage "\n\n"
+//usage:       "Retrieve files via HTTP or FTP\n"
+//usage:     "\nOptions:"
+//usage:     "\n       -s      Spider mode - only check file existence"
+//usage:     "\n       -c      Continue retrieval of aborted transfer"
+//usage:     "\n       -q      Quiet"
+//usage:     "\n       -P DIR  Save to DIR (default .)"
+//usage:       IF_FEATURE_WGET_TIMEOUT(
+//usage:     "\n       -T SEC  Network read timeout is SEC seconds"
+//usage:       )
+//usage:     "\n       -O FILE Save to FILE ('-' for stdout)"
+//usage:     "\n       -U STR  Use STR for User-Agent header"
+//usage:     "\n       -Y      Use proxy ('on' or 'off')"
+
 #include "libbb.h"
 
 //#define log_io(...) bb_error_msg(__VA_ARGS__)
@@ -44,6 +70,8 @@ struct globals {
 #if ENABLE_FEATURE_WGET_TIMEOUT
        unsigned timeout_seconds;
 #endif
+       int output_fd;
+       int o_flags;
        smallint chunked;         /* chunked transfer encoding */
        smallint got_clen;        /* got content-length: from server  */
        /* Local downloads do benefit from big buffer.
@@ -90,8 +118,11 @@ static void progress_meter(int flag)
        if (flag == PROGRESS_START)
                bb_progress_init(&G.pmt, G.curfile);
 
-       bb_progress_update(&G.pmt, G.beg_range, G.transferred,
-                          G.chunked ? 0 : G.beg_range + G.transferred + G.content_len);
+       bb_progress_update(&G.pmt,
+                       G.beg_range,
+                       G.transferred,
+                       (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len
+       );
 
        if (flag == PROGRESS_END) {
                bb_progress_free(&G.pmt);
@@ -140,41 +171,6 @@ static void strip_ipv6_scope_id(char *host)
        overlapping_strcpy(scope, cp);
 }
 
-#if 0 /* were needed when we used signal-driven progress bar */
-/* Read NMEMB bytes into PTR from STREAM.  Returns the number of bytes read,
- * and a short count if an eof or non-interrupt error is encountered.  */
-static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
-{
-       size_t ret;
-       char *p = (char*)ptr;
-
-       do {
-               clearerr(stream);
-               errno = 0;
-               ret = fread(p, 1, nmemb, stream);
-               p += ret;
-               nmemb -= ret;
-       } while (nmemb && ferror(stream) && errno == EINTR);
-
-       return p - (char*)ptr;
-}
-
-/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
- * Returns S, or NULL if an eof or non-interrupt error is encountered.  */
-static char *safe_fgets(char *s, int size, FILE *stream)
-{
-       char *ret;
-
-       do {
-               clearerr(stream);
-               errno = 0;
-               ret = fgets(s, size, stream);
-       } while (ret == NULL && ferror(stream) && errno == EINTR);
-
-       return ret;
-}
-#endif
-
 #if ENABLE_FEATURE_WGET_AUTHENTICATION
 /* Base64-encode character string. */
 static char *base64enc(const char *str)
@@ -465,7 +461,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
        return sfp;
 }
 
-static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
+static void NOINLINE retrieve_file_data(FILE *dfp)
 {
 #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
 # if ENABLE_FEATURE_WGET_TIMEOUT
@@ -551,7 +547,7 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
                                break; /* EOF, not error */
                        }
 
-                       xwrite(output_fd, G.wget_buf, n);
+                       xwrite(G.output_fd, G.wget_buf, n);
 
 #if ENABLE_FEATURE_WGET_STATUSBAR
                        G.transferred += n;
@@ -581,15 +577,15 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
        }
 
        /* Draw full bar and free its resources */
-       G.chunked = 0; /* makes it show 100% even for chunked download */
+       G.chunked = 0;  /* makes it show 100% even for chunked download */
+       G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */
        progress_meter(PROGRESS_END);
 }
 
-static int download_one_url(const char *url)
+static void download_one_url(const char *url)
 {
        bool use_proxy;                 /* Use proxies if env vars are set  */
        int redir_limit;
-       int output_fd;
        len_and_sockaddr *lsa;
        FILE *sfp;                      /* socket to web/ftp server         */
        FILE *dfp;                      /* socket to ftp server (data)      */
@@ -609,11 +605,9 @@ static int download_one_url(const char *url)
        use_proxy = (strcmp(G.proxy_flag, "off") != 0);
        if (use_proxy) {
                proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
-               if (proxy && proxy[0]) {
+               use_proxy = (proxy && proxy[0]);
+               if (use_proxy)
                        parse_url(proxy, &server);
-               } else {
-                       use_proxy = 0;
-               }
        }
        if (!use_proxy) {
                server.port = target.port;
@@ -629,21 +623,20 @@ static int download_one_url(const char *url)
                strip_ipv6_scope_id(target.host);
 
        /* If there was no -O FILE, guess output filename */
-       output_fd = -1;
        fname_out_alloc = NULL;
-       if (!G.fname_out) {
+       if (!(option_mask32 & WGET_OPT_OUTNAME)) {
                G.fname_out = bb_get_last_path_component_nostrip(target.path);
                /* handle "wget http://kernel.org//" */
                if (G.fname_out[0] == '/' || !G.fname_out[0])
                        G.fname_out = (char*)"index.html";
                /* -P DIR is considered only if there was no -O FILE */
-               if (G.dir_prefix)
-                       G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
-       } else {
-               if (LONE_DASH(G.fname_out)) {
-                       /* -O - */
-                       output_fd = 1;
-                       option_mask32 &= ~WGET_OPT_CONTINUE;
+               else {
+                       if (G.dir_prefix)
+                               G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
+                       else {
+                               /* redirects may free target.path later, need to make a copy */
+                               G.fname_out = fname_out_alloc = xstrdup(G.fname_out);
+                       }
                }
        }
 #if ENABLE_FEATURE_WGET_STATUSBAR
@@ -651,10 +644,11 @@ static int download_one_url(const char *url)
 #endif
 
        /* Determine where to start transfer */
+       G.beg_range = 0;
        if (option_mask32 & WGET_OPT_CONTINUE) {
-               output_fd = open(G.fname_out, O_WRONLY);
-               if (output_fd >= 0) {
-                       G.beg_range = xlseek(output_fd, 0, SEEK_END);
+               G.output_fd = open(G.fname_out, O_WRONLY);
+               if (G.output_fd >= 0) {
+                       G.beg_range = xlseek(G.output_fd, 0, SEEK_END);
                }
                /* File doesn't exist. We do not create file here yet.
                 * We are not sure it exists on remote side */
@@ -669,7 +663,9 @@ static int download_one_url(const char *url)
                free(s);
        }
  establish_session:
-       G.chunked = G.got_clen = 0;
+       /*G.content_len = 0; - redundant, got_clen = 0 is enough */
+       G.got_clen = 0;
+       G.chunked = 0;
        if (use_proxy || !target.is_ftp) {
                /*
                 *  HTTP session
@@ -840,6 +836,7 @@ However, in real world it was observed that some web servers
                                        parse_url(str, &target);
                                        if (!use_proxy) {
                                                free(server.allocated);
+                                               server.allocated = NULL;
                                                server.host = target.host;
                                                /* strip_ipv6_scope_id(target.host); - no! */
                                                /* we assume remote never gives us IPv6 addr with scope id */
@@ -865,28 +862,17 @@ However, in real world it was observed that some web servers
        }
 
        free(lsa);
-       free(server.allocated);
-       free(target.allocated);
 
-       if (option_mask32 & WGET_OPT_SPIDER) {
-               free(fname_out_alloc);
-               fclose(sfp);
-               return EXIT_SUCCESS;
-       }
-
-       if (output_fd < 0) {
-               int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
-               /* compat with wget: -O FILE can overwrite */
-               if (option_mask32 & WGET_OPT_OUTNAME)
-                       o_flags = O_WRONLY | O_CREAT | O_TRUNC;
-               output_fd = xopen(G.fname_out, o_flags);
+       if (!(option_mask32 & WGET_OPT_SPIDER)) {
+               if (G.output_fd < 0)
+                       G.output_fd = xopen(G.fname_out, G.o_flags);
+               retrieve_file_data(dfp);
+               if (!(option_mask32 & WGET_OPT_OUTNAME)) {
+                       xclose(G.output_fd);
+                       G.output_fd = -1;
+               }
        }
 
-       free(fname_out_alloc);
-
-       retrieve_file_data(dfp, output_fd);
-       xclose(output_fd);
-
        if (dfp != sfp) {
                /* It's ftp. Close data connection properly */
                fclose(dfp);
@@ -896,7 +882,9 @@ However, in real world it was observed that some web servers
        }
        fclose(sfp);
 
-       return EXIT_SUCCESS;
+       free(server.allocated);
+       free(target.allocated);
+       free(fname_out_alloc);
 }
 
 int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -927,7 +915,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                ;
 #endif
 
-       int exitcode;
 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
        llist_t *headers_llist = NULL;
 #endif
@@ -968,9 +955,22 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
        }
 #endif
 
-       exitcode = 0;
+       G.output_fd = -1;
+       G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
+       if (G.fname_out) { /* -O FILE ? */
+               if (LONE_DASH(G.fname_out)) { /* -O - ? */
+                       G.output_fd = 1;
+                       option_mask32 &= ~WGET_OPT_CONTINUE;
+               }
+               /* compat with wget: -O FILE can overwrite */
+               G.o_flags = O_WRONLY | O_CREAT | O_TRUNC;
+       }
+
        while (*argv)
-               exitcode |= download_one_url(*argv++);
+               download_one_url(*argv++);
 
-       return exitcode;
+       if (G.output_fd >= 0)
+               xclose(G.output_fd);
+
+       return EXIT_SUCCESS;
 }