Sync libfetch and fetch with FreeBSD.
authorSascha Wildner <swildner@dragonflybsd.org>
Sun, 5 Aug 2007 21:48:12 +0000 (21:48 +0000)
committerSascha Wildner <swildner@dragonflybsd.org>
Sun, 5 Aug 2007 21:48:12 +0000 (21:48 +0000)
* Plug a memory leak in libfetch.

* First try to change the entire directory path at once. If an error
  occurs, fall back to the default method of one CWD per directory
  element.

* fetch will now show the transfer speed while fetching (better progress
  output format).

* Numerous other fixes.

13 files changed:
lib/libfetch/Makefile
lib/libfetch/common.c
lib/libfetch/common.h
lib/libfetch/fetch.3
lib/libfetch/fetch.c
lib/libfetch/fetch.h
lib/libfetch/file.c
lib/libfetch/ftp.c
lib/libfetch/ftp.errors
lib/libfetch/http.c
lib/libfetch/http.errors
usr.bin/fetch/fetch.1
usr.bin/fetch/fetch.c

index d8e4591..6358e62 100644 (file)
@@ -1,10 +1,10 @@
 # $FreeBSD: src/lib/libfetch/Makefile,v 1.14.2.5 2003/01/09 11:50:32 des Exp $
-# $DragonFly: src/lib/libfetch/Makefile,v 1.8 2005/09/06 18:55:21 dillon Exp $
+# $DragonFly: src/lib/libfetch/Makefile,v 1.9 2007/08/05 21:48:12 swildner Exp $
 
 LIB=           fetch
 WARNS?=                6
 CFLAGS+=       -I.
-CFLAGS+=       -DINET6
+CFLAGS+=       -DINET6 -DFTP_COMBINE_CWDS
 SRCS=          fetch.c common.c ftp.c http.c file.c \
                ftperr.h httperr.h
 INCS=          fetch.h
index 019d7fd..39333e6 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/common.c,v 1.7.2.13 2003/06/06 06:45:25 des Exp $
- * $DragonFly: src/lib/libfetch/common.c,v 1.3 2004/08/16 14:19:31 joerg Exp $
+ * $FreeBSD: src/lib/libfetch/common.c,v 1.50 2005/02/16 12:46:46 des Exp $
+ * $DragonFly: src/lib/libfetch/common.c,v 1.4 2007/08/05 21:48:12 swildner Exp $
  */
 
 #include <sys/param.h>
@@ -54,7 +54,9 @@
  * Error messages for resolver errors
  */
 static struct fetcherr _netdb_errlist[] = {
+#ifdef EAI_NODATA
        { EAI_NODATA,   FETCH_RESOLV,   "Host not found" },
+#endif
        { EAI_AGAIN,    FETCH_TEMP,     "Transient resolver failure" },
        { EAI_FAIL,     FETCH_RESOLV,   "Non-recoverable resolver failure" },
        { EAI_NONAME,   FETCH_RESOLV,   "No address record" },
@@ -487,7 +489,6 @@ _fetch_write(conn_t *conn, const char *buf, size_t len)
 {
        struct iovec iov;
 
-       /* This is correct, because writev doesn't change the buffer */
        iov.iov_base = __DECONST(char *, buf);
        iov.iov_len = len;
        return _fetch_writev(conn, &iov, 1);
@@ -562,7 +563,7 @@ _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
                }
                if (iovcnt > 0) {
                        iov->iov_len -= wlen;
-                       iov->iov_base = (char *)iov->iov_base + wlen;
+                       iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
                }
        }
        return (total);
@@ -579,7 +580,6 @@ _fetch_putln(conn_t *conn, const char *str, size_t len)
        int ret;
 
        DEBUG(fprintf(stderr, ">>> %s\n", str));
-       /* This is correct, because writev doesn't change the buffer */
        iov[0].iov_base = __DECONST(char *, str);
        iov[0].iov_len = len;
        iov[1].iov_base = __DECONST(char *, ENDL);
@@ -605,6 +605,7 @@ _fetch_close(conn_t *conn)
        if (--conn->ref > 0)
                return (0);
        ret = close(conn->sd);
+       free(conn->buf);
        free(conn);
        return (ret);
 }
index baf2df3..4561a86 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/common.h,v 1.7.2.10 2003/06/06 06:45:25 des Exp $
- * $DragonFly: src/lib/libfetch/common.h,v 1.2 2003/06/17 04:26:49 dillon Exp $
+ * $FreeBSD: src/lib/libfetch/common.h,v 1.28 2004/09/21 18:35:20 des Exp $
+ * $DragonFly: src/lib/libfetch/common.h,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
 #ifndef _COMMON_H_INCLUDED
index 22e497e..a35353f 100644 (file)
@@ -22,8 +22,8 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libfetch/fetch.3,v 1.11.2.29 2003/06/13 14:36:58 trhodes Exp $
-.\" $DragonFly: src/lib/libfetch/fetch.3,v 1.3 2006/05/26 19:39:38 swildner Exp $
+.\" $FreeBSD: src/lib/libfetch/fetch.3,v 1.63 2007/05/24 20:28:14 des Exp $
+.\" $DragonFly: src/lib/libfetch/fetch.3,v 1.4 2007/08/05 21:48:12 swildner Exp $
 .\"
 .Dd July 1, 1998
 .Dt FETCH 3
@@ -341,7 +341,7 @@ The
 and
 .Fn fetchPutHTTP
 functions implement the HTTP/1.1 protocol.
-With a little luck, there's
+With a little luck, there is
 even a chance that they comply with RFC2616 and RFC2617.
 .Pp
 If the
@@ -381,7 +381,7 @@ To register the authentication callback, simply set
 .Va fetchAuthMethod
 to point at it.
 The callback will be used whenever a site requires authentication and
-the appropriate environment variables aren't set.
+the appropriate environment variables are not set.
 .Pp
 This interface is experimental and may be subject to change.
 .Sh RETURN VALUES
@@ -443,7 +443,7 @@ Invalid URL
 .El
 .Pp
 The accompanying error message includes a protocol-specific error code
-and message, e.g. "File is not available (404 Not Found)"
+and message, e.g.\& "File is not available (404 Not Found)"
 .Sh ENVIRONMENT
 .Bl -tag -width ".Ev FETCH_BIND_ADDRESS"
 .It Ev FETCH_BIND_ADDRESS
@@ -616,7 +616,7 @@ library first appeared in
 The
 .Nm fetch
 library was mostly written by
-.An Dag-Erling Co\(:idan Sm\(/orgrav Aq des@FreeBSD.org
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org
 with numerous suggestions from
 .An Jordan K. Hubbard Aq jkh@FreeBSD.org ,
 .An Eugene Skepner Aq eu@qub.com
@@ -626,7 +626,7 @@ developers.
 It replaces the older
 .Nm ftpio
 library written by
-.An Poul-Henning Kamp Aq pkh@FreeBSD.org
+.An Poul-Henning Kamp Aq phk@FreeBSD.org
 and
 .An Jordan K. Hubbard Aq jkh@FreeBSD.org .
 .Pp
@@ -641,7 +641,7 @@ examples of this are
 .Fn fetchListFTP
 and FTP proxy support.
 .Pp
-There's no way to select a proxy at run-time other than setting the
+There is no way to select a proxy at run-time other than setting the
 .Ev HTTP_PROXY
 or
 .Ev FTP_PROXY
index 3a56ea8..1b5754d 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/fetch.c,v 1.10.2.14 2003/06/06 06:45:25 des Exp $
- * $DragonFly: src/lib/libfetch/fetch.c,v 1.2 2003/06/17 04:26:49 dillon Exp $
+ * $FreeBSD: src/lib/libfetch/fetch.c,v 1.38 2004/09/21 18:35:20 des Exp $
+ * $DragonFly: src/lib/libfetch/fetch.c,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
 #include <sys/param.h>
index 4a52a80..6fc5af2 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/fetch.h,v 1.10.2.8 2002/07/26 11:10:54 des Exp $
- * $DragonFly: src/lib/libfetch/fetch.h,v 1.2 2003/06/17 04:26:49 dillon Exp $
+ * $FreeBSD: src/lib/libfetch/fetch.h,v 1.26 2004/09/21 18:35:20 des Exp $
+ * $DragonFly: src/lib/libfetch/fetch.h,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
 #ifndef _FETCH_H_INCLUDED
index 2b25f6d..ed955ad 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/file.c,v 1.6.2.8 2003/02/01 18:14:32 des Exp $
- * $DragonFly: src/lib/libfetch/file.c,v 1.2 2003/06/17 04:26:49 dillon Exp $
+ * $FreeBSD: src/lib/libfetch/file.c,v 1.17 2004/09/21 18:35:20 des Exp $
+ * $DragonFly: src/lib/libfetch/file.c,v 1.3 2007/08/05 21:48:12 swildner Exp $
  */
 
 #include <sys/param.h>
index 351345d..0fa3b2d 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/ftp.c,v 1.16.2.31 2003/06/06 06:45:25 des Exp $
- * $DragonFly: src/lib/libfetch/ftp.c,v 1.3 2003/12/01 22:58:44 drhodus Exp $
+ * $FreeBSD: src/lib/libfetch/ftp.c,v 1.96 2007/04/22 22:33:29 njl Exp $
+ * $DragonFly: src/lib/libfetch/ftp.c,v 1.4 2007/08/05 21:48:12 swildner Exp $
  */
 
 /*
@@ -66,6 +66,7 @@
 #include <fcntl.h>
 #include <netdb.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -89,6 +90,9 @@
 #define FTP_EPASSIVE_MODE              229
 #define FTP_LOGGED_IN                  230
 #define FTP_FILE_ACTION_OK             250
+#define FTP_DIRECTORY_CREATED          257 /* multiple meanings */
+#define FTP_FILE_CREATED               257 /* multiple meanings */
+#define FTP_WORKING_DIRECTORY          257 /* multiple meanings */
 #define FTP_NEED_PASSWORD              331
 #define FTP_NEED_ACCOUNT               332
 #define FTP_FILE_OK                    350
@@ -198,14 +202,58 @@ _ftp_cmd(conn_t *conn, const char *fmt, ...)
  * Return a pointer to the filename part of a path
  */
 static const char *
-_ftp_filename(const char *file)
+_ftp_filename(const char *file, int *len, int *type)
 {
-       char *s;
+       const char *s;
 
        if ((s = strrchr(file, '/')) == NULL)
-               return (file);
+               s = file;
        else
-               return (s + 1);
+               s = s + 1;
+       *len = strlen(s);
+       if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) {
+               *type = s[*len - 1];
+               *len -= 7;
+       } else {
+               *type = '\0';
+       }
+       return (s);
+}
+
+/*
+ * Get current working directory from the reply to a CWD, PWD or CDUP
+ * command.
+ */
+static int
+_ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen)
+{
+       char *src, *dst, *end;
+       int q;
+
+       if (conn->err != FTP_WORKING_DIRECTORY &&
+           conn->err != FTP_FILE_ACTION_OK)
+               return (FTP_PROTOCOL_ERROR);
+       end = conn->buf + conn->buflen;
+       src = conn->buf + 4;
+       if (src >= end || *src++ != '"')
+               return (FTP_PROTOCOL_ERROR);
+       for (q = 0, dst = pwd; src < end && pwdlen--; ++src) {
+               if (!q && *src == '"')
+                       q = 1;
+               else if (q && *src != '"')
+                       break;
+               else if (q)
+                       *dst++ = '"', q = 0;
+               else
+                       *dst++ = *src;
+       }
+       if (!pwdlen)
+               return (FTP_PROTOCOL_ERROR);
+       *dst = '\0';
+#if 0
+       DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd));
+#endif
+       return (FTP_OK);
 }
 
 /*
@@ -215,21 +263,129 @@ _ftp_filename(const char *file)
 static int
 _ftp_cwd(conn_t *conn, const char *file)
 {
-       char *s;
-       int e;
+       const char *beg, *end;
+       char pwd[PATH_MAX];
+       int e, i, len;
 
-       if ((s = strrchr(file, '/')) == NULL || s == file) {
-               e = _ftp_cmd(conn, "CWD /");
-       } else {
-               e = _ftp_cmd(conn, "CWD %.*s", s - file, file);
-       }
-       if (e != FTP_FILE_ACTION_OK) {
+       /* If no slashes in name, no need to change dirs. */
+       if ((end = strrchr(file, '/')) == NULL)
+               return (0);
+       if ((e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
+           (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
                _ftp_seterr(e);
                return (-1);
        }
+       for (;;) {
+               len = strlen(pwd);
+
+               /* Look for a common prefix between PWD and dir to fetch. */
+               for (i = 0; i <= len && i <= end - file; ++i)
+                       if (pwd[i] != file[i])
+                               break;
+#if 0
+               DEBUG(fprintf(stderr, "have: [%.*s|%s]\n", i, pwd, pwd + i));
+               DEBUG(fprintf(stderr, "want: [%.*s|%s]\n", i, file, file + i));
+#endif
+               /* Keep going up a dir until we have a matching prefix. */
+               if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/'))
+                       break;
+               if ((e = _ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK ||
+                   (e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
+                   (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
+                       _ftp_seterr(e);
+                       return (-1);
+               }
+       }
+
+#ifdef FTP_COMBINE_CWDS
+       /* Skip leading slashes, even "////". */
+       for (beg = file + i; beg < end && *beg == '/'; ++beg, ++i)
+               /* nothing */ ;
+
+       /* If there is no trailing dir, we're already there. */
+       if (beg >= end)
+               return (0);
+
+       /* Change to the directory all in one chunk (e.g., foo/bar/baz). */
+       e = _ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg);
+       if (e == FTP_FILE_ACTION_OK)
+               return (0);
+#endif /* FTP_COMBINE_CWDS */
+
+       /* That didn't work so go back to legacy behavior (multiple CWDs). */
+       for (beg = file + i; beg < end; beg = file + i + 1) {
+               while (*beg == '/')
+                       ++beg, ++i;
+               for (++i; file + i < end && file[i] != '/'; ++i)
+                       /* nothing */ ;
+               e = _ftp_cmd(conn, "CWD %.*s", file + i - beg, beg);
+               if (e != FTP_FILE_ACTION_OK) {
+                       _ftp_seterr(e);
+                       return (-1);
+               }
+       }
        return (0);
 }
 
+/*
+ * Set transfer mode and data type
+ */
+static int
+_ftp_mode_type(conn_t *conn, int mode, int type)
+{
+       int e;
+
+       switch (mode) {
+       case 0:
+       case 's':
+               mode = 'S';
+       case 'S':
+               break;
+       default:
+               return (FTP_PROTOCOL_ERROR);
+       }
+       if ((e = _ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) {
+               if (mode == 'S') {
+                       /*
+                        * Stream mode is supposed to be the default - so
+                        * much so that some servers not only do not
+                        * support any other mode, but do not support the
+                        * MODE command at all.
+                        *
+                        * If "MODE S" fails, it is unlikely that we
+                        * previously succeeded in setting a different
+                        * mode.  Therefore, we simply hope that the
+                        * server is already in the correct mode, and
+                        * silently ignore the failure.
+                        */
+               } else {
+                       return (e);
+               }
+       }
+
+       switch (type) {
+       case 0:
+       case 'i':
+               type = 'I';
+       case 'I':
+               break;
+       case 'a':
+               type = 'A';
+       case 'A':
+               break;
+       case 'd':
+               type = 'D';
+       case 'D':
+               /* can't handle yet */
+       default:
+               return (FTP_PROTOCOL_ERROR);
+       }
+       if ((e = _ftp_cmd(conn, "TYPE %c", type)) != FTP_OK)
+               return (e);
+
+       return (FTP_OK);
+}
+
 /*
  * Request and parse file stats
  */
@@ -237,7 +393,8 @@ static int
 _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
 {
        char *ln;
-       const char *s;
+       const char *filename;
+       int filenamelen, type;
        struct tm tm;
        time_t t;
        int e;
@@ -245,12 +402,15 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
        us->size = -1;
        us->atime = us->mtime = 0;
 
-       if ((s = strrchr(file, '/')) == NULL)
-               s = file;
-       else
-               ++s;
+       filename = _ftp_filename(file, &filenamelen, &type);
+
+       if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK) {
+               _ftp_seterr(e);
+               return (-1);
+       }
 
-       if ((e = _ftp_cmd(conn, "SIZE %s", s)) != FTP_FILE_STATUS) {
+       e = _ftp_cmd(conn, "SIZE %.*s", filenamelen, filename);
+       if (e != FTP_FILE_STATUS) {
                _ftp_seterr(e);
                return (-1);
        }
@@ -267,7 +427,8 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
                us->size = -1;
        DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size));
 
-       if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) {
+       e = _ftp_cmd(conn, "MDTM %.*s", filenamelen, filename);
+       if (e != FTP_FILE_STATUS) {
                _ftp_seterr(e);
                return (-1);
        }
@@ -455,6 +616,9 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
        struct sockaddr_storage sa;
        struct sockaddr_in6 *sin6;
        struct sockaddr_in *sin4;
+       const char *bindaddr;
+       const char *filename;
+       int filenamelen, type;
        int low, pasv, verbose;
        int e, sd = -1;
        socklen_t l;
@@ -471,6 +635,13 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
                pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL &&
                    strncasecmp(s, "no", 2) != 0);
 
+       /* isolate filename */
+       filename = _ftp_filename(file, &filenamelen, &type);
+
+       /* set transfer mode and data type */
+       if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK)
+               goto ouch;
+
        /* find our own address, bind, and listen */
        l = sizeof(sa);
        if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1)
@@ -590,13 +761,17 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
                /* connect to data port */
                if (verbose)
                        _fetch_info("opening data connection");
+               bindaddr = getenv("FETCH_BIND_ADDRESS");
+               if (bindaddr != NULL && *bindaddr != '\0' &&
+                   _fetch_bind(sd, sa.ss_family, bindaddr) != 0)
+                       goto sysouch;
                if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
                        goto sysouch;
 
                /* make the server initiate the transfer */
                if (verbose)
                        _fetch_info("initiating transfer");
-               e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file));
+               e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
                if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
                        goto ouch;
 
@@ -681,15 +856,14 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file,
 
                /* seek to required offset */
                if (offset)
-                       if (_ftp_cmd(conn, "REST %llu",
-                           (unsigned long long)offset) != FTP_FILE_OK)
+                       if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK)
                                goto sysouch;
 
                /* make the server initiate the transfer */
                if (verbose)
                        _fetch_info("initiating transfer");
-               e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file));
-               if (e != FTP_OPEN_DATA_CONNECTION)
+               e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
+               if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
                        goto ouch;
 
                /* accept the incoming connection and go to town */
@@ -812,13 +986,7 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags)
        if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
                goto fouch;
 
-       /* might as well select mode and type at once */
-#ifdef FTP_FORCE_STREAM_MODE
-       if ((e = _ftp_cmd(conn, "MODE S")) != FTP_OK) /* default is S */
-               goto fouch;
-#endif
-       if ((e = _ftp_cmd(conn, "TYPE I")) != FTP_OK) /* default is A */
-               goto fouch;
+       /* TODO: Request extended features supported, if any (RFC 3659). */
 
        /* done */
        return (conn);
index 6e646b4..4a26ef0 100644 (file)
@@ -1,5 +1,5 @@
-# $FreeBSD: src/lib/libfetch/ftp.errors,v 1.4.2.3 2002/11/27 15:41:24 des Exp $
-# $DragonFly: src/lib/libfetch/ftp.errors,v 1.2 2003/06/17 04:26:49 dillon Exp $
+# $FreeBSD: src/lib/libfetch/ftp.errors,v 1.6 2002/10/30 06:06:16 des Exp $
+# $DragonFly: src/lib/libfetch/ftp.errors,v 1.3 2007/08/05 21:48:12 swildner Exp $
 #
 # This list is taken from RFC 959.
 # It probably needs a going over.
index c6bfba6..f9ce043 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libfetch/http.c,v 1.13.2.23 2003/06/06 06:45:25 des Exp $
- * $DragonFly: src/lib/libfetch/http.c,v 1.3 2005/03/02 05:15:13 joerg Exp $
+ * $FreeBSD: src/lib/libfetch/http.c,v 1.78 2007/05/08 19:28:03 des Exp $
+ * $DragonFly: src/lib/libfetch/http.c,v 1.4 2007/08/05 21:48:12 swildner Exp $
  */
 
 /*
@@ -76,6 +76,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
 #include "fetch.h"
 #include "common.h"
 #include "httperr.h"
 #define HTTP_MOVED_PERM                301
 #define HTTP_MOVED_TEMP                302
 #define HTTP_SEE_OTHER         303
+#define HTTP_TEMP_REDIRECT     307
 #define HTTP_NEED_AUTH         401
 #define HTTP_NEED_PROXY_AUTH   407
+#define HTTP_BAD_RANGE         416
 #define HTTP_PROTOCOL_ERROR    999
 
 #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \
                            || (xyz) == HTTP_MOVED_TEMP \
+                           || (xyz) == HTTP_TEMP_REDIRECT \
                            || (xyz) == HTTP_SEE_OTHER)
 
 #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)
@@ -513,22 +519,34 @@ _http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
 
        if (strncasecmp(p, "bytes ", 6) != 0)
                return (-1);
-       for (first = 0, p += 6; *p && isdigit(*p); ++p)
-               first = first * 10 + *p - '0';
-       if (*p != '-')
-               return (-1);
-       for (last = 0, ++p; *p && isdigit(*p); ++p)
-               last = last * 10 + *p - '0';
+       p += 6;
+       if (*p == '*') {
+               first = last = -1;
+               ++p;
+       } else {
+               for (first = 0; *p && isdigit(*p); ++p)
+                       first = first * 10 + *p - '0';
+               if (*p != '-')
+                       return (-1);
+               for (last = 0, ++p; *p && isdigit(*p); ++p)
+                       last = last * 10 + *p - '0';
+       }
        if (first > last || *p != '/')
                return (-1);
        for (len = 0, ++p; *p && isdigit(*p); ++p)
                len = len * 10 + *p - '0';
        if (*p || len < last - first + 1)
                return (-1);
-       DEBUG(fprintf(stderr, "content range: [%lld-%lld/%lld]\n",
-           (long long)first, (long long)last, (long long)len));
+       if (first == -1) {
+               DEBUG(fprintf(stderr, "content range: [*/%lld]\n",
+                   (long long)len));
+               *length = 0;
+       } else {
+               DEBUG(fprintf(stderr, "content range: [%lld-%lld/%lld]\n",
+                   (long long)first, (long long)last, (long long)len));
+               *length = last - first + 1;
+       }
        *offset = first;
-       *length = last - first + 1;
        *size = len;
        return (0);
 }
@@ -553,7 +571,7 @@ _http_base64(const char *src)
        int t, r;
 
        l = strlen(src);
-       if ((str = malloc(((l + 2) / 3) * 4)) == NULL)
+       if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL)
                return (NULL);
        dst = str;
        r = 0;
@@ -657,7 +675,7 @@ _http_connect(struct url *URL, struct url *purl, const char *flags)
 {
        conn_t *conn;
        int verbose;
-       int af;
+       int af, val;
 
 #ifdef INET6
        af = AF_UNSPEC;
@@ -692,6 +710,10 @@ _http_connect(struct url *URL, struct url *purl, const char *flags)
                _fetch_syserr();
                return (NULL);
        }
+
+       val = 1;
+       setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val));
+
        return (conn);
 }
 
@@ -704,7 +726,7 @@ _http_get_proxy(const char *flags)
        if (flags != NULL && strchr(flags, 'd') != NULL)
                return (NULL);
        if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
-           (purl = fetchParseURL(p))) {
+           *p && (purl = fetchParseURL(p))) {
                if (!*purl->scheme)
                        strcpy(purl->scheme, SCHEME_HTTP);
                if (!purl->port)
@@ -772,7 +794,7 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
        conn_t *conn;
        struct url *url, *new;
        int chunked, direct, need_auth, noredirect, verbose;
-       int e, i, n;
+       int e, i, n, val;
        off_t offset, clength, length, size;
        time_t mtime;
        const char *p;
@@ -894,6 +916,20 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
                _http_cmd(conn, "Connection: close");
                _http_cmd(conn, "");
 
+               /*
+                * Force the queued request to be dispatched.  Normally, one
+                * would do this with shutdown(2) but squid proxies can be
+                * configured to disallow such half-closed connections.  To
+                * be compatible with such configurations, fiddle with socket
+                * options to force the pending data to be written.
+                */
+               val = 0;
+               setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val,
+                          sizeof(val));
+               val = 1;
+               setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val,
+                          sizeof(val));
+
                /* get reply */
                switch (_http_get_reply(conn)) {
                case HTTP_OK:
@@ -904,15 +940,15 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
                case HTTP_MOVED_TEMP:
                case HTTP_SEE_OTHER:
                        /*
-                        * Not so fine, but we still have to read the headers to
-                        * get the new location.
+                        * Not so fine, but we still have to read the
+                        * headers to get the new location.
                         */
                        break;
                case HTTP_NEED_AUTH:
                        if (need_auth) {
                                /*
-                                * We already sent out authorization code, so there's
-                                * nothing more we can do.
+                                * We already sent out authorization code,
+                                * so there's nothing more we can do.
                                 */
                                _http_seterr(conn->err);
                                goto ouch;
@@ -923,11 +959,19 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
                        break;
                case HTTP_NEED_PROXY_AUTH:
                        /*
-                        * If we're talking to a proxy, we already sent our proxy
-                        * authorization code, so there's nothing more we can do.
+                        * If we're talking to a proxy, we already sent
+                        * our proxy authorization code, so there's
+                        * nothing more we can do.
                         */
                        _http_seterr(conn->err);
                        goto ouch;
+               case HTTP_BAD_RANGE:
+                       /*
+                        * This can happen if we ask for 0 bytes because
+                        * we already have the whole file.  Consider this
+                        * a success for now, and check sizes later.
+                        */
+                       break;
                case HTTP_PROTOCOL_ERROR:
                        /* fall through */
                case -1:
@@ -1009,6 +1053,19 @@ _http_request(struct url *URL, const char *op, struct url_stat *us,
                        continue;
                }
 
+               /* requested range not satisfiable */
+               if (conn->err == HTTP_BAD_RANGE) {
+                       if (url->offset == size && url->length == 0) {
+                               /* asked for 0 bytes; fake it */
+                               offset = url->offset;
+                               conn->err = HTTP_OK;
+                               break;
+                       } else {
+                               _http_seterr(conn->err);
+                               goto ouch;
+                       }
+               }
+
                /* we have a hit or an error */
                if (conn->err == HTTP_OK || conn->err == HTTP_PARTIAL || HTTP_ERROR(conn->err))
                        break;
index 9869d90..bb6a461 100644 (file)
@@ -1,5 +1,5 @@
-# $FreeBSD: src/lib/libfetch/http.errors,v 1.4.2.1 2001/05/26 17:31:50 des Exp $
-# $DragonFly: src/lib/libfetch/http.errors,v 1.2 2003/06/17 04:26:49 dillon Exp $
+# $FreeBSD: src/lib/libfetch/http.errors,v 1.5 2001/05/23 18:52:02 des Exp $
+# $DragonFly: src/lib/libfetch/http.errors,v 1.3 2007/08/05 21:48:12 swildner Exp $
 #
 # This list is taken from RFC 2068.
 #
index 7442459..68d4a17 100644 (file)
@@ -1,5 +1,5 @@
 .\"-
-.\" Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
+.\" Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav
 .\" All rights reserved.
 .\" Portions Copyright (c) 1999 Massachusetts Institute of Technology; used
 .\" by permission.
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\"      $FreeBSD: src/usr.bin/fetch/fetch.1,v 1.33.2.12 2003/06/06 06:48:42 des Exp $
-.\"      $DragonFly: src/usr.bin/fetch/fetch.1,v 1.3 2006/02/17 19:39:03 swildner Exp $
+.\"      $FreeBSD: src/usr.bin/fetch/fetch.1,v 1.67 2006/04/22 03:04:24 jkoshy Exp $
+.\"      $DragonFly: src/usr.bin/fetch/fetch.1,v 1.4 2007/08/05 21:48:12 swildner Exp $
 .\"
-.Dd June 28, 2000
+.Dd August 5, 2007
 .Dt FETCH 1
 .Os
 .Sh NAME
@@ -134,7 +134,7 @@ See
 for a description of the file format.
 This feature is experimental.
 .It Fl n
-Don't preserve the modification time of the transferred file.
+Do not preserve the modification time of the transferred file.
 .It Fl o Ar file
 Set the output file name to
 .Ar file .
@@ -145,6 +145,10 @@ A
 argument of
 .Sq Li \&-
 indicates that results are to be directed to the standard output.
+If the
+.Ar file
+argument is a directory, fetched file(s) will be placed within the
+directory, with name(s) selected as in the default behaviour.
 .It Fl P
 .It Fl p
 Use passive FTP.
@@ -209,28 +213,28 @@ message.
 .Sh ENVIRONMENT
 .Bl -tag -width HTTP_TIMEOUT
 .It Ev FTP_TIMEOUT
-maximum time, in seconds, to wait before aborting an
-.Tn FTP
-connection.
+maximum time, in seconds, to wait before aborting an FTP connection.
 .It Ev HTTP_TIMEOUT
-maximum time, in seconds, to wait before aborting an
-.Tn HTTP
-connection.
+maximum time, in seconds, to wait before aborting an HTTP connection.
 .El
 .Pp
 All environment variables mentioned in the documentation for the
 .Xr fetch 3
 library are supported.
-.Sh DIAGNOSTICS
+A number of these are quite important to the proper operation of
+.Nm ;
+you are strongly encouraged to read
+.Xr fetch 3
+as well.
+.Sh EXIT STATUS
 The
 .Nm
 command returns zero on success, or one on failure.
 If multiple URLs are listed on the command line,
 .Nm
-will attempt to retrieve them each of them in turn, and return zero
-only if they were all successfully retrieved.
+will attempt to retrieve each one of them in turn, and will return
+zero only if they were all successfully retrieved.
 .Sh SEE ALSO
-.Xr sh 1 ,
 .Xr fetch 3
 .Sh HISTORY
 The
@@ -244,15 +248,15 @@ This implementation first appeared in
 The original implementation of
 .Nm
 was done by
-.An Jean-Marc Zucconi .
+.An Jean-Marc Zucconi Aq jmz@FreeBSD.org .
 It was extensively re-worked for
 .Fx 2.2
 by
-.An Garrett Wollman ,
+.An Garrett Wollman Aq wollman@FreeBSD.org ,
 and later completely rewritten to use the
 .Xr fetch 3
 library by
-.An Dag-Erling Sm\(/orgrav .
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
 .Sh NOTES
 The
 .Fl b
index d59f07a..817219f 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
+ * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,8 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.10.2.21 2003/06/06 06:48:42 des Exp $
- * $DragonFly: src/usr.bin/fetch/fetch.c,v 1.7 2006/03/06 03:21:26 swildner Exp $
+ * $FreeBSD: src/usr.bin/fetch/fetch.c,v 1.78 2006/11/10 22:05:41 des Exp $
+ * $DragonFly: src/usr.bin/fetch/fetch.c,v 1.8 2007/08/05 21:48:12 swildner Exp $
  */
 
 #include <sys/param.h>
@@ -38,6 +38,7 @@
 #include <err.h>
 #include <errno.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -88,7 +89,7 @@ int    sigint;        /* SIGINT received */
 
 long    ftp_timeout;   /* default timeout for FTP transfers */
 long    http_timeout;  /* default timeout for HTTP transfers */
-u_char *buf;           /* transfer buffer */
+char   *buf;           /* transfer buffer */
 
 
 /*
@@ -111,7 +112,7 @@ sig_handler(int sig)
 }
 
 struct xferstat {
-       char             name[40];
+       char             name[64];
        struct timeval   start;
        struct timeval   last;
        off_t            size;
@@ -122,43 +123,62 @@ struct xferstat {
 /*
  * Compute and display ETA
  */
-static void
+static const char *
 stat_eta(struct xferstat *xs)
 {
-       long elapsed, received, expected, eta;
+       static char str[16];
+       long elapsed, eta;
+       off_t received, expected;
 
        elapsed = xs->last.tv_sec - xs->start.tv_sec;
        received = xs->rcvd - xs->offset;
        expected = xs->size - xs->rcvd;
        eta = (long)((double)elapsed * expected / received);
-       if (eta > 3600) {
-               fprintf(stderr, "%02ld:", eta / 3600);
-               eta %= 3600;
+       if (eta > 3600)
+               snprintf(str, sizeof str, "%02ldh%02ldm",
+                   eta / 3600, (eta % 3600) / 60);
+       else
+               snprintf(str, sizeof str, "%02ldm%02lds",
+                   eta / 60, eta % 60);
+       return (str);
+}
+
+/*
+ * Format a number as "xxxx YB" where Y is ' ', 'k', 'M'...
+ */
+static const char *prefixes = " kMGTP";
+static const char *
+stat_bytes(off_t bytes)
+{
+       static char str[16];
+       const char *prefix = prefixes;
+
+       while (bytes > 9999 && prefix[1] != '\0') {
+               bytes /= 1024;
+               prefix++;
        }
-       fprintf(stderr, "%02ld:%02ld", eta / 60, eta % 60);
+       snprintf(str, sizeof str, "%4jd %cB", (intmax_t)bytes, *prefix);
+       return (str);
 }
 
 /*
  * Compute and display transfer rate
  */
-static void
+static const char *
 stat_bps(struct xferstat *xs)
 {
+       static char str[16];
        double delta, bps;
 
        delta = (xs->last.tv_sec + (xs->last.tv_usec / 1.e6))
            - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6));
        if (delta == 0.0) {
-               fprintf(stderr, "?? Bps");
-               return;
+               snprintf(str, sizeof str, "?? Bps");
+       } else {
+               bps = (xs->rcvd - xs->offset) / delta;
+               snprintf(str, sizeof str, "%sps", stat_bytes((off_t)bps));
        }
-       bps = (xs->rcvd - xs->offset) / delta;
-       if (bps > 1024*1024)
-               fprintf(stderr, "%.2f MBps", bps / (1024*1024));
-       else if (bps > 1024)
-               fprintf(stderr, "%.2f kBps", bps / 1024);
-       else
-               fprintf(stderr, "%.2f Bps", bps);
+       return (str);
 }
 
 /*
@@ -170,9 +190,6 @@ stat_display(struct xferstat *xs, int force)
        struct timeval now;
        int ctty_pgrp;
 
-       if (!v_tty || !v_level)
-               return;
-
        /* check if we're the foreground process */
        if (ioctl(STDERR_FILENO, TIOCGPGRP, &ctty_pgrp) == -1 ||
            (pid_t)ctty_pgrp != pgrp)
@@ -183,22 +200,22 @@ stat_display(struct xferstat *xs, int force)
                return;
        xs->last = now;
 
-       fprintf(stderr, "\rReceiving %s", xs->name);
+       fprintf(stderr, "\r%-46.46s", xs->name);
        if (xs->size <= 0) {
-               fprintf(stderr, ": %lld bytes", (long long)xs->rcvd);
+               setproctitle("%s [%s]", xs->name, stat_bytes(xs->rcvd));
+               fprintf(stderr, "        %s", stat_bytes(xs->rcvd));
        } else {
-               fprintf(stderr, " (%lld bytes): %d%%", (long long)xs->size,
-                   (int)((100.0 * xs->rcvd) / xs->size));
-               if (xs->rcvd > 0 && xs->last.tv_sec >= xs->start.tv_sec + 30) {
-                       fprintf(stderr, " (ETA ");
-                       stat_eta(xs);
-                       if (v_level > 1) {
-                               fprintf(stderr, " at ");
-                               stat_bps(xs);
-                       }
-                       fprintf(stderr, ")  ");
-               }
-       }
+               setproctitle("%s [%d%% of %s]", xs->name,
+                   (int)((100.0 * xs->rcvd) / xs->size),
+                   stat_bytes(xs->size));
+               fprintf(stderr, "%3d%% of %s",
+                   (int)((100.0 * xs->rcvd) / xs->size),
+                   stat_bytes(xs->size));
+       }
+       fprintf(stderr, " %s", stat_bps(xs));
+       if (xs->size > 0 && xs->rcvd > 0 &&
+           xs->last.tv_sec >= xs->start.tv_sec + 10)
+               fprintf(stderr, " %s", stat_eta(xs));
 }
 
 /*
@@ -213,7 +230,10 @@ stat_start(struct xferstat *xs, const char *name, off_t size, off_t offset)
        xs->size = size;
        xs->offset = offset;
        xs->rcvd = offset;
-       stat_display(xs, 1);
+       if (v_tty && v_level > 0)
+               stat_display(xs, 1);
+       else if (v_level > 0)
+               fprintf(stderr, "%-46s", xs->name);
 }
 
 /*
@@ -223,7 +243,8 @@ static void
 stat_update(struct xferstat *xs, off_t rcvd)
 {
        xs->rcvd = rcvd;
-       stat_display(xs, 0);
+       if (v_tty && v_level > 0)
+               stat_display(xs, 0);
 }
 
 /*
@@ -232,21 +253,14 @@ stat_update(struct xferstat *xs, off_t rcvd)
 static void
 stat_end(struct xferstat *xs)
 {
-       double delta;
-
-       if (!v_level)
-               return;
-
        gettimeofday(&xs->last, NULL);
-
-       stat_display(xs, 1);
-       fputc('\n', stderr);
-       delta = (xs->last.tv_sec + (xs->last.tv_usec / 1.e6))
-           - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6));
-       fprintf(stderr, "%lld bytes transferred in %.1f seconds (",
-           (long long)(xs->rcvd - xs->offset), delta);
-       stat_bps(xs);
-       fprintf(stderr, ")\n");
+       if (v_tty && v_level > 0) {
+               stat_display(xs, 1);
+               putc('\n', stderr);
+       } else if (v_level > 0) {
+               fprintf(stderr, "        %s %s\n",
+                   stat_bytes(xs->size), stat_bps(xs));
+       }
 }
 
 /*
@@ -259,15 +273,14 @@ query_auth(struct url *URL)
        tcflag_t saved_flags;
        int i, nopwd;
 
-
        fprintf(stderr, "Authentication required for <%s://%s:%d/>!\n",
            URL->scheme, URL->host, URL->port);
 
        fprintf(stderr, "Login: ");
        if (fgets(URL->user, sizeof URL->user, stdin) == NULL)
-               return -1;
-       for (i = 0; URL->user[i]; ++i)
-               if (isspace(URL->user[i]))
+               return (-1);
+       for (i = strlen(URL->user); i >= 0; --i)
+               if (URL->user[i] == '\r' || URL->user[i] == '\n')
                        URL->user[i] = '\0';
 
        fprintf(stderr, "Password: ");
@@ -283,12 +296,12 @@ query_auth(struct url *URL)
                nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL);
        }
        if (nopwd)
-               return -1;
-
-       for (i = 0; URL->pwd[i]; ++i)
-               if (isspace(URL->pwd[i]))
+               return (-1);
+       for (i = strlen(URL->pwd); i >= 0; --i)
+               if (URL->pwd[i] == '\r' || URL->pwd[i] == '\n')
                        URL->pwd[i] = '\0';
-       return 0;
+
+       return (0);
 }
 
 /*
@@ -308,8 +321,8 @@ fetch(char *URL, const char *path)
        const char *slash;
        char *tmppath;
        int r;
-       u_int timeout;
-       u_char *ptr;
+       unsigned timeout;
+       char *ptr;
 
        f = of = NULL;
        tmppath = NULL;
@@ -389,7 +402,7 @@ fetch(char *URL, const char *path)
                if (us.size == -1)
                        printf("Unknown\n");
                else
-                       printf("%lld\n", (long long)us.size);
+                       printf("%jd\n", (intmax_t)us.size);
                goto success;
        }
 
@@ -407,12 +420,23 @@ fetch(char *URL, const char *path)
         * the connection later if we change our minds.
         */
        sb.st_size = -1;
-       if (!o_stdout && stat(path, &sb) == -1 && errno != ENOENT) {
-               warnx("%s: stat()", path);
-               goto failure;
+       if (!o_stdout) {
+               r = stat(path, &sb);
+               if (r == 0 && r_flag && S_ISREG(sb.st_mode)) {
+                       url->offset = sb.st_size;
+               } else if (r == -1 || !S_ISREG(sb.st_mode)) {
+                       /*
+                        * Whatever value sb.st_size has now is either
+                        * wrong (if stat(2) failed) or irrelevant (if the
+                        * path does not refer to a regular file)
+                        */
+                       sb.st_size = -1;
+               }
+               if (r == -1 && errno != ENOENT) {
+                       warnx("%s: stat()", path);
+                       goto failure;
+               }
        }
-       if (!o_stdout && r_flag && S_ISREG(sb.st_mode))
-               url->offset = sb.st_size;
 
        /* start the transfer */
        if (timeout)
@@ -433,10 +457,9 @@ fetch(char *URL, const char *path)
        if (S_size) {
                if (us.size == -1) {
                        warnx("%s: size unknown", URL);
-                       goto failure;
                } else if (us.size != S_size) {
-                       warnx("%s: size mismatch: expected %lld, actual %lld",
-                           URL, (long long)S_size, (long long)us.size);
+                       warnx("%s: size mismatch: expected %jd, actual %jd",
+                           URL, (intmax_t)S_size, (intmax_t)us.size);
                        goto failure;
                }
        }
@@ -454,11 +477,11 @@ fetch(char *URL, const char *path)
                warnx("%s: size of remote file is not known", URL);
        if (v_level > 1) {
                if (sb.st_size != -1)
-                       fprintf(stderr, "local size / mtime: %lld / %ld\n",
-                           (long long)sb.st_size, (long)sb.st_mtime);
+                       fprintf(stderr, "local size / mtime: %jd / %ld\n",
+                           (intmax_t)sb.st_size, (long)sb.st_mtime);
                if (us.size != -1)
-                       fprintf(stderr, "remote size / mtime: %lld / %ld\n",
-                           (long long)us.size, (long)us.mtime);
+                       fprintf(stderr, "remote size / mtime: %jd / %ld\n",
+                           (intmax_t)us.size, (long)us.mtime);
        }
 
        /* open output file */
@@ -476,15 +499,15 @@ fetch(char *URL, const char *path)
                                    "does not match remote", path);
                                goto failure_keep;
                        }
-               } else {
+               } else if (us.size != -1) {
                        if (us.size == sb.st_size)
                                /* nothing to do */
                                goto success;
                        if (sb.st_size > us.size) {
                                /* local file too long! */
-                               warnx("%s: local file (%lld bytes) is longer "
-                                   "than remote file (%lld bytes)", path,
-                                   (long long)sb.st_size, (long long)us.size);
+                               warnx("%s: local file (%jd bytes) is longer "
+                                   "than remote file (%jd bytes)", path,
+                                   (intmax_t)sb.st_size, (intmax_t)us.size);
                                goto failure;
                        }
                        /* we got it, open local file */
@@ -550,6 +573,8 @@ fetch(char *URL, const char *path)
                                }
 
                                of = fopen(tmppath, "w");
+                               chown(tmppath, sb.st_uid, sb.st_gid);
+                               chmod(tmppath, sb.st_mode & ALLPERMS);
                        }
                }
 
@@ -644,8 +669,8 @@ fetch(char *URL, const char *path)
 
        /* did the transfer complete normally? */
        if (us.size != -1 && count < us.size) {
-               warnx("%s appears to be truncated: %lld/%lld bytes",
-                   path, (long long)count, (long long)us.size);
+               warnx("%s appears to be truncated: %jd/%jd bytes",
+                   path, (intmax_t)count, (intmax_t)us.size);
                goto failure_keep;
        }
 
@@ -683,7 +708,7 @@ fetch(char *URL, const char *path)
                fetchFreeURL(url);
        if (tmppath != NULL)
                free(tmppath);
-       return r;
+       return (r);
 }
 
 static void