[Midnightbsd-cvs] src: lib/libfetch: Merge in changes from FreeBSD CURRENT and Dragonfly

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Wed Jul 9 14:57:21 EDT 2008


Log Message:
-----------
Merge in changes from FreeBSD CURRENT and Dragonfly

Modified Files:
--------------
    src/lib/libfetch:
        Makefile (r1.1.1.1 -> r1.2)
        common.c (r1.1.1.1 -> r1.2)
        common.h (r1.1.1.1 -> r1.2)
        fetch.3 (r1.1.1.1 -> r1.2)
        fetch.c (r1.1.1.1 -> r1.2)
        fetch.h (r1.1.1.1 -> r1.2)
        file.c (r1.1.1.1 -> r1.2)
        ftp.c (r1.1.1.1 -> r1.2)
        ftp.errors (r1.1.1.1 -> r1.2)
        http.c (r1.1.1.1 -> r1.2)
        http.errors (r1.1.1.1 -> r1.2)

-------------- next part --------------
Index: file.c
===================================================================
RCS file: /home/cvs/src/lib/libfetch/file.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/file.c -L lib/libfetch/file.c -u -r1.1.1.1 -r1.2
--- lib/libfetch/file.c
+++ lib/libfetch/file.c
@@ -24,10 +24,12 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (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.18 2007/12/14 10:26:58 des Exp $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libfetch/file.c,v 1.17 2004/09/21 18:35:20 des Exp $");
+__MBSDID("$MidnightBSD$");
 
 #include <sys/param.h>
 #include <sys/stat.h>
@@ -50,11 +52,11 @@
 	f = fopen(u->doc, "r");
 
 	if (f == NULL)
-		_fetch_syserr();
+		fetch_syserr();
 
 	if (u->offset && fseeko(f, u->offset, SEEK_SET) == -1) {
 		fclose(f);
-		_fetch_syserr();
+		fetch_syserr();
 	}
 
 	return (f);
@@ -77,25 +79,25 @@
 		f = fopen(u->doc, "w+");
 
 	if (f == NULL)
-		_fetch_syserr();
+		fetch_syserr();
 
 	if (u->offset && fseeko(f, u->offset, SEEK_SET) == -1) {
 		fclose(f);
-		_fetch_syserr();
+		fetch_syserr();
 	}
 
 	return (f);
 }
 
 static int
-_fetch_stat_file(const char *fn, struct url_stat *us)
+fetch_stat_file(const char *fn, struct url_stat *us)
 {
 	struct stat sb;
 
 	us->size = -1;
 	us->atime = us->mtime = 0;
 	if (stat(fn, &sb) == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 	us->size = sb.st_size;
@@ -107,7 +109,7 @@
 int
 fetchStatFile(struct url *u, struct url_stat *us, const char *flags __unused)
 {
-	return (_fetch_stat_file(u->doc, us));
+	return (fetch_stat_file(u->doc, us));
 }
 
 struct url_ent *
@@ -122,7 +124,7 @@
 	int l;
 
 	if ((dir = opendir(u->doc)) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -136,10 +138,10 @@
 	while ((de = readdir(dir)) != NULL) {
 		strncpy(p, de->d_name, l - 1);
 		p[l - 1] = 0;
-		if (_fetch_stat_file(fn, &us) == -1)
+		if (fetch_stat_file(fn, &us) == -1)
 			/* should I return a partial result, or abort? */
 			break;
-		_fetch_add_entry(&ue, &size, &len, de->d_name, &us);
+		fetch_add_entry(&ue, &size, &len, de->d_name, &us);
 	}
 
 	return (ue);
Index: fetch.3
===================================================================
RCS file: /home/cvs/src/lib/libfetch/fetch.3,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/fetch.3 -L lib/libfetch/fetch.3 -u -r1.1.1.1 -r1.2
--- lib/libfetch/fetch.3
+++ lib/libfetch/fetch.3
@@ -23,9 +23,10 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libfetch/fetch.3,v 1.60.2.1 2005/11/30 04:19:40 tmclaugh Exp $
+.\" $MidnightBSD$
+.\" $FreeBSD: src/lib/libfetch/fetch.3,v 1.65 2008/02/08 09:44:34 des Exp $
 .\"
-.Dd July 1, 1998
+.Dd July 9, 2008
 .Dt FETCH 3
 .Os
 .Sh NAME
@@ -236,7 +237,7 @@
 .In fetch.h :
 .Bd -literal
 struct url_ent {
-    char         name[MAXPATHLEN];
+    char         name[PATH_MAX];
     struct url_stat stat;
 };
 .Ed
@@ -532,6 +533,14 @@
 .Xr ftp 1
 for a description of the file format.
 This feature is experimental.
+.It Ev NO_PROXY
+Either a single asterisk, which disables the use of proxies
+altogether, or a comma- or whitespace-separated list of hosts for
+which proxies should not be used.
+.It Ev no_proxy
+Same as
+.Ev NO_PROXY ,
+for compatibility.
 .El
 .Sh EXAMPLES
 To access a proxy server on
@@ -555,6 +564,14 @@
 HTTP_PROXY=http://proxy.example.com:8080
 HTTP_PROXY_AUTH=basic:*:<user>:<pwd>
 .Ed
+.Pp
+To disable the use of a proxy for an HTTP server running on the local
+host, define
+.Ev NO_PROXY
+as follows:
+.Bd -literal -offset indent
+NO_PROXY=localhost,127.0.0.1
+.Ed
 .Sh SEE ALSO
 .Xr fetch 1 ,
 .Xr ftpio 3 ,
Index: common.c
===================================================================
RCS file: /home/cvs/src/lib/libfetch/common.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/common.c -L lib/libfetch/common.c -u -r1.1.1.1 -r1.2
--- lib/libfetch/common.c
+++ lib/libfetch/common.c
@@ -24,17 +24,21 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (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.56 2008/04/15 23:29:51 cperciva Exp $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libfetch/common.c,v 1.50 2005/02/16 12:46:46 des Exp $");
+__MBSDID("$MidnightBSD$");
 
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/uio.h>
+
 #include <netinet/in.h>
 
+#include <ctype.h>
 #include <errno.h>
 #include <netdb.h>
 #include <pwd.h>
@@ -53,7 +57,7 @@
 /*
  * Error messages for resolver errors
  */
-static struct fetcherr _netdb_errlist[] = {
+static struct fetcherr netdb_errlist[] = {
 #ifdef EAI_NODATA
 	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
 #endif
@@ -73,7 +77,7 @@
  * Map error code to string
  */
 static struct fetcherr *
-_fetch_finderr(struct fetcherr *p, int e)
+fetch_finderr(struct fetcherr *p, int e)
 {
 	while (p->num != -1 && p->num != e)
 		p++;
@@ -84,9 +88,9 @@
  * Set error code
  */
 void
-_fetch_seterr(struct fetcherr *p, int e)
+fetch_seterr(struct fetcherr *p, int e)
 {
-	p = _fetch_finderr(p, e);
+	p = fetch_finderr(p, e);
 	fetchLastErrCode = p->cat;
 	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
 }
@@ -95,7 +99,7 @@
  * Set error code according to errno
  */
 void
-_fetch_syserr(void)
+fetch_syserr(void)
 {
 	switch (errno) {
 	case 0:
@@ -155,7 +159,7 @@
  * Emit status message
  */
 void
-_fetch_info(const char *fmt, ...)
+fetch_info(const char *fmt, ...)
 {
 	va_list ap;
 
@@ -172,7 +176,7 @@
  * Return the default port for a scheme
  */
 int
-_fetch_default_port(const char *scheme)
+fetch_default_port(const char *scheme)
 {
 	struct servent *se;
 
@@ -189,7 +193,7 @@
  * Return the default proxy port for a scheme
  */
 int
-_fetch_default_proxy_port(const char *scheme)
+fetch_default_proxy_port(const char *scheme)
 {
 	if (strcasecmp(scheme, SCHEME_FTP) == 0)
 		return (FTP_DEFAULT_PROXY_PORT);
@@ -203,7 +207,7 @@
  * Create a connection for an existing descriptor.
  */
 conn_t *
-_fetch_reopen(int sd)
+fetch_reopen(int sd)
 {
 	conn_t *conn;
 
@@ -220,7 +224,7 @@
  * Bump a connection's reference count.
  */
 conn_t *
-_fetch_ref(conn_t *conn)
+fetch_ref(conn_t *conn)
 {
 
 	++conn->ref;
@@ -232,7 +236,7 @@
  * Bind a socket to a specific local address
  */
 int
-_fetch_bind(int sd, int af, const char *addr)
+fetch_bind(int sd, int af, const char *addr)
 {
 	struct addrinfo hints, *res, *res0;
 	int err;
@@ -254,7 +258,7 @@
  * Establish a TCP connection to the specified port on the specified host.
  */
 conn_t *
-_fetch_connect(const char *host, int port, int af, int verbose)
+fetch_connect(const char *host, int port, int af, int verbose)
 {
 	conn_t *conn;
 	char pbuf[10];
@@ -265,7 +269,7 @@
 	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
 
 	if (verbose)
-		_fetch_info("looking up %s", host);
+		fetch_info("looking up %s", host);
 
 	/* look up host name and set up socket address structure */
 	snprintf(pbuf, sizeof(pbuf), "%d", port);
@@ -274,13 +278,13 @@
 	hints.ai_socktype = SOCK_STREAM;
 	hints.ai_protocol = 0;
 	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
-		_netdb_seterr(err);
+		netdb_seterr(err);
 		return (NULL);
 	}
 	bindaddr = getenv("FETCH_BIND_ADDRESS");
 
 	if (verbose)
-		_fetch_info("connecting to %s:%d", host, port);
+		fetch_info("connecting to %s:%d", host, port);
 
 	/* try to connect */
 	for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
@@ -288,8 +292,8 @@
 			 res->ai_protocol)) == -1)
 			continue;
 		if (bindaddr != NULL && *bindaddr != '\0' &&
-		    _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
-			_fetch_info("failed to bind to '%s'", bindaddr);
+		    fetch_bind(sd, res->ai_family, bindaddr) != 0) {
+			fetch_info("failed to bind to '%s'", bindaddr);
 			close(sd);
 			continue;
 		}
@@ -299,12 +303,12 @@
 	}
 	freeaddrinfo(res0);
 	if (sd == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
-	if ((conn = _fetch_reopen(sd)) == NULL) {
-		_fetch_syserr();
+	if ((conn = fetch_reopen(sd)) == NULL) {
+		fetch_syserr();
 		close(sd);
 	}
 	return (conn);
@@ -315,7 +319,7 @@
  * Enable SSL on a connection.
  */
 int
-_fetch_ssl(conn_t *conn, int verbose)
+fetch_ssl(conn_t *conn, int verbose)
 {
 
 #ifdef WITH_SSL
@@ -373,9 +377,9 @@
  * Read a character from a connection w/ timeout
  */
 ssize_t
-_fetch_read(conn_t *conn, char *buf, size_t len)
+fetch_read(conn_t *conn, char *buf, size_t len)
 {
-	struct timeval now, timeout, wait;
+	struct timeval now, timeout, delta;
 	fd_set readfds;
 	ssize_t rlen, total;
 	int r;
@@ -391,23 +395,23 @@
 		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
 			FD_SET(conn->sd, &readfds);
 			gettimeofday(&now, NULL);
-			wait.tv_sec = timeout.tv_sec - now.tv_sec;
-			wait.tv_usec = timeout.tv_usec - now.tv_usec;
-			if (wait.tv_usec < 0) {
-				wait.tv_usec += 1000000;
-				wait.tv_sec--;
+			delta.tv_sec = timeout.tv_sec - now.tv_sec;
+			delta.tv_usec = timeout.tv_usec - now.tv_usec;
+			if (delta.tv_usec < 0) {
+				delta.tv_usec += 1000000;
+				delta.tv_sec--;
 			}
-			if (wait.tv_sec < 0) {
+			if (delta.tv_sec < 0) {
 				errno = ETIMEDOUT;
-				_fetch_syserr();
+				fetch_syserr();
 				return (-1);
 			}
 			errno = 0;
-			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
+			r = select(conn->sd + 1, &readfds, NULL, NULL, &delta);
 			if (r == -1) {
 				if (errno == EINTR && fetchRestartCalls)
 					continue;
-				_fetch_syserr();
+				fetch_syserr();
 				return (-1);
 			}
 		}
@@ -438,7 +442,7 @@
 #define MIN_BUF_SIZE 1024
 
 int
-_fetch_getln(conn_t *conn)
+fetch_getln(conn_t *conn)
 {
 	char *tmp;
 	size_t tmpsize;
@@ -457,7 +461,7 @@
 	conn->buflen = 0;
 
 	do {
-		len = _fetch_read(conn, &c, 1);
+		len = fetch_read(conn, &c, 1);
 		if (len == -1)
 			return (-1);
 		if (len == 0)
@@ -485,13 +489,13 @@
  * Write to a connection w/ timeout
  */
 ssize_t
-_fetch_write(conn_t *conn, const char *buf, size_t len)
+fetch_write(conn_t *conn, const char *buf, size_t len)
 {
 	struct iovec iov;
 
 	iov.iov_base = __DECONST(char *, buf);
 	iov.iov_len = len;
-	return _fetch_writev(conn, &iov, 1);
+	return fetch_writev(conn, &iov, 1);
 }
 
 /*
@@ -499,9 +503,9 @@
  * Note: can modify the iovec.
  */
 ssize_t
-_fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
+fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
 {
-	struct timeval now, timeout, wait;
+	struct timeval now, timeout, delta;
 	fd_set writefds;
 	ssize_t wlen, total;
 	int r;
@@ -517,19 +521,19 @@
 		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
 			FD_SET(conn->sd, &writefds);
 			gettimeofday(&now, NULL);
-			wait.tv_sec = timeout.tv_sec - now.tv_sec;
-			wait.tv_usec = timeout.tv_usec - now.tv_usec;
-			if (wait.tv_usec < 0) {
-				wait.tv_usec += 1000000;
-				wait.tv_sec--;
+			delta.tv_sec = timeout.tv_sec - now.tv_sec;
+			delta.tv_usec = timeout.tv_usec - now.tv_usec;
+			if (delta.tv_usec < 0) {
+				delta.tv_usec += 1000000;
+				delta.tv_sec--;
 			}
-			if (wait.tv_sec < 0) {
+			if (delta.tv_sec < 0) {
 				errno = ETIMEDOUT;
-				_fetch_syserr();
+				fetch_syserr();
 				return (-1);
 			}
 			errno = 0;
-			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
+			r = select(conn->sd + 1, NULL, &writefds, NULL, &delta);
 			if (r == -1) {
 				if (errno == EINTR && fetchRestartCalls)
 					continue;
@@ -547,7 +551,7 @@
 		if (wlen == 0) {
 			/* we consider a short write a failure */
 			errno = EPIPE;
-			_fetch_syserr();
+			fetch_syserr();
 			return (-1);
 		}
 		if (wlen < 0) {
@@ -574,7 +578,7 @@
  * Write a line of text to a connection w/ timeout
  */
 int
-_fetch_putln(conn_t *conn, const char *str, size_t len)
+fetch_putln(conn_t *conn, const char *str, size_t len)
 {
 	struct iovec iov[2];
 	int ret;
@@ -585,9 +589,9 @@
 	iov[1].iov_base = __DECONST(char *, ENDL);
 	iov[1].iov_len = sizeof(ENDL);
 	if (len == 0)
-		ret = _fetch_writev(conn, &iov[1], 1);
+		ret = fetch_writev(conn, &iov[1], 1);
 	else
-		ret = _fetch_writev(conn, iov, 2);
+		ret = fetch_writev(conn, iov, 2);
 	if (ret == -1)
 		return (-1);
 	return (0);
@@ -598,7 +602,7 @@
  * Close connection
  */
 int
-_fetch_close(conn_t *conn)
+fetch_close(conn_t *conn)
 {
 	int ret;
 
@@ -614,7 +618,7 @@
 /*** Directory-related utility functions *************************************/
 
 int
-_fetch_add_entry(struct url_ent **p, int *size, int *len,
+fetch_add_entry(struct url_ent **p, int *size, int *len,
     const char *name, struct url_stat *us)
 {
 	struct url_ent *tmp;
@@ -628,7 +632,7 @@
 		tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
 		if (tmp == NULL) {
 			errno = ENOMEM;
-			_fetch_syserr();
+			fetch_syserr();
 			return (-1);
 		}
 		*size = (*size * 2 + 1);
@@ -637,7 +641,7 @@
 
 	tmp = *p + *len;
 	snprintf(tmp->name, PATH_MAX, "%s", name);
-	bcopy(us, &tmp->stat, sizeof(*us));
+	memcpy(&tmp->stat, us, sizeof(*us));
 
 	(*len)++;
 	(++tmp)->name[0] = 0;
@@ -649,11 +653,11 @@
 /*** Authentication-related utility functions ********************************/
 
 static const char *
-_fetch_read_word(FILE *f)
+fetch_read_word(FILE *f)
 {
 	static char word[1024];
 
-	if (fscanf(f, " %1024s ", word) != 1)
+	if (fscanf(f, " %1023s ", word) != 1)
 		return (NULL);
 	return (word);
 }
@@ -662,7 +666,7 @@
  * Get authentication data for a URL from .netrc
  */
 int
-_fetch_netrc_auth(struct url *url)
+fetch_netrc_auth(struct url *url)
 {
 	char fn[PATH_MAX];
 	const char *word;
@@ -671,7 +675,7 @@
 
 	if ((p = getenv("NETRC")) != NULL) {
 		if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
-			_fetch_info("$NETRC specifies a file name "
+			fetch_info("$NETRC specifies a file name "
 			    "longer than PATH_MAX");
 			return (-1);
 		}
@@ -689,39 +693,39 @@
 
 	if ((f = fopen(fn, "r")) == NULL)
 		return (-1);
-	while ((word = _fetch_read_word(f)) != NULL) {
+	while ((word = fetch_read_word(f)) != NULL) {
 		if (strcmp(word, "default") == 0) {
-			DEBUG(_fetch_info("Using default .netrc settings"));
+			DEBUG(fetch_info("Using default .netrc settings"));
 			break;
 		}
 		if (strcmp(word, "machine") == 0 &&
-		    (word = _fetch_read_word(f)) != NULL &&
+		    (word = fetch_read_word(f)) != NULL &&
 		    strcasecmp(word, url->host) == 0) {
-			DEBUG(_fetch_info("Using .netrc settings for %s", word));
+			DEBUG(fetch_info("Using .netrc settings for %s", word));
 			break;
 		}
 	}
 	if (word == NULL)
 		goto ferr;
-	while ((word = _fetch_read_word(f)) != NULL) {
+	while ((word = fetch_read_word(f)) != NULL) {
 		if (strcmp(word, "login") == 0) {
-			if ((word = _fetch_read_word(f)) == NULL)
+			if ((word = fetch_read_word(f)) == NULL)
 				goto ferr;
 			if (snprintf(url->user, sizeof(url->user),
 				"%s", word) > (int)sizeof(url->user)) {
-				_fetch_info("login name in .netrc is too long");
+				fetch_info("login name in .netrc is too long");
 				url->user[0] = '\0';
 			}
 		} else if (strcmp(word, "password") == 0) {
-			if ((word = _fetch_read_word(f)) == NULL)
+			if ((word = fetch_read_word(f)) == NULL)
 				goto ferr;
 			if (snprintf(url->pwd, sizeof(url->pwd),
 				"%s", word) > (int)sizeof(url->pwd)) {
-				_fetch_info("password in .netrc is too long");
+				fetch_info("password in .netrc is too long");
 				url->pwd[0] = '\0';
 			}
 		} else if (strcmp(word, "account") == 0) {
-			if ((word = _fetch_read_word(f)) == NULL)
+			if ((word = fetch_read_word(f)) == NULL)
 				goto ferr;
 			/* XXX not supported! */
 		} else {
@@ -734,3 +738,51 @@
 	fclose(f);
 	return (-1);
 }
+
+/*
+ * The no_proxy environment variable specifies a set of domains for
+ * which the proxy should not be consulted; the contents is a comma-,
+ * or space-separated list of domain names.  A single asterisk will
+ * override all proxy variables and no transactions will be proxied
+ * (for compatability with lynx and curl, see the discussion at
+ * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
+ */
+int
+fetch_no_proxy_match(const char *host)
+{
+	const char *no_proxy, *p, *q;
+	size_t h_len, d_len;
+
+	if ((no_proxy = getenv("NO_PROXY")) == NULL &&
+	    (no_proxy = getenv("no_proxy")) == NULL)
+		return (0);
+
+	/* asterisk matches any hostname */
+	if (strcmp(no_proxy, "*") == 0)
+		return (1);
+
+	h_len = strlen(host);
+	p = no_proxy;
+	do {
+		/* position p at the beginning of a domain suffix */
+		while (*p == ',' || isspace((unsigned char)*p))
+			p++;
+
+		/* position q at the first separator character */
+		for (q = p; *q; ++q)
+			if (*q == ',' || isspace((unsigned char)*q))
+				break;
+
+		d_len = q - p;
+		if (d_len > 0 && h_len > d_len &&
+		    strncasecmp(host + h_len - d_len,
+			p, d_len) == 0) {
+			/* domain name matches */
+			return (1);
+		}
+
+		p = q + 1;
+	} while (*q);
+
+	return (0);
+}
Index: http.errors
===================================================================
RCS file: /home/cvs/src/lib/libfetch/http.errors,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/http.errors -L lib/libfetch/http.errors -u -r1.1.1.1 -r1.2
--- lib/libfetch/http.errors
+++ lib/libfetch/http.errors
@@ -1,3 +1,4 @@
+# $MidnightBSD$
 # $FreeBSD: src/lib/libfetch/http.errors,v 1.5 2001/05/23 18:52:02 des Exp $
 #
 # This list is taken from RFC 2068.
Index: common.h
===================================================================
RCS file: /home/cvs/src/lib/libfetch/common.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/common.h -L lib/libfetch/common.h -u -r1.1.1.1 -r1.2
--- lib/libfetch/common.h
+++ lib/libfetch/common.h
@@ -25,7 +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.28 2004/09/21 18:35:20 des Exp $
+ * $MidnightBSD$
+ * $FreeBSD: src/lib/libfetch/common.h,v 1.30 2007/12/18 11:03:07 des Exp $
  */
 
 #ifndef _COMMON_H_INCLUDED
@@ -68,33 +69,34 @@
 	const char	*string;
 };
 
-/* for _fetch_writev */
+/* for fetch_writev */
 struct iovec;
 
-void		 _fetch_seterr(struct fetcherr *, int);
-void		 _fetch_syserr(void);
-void		 _fetch_info(const char *, ...);
-int		 _fetch_default_port(const char *);
-int		 _fetch_default_proxy_port(const char *);
-int		 _fetch_bind(int, int, const char *);
-conn_t		*_fetch_connect(const char *, int, int, int);
-conn_t		*_fetch_reopen(int);
-conn_t		*_fetch_ref(conn_t *);
-int		 _fetch_ssl(conn_t *, int);
-ssize_t		 _fetch_read(conn_t *, char *, size_t);
-int		 _fetch_getln(conn_t *);
-ssize_t		 _fetch_write(conn_t *, const char *, size_t);
-ssize_t		 _fetch_writev(conn_t *, struct iovec *, int);
-int		 _fetch_putln(conn_t *, const char *, size_t);
-int		 _fetch_close(conn_t *);
-int		 _fetch_add_entry(struct url_ent **, int *, int *,
+void		 fetch_seterr(struct fetcherr *, int);
+void		 fetch_syserr(void);
+void		 fetch_info(const char *, ...);
+int		 fetch_default_port(const char *);
+int		 fetch_default_proxy_port(const char *);
+int		 fetch_bind(int, int, const char *);
+conn_t		*fetch_connect(const char *, int, int, int);
+conn_t		*fetch_reopen(int);
+conn_t		*fetch_ref(conn_t *);
+int		 fetch_ssl(conn_t *, int);
+ssize_t		 fetch_read(conn_t *, char *, size_t);
+int		 fetch_getln(conn_t *);
+ssize_t		 fetch_write(conn_t *, const char *, size_t);
+ssize_t		 fetch_writev(conn_t *, struct iovec *, int);
+int		 fetch_putln(conn_t *, const char *, size_t);
+int		 fetch_close(conn_t *);
+int		 fetch_add_entry(struct url_ent **, int *, int *,
 		     const char *, struct url_stat *);
-int		 _fetch_netrc_auth(struct url *url);
+int		 fetch_netrc_auth(struct url *url);
+int		 fetch_no_proxy_match(const char *);
 
-#define _ftp_seterr(n)	 _fetch_seterr(_ftp_errlist, n)
-#define _http_seterr(n)	 _fetch_seterr(_http_errlist, n)
-#define _netdb_seterr(n) _fetch_seterr(_netdb_errlist, n)
-#define _url_seterr(n)	 _fetch_seterr(_url_errlist, n)
+#define ftp_seterr(n)	 fetch_seterr(ftp_errlist, n)
+#define http_seterr(n)	 fetch_seterr(http_errlist, n)
+#define netdb_seterr(n)	 fetch_seterr(netdb_errlist, n)
+#define url_seterr(n)	 fetch_seterr(url_errlist, n)
 
 #ifndef NDEBUG
 #define DEBUG(x) do { if (fetchDebug) { x; } } while (0)
@@ -103,7 +105,7 @@
 #endif
 
 /*
- * I don't really like exporting _http_request() and _ftp_request(),
+ * I don't really like exporting http_request() and ftp_request(),
  * but the HTTP and FTP code occasionally needs to cross-call
  * eachother, and this saves me from adding a lot of special-case code
  * to handle those cases.
@@ -111,9 +113,9 @@
  * Note that _*_request() free purl, which is way ugly but saves us a
  * whole lot of trouble.
  */
-FILE		*_http_request(struct url *, const char *,
+FILE		*http_request(struct url *, const char *,
 		     struct url_stat *, struct url *, const char *);
-FILE		*_ftp_request(struct url *, const char *,
+FILE		*ftp_request(struct url *, const char *,
 		     struct url_stat *, struct url *, const char *);
 
 /*
Index: http.c
===================================================================
RCS file: /home/cvs/src/lib/libfetch/http.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/http.c -L lib/libfetch/http.c -u -r1.1.1.1 -r1.2
--- lib/libfetch/http.c
+++ lib/libfetch/http.c
@@ -24,10 +24,12 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (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.84 2008/02/08 09:48:48 des Exp $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libfetch/http.c,v 1.76.2.1 2005/12/14 09:10:13 des Exp $");
+__MBSDID("$MidnightBSD$");
 
 /*
  * The following copyright applies to the base64 code:
@@ -92,6 +94,7 @@
 #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
@@ -99,6 +102,7 @@
 
 #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)
@@ -128,27 +132,27 @@
  * Get next chunk header
  */
 static int
-_http_new_chunk(struct httpio *io)
+http_new_chunk(struct httpio *io)
 {
 	char *p;
 
-	if (_fetch_getln(io->conn) == -1)
+	if (fetch_getln(io->conn) == -1)
 		return (-1);
 
-	if (io->conn->buflen < 2 || !ishexnumber(*io->conn->buf))
+	if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf))
 		return (-1);
 
-	for (p = io->conn->buf; *p && !isspace(*p); ++p) {
+	for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) {
 		if (*p == ';')
 			break;
-		if (!ishexnumber(*p))
+		if (!isxdigit((unsigned char)*p))
 			return (-1);
-		if (isdigit(*p)) {
+		if (isdigit((unsigned char)*p)) {
 			io->chunksize = io->chunksize * 16 +
 			    *p - '0';
 		} else {
 			io->chunksize = io->chunksize * 16 +
-			    10 + tolower(*p) - 'a';
+			    10 + tolower((unsigned char)*p) - 'a';
 		}
 	}
 
@@ -171,7 +175,7 @@
  * Grow the input buffer to at least len bytes
  */
 static inline int
-_http_growbuf(struct httpio *io, size_t len)
+http_growbuf(struct httpio *io, size_t len)
 {
 	char *tmp;
 
@@ -189,7 +193,7 @@
  * Fill the input buffer, do chunk decoding on the fly
  */
 static int
-_http_fillbuf(struct httpio *io, size_t len)
+http_fillbuf(struct httpio *io, size_t len)
 {
 	if (io->error)
 		return (-1);
@@ -197,9 +201,9 @@
 		return (0);
 
 	if (io->chunked == 0) {
-		if (_http_growbuf(io, len) == -1)
+		if (http_growbuf(io, len) == -1)
 			return (-1);
-		if ((io->buflen = _fetch_read(io->conn, io->buf, len)) == -1) {
+		if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
 			io->error = 1;
 			return (-1);
 		}
@@ -208,7 +212,7 @@
 	}
 
 	if (io->chunksize == 0) {
-		switch (_http_new_chunk(io)) {
+		switch (http_new_chunk(io)) {
 		case -1:
 			io->error = 1;
 			return (-1);
@@ -220,9 +224,9 @@
 
 	if (len > io->chunksize)
 		len = io->chunksize;
-	if (_http_growbuf(io, len) == -1)
+	if (http_growbuf(io, len) == -1)
 		return (-1);
-	if ((io->buflen = _fetch_read(io->conn, io->buf, len)) == -1) {
+	if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) {
 		io->error = 1;
 		return (-1);
 	}
@@ -231,7 +235,7 @@
 	if (io->chunksize == 0) {
 		char endl[2];
 
-		if (_fetch_read(io->conn, endl, 2) != 2 ||
+		if (fetch_read(io->conn, endl, 2) != 2 ||
 		    endl[0] != '\r' || endl[1] != '\n')
 			return (-1);
 	}
@@ -245,7 +249,7 @@
  * Read function
  */
 static int
-_http_readfn(void *v, char *buf, int len)
+http_readfn(void *v, char *buf, int len)
 {
 	struct httpio *io = (struct httpio *)v;
 	int l, pos;
@@ -258,12 +262,12 @@
 	for (pos = 0; len > 0; pos += l, len -= l) {
 		/* empty buffer */
 		if (!io->buf || io->bufpos == io->buflen)
-			if (_http_fillbuf(io, len) < 1)
+			if (http_fillbuf(io, len) < 1)
 				break;
 		l = io->buflen - io->bufpos;
 		if (len < l)
 			l = len;
-		bcopy(io->buf + io->bufpos, buf + pos, l);
+		memcpy(buf + pos, io->buf + io->bufpos, l);
 		io->bufpos += l;
 	}
 
@@ -276,23 +280,23 @@
  * Write function
  */
 static int
-_http_writefn(void *v, const char *buf, int len)
+http_writefn(void *v, const char *buf, int len)
 {
 	struct httpio *io = (struct httpio *)v;
 
-	return (_fetch_write(io->conn, buf, len));
+	return (fetch_write(io->conn, buf, len));
 }
 
 /*
  * Close function
  */
 static int
-_http_closefn(void *v)
+http_closefn(void *v)
 {
 	struct httpio *io = (struct httpio *)v;
 	int r;
 
-	r = _fetch_close(io->conn);
+	r = fetch_close(io->conn);
 	if (io->buf)
 		free(io->buf);
 	free(io);
@@ -303,20 +307,20 @@
  * Wrap a file descriptor up
  */
 static FILE *
-_http_funopen(conn_t *conn, int chunked)
+http_funopen(conn_t *conn, int chunked)
 {
 	struct httpio *io;
 	FILE *f;
 
 	if ((io = calloc(1, sizeof(*io))) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 	io->conn = conn;
 	io->chunked = chunked;
-	f = funopen(io, _http_readfn, _http_writefn, NULL, _http_closefn);
+	f = funopen(io, http_readfn, http_writefn, NULL, http_closefn);
 	if (f == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		free(io);
 		return (NULL);
 	}
@@ -360,7 +364,7 @@
  * Send a formatted line; optionally echo to terminal
  */
 static int
-_http_cmd(conn_t *conn, const char *fmt, ...)
+http_cmd(conn_t *conn, const char *fmt, ...)
 {
 	va_list ap;
 	size_t len;
@@ -373,15 +377,15 @@
 
 	if (msg == NULL) {
 		errno = ENOMEM;
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
-	r = _fetch_putln(conn, msg, len);
+	r = fetch_putln(conn, msg, len);
 	free(msg);
 
 	if (r == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
@@ -392,11 +396,11 @@
  * Get and parse status line
  */
 static int
-_http_get_reply(conn_t *conn)
+http_get_reply(conn_t *conn)
 {
 	char *p;
 
-	if (_fetch_getln(conn) == -1)
+	if (fetch_getln(conn) == -1)
 		return (-1);
 	/*
 	 * A valid status line looks like "HTTP/m.n xyz reason" where m
@@ -415,7 +419,10 @@
 			return (HTTP_PROTOCOL_ERROR);
 		p += 4;
 	}
-	if (*p != ' ' || !isdigit(p[1]) || !isdigit(p[2]) || !isdigit(p[3]))
+	if (*p != ' ' ||
+	    !isdigit((unsigned char)p[1]) ||
+	    !isdigit((unsigned char)p[2]) ||
+	    !isdigit((unsigned char)p[3]))
 		return (HTTP_PROTOCOL_ERROR);
 
 	conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0');
@@ -427,13 +434,14 @@
  * to the beginning of the value.
  */
 static const char *
-_http_match(const char *str, const char *hdr)
+http_match(const char *str, const char *hdr)
 {
-	while (*str && *hdr && tolower(*str++) == tolower(*hdr++))
+	while (*str && *hdr &&
+	    tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++))
 		/* nothing */;
 	if (*str || *hdr != ':')
 		return (NULL);
-	while (*hdr && isspace(*++hdr))
+	while (*hdr && isspace((unsigned char)*++hdr))
 		/* nothing */;
 	return (hdr);
 }
@@ -442,13 +450,13 @@
  * Get the next header and return the appropriate symbolic code.
  */
 static hdr_t
-_http_next_header(conn_t *conn, const char **p)
+http_next_header(conn_t *conn, const char **p)
 {
 	int i;
 
-	if (_fetch_getln(conn) == -1)
+	if (fetch_getln(conn) == -1)
 		return (hdr_syserror);
-	while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
+	while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1]))
 		conn->buflen--;
 	conn->buf[conn->buflen] = '\0';
 	if (conn->buflen == 0)
@@ -460,7 +468,7 @@
 	 * characters except "()<>@,;:\\\"{}".
 	 */
 	for (i = 0; hdr_names[i].num != hdr_unknown; i++)
-		if ((*p = _http_match(hdr_names[i].name, conn->buf)) != NULL)
+		if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL)
 			return (hdr_names[i].num);
 	return (hdr_unknown);
 }
@@ -469,7 +477,7 @@
  * Parse a last-modified header
  */
 static int
-_http_parse_mtime(const char *p, time_t *mtime)
+http_parse_mtime(const char *p, time_t *mtime)
 {
 	char locale[64], *r;
 	struct tm tm;
@@ -493,11 +501,11 @@
  * Parse a content-length header
  */
 static int
-_http_parse_length(const char *p, off_t *length)
+http_parse_length(const char *p, off_t *length)
 {
 	off_t len;
 
-	for (len = 0; *p && isdigit(*p); ++p)
+	for (len = 0; *p && isdigit((unsigned char)*p); ++p)
 		len = len * 10 + (*p - '0');
 	if (*p)
 		return (-1);
@@ -511,7 +519,7 @@
  * Parse a content-range header
  */
 static int
-_http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
+http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
 {
 	off_t first, last, len;
 
@@ -522,16 +530,16 @@
 		first = last = -1;
 		++p;
 	} else {
-		for (first = 0; *p && isdigit(*p); ++p)
+		for (first = 0; *p && isdigit((unsigned char)*p); ++p)
 			first = first * 10 + *p - '0';
 		if (*p != '-')
 			return (-1);
-		for (last = 0, ++p; *p && isdigit(*p); ++p)
+		for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
 			last = last * 10 + *p - '0';
 	}
 	if (first > last || *p != '/')
 		return (-1);
-	for (len = 0, ++p; *p && isdigit(*p); ++p)
+	for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
 		len = len * 10 + *p - '0';
 	if (*p || len < last - first + 1)
 		return (-1);
@@ -558,7 +566,7 @@
  * Base64 encoding
  */
 static char *
-_http_base64(const char *src)
+http_base64(const char *src)
 {
 	static const char base64[] =
 	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -614,7 +622,7 @@
  * Encode username and password
  */
 static int
-_http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)
+http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)
 {
 	char *upw, *auth;
 	int r;
@@ -623,11 +631,11 @@
 	DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd));
 	if (asprintf(&upw, "%s:%s", usr, pwd) == -1)
 		return (-1);
-	auth = _http_base64(upw);
+	auth = http_base64(upw);
 	free(upw);
 	if (auth == NULL)
 		return (-1);
-	r = _http_cmd(conn, "%s: Basic %s", hdr, auth);
+	r = http_cmd(conn, "%s: Basic %s", hdr, auth);
 	free(auth);
 	return (r);
 }
@@ -636,7 +644,7 @@
  * Send an authorization header
  */
 static int
-_http_authorize(conn_t *conn, const char *hdr, const char *p)
+http_authorize(conn_t *conn, const char *hdr, const char *p)
 {
 	/* basic authorization */
 	if (strncasecmp(p, "basic:", 6) == 0) {
@@ -653,7 +661,7 @@
 		user = str;
 		pwd = strchr(str, ':');
 		*pwd++ = '\0';
-		r = _http_basic_auth(conn, hdr, user, pwd);
+		r = http_basic_auth(conn, hdr, user, pwd);
 		free(str);
 		return (r);
 	}
@@ -669,7 +677,7 @@
  * Connect to the correct HTTP server or proxy.
  */
 static conn_t *
-_http_connect(struct url *URL, struct url *purl, const char *flags)
+http_connect(struct url *URL, struct url *purl, const char *flags)
 {
 	conn_t *conn;
 	int verbose;
@@ -697,15 +705,15 @@
 		return (NULL);
 	}
 
-	if ((conn = _fetch_connect(URL->host, URL->port, af, verbose)) == NULL)
-		/* _fetch_connect() has already set an error code */
+	if ((conn = fetch_connect(URL->host, URL->port, af, verbose)) == NULL)
+		/* fetch_connect() has already set an error code */
 		return (NULL);
 	if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 &&
-	    _fetch_ssl(conn, verbose) == -1) {
-		_fetch_close(conn);
+	    fetch_ssl(conn, verbose) == -1) {
+		fetch_close(conn);
 		/* grrr */
 		errno = EAUTH;
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -716,19 +724,21 @@
 }
 
 static struct url *
-_http_get_proxy(const char *flags)
+http_get_proxy(struct url * url, const char *flags)
 {
 	struct url *purl;
 	char *p;
 
 	if (flags != NULL && strchr(flags, 'd') != NULL)
 		return (NULL);
+	if (fetch_no_proxy_match(url->host))
+		return (NULL);
 	if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
 	    *p && (purl = fetchParseURL(p))) {
 		if (!*purl->scheme)
 			strcpy(purl->scheme, SCHEME_HTTP);
 		if (!purl->port)
-			purl->port = _fetch_default_proxy_port(purl->scheme);
+			purl->port = fetch_default_proxy_port(purl->scheme);
 		if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
 			return (purl);
 		fetchFreeURL(purl);
@@ -737,7 +747,7 @@
 }
 
 static void
-_http_print_html(FILE *out, FILE *in)
+http_print_html(FILE *out, FILE *in)
 {
 	size_t len;
 	char *line, *p, *q;
@@ -745,7 +755,7 @@
 
 	comment = tag = 0;
 	while ((line = fgetln(in, &len)) != NULL) {
-		while (len && isspace(line[len - 1]))
+		while (len && isspace((unsigned char)line[len - 1]))
 			--len;
 		for (p = q = line; q < line + len; ++q) {
 			if (comment && *q == '-') {
@@ -786,7 +796,7 @@
  * XXX off into a separate function.
  */
 FILE *
-_http_request(struct url *URL, const char *op, struct url_stat *us,
+http_request(struct url *URL, const char *op, struct url_stat *us,
     struct url *purl, const char *flags)
 {
 	conn_t *conn;
@@ -829,18 +839,18 @@
 
 		/* check port */
 		if (!url->port)
-			url->port = _fetch_default_port(url->scheme);
+			url->port = fetch_default_port(url->scheme);
 
 		/* were we redirected to an FTP URL? */
 		if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) {
 			if (strcmp(op, "GET") == 0)
-				return (_ftp_request(url, "RETR", us, purl, flags));
+				return (ftp_request(url, "RETR", us, purl, flags));
 			else if (strcmp(op, "HEAD") == 0)
-				return (_ftp_request(url, "STAT", us, purl, flags));
+				return (ftp_request(url, "STAT", us, purl, flags));
 		}
 
 		/* connect to server or proxy */
-		if ((conn = _http_connect(url, purl, flags)) == NULL)
+		if ((conn = http_connect(url, purl, flags)) == NULL)
 			goto ouch;
 
 		host = url->host;
@@ -850,7 +860,7 @@
 			host = hbuf;
 		}
 #endif
-		if (url->port != _fetch_default_port(url->scheme)) {
+		if (url->port != fetch_default_port(url->scheme)) {
 			if (host != hbuf) {
 				strcpy(hbuf, host);
 				host = hbuf;
@@ -861,38 +871,38 @@
 
 		/* send request */
 		if (verbose)
-			_fetch_info("requesting %s://%s%s",
+			fetch_info("requesting %s://%s%s",
 			    url->scheme, host, url->doc);
 		if (purl) {
-			_http_cmd(conn, "%s %s://%s%s HTTP/1.1",
+			http_cmd(conn, "%s %s://%s%s HTTP/1.1",
 			    op, url->scheme, host, url->doc);
 		} else {
-			_http_cmd(conn, "%s %s HTTP/1.1",
+			http_cmd(conn, "%s %s HTTP/1.1",
 			    op, url->doc);
 		}
 
 		/* virtual host */
-		_http_cmd(conn, "Host: %s", host);
+		http_cmd(conn, "Host: %s", host);
 
 		/* proxy authorization */
 		if (purl) {
 			if (*purl->user || *purl->pwd)
-				_http_basic_auth(conn, "Proxy-Authorization",
+				http_basic_auth(conn, "Proxy-Authorization",
 				    purl->user, purl->pwd);
 			else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
-				_http_authorize(conn, "Proxy-Authorization", p);
+				http_authorize(conn, "Proxy-Authorization", p);
 		}
 
 		/* server authorization */
 		if (need_auth || *url->user || *url->pwd) {
 			if (*url->user || *url->pwd)
-				_http_basic_auth(conn, "Authorization", url->user, url->pwd);
+				http_basic_auth(conn, "Authorization", url->user, url->pwd);
 			else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')
-				_http_authorize(conn, "Authorization", p);
+				http_authorize(conn, "Authorization", p);
 			else if (fetchAuthMethod && fetchAuthMethod(url) == 0) {
-				_http_basic_auth(conn, "Authorization", url->user, url->pwd);
+				http_basic_auth(conn, "Authorization", url->user, url->pwd);
 			} else {
-				_http_seterr(HTTP_NEED_AUTH);
+				http_seterr(HTTP_NEED_AUTH);
 				goto ouch;
 			}
 		}
@@ -900,19 +910,19 @@
 		/* other headers */
 		if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') {
 			if (strcasecmp(p, "auto") == 0)
-				_http_cmd(conn, "Referer: %s://%s%s",
+				http_cmd(conn, "Referer: %s://%s%s",
 				    url->scheme, host, url->doc);
 			else
-				_http_cmd(conn, "Referer: %s", p);
+				http_cmd(conn, "Referer: %s", p);
 		}
 		if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0')
-			_http_cmd(conn, "User-Agent: %s", p);
+			http_cmd(conn, "User-Agent: %s", p);
 		else
-			_http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname());
+			http_cmd(conn, "User-Agent: %s " _LIBFETCH_VER, getprogname());
 		if (url->offset > 0)
-			_http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);
-		_http_cmd(conn, "Connection: close");
-		_http_cmd(conn, "");
+			http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);
+		http_cmd(conn, "Connection: close");
+		http_cmd(conn, "");
 
 		/*
 		 * Force the queued request to be dispatched.  Normally, one
@@ -929,7 +939,7 @@
 			   sizeof(val));
 
 		/* get reply */
-		switch (_http_get_reply(conn)) {
+		switch (http_get_reply(conn)) {
 		case HTTP_OK:
 		case HTTP_PARTIAL:
 			/* fine */
@@ -948,12 +958,12 @@
 				 * We already sent out authorization code,
 				 * so there's nothing more we can do.
 				 */
-				_http_seterr(conn->err);
+				http_seterr(conn->err);
 				goto ouch;
 			}
 			/* try again, but send the password this time */
 			if (verbose)
-				_fetch_info("server requires authorization");
+				fetch_info("server requires authorization");
 			break;
 		case HTTP_NEED_PROXY_AUTH:
 			/*
@@ -961,7 +971,7 @@
 			 * our proxy authorization code, so there's
 			 * nothing more we can do.
 			 */
-			_http_seterr(conn->err);
+			http_seterr(conn->err);
 			goto ouch;
 		case HTTP_BAD_RANGE:
 			/*
@@ -973,10 +983,10 @@
 		case HTTP_PROTOCOL_ERROR:
 			/* fall through */
 		case -1:
-			_fetch_syserr();
+			fetch_syserr();
 			goto ouch;
 		default:
-			_http_seterr(conn->err);
+			http_seterr(conn->err);
 			if (!verbose)
 				goto ouch;
 			/* fall through so we can get the full error message */
@@ -984,21 +994,21 @@
 
 		/* get headers */
 		do {
-			switch ((h = _http_next_header(conn, &p))) {
+			switch ((h = http_next_header(conn, &p))) {
 			case hdr_syserror:
-				_fetch_syserr();
+				fetch_syserr();
 				goto ouch;
 			case hdr_error:
-				_http_seterr(HTTP_PROTOCOL_ERROR);
+				http_seterr(HTTP_PROTOCOL_ERROR);
 				goto ouch;
 			case hdr_content_length:
-				_http_parse_length(p, &clength);
+				http_parse_length(p, &clength);
 				break;
 			case hdr_content_range:
-				_http_parse_range(p, &offset, &length, &size);
+				http_parse_range(p, &offset, &length, &size);
 				break;
 			case hdr_last_modified:
-				_http_parse_mtime(p, &mtime);
+				http_parse_mtime(p, &mtime);
 				break;
 			case hdr_location:
 				if (!HTTP_REDIRECT(conn->err))
@@ -1006,7 +1016,7 @@
 				if (new)
 					free(new);
 				if (verbose)
-					_fetch_info("%d redirect to %s", conn->err, p);
+					fetch_info("%d redirect to %s", conn->err, p);
 				if (*p == '/')
 					/* absolute path */
 					new = fetchMakeURL(url->scheme, url->host, url->port, p,
@@ -1046,7 +1056,7 @@
 		if (conn->err == HTTP_NEED_AUTH) {
 			e = conn->err;
 			need_auth = 1;
-			_fetch_close(conn);
+			fetch_close(conn);
 			conn = NULL;
 			continue;
 		}
@@ -1059,7 +1069,7 @@
 				conn->err = HTTP_OK;
 				break;
 			} else {
-				_http_seterr(conn->err);
+				http_seterr(conn->err);
 				goto ouch;
 			}
 		}
@@ -1071,7 +1081,7 @@
 		/* all other cases: we got a redirect */
 		e = conn->err;
 		need_auth = 0;
-		_fetch_close(conn);
+		fetch_close(conn);
 		conn = NULL;
 		if (!new) {
 			DEBUG(fprintf(stderr, "redirect with no new location\n"));
@@ -1084,7 +1094,7 @@
 
 	/* we failed, or ran out of retries */
 	if (conn == NULL) {
-		_http_seterr(e);
+		http_seterr(e);
 		goto ouch;
 	}
 
@@ -1095,7 +1105,7 @@
 
 	/* check for inconsistencies */
 	if (clength != -1 && length != -1 && clength != length) {
-		_http_seterr(HTTP_PROTOCOL_ERROR);
+		http_seterr(HTTP_PROTOCOL_ERROR);
 		goto ouch;
 	}
 	if (clength == -1)
@@ -1103,7 +1113,7 @@
 	if (clength != -1)
 		length = offset + clength;
 	if (length != -1 && size != -1 && length != size) {
-		_http_seterr(HTTP_PROTOCOL_ERROR);
+		http_seterr(HTTP_PROTOCOL_ERROR);
 		goto ouch;
 	}
 	if (size == -1)
@@ -1117,7 +1127,7 @@
 
 	/* too far? */
 	if (URL->offset > 0 && offset > URL->offset) {
-		_http_seterr(HTTP_PROTOCOL_ERROR);
+		http_seterr(HTTP_PROTOCOL_ERROR);
 		goto ouch;
 	}
 
@@ -1126,8 +1136,8 @@
 	URL->length = clength;
 
 	/* wrap it up in a FILE */
-	if ((f = _http_funopen(conn, chunked)) == NULL) {
-		_fetch_syserr();
+	if ((f = http_funopen(conn, chunked)) == NULL) {
+		fetch_syserr();
 		goto ouch;
 	}
 
@@ -1137,7 +1147,7 @@
 		fetchFreeURL(purl);
 
 	if (HTTP_ERROR(conn->err)) {
-		_http_print_html(stderr, f);
+		http_print_html(stderr, f);
 		fclose(f);
 		f = NULL;
 	}
@@ -1150,7 +1160,7 @@
 	if (purl)
 		fetchFreeURL(purl);
 	if (conn != NULL)
-		_fetch_close(conn);
+		fetch_close(conn);
 	return (NULL);
 }
 
@@ -1165,7 +1175,7 @@
 FILE *
 fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags)
 {
-	return (_http_request(URL, "GET", us, _http_get_proxy(flags), flags));
+	return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags));
 }
 
 /*
@@ -1195,7 +1205,7 @@
 {
 	FILE *f;
 
-	f = _http_request(URL, "HEAD", us, _http_get_proxy(flags), flags);
+	f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags);
 	if (f == NULL)
 		return (-1);
 	fclose(f);
Index: ftp.errors
===================================================================
RCS file: /home/cvs/src/lib/libfetch/ftp.errors,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/ftp.errors -L lib/libfetch/ftp.errors -u -r1.1.1.1 -r1.2
--- lib/libfetch/ftp.errors
+++ lib/libfetch/ftp.errors
@@ -1,3 +1,4 @@
+# $MidnightBSD$
 # $FreeBSD: src/lib/libfetch/ftp.errors,v 1.6 2002/10/30 06:06:16 des Exp $
 #
 # This list is taken from RFC 959.
Index: Makefile
===================================================================
RCS file: /home/cvs/src/lib/libfetch/Makefile,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/Makefile -L lib/libfetch/Makefile -u -r1.1.1.1 -r1.2
--- lib/libfetch/Makefile
+++ lib/libfetch/Makefile
@@ -1,3 +1,4 @@
+# $MidnightBSD$
 # $FreeBSD: src/lib/libfetch/Makefile,v 1.44.2.1 2005/07/22 17:29:05 kensmith Exp $
 
 LIB=		fetch
@@ -15,14 +16,16 @@
 LDADD=		-lssl -lcrypto
 .endif
 
+CFLAGS+=	-DFTP_COMBINE_CWDS
+
 CSTD?=		c99
 WARNS?=		2
 
 SHLIB_MAJOR=    4
 
-ftperr.h: ftp.errors
-	@echo "static struct fetcherr _ftp_errlist[] = {" > ${.TARGET}
-	@cat ${.ALLSRC} \
+ftperr.h: ftp.errors ${.CURDIR}/Makefile
+	@echo "static struct fetcherr ftp_errlist[] = {" > ${.TARGET}
+	@cat ${.CURDIR}/ftp.errors \
 	  | grep -v ^# \
 	  | sort \
 	  | while read NUM CAT STRING; do \
@@ -31,9 +34,9 @@
 	@echo "    { -1, FETCH_UNKNOWN, \"Unknown FTP error\" }" >> ${.TARGET}
 	@echo "};" >> ${.TARGET}
 
-httperr.h: http.errors
-	@echo "static struct fetcherr _http_errlist[] = {" > ${.TARGET}
-	@cat ${.ALLSRC} \
+httperr.h: http.errors ${.CURDIR}/Makefile
+	@echo "static struct fetcherr http_errlist[] = {" > ${.TARGET}
+	@cat ${.CURDIR}/http.errors \
 	  | grep -v ^# \
 	  | sort \
 	  | while read NUM CAT STRING; do \
Index: fetch.c
===================================================================
RCS file: /home/cvs/src/lib/libfetch/fetch.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/fetch.c -L lib/libfetch/fetch.c -u -r1.1.1.1 -r1.2
--- lib/libfetch/fetch.c
+++ lib/libfetch/fetch.c
@@ -24,10 +24,12 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (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.41 2007/12/19 00:26:36 des Exp $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libfetch/fetch.c,v 1.38 2004/09/21 18:35:20 des Exp $");
+__MBSDID("$MidnightBSD$");
 
 #include <sys/param.h>
 #include <sys/errno.h>
@@ -56,7 +58,7 @@
 #define URL_MALFORMED		1
 #define URL_BAD_SCHEME		2
 #define URL_BAD_PORT		3
-static struct fetcherr _url_errlist[] = {
+static struct fetcherr url_errlist[] = {
 	{ URL_MALFORMED,	FETCH_URL,	"Malformed URL" },
 	{ URL_BAD_SCHEME,	FETCH_URL,	"Invalid URL scheme" },
 	{ URL_BAD_PORT,		FETCH_URL,	"Invalid server port" },
@@ -89,7 +91,7 @@
 		return (fetchXGetHTTP(URL, us, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchXGetHTTP(URL, us, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (NULL);
 }
 
@@ -121,7 +123,7 @@
 		return (fetchPutHTTP(URL, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchPutHTTP(URL, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (NULL);
 }
 
@@ -147,7 +149,7 @@
 		return (fetchStatHTTP(URL, us, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchStatHTTP(URL, us, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (-1);
 }
 
@@ -169,7 +171,7 @@
 		return (fetchListHTTP(URL, flags));
 	else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
 		return (fetchListHTTP(URL, flags));
-	_url_seterr(URL_BAD_SCHEME);
+	url_seterr(URL_BAD_SCHEME);
 	return (NULL);
 }
 
@@ -264,23 +266,23 @@
 	struct url *u;
 
 	if (!scheme || (!host && !doc)) {
-		_url_seterr(URL_MALFORMED);
+		url_seterr(URL_MALFORMED);
 		return (NULL);
 	}
 
 	if (port < 0 || port > 65535) {
-		_url_seterr(URL_BAD_PORT);
+		url_seterr(URL_BAD_PORT);
 		return (NULL);
 	}
 
 	/* allocate struct url */
 	if ((u = calloc(1, sizeof(*u))) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
 	if ((u->doc = strdup(doc ? doc : "/")) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		free(u);
 		return (NULL);
 	}
@@ -311,7 +313,7 @@
 
 	/* allocate struct url */
 	if ((u = calloc(1, sizeof(*u))) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -369,11 +371,11 @@
 	/* port */
 	if (*p == ':') {
 		for (q = ++p; *q && (*q != '/'); q++)
-			if (isdigit(*q))
+			if (isdigit((unsigned char)*q))
 				u->port = u->port * 10 + (*q - '0');
 			else {
 				/* invalid port */
-				_url_seterr(URL_BAD_PORT);
+				url_seterr(URL_BAD_PORT);
 				goto ouch;
 			}
 		p = q;
@@ -390,12 +392,12 @@
 
 		/* percent-escape whitespace. */
 		if ((doc = malloc(strlen(p) * 3 + 1)) == NULL) {
-			_fetch_syserr();
+			fetch_syserr();
 			goto ouch;
 		}
 		u->doc = doc;
 		while (*p != '\0') {
-			if (!isspace(*p)) {
+			if (!isspace((unsigned char)*p)) {
 				*doc++ = *p++;
 			} else {
 				*doc++ = '%';
@@ -406,7 +408,7 @@
 		}
 		*doc = '\0';
 	} else if ((u->doc = strdup(p)) == NULL) {
-		_fetch_syserr();
+		fetch_syserr();
 		goto ouch;
 	}
 
Index: ftp.c
===================================================================
RCS file: /home/cvs/src/lib/libfetch/ftp.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/ftp.c -L lib/libfetch/ftp.c -u -r1.1.1.1 -r1.2
--- lib/libfetch/ftp.c
+++ lib/libfetch/ftp.c
@@ -24,10 +24,12 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (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.102 2008/02/08 09:48:48 des Exp $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libfetch/ftp.c,v 1.91 2004/09/21 18:35:20 des Exp $");
+__MBSDID("$MidnightBSD$");
 
 /*
  * Portions of this code were taken from or based on ftpio.c:
@@ -90,6 +92,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
@@ -99,11 +104,16 @@
 static struct url cached_host;
 static conn_t	*cached_connection;
 
-#define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
-			 && isdigit(foo[2]) \
-			 && (foo[3] == ' ' || foo[3] == '\0'))
-#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
-			&& isdigit(foo[2]) && foo[3] == '-')
+#define isftpreply(foo)				\
+	(isdigit((unsigned char)foo[0]) &&	\
+	    isdigit((unsigned char)foo[1]) &&	\
+	    isdigit((unsigned char)foo[2]) &&	\
+	    (foo[3] == ' ' || foo[3] == '\0'))
+#define isftpinfo(foo) \
+	(isdigit((unsigned char)foo[0]) &&	\
+	    isdigit((unsigned char)foo[1]) &&	\
+	    isdigit((unsigned char)foo[2]) &&	\
+	    foo[3] == '-')
 
 /*
  * Translate IPv4 mapped IPv6 address to IPv4 address
@@ -132,27 +142,28 @@
  * Get server response
  */
 static int
-_ftp_chkerr(conn_t *conn)
+ftp_chkerr(conn_t *conn)
 {
-	if (_fetch_getln(conn) == -1) {
-		_fetch_syserr();
+	if (fetch_getln(conn) == -1) {
+		fetch_syserr();
 		return (-1);
 	}
 	if (isftpinfo(conn->buf)) {
 		while (conn->buflen && !isftpreply(conn->buf)) {
-			if (_fetch_getln(conn) == -1) {
-				_fetch_syserr();
+			if (fetch_getln(conn) == -1) {
+				fetch_syserr();
 				return (-1);
 			}
 		}
 	}
 
-	while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))
+	while (conn->buflen &&
+	    isspace((unsigned char)conn->buf[conn->buflen - 1]))
 		conn->buflen--;
 	conn->buf[conn->buflen] = '\0';
 
 	if (!isftpreply(conn->buf)) {
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		return (-1);
 	}
 
@@ -167,7 +178,7 @@
  * Send a command and check reply
  */
 static int
-_ftp_cmd(conn_t *conn, const char *fmt, ...)
+ftp_cmd(conn_t *conn, const char *fmt, ...)
 {
 	va_list ap;
 	size_t len;
@@ -180,33 +191,77 @@
 
 	if (msg == NULL) {
 		errno = ENOMEM;
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
-	r = _fetch_putln(conn, msg, len);
+	r = fetch_putln(conn, msg, len);
 	free(msg);
 
 	if (r == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (-1);
 	}
 
-	return (_ftp_chkerr(conn));
+	return (ftp_chkerr(conn));
 }
 
 /*
  * 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);
 }
 
 /*
@@ -214,31 +269,140 @@
  * file.
  */
 static int
-_ftp_cwd(conn_t *conn, const char *file)
+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) {
-		_ftp_seterr(e);
+	/* 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
  */
 static int
-_ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
+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;
@@ -246,21 +410,24 @@
 	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) {
-		_ftp_seterr(e);
+	e = ftp_cmd(conn, "SIZE %.*s", filenamelen, filename);
+	if (e != FTP_FILE_STATUS) {
+		ftp_seterr(e);
 		return (-1);
 	}
-	for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
+	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
 		/* nothing */ ;
-	for (us->size = 0; *ln && isdigit(*ln); ln++)
+	for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
 		us->size = us->size * 10 + *ln - '0';
-	if (*ln && !isspace(*ln)) {
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+	if (*ln && !isspace((unsigned char)*ln)) {
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		us->size = -1;
 		return (-1);
 	}
@@ -268,11 +435,12 @@
 		us->size = -1;
 	DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size));
 
-	if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) {
-		_ftp_seterr(e);
+	e = ftp_cmd(conn, "MDTM %.*s", filenamelen, filename);
+	if (e != FTP_FILE_STATUS) {
+		ftp_seterr(e);
 		return (-1);
 	}
-	for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)
+	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
 		/* nothing */ ;
 	switch (strspn(ln, "0123456789")) {
 	case 14:
@@ -283,13 +451,13 @@
 		ln[1] = '0';
 		break;
 	default:
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		return (-1);
 	}
 	if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
 	    &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
 	    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
-		_ftp_seterr(FTP_PROTOCOL_ERROR);
+		ftp_seterr(FTP_PROTOCOL_ERROR);
 		return (-1);
 	}
 	tm.tm_mon--;
@@ -318,13 +486,13 @@
 	int	 err;		/* Error code */
 };
 
-static int	 _ftp_readfn(void *, char *, int);
-static int	 _ftp_writefn(void *, const char *, int);
-static fpos_t	 _ftp_seekfn(void *, fpos_t, int);
-static int	 _ftp_closefn(void *);
+static int	 ftp_readfn(void *, char *, int);
+static int	 ftp_writefn(void *, const char *, int);
+static fpos_t	 ftp_seekfn(void *, fpos_t, int);
+static int	 ftp_closefn(void *);
 
 static int
-_ftp_readfn(void *v, char *buf, int len)
+ftp_readfn(void *v, char *buf, int len)
 {
 	struct ftpio *io;
 	int r;
@@ -344,7 +512,7 @@
 	}
 	if (io->eof)
 		return (0);
-	r = _fetch_read(io->dconn, buf, len);
+	r = fetch_read(io->dconn, buf, len);
 	if (r > 0)
 		return (r);
 	if (r == 0) {
@@ -357,7 +525,7 @@
 }
 
 static int
-_ftp_writefn(void *v, const char *buf, int len)
+ftp_writefn(void *v, const char *buf, int len)
 {
 	struct ftpio *io;
 	int w;
@@ -375,7 +543,7 @@
 		errno = io->err;
 		return (-1);
 	}
-	w = _fetch_write(io->dconn, buf, len);
+	w = fetch_write(io->dconn, buf, len);
 	if (w >= 0)
 		return (w);
 	if (errno != EINTR)
@@ -384,7 +552,7 @@
 }
 
 static fpos_t
-_ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
+ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused)
 {
 	struct ftpio *io;
 
@@ -398,7 +566,7 @@
 }
 
 static int
-_ftp_closefn(void *v)
+ftp_closefn(void *v)
 {
 	struct ftpio *io;
 	int r;
@@ -414,20 +582,20 @@
 		errno = EBADF;
 		return (-1);
 	}
-	_fetch_close(io->dconn);
+	fetch_close(io->dconn);
 	io->dir = -1;
 	io->dconn = NULL;
 	DEBUG(fprintf(stderr, "Waiting for final status\n"));
-	r = _ftp_chkerr(io->cconn);
+	r = ftp_chkerr(io->cconn);
 	if (io->cconn == cached_connection && io->cconn->ref == 1)
 		cached_connection = NULL;
-	_fetch_close(io->cconn);
+	fetch_close(io->cconn);
 	free(io);
 	return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
 }
 
 static FILE *
-_ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
+ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
 {
 	struct ftpio *io;
 	FILE *f;
@@ -440,7 +608,7 @@
 	io->dconn = dconn;
 	io->dir = mode;
 	io->eof = io->err = 0;
-	f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
+	f = funopen(io, ftp_readfn, ftp_writefn, ftp_seekfn, ftp_closefn);
 	if (f == NULL)
 		free(io);
 	return (f);
@@ -450,12 +618,15 @@
  * Transfer file
  */
 static FILE *
-_ftp_transfer(conn_t *conn, const char *oper, const char *file,
+ftp_transfer(conn_t *conn, const char *oper, const char *file,
     int mode, off_t offset, const char *flags)
 {
 	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;
@@ -472,6 +643,13 @@
 		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)
@@ -481,7 +659,7 @@
 
 	/* open data socket */
 	if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		_fetch_syserr();
+		fetch_syserr();
 		return (NULL);
 	}
 
@@ -493,17 +671,17 @@
 
 		/* send PASV command */
 		if (verbose)
-			_fetch_info("setting passive mode");
+			fetch_info("setting passive mode");
 		switch (sa.ss_family) {
 		case AF_INET:
-			if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
+			if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
 				goto ouch;
 			break;
 		case AF_INET6:
-			if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
+			if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
 				if (e == -1)
 					goto ouch;
-				if ((e = _ftp_cmd(conn, "LPSV")) !=
+				if ((e = ftp_cmd(conn, "LPSV")) !=
 				    FTP_LPASSIVE_MODE)
 					goto ouch;
 			}
@@ -521,7 +699,7 @@
 		switch (e) {
 		case FTP_PASSIVE_MODE:
 		case FTP_LPASSIVE_MODE:
-			for (p = ln + 3; *p && !isdigit(*p); p++)
+			for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
 				/* nothing */ ;
 			if (!*p) {
 				e = FTP_PROTOCOL_ERROR;
@@ -555,7 +733,7 @@
 
 		/* seek to required offset */
 		if (offset)
-			if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
+			if (ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
 				goto sysouch;
 
 		/* construct sockaddr for data socket */
@@ -570,8 +748,8 @@
 			if (e == FTP_EPASSIVE_MODE)
 				sin6->sin6_port = htons(port);
 			else {
-				bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
-				bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
+				memcpy(&sin6->sin6_addr, addr + 2, 16);
+				memcpy(&sin6->sin6_port, addr + 19, 2);
 			}
 			break;
 		case AF_INET:
@@ -579,8 +757,8 @@
 			if (e == FTP_EPASSIVE_MODE)
 				sin4->sin_port = htons(port);
 			else {
-				bcopy(addr, (char *)&sin4->sin_addr, 4);
-				bcopy(addr + 4, (char *)&sin4->sin_port, 2);
+				memcpy(&sin4->sin_addr, addr, 4);
+				memcpy(&sin4->sin_port, addr + 4, 2);
 			}
 			break;
 		default:
@@ -590,14 +768,18 @@
 
 		/* connect to data port */
 		if (verbose)
-			_fetch_info("opening data connection");
+			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));
+			fetch_info("initiating transfer");
+		e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
 		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
 			goto ouch;
 
@@ -627,7 +809,7 @@
 			break;
 		}
 		if (verbose)
-			_fetch_info("binding data socket");
+			fetch_info("binding data socket");
 		if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
 			goto sysouch;
 		if (listen(sd, 1) == -1)
@@ -641,7 +823,7 @@
 			sin4 = (struct sockaddr_in *)&sa;
 			a = ntohl(sin4->sin_addr.s_addr);
 			p = ntohs(sin4->sin_port);
-			e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
+			e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
 			    (a >> 24) & 0xff, (a >> 16) & 0xff,
 			    (a >> 8) & 0xff, a & 0xff,
 			    (p >> 8) & 0xff, p & 0xff);
@@ -654,14 +836,14 @@
 			if (getnameinfo((struct sockaddr *)&sa, sa.ss_len,
 				hname, sizeof(hname),
 				NULL, 0, NI_NUMERICHOST) == 0) {
-				e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
+				e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
 				    htons(sin6->sin6_port));
 				if (e == -1)
 					goto ouch;
 			}
 			if (e != FTP_OK) {
 				ap = (char *)&sin6->sin6_addr;
-				e = _ftp_cmd(conn,
+				e = ftp_cmd(conn,
 				    "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
 				    6, 16,
 				    UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
@@ -682,13 +864,13 @@
 
 		/* seek to required offset */
 		if (offset)
-			if (_ftp_cmd(conn, "REST %ju", (uintmax_t)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));
+			fetch_info("initiating transfer");
+		e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
 		if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
 			goto ouch;
 
@@ -699,19 +881,19 @@
 		sd = d;
 	}
 
-	if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL)
+	if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL)
 		goto sysouch;
 	return (df);
 
 sysouch:
-	_fetch_syserr();
+	fetch_syserr();
 	if (sd >= 0)
 		close(sd);
 	return (NULL);
 
 ouch:
 	if (e != -1)
-		_ftp_seterr(e);
+		ftp_seterr(e);
 	if (sd >= 0)
 		close(sd);
 	return (NULL);
@@ -721,7 +903,7 @@
  * Authenticate
  */
 static int
-_ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
+ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
 {
 	const char *user, *pwd, *logname;
 	char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
@@ -731,18 +913,18 @@
 
 	/* send user name and password */
 	if (url->user[0] == '\0')
-		_fetch_netrc_auth(url);
+		fetch_netrc_auth(url);
 	user = url->user;
 	if (*user == '\0')
 		user = getenv("FTP_LOGIN");
 	if (user == NULL || *user == '\0')
 		user = FTP_ANONYMOUS_USER;
-	if (purl && url->port == _fetch_default_port(url->scheme))
-		e = _ftp_cmd(conn, "USER %s@%s", user, url->host);
+	if (purl && url->port == fetch_default_port(url->scheme))
+		e = ftp_cmd(conn, "USER %s@%s", user, url->host);
 	else if (purl)
-		e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
+		e = ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
 	else
-		e = _ftp_cmd(conn, "USER %s", user);
+		e = ftp_cmd(conn, "USER %s", user);
 
 	/* did the server request a password? */
 	if (e == FTP_NEED_PASSWORD) {
@@ -759,7 +941,7 @@
 			gethostname(pbuf + len, sizeof(pbuf) - len);
 			pwd = pbuf;
 		}
-		e = _ftp_cmd(conn, "PASS %s", pwd);
+		e = ftp_cmd(conn, "PASS %s", pwd);
 	}
 
 	return (e);
@@ -769,7 +951,7 @@
  * Log on to FTP server
  */
 static conn_t *
-_ftp_connect(struct url *url, struct url *purl, const char *flags)
+ftp_connect(struct url *url, struct url *purl, const char *flags)
 {
 	conn_t *conn;
 	int e, direct, verbose;
@@ -792,41 +974,35 @@
 	/* check for proxy */
 	if (purl) {
 		/* XXX proxy authentication! */
-		conn = _fetch_connect(purl->host, purl->port, af, verbose);
+		conn = fetch_connect(purl->host, purl->port, af, verbose);
 	} else {
 		/* no proxy, go straight to target */
-		conn = _fetch_connect(url->host, url->port, af, verbose);
+		conn = fetch_connect(url->host, url->port, af, verbose);
 		purl = NULL;
 	}
 
 	/* check connection */
 	if (conn == NULL)
-		/* _fetch_connect() has already set an error code */
+		/* fetch_connect() has already set an error code */
 		return (NULL);
 
 	/* expect welcome message */
-	if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY)
+	if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY)
 		goto fouch;
 
 	/* authenticate */
-	if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
+	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);
 
 fouch:
 	if (e != -1)
-		_ftp_seterr(e);
-	_fetch_close(conn);
+		ftp_seterr(e);
+	fetch_close(conn);
 	return (NULL);
 }
 
@@ -834,19 +1010,19 @@
  * Disconnect from server
  */
 static void
-_ftp_disconnect(conn_t *conn)
+ftp_disconnect(conn_t *conn)
 {
-	(void)_ftp_cmd(conn, "QUIT");
+	(void)ftp_cmd(conn, "QUIT");
 	if (conn == cached_connection && conn->ref == 1)
 		cached_connection = NULL;
-	_fetch_close(conn);
+	fetch_close(conn);
 }
 
 /*
  * Check if we're already connected
  */
 static int
-_ftp_isconnected(struct url *url)
+ftp_isconnected(struct url *url)
 {
 	return (cached_connection
 	    && (strcmp(url->host, cached_host.host) == 0)
@@ -859,28 +1035,28 @@
  * Check the cache, reconnect if no luck
  */
 static conn_t *
-_ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
+ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
 {
 	conn_t *conn;
 	int e;
 
 	/* set default port */
 	if (!url->port)
-		url->port = _fetch_default_port(url->scheme);
+		url->port = fetch_default_port(url->scheme);
 
 	/* try to use previously cached connection */
-	if (_ftp_isconnected(url)) {
-		e = _ftp_cmd(cached_connection, "NOOP");
+	if (ftp_isconnected(url)) {
+		e = ftp_cmd(cached_connection, "NOOP");
 		if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
-			return (_fetch_ref(cached_connection));
+			return (fetch_ref(cached_connection));
 	}
 
 	/* connect to server */
-	if ((conn = _ftp_connect(url, purl, flags)) == NULL)
+	if ((conn = ftp_connect(url, purl, flags)) == NULL)
 		return (NULL);
 	if (cached_connection)
-		_ftp_disconnect(cached_connection);
-	cached_connection = _fetch_ref(conn);
+		ftp_disconnect(cached_connection);
+	cached_connection = fetch_ref(conn);
 	memcpy(&cached_host, url, sizeof(*url));
 	return (conn);
 }
@@ -889,13 +1065,15 @@
  * Check the proxy settings
  */
 static struct url *
-_ftp_get_proxy(const char *flags)
+ftp_get_proxy(struct url * url, const char *flags)
 {
 	struct url *purl;
 	char *p;
 
 	if (flags != NULL && strchr(flags, 'd') != NULL)
 		return (NULL);
+	if (fetch_no_proxy_match(url->host))
+		return (NULL);
 	if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||
 		(p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
 	    *p && (purl = fetchParseURL(p)) != NULL) {
@@ -906,7 +1084,7 @@
 				strcpy(purl->scheme, SCHEME_HTTP);
 		}
 		if (!purl->port)
-			purl->port = _fetch_default_proxy_port(purl->scheme);
+			purl->port = fetch_default_proxy_port(purl->scheme);
 		if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
 		    strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
 			return (purl);
@@ -919,7 +1097,7 @@
  * Process an FTP request
  */
 FILE *
-_ftp_request(struct url *url, const char *op, struct url_stat *us,
+ftp_request(struct url *url, const char *op, struct url_stat *us,
     struct url *purl, const char *flags)
 {
 	conn_t *conn;
@@ -928,9 +1106,9 @@
 	/* check if we should use HTTP instead */
 	if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
 		if (strcmp(op, "STAT") == 0)
-			return (_http_request(url, "HEAD", us, purl, flags));
+			return (http_request(url, "HEAD", us, purl, flags));
 		else if (strcmp(op, "RETR") == 0)
-			return (_http_request(url, "GET", us, purl, flags));
+			return (http_request(url, "GET", us, purl, flags));
 		/*
 		 * Our HTTP code doesn't support PUT requests yet, so try
 		 * a direct connection.
@@ -938,18 +1116,18 @@
 	}
 
 	/* connect to server */
-	conn = _ftp_cached_connect(url, purl, flags);
+	conn = ftp_cached_connect(url, purl, flags);
 	if (purl)
 		fetchFreeURL(purl);
 	if (conn == NULL)
 		return (NULL);
 
 	/* change directory */
-	if (_ftp_cwd(conn, url->doc) == -1)
+	if (ftp_cwd(conn, url->doc) == -1)
 		return (NULL);
 
 	/* stat file */
-	if (us && _ftp_stat(conn, url->doc, us) == -1
+	if (us && ftp_stat(conn, url->doc, us) == -1
 	    && fetchLastErrCode != FETCH_PROTO
 	    && fetchLastErrCode != FETCH_UNAVAIL)
 		return (NULL);
@@ -963,7 +1141,7 @@
 		oflag = O_RDONLY;
 
 	/* initiate the transfer */
-	return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
+	return (ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
 }
 
 /*
@@ -972,7 +1150,7 @@
 FILE *
 fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
 {
-	return (_ftp_request(url, "RETR", us, _ftp_get_proxy(flags), flags));
+	return (ftp_request(url, "RETR", us, ftp_get_proxy(url, flags), flags));
 }
 
 /*
@@ -990,9 +1168,8 @@
 FILE *
 fetchPutFTP(struct url *url, const char *flags)
 {
-
-	return (_ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
-	    _ftp_get_proxy(flags), flags));
+	return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
+	    ftp_get_proxy(url, flags), flags));
 }
 
 /*
@@ -1003,9 +1180,14 @@
 {
 	FILE *f;
 
-	f = _ftp_request(url, "STAT", us, _ftp_get_proxy(flags), flags);
+	f = ftp_request(url, "STAT", us, ftp_get_proxy(url, flags), flags);
 	if (f == NULL)
 		return (-1);
+	/*
+	 * When op is "STAT", ftp_request() will return either NULL or
+	 * (FILE *)1, never a valid FILE *, so we mustn't fclose(f) before
+	 * returning, as it would cause a segfault.
+	 */
 	return (0);
 }
 
Index: fetch.h
===================================================================
RCS file: /home/cvs/src/lib/libfetch/fetch.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L lib/libfetch/fetch.h -L lib/libfetch/fetch.h -u -r1.1.1.1 -r1.2
--- lib/libfetch/fetch.h
+++ lib/libfetch/fetch.h
@@ -25,6 +25,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
+ * $MidnightBSD$
  * $FreeBSD: src/lib/libfetch/fetch.h,v 1.26 2004/09/21 18:35:20 des Exp $
  */
 


More information about the Midnightbsd-cvs mailing list