last_patch95 from vodz:
[oweals/busybox.git] / networking / wget.c
index 8261d9cb626c4a1f0718499cf631ecc7bf233e38..a9ead7fc21cd7fe9752fb8b5aa8ddab6de51d1a5 100644 (file)
 #include <arpa/inet.h>
 #include <netdb.h>
 
-#include "busybox.h"
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <getopt.h>
 
-/* Stupid libc5 doesn't define this... */
-#ifndef timersub
-#define        timersub(a, b, result)                                                \
-  do {                                                                       \
-    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                            \
-    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                         \
-    if ((result)->tv_usec < 0) {                                             \
-      --(result)->tv_sec;                                                    \
-      (result)->tv_usec += 1000000;                                          \
-    }                                                                        \
-  } while (0)
-#endif 
+#include "busybox.h"
 
 struct host_info {
        char *host;
@@ -51,12 +43,12 @@ static void parse_url(char *url, struct host_info *h);
 static FILE *open_socket(char *host, int port);
 static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
 static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);
-static void progressmeter(int flag);
 
 /* Globals (can be accessed from signal handlers */
 static off_t filesize = 0;             /* content-length of the file */
 static int chunked = 0;                        /* chunked transfer encoding */
-#ifdef BB_FEATURE_WGET_STATUSBAR
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
+static void progressmeter(int flag);
 static char *curfile;                  /* Name of current file being transferred. */
 static struct timeval start;   /* Time a transfer started. */
 static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */
@@ -72,12 +64,56 @@ static void close_and_delete_outfile(FILE* output, char *fname_out, int do_conti
        }
 }
 
+/* Read NMEMB elements of SIZE bytes into PTR from STREAM.  Returns the
+ * number of elements read, and a short count if an eof or non-interrupt
+ * error is encountered.  */
+static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       size_t ret = 0;
+
+       do {
+               clearerr(stream);
+               ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
+       } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+       return ret;
+}
+
+/* Write NMEMB elements of SIZE bytes from PTR to STREAM.  Returns the
+ * number of elements written, and a short count if an eof or non-interrupt
+ * error is encountered.  */
+static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       size_t ret = 0;
+
+       do {
+               clearerr(stream);
+               ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
+       } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+       return ret;
+}
+
+/* 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);
+               ret = fgets(s, size, stream);
+       } while (ret == NULL && ferror(stream) && errno == EINTR);
+
+       return ret;
+}
+
 #define close_delete_and_die(s...) { \
        close_and_delete_outfile(output, fname_out, do_continue); \
-       error_msg_and_die(s); }
+       bb_error_msg_and_die(s); }
 
 
-#ifdef BB_FEATURE_WGET_AUTHENTICATION
+#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
 /*
  *  Base64-encode character string
  *  oops... isn't something similar in uuencode.c?
@@ -91,7 +127,7 @@ char *base64enc(char *p, char *buf, int len) {
 
         while(*p) {
                                if (s >= buf+len-4)
-                                       error_msg_and_die("buffer overflow");
+                                       bb_error_msg_and_die("buffer overflow");
                 *(s++) = al[(*p >> 2) & 0x3F];
                 *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
                 *s = *(s+1) = '=';
@@ -110,10 +146,14 @@ int wget_main(int argc, char **argv)
 {
        int n, try=5, status;
        int port;
-       char *proxy;
+       char *proxy = 0;
+       char *dir_prefix=NULL;
        char *s, buf[512];
        struct stat sbuf;
-
+       char extra_headers[1024];
+       char *extra_headers_ptr = extra_headers;
+       int extra_headers_left = sizeof(extra_headers);
+       int which_long_opt = 0, option_index = -1;
        struct host_info server, target;
 
        FILE *sfp = NULL;                       /* socket to web/ftp server                     */
@@ -124,15 +164,31 @@ int wget_main(int argc, char **argv)
        int got_clen = 0;                       /* got content-length: from server      */
        FILE *output;                           /* socket to web server                         */
        int quiet_flag = FALSE;         /* Be verry, verry quiet...                     */
-
+       int noproxy = 0;            /* Use proxies if env vars are set  */
+
+#define LONG_HEADER    1
+#define LONG_PASSIVE   2
+
+       struct option long_options[] = {
+               { "continue",        0, NULL, 'c' },
+               { "quiet",           0, NULL, 'q' },
+               { "output-document", 1, NULL, 'O' },
+               { "header",              1, &which_long_opt, LONG_HEADER },
+               { "proxy",           1, NULL, 'Y' },
+               { "passive-ftp",     0, &which_long_opt, LONG_PASSIVE },
+               { 0,                 0, 0, 0 }
+       };
        /*
         * Crack command line.
         */
-       while ((n = getopt(argc, argv, "cqO:")) != EOF) {
+       while ((n = getopt_long(argc, argv, "cqO:P:Y:", long_options, &option_index)) != EOF) {
                switch (n) {
                case 'c':
                        ++do_continue;
                        break;
+               case 'P':
+                       dir_prefix = optarg;
+                       break;
                case 'q':
                        quiet_flag = TRUE;
                        break;
@@ -143,13 +199,36 @@ int wget_main(int argc, char **argv)
                         */
                        fname_out = optarg;
                        break;
+               case 'Y':
+                       if (strcmp(optarg, "off") == 0)
+                               noproxy=1;      
+                       break;
+               case 0:
+                       switch (which_long_opt) {
+                               case LONG_HEADER: {
+                                       int arglen = strlen(optarg);
+                                       if(extra_headers_left - arglen - 2 <= 0)
+                                               bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen);
+                                       strcpy(extra_headers_ptr, optarg);
+                                       extra_headers_ptr += arglen;
+                                       extra_headers_left -= ( arglen + 2 );
+                                       *extra_headers_ptr++ = '\r';
+                                       *extra_headers_ptr++ = '\n';
+                                       *(extra_headers_ptr + 1) = 0;
+                                       break;
+                               }
+                               case LONG_PASSIVE:
+                                       // ignore -- we always use passive mode
+                                       break;
+                       }
+                       break;
                default:
-                       show_usage();
+                       bb_show_usage();
                }
        }
 
        if (argc - optind != 1)
-                       show_usage();
+                       bb_show_usage();
 
        parse_url(argv[optind], &target);
        server.host = target.host;
@@ -158,31 +237,35 @@ int wget_main(int argc, char **argv)
        /*
         * Use the proxy if necessary.
         */
-       proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
-       if (proxy)
-               parse_url(xstrdup(proxy), &server);
+       if (!noproxy) {
+               proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+               if (proxy)
+                       parse_url(bb_xstrdup(proxy), &server);
+       }
        
        /* Guess an output filename */
        if (!fname_out) {
                fname_out = 
-#ifdef BB_FEATURE_WGET_STATUSBAR
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
                        curfile = 
 #endif
-                       get_last_path_component(target.path);
+                       bb_get_last_path_component(target.path);
                if (fname_out==NULL || strlen(fname_out)<1) {
                        fname_out = 
-#ifdef BB_FEATURE_WGET_STATUSBAR
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
                                curfile = 
 #endif
                                "index.html";
                }
-#ifdef BB_FEATURE_WGET_STATUSBAR
+               if (dir_prefix != NULL)
+                       fname_out = concat_path_file(dir_prefix, fname_out);
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
        } else {
-               curfile = get_last_path_component(fname_out);
+               curfile = bb_get_last_path_component(fname_out);
 #endif
        }
        if (do_continue && !fname_out)
-               error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
+               bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
 
 
        /*
@@ -190,8 +273,9 @@ int wget_main(int argc, char **argv)
         */
        if (strcmp(fname_out, "-") == 0) {
                output = stdout;
+               quiet_flag = TRUE;
        } else {
-               output = xfopen(fname_out, (do_continue ? "a" : "w"));
+               output = bb_xfopen(fname_out, (do_continue ? "a" : "w"));
        }
 
        /*
@@ -199,7 +283,7 @@ int wget_main(int argc, char **argv)
         */
        if (do_continue) {
                if (fstat(fileno(output), &sbuf) < 0)
-                       perror_msg_and_die("fstat()");
+                       bb_perror_msg_and_die("fstat()");
                if (sbuf.st_size > 0)
                        beg_range = sbuf.st_size;
                else
@@ -233,7 +317,7 @@ int wget_main(int argc, char **argv)
 
                        fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host);
 
-#ifdef BB_FEATURE_WGET_AUTHENTICATION
+#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
                        if (target.user) {
                                fprintf(sfp, "Authorization: Basic %s\r\n",
                                        base64enc(target.user, buf, sizeof(buf)));
@@ -246,6 +330,8 @@ int wget_main(int argc, char **argv)
 
                        if (do_continue)
                                fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range);
+                       if(extra_headers_left < sizeof(extra_headers))
+                               fputs(extra_headers,sfp);
                        fprintf(sfp,"Connection: close\r\n\r\n");
 
                        /*
@@ -300,9 +386,9 @@ read_response:              if (fgets(buf, sizeof(buf), sfp) == NULL)
                                }
                                if (strcasecmp(buf, "location") == 0) {
                                        if (s[0] == '/')
-                                               target.path = xstrdup(s+1);
+                                               target.path = bb_xstrdup(s+1);
                                        else {
-                                               parse_url(xstrdup(s), &target);
+                                               parse_url(bb_xstrdup(s), &target);
                                                if (!proxy) {
                                                        server.host = target.host;
                                                        server.port = target.port;
@@ -320,7 +406,7 @@ read_response:              if (fgets(buf, sizeof(buf), sfp) == NULL)
                 *  FTP session
                 */
                if (! target.user)
-                       target.user = xstrdup("anonymous:busybox@");
+                       target.user = bb_xstrdup("anonymous:busybox@");
 
                sfp = open_socket(server.host, server.port);
                if (ftpcmd(NULL, NULL, sfp, buf) != 220)
@@ -390,14 +476,15 @@ read_response:            if (fgets(buf, sizeof(buf), sfp) == NULL)
                fgets(buf, sizeof(buf), dfp);
                filesize = strtol(buf, (char **) NULL, 16);
        }
-#ifdef BB_FEATURE_WGET_STATUSBAR
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
        if (quiet_flag==FALSE)
                progressmeter(-1);
 #endif
        do {
-               while ((filesize > 0 || !got_clen) && (n = fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) {
-               fwrite(buf, 1, n, output);
-#ifdef BB_FEATURE_WGET_STATUSBAR
+               while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, chunked ? (filesize > sizeof(buf) ? sizeof(buf) : filesize) : sizeof(buf), dfp)) > 0) {
+                       if (safe_fwrite(buf, 1, n, output) != n)
+                               bb_perror_msg_and_die("write error");
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
                statbytes+=n;
 #endif
                if (got_clen)
@@ -405,23 +492,23 @@ read_response:            if (fgets(buf, sizeof(buf), sfp) == NULL)
        }
 
                if (chunked) {
-                       fgets(buf, sizeof(buf), dfp); /* This is a newline */
-                       fgets(buf, sizeof(buf), dfp);
+                       safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+                       safe_fgets(buf, sizeof(buf), dfp);
                        filesize = strtol(buf, (char **) NULL, 16);
                        if (filesize==0) chunked = 0; /* all done! */
                }
 
        if (n == 0 && ferror(dfp))
-               perror_msg_and_die("network read error");
+               bb_perror_msg_and_die("network read error");
        } while (chunked);
-#ifdef BB_FEATURE_WGET_STATUSBAR
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
        if (quiet_flag==FALSE)
                progressmeter(1);
 #endif
        if (!proxy && target.is_ftp) {
                fclose(dfp);
                if (ftpcmd(NULL, NULL, sfp, buf) != 226)
-                       error_msg_and_die("ftp error: %s", buf+4);
+                       bb_error_msg_and_die("ftp error: %s", buf+4);
                ftpcmd("QUIT", NULL, sfp, buf);
        }
        exit(EXIT_SUCCESS);
@@ -441,14 +528,14 @@ void parse_url(char *url, struct host_info *h)
                h->host = url + 6;
                h->is_ftp = 1;
        } else
-               error_msg_and_die("not an http or ftp url: %s", url);
+               bb_error_msg_and_die("not an http or ftp url: %s", url);
 
        sp = strchr(h->host, '/');
        if (sp != NULL) {
                *sp++ = '\0';
                h->path = sp;
        } else
-               h->path = "";
+               h->path = bb_xstrdup("");
 
        up = strrchr(h->host, '@');
        if (up != NULL) {
@@ -469,27 +556,18 @@ void parse_url(char *url, struct host_info *h)
 
 FILE *open_socket(char *host, int port)
 {
-       struct sockaddr_in s_in;
-       struct hostent *hp;
        int fd;
        FILE *fp;
+       char port_str[10];
 
-       memset(&s_in, 0, sizeof(s_in));
-       s_in.sin_family = AF_INET;
-       if ((hp = (struct hostent *) gethostbyname(host)) == NULL)
-               error_msg_and_die("cannot resolve %s", host);
-       memcpy(&s_in.sin_addr, hp->h_addr_list[0], hp->h_length);
-       s_in.sin_port = htons(port);
+       snprintf(port_str, sizeof(port_str), "%d", port);
+       fd=xconnect(host, port_str);
 
        /*
         * Get the server onto a stdio stream.
         */
-       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
-               perror_msg_and_die("socket()");
-       if (connect(fd, (struct sockaddr *) &s_in, sizeof(s_in)) < 0)
-               perror_msg_and_die("connect(%s)", host);
        if ((fp = fdopen(fd, "r+")) == NULL)
-               perror_msg_and_die("fdopen()");
+               bb_perror_msg_and_die("fdopen()");
 
        return fp;
 }
@@ -518,7 +596,7 @@ char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
 
        /* verify we are at the end of the header name */
        if (*s != ':')
-               error_msg_and_die("bad header line: %s", buf);
+               bb_error_msg_and_die("bad header line: %s", buf);
 
        /* locate the start of the header value */
        for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s)
@@ -548,20 +626,20 @@ static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)
        
        if (s1) {
                if (!s2) s2="";
-               fprintf(fp, "%s%s\n", s1, s2);
+               fprintf(fp, "%s%s\r\n", s1, s2);
                fflush(fp);
        }
        
        do {
                p = fgets(buf, 510, fp);
                if (!p)
-                       perror_msg_and_die("fgets()");
+                       bb_perror_msg_and_die("fgets()");
        } while (! isdigit(buf[0]) || buf[3] != ' ');
        
        return atoi(buf);
 }
 
-#ifdef BB_FEATURE_WGET_STATUSBAR
+#ifdef CONFIG_FEATURE_WGET_STATUSBAR
 /* Stuff below is from BSD rcp util.c, as added to openshh. 
  * Original copyright notice is retained at the end of this file.
  * 
@@ -698,7 +776,7 @@ progressmeter(int flag)
 }
 #endif
 
-/* Original copyright notice which applies to the BB_FEATURE_WGET_STATUSBAR stuff,
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
  * much of which was blatently stolen from openssh.  */
  
 /*-
@@ -733,7 +811,7 @@ progressmeter(int flag)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     $Id: wget.c,v 1.36 2001/04/17 18:13:16 markw Exp $
+ *     $Id: wget.c,v 1.54 2003/07/22 08:56:51 andersen Exp $
  */