Module Name:    src
Committed By:   christos
Date:           Wed Dec 16 19:17:16 UTC 2015

Modified Files:
        src/usr.bin/ftp: fetch.c

Log Message:
more refactoring:
        - introduce authinfo and urlinfo structures
        - split negotiation code out.


To generate a diff of this commit:
cvs rdiff -u -r1.213 -r1.214 src/usr.bin/ftp/fetch.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/ftp/fetch.c
diff -u src/usr.bin/ftp/fetch.c:1.213 src/usr.bin/ftp/fetch.c:1.214
--- src/usr.bin/ftp/fetch.c:1.213	Tue Dec 15 20:20:05 2015
+++ src/usr.bin/ftp/fetch.c	Wed Dec 16 14:17:16 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: fetch.c,v 1.213 2015/12/16 01:20:05 nonaka Exp $	*/
+/*	$NetBSD: fetch.c,v 1.214 2015/12/16 19:17:16 christos Exp $	*/
 
 /*-
  * Copyright (c) 1997-2015 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: fetch.c,v 1.213 2015/12/16 01:20:05 nonaka Exp $");
+__RCSID("$NetBSD: fetch.c,v 1.214 2015/12/16 19:17:16 christos Exp $");
 #endif /* not lint */
 
 /*
@@ -80,19 +80,35 @@ typedef enum {
 	CLASSIC_URL_T
 } url_t;
 
+struct authinfo {
+	char *auth;
+	char *user;
+	char *pass;
+};
+
+struct urlinfo {
+	char *host;
+	char *port;
+	char *path;
+	url_t utype;
+	in_port_t portnum;
+};
+
 __dead static void	aborthttp(int);
 __dead static void	timeouthttp(int);
 #ifndef NO_AUTH
-static int	auth_url(const char *, char **, const char *, const char *);
+static int	auth_url(const char *, char **, const struct authinfo *);
 static void	base64_encode(const unsigned char *, size_t, unsigned char *);
 #endif
 static int	go_fetch(const char *);
 static int	fetch_ftp(const char *);
 static int	fetch_url(const char *, const char *, char *, char *);
 static const char *match_token(const char **, const char *);
-static int	parse_url(const char *, const char *, url_t *, char **,
-			    char **, char **, char **, in_port_t *, char **);
+static int	parse_url(const char *, const char *, struct urlinfo *,
+    struct authinfo *);
 static void	url_decode(char *);
+static void	freeauthinfo(struct authinfo *);
+static void	freeurlinfo(struct urlinfo *);
 
 static int	redirect_loop;
 
@@ -144,6 +160,38 @@ match_token(const char **buf, const char
 	return orig;
 }
 
+static void
+initauthinfo(struct authinfo *ai, char *auth)
+{
+	ai->auth = auth;
+	ai->user = ai->pass = 0;
+}
+
+static void
+freeauthinfo(struct authinfo *a)
+{
+	FREEPTR(a->user);
+	if (a->pass != NULL)
+		memset(a->pass, 0, strlen(a->pass));
+	FREEPTR(a->pass);
+}
+
+static void
+initurlinfo(struct urlinfo *ui)
+{
+	ui->host = ui->port = ui->path = 0;
+	ui->utype = UNKNOWN_URL_T;
+	ui->portnum = 0;
+}
+
+static void
+freeurlinfo(struct urlinfo *ui)
+{
+	FREEPTR(ui->host);
+	FREEPTR(ui->port);
+	FREEPTR(ui->path);
+}
+
 #ifndef NO_AUTH
 /*
  * Generate authorization response based on given authentication challenge.
@@ -151,8 +199,7 @@ match_token(const char **buf, const char
  * Sets response to a malloc(3)ed string; caller should free.
  */
 static int
-auth_url(const char *challenge, char **response, const char *guser,
-	const char *gpass)
+auth_url(const char *challenge, char **response, const struct authinfo *auth)
 {
 	const char	*cp, *scheme, *errormsg;
 	char		*ep, *clear, *realm;
@@ -196,8 +243,8 @@ auth_url(const char *challenge, char **r
 	}
 
 	fprintf(ttyout, "Username for `%s': ", realm);
-	if (guser != NULL) {
-		(void)strlcpy(uuser, guser, sizeof(uuser));
+	if (auth->user != NULL) {
+		(void)strlcpy(uuser, auth->user, sizeof(uuser));
 		fprintf(ttyout, "%s\n", uuser);
 	} else {
 		(void)fflush(ttyout);
@@ -206,8 +253,8 @@ auth_url(const char *challenge, char **r
 			goto cleanup_auth_url;
 		}
 	}
-	if (gpass != NULL)
-		upass = gpass;
+	if (auth->pass != NULL)
+		upass = auth->pass;
 	else {
 		gotpass = getpass("Password: ");
 		if (gotpass == NULL) {
@@ -227,7 +274,7 @@ auth_url(const char *challenge, char **r
 
 						/* scheme + " " + enc + "\0" */
 	rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1;
-	*response = (char *)ftp_malloc(rlen);
+	*response = ftp_malloc(rlen);
 	(void)strlcpy(*response, scheme, rlen);
 	len = strlcat(*response, " ", rlen);
 			/* use  `clen - 1'  to not encode the trailing NUL */
@@ -326,57 +373,47 @@ url_decode(char *url)
  *	"ftp://host/dir/file";		"dir/file"
  *	"ftp://host//dir/file";		"/dir/file"
  */
+
 static int
-parse_url(const char *url, const char *desc, url_t *utype,
-		char **uuser, char **pass, char **host, char **port,
-		in_port_t *portnum, char **path)
+parse_url(const char *url, const char *desc, struct urlinfo *ui,
+    struct authinfo *auth) 
 {
 	const char	*origurl, *tport;
 	char		*cp, *ep, *thost;
 	size_t		 len;
 
-	if (url == NULL || desc == NULL || utype == NULL || uuser == NULL
-	    || pass == NULL || host == NULL || port == NULL || portnum == NULL
-	    || path == NULL)
+	if (url == NULL || desc == NULL || ui == NULL || auth == NULL)
 		errx(1, "parse_url: invoked with NULL argument!");
 	DPRINTF("parse_url: %s `%s'\n", desc, url);
 
 	origurl = url;
-	*utype = UNKNOWN_URL_T;
-	*uuser = *pass = *host = *port = *path = NULL;
-	*portnum = 0;
 	tport = NULL;
 
 	if (STRNEQUAL(url, HTTP_URL)) {
 		url += sizeof(HTTP_URL) - 1;
-		*utype = HTTP_URL_T;
-		*portnum = HTTP_PORT;
+		ui->utype = HTTP_URL_T;
+		ui->portnum = HTTP_PORT;
 		tport = httpport;
 	} else if (STRNEQUAL(url, FTP_URL)) {
 		url += sizeof(FTP_URL) - 1;
-		*utype = FTP_URL_T;
-		*portnum = FTP_PORT;
+		ui->utype = FTP_URL_T;
+		ui->portnum = FTP_PORT;
 		tport = ftpport;
 	} else if (STRNEQUAL(url, FILE_URL)) {
 		url += sizeof(FILE_URL) - 1;
-		*utype = FILE_URL_T;
+		ui->utype = FILE_URL_T;
 #ifdef WITH_SSL
 	} else if (STRNEQUAL(url, HTTPS_URL)) {
 		url += sizeof(HTTPS_URL) - 1;
-		*utype = HTTPS_URL_T;
-		*portnum = HTTPS_PORT;
+		ui->utype = HTTPS_URL_T;
+		ui->portnum = HTTPS_PORT;
 		tport = httpsport;
 #endif
 	} else {
 		warnx("Invalid %s `%s'", desc, url);
  cleanup_parse_url:
-		FREEPTR(*uuser);
-		if (*pass != NULL)
-			memset(*pass, 0, strlen(*pass));
-		FREEPTR(*pass);
-		FREEPTR(*host);
-		FREEPTR(*port);
-		FREEPTR(*path);
+		freeauthinfo(auth);
+		freeurlinfo(ui);
 		return (-1);
 	}
 
@@ -391,26 +428,26 @@ parse_url(const char *url, const char *d
 		len = ep - url;
 		thost = (char *)ftp_malloc(len + 1);
 		(void)strlcpy(thost, url, len + 1);
-		if (*utype == FTP_URL_T)	/* skip first / for ftp URLs */
+		if (ui->utype == FTP_URL_T)	/* skip first / for ftp URLs */
 			ep++;
-		*path = ftp_strdup(ep);
+		ui->path = ftp_strdup(ep);
 	}
 
 	cp = strchr(thost, '@');	/* look for user[:pass]@ in URLs */
 	if (cp != NULL) {
-		if (*utype == FTP_URL_T)
+		if (ui->utype == FTP_URL_T)
 			anonftp = 0;	/* disable anonftp */
-		*uuser = thost;
+		auth->user = thost;
 		*cp = '\0';
 		thost = ftp_strdup(cp + 1);
-		cp = strchr(*uuser, ':');
+		cp = strchr(auth->user, ':');
 		if (cp != NULL) {
 			*cp = '\0';
-			*pass = ftp_strdup(cp + 1);
+			auth->pass = ftp_strdup(cp + 1);
 		}
-		url_decode(*uuser);
-		if (*pass)
-			url_decode(*pass);
+		url_decode(auth->user);
+		if (auth->pass)
+			url_decode(auth->pass);
 	}
 
 #ifdef INET6
@@ -444,7 +481,7 @@ parse_url(const char *url, const char *d
 #endif /* INET6 */
 		if ((cp = strchr(thost, ':')) != NULL)
 			*cp++ = '\0';
-	*host = thost;
+	ui->host = thost;
 
 			/* look for [:port] */
 	if (cp != NULL) {
@@ -457,24 +494,24 @@ parse_url(const char *url, const char *d
 			    cp, desc, origurl);
 			goto cleanup_parse_url;
 		}
-		*portnum = nport;
+		ui->portnum = nport;
 		tport = cp;
 	}
 
 	if (tport != NULL)
-		*port = ftp_strdup(tport);
-	if (*path == NULL) {
+		ui->port = ftp_strdup(tport);
+	if (ui->path == NULL) {
 		const char *emptypath = "/";
-		if (*utype == FTP_URL_T)	/* skip first / for ftp URLs */
+		if (ui->utype == FTP_URL_T)	/* skip first / for ftp URLs */
 			emptypath++;
-		*path = ftp_strdup(emptypath);
+		ui->path = ftp_strdup(emptypath);
 	}
 
 	DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) "
 	    "path `%s'\n",
-	    STRorNULL(*uuser), STRorNULL(*pass),
-	    STRorNULL(*host), STRorNULL(*port),
-	    *portnum ? *portnum : -1, STRorNULL(*path));
+	    STRorNULL(auth->user), STRorNULL(auth->pass),
+	    STRorNULL(ui->host), STRorNULL(ui->port),
+	    ui->portnum ? ui->portnum : -1, STRorNULL(ui->path));
 
 	return (0);
 }
@@ -482,11 +519,16 @@ parse_url(const char *url, const char *d
 sigjmp_buf	httpabort;
 
 static int
-ftp_socket(const char *host, const char *port, void **ssl)
+ftp_socket(const struct urlinfo *ui, void **ssl)
 {
 	struct addrinfo	hints, *res, *res0 = NULL;
 	int error;
 	int s;
+	const char *host = ui->host;
+	const char *port = ui->port;
+
+	if (ui->utype != HTTPS_URL_T)
+		ssl = NULL;
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_flags = 0;
@@ -604,45 +646,436 @@ handle_noproxy(const char *host, in_port
 }
 
 static int
-handle_proxy(const char *penv, char **host, char **port, char **puser,
-    char **ppass, url_t *urltype)
+handle_proxy(const char *url, const char *penv, struct urlinfo *ui,
+    struct authinfo *pauth)
 {
-	url_t purltype;
-	char *phost, *ppath;
-	char *pport;
-	in_port_t pportnum;
+	struct urlinfo pui;
 
-	if (isipv6addr(*host) && strchr(*host, '%') != NULL) {
+	if (isipv6addr(ui->host) && strchr(ui->host, '%') != NULL) {
 		warnx("Scoped address notation `%s' disallowed via web proxy",
-		    *host);
+		    ui->host);
 		return -1;
 	}
 
-	if (parse_url(penv, "proxy URL", &purltype, puser, ppass, &phost,
-	    &pport, &pportnum, &ppath) == -1)
+	initurlinfo(&pui);
+	if (parse_url(penv, "proxy URL", &pui, pauth) == -1)
 		return -1;
 
-	if ((!IS_HTTP_TYPE(purltype) && purltype != FTP_URL_T) ||
-	    EMPTYSTRING(phost) ||
-	    (! EMPTYSTRING(ppath) && strcmp(ppath, "/") != 0)) {
+	if ((!IS_HTTP_TYPE(pui.utype) && pui.utype != FTP_URL_T) ||
+	    EMPTYSTRING(pui.host) ||
+	    (! EMPTYSTRING(pui.path) && strcmp(pui.path, "/") != 0)) {
 		warnx("Malformed proxy URL `%s'", penv);
-		FREEPTR(phost);
-		FREEPTR(pport);
-		FREEPTR(ppath);
+		freeurlinfo(&pui);
 		return -1;
 	}
-	FREEPTR(ppath);
 
-	FREEPTR(*host);
-	*host = phost;
-	FREEPTR(*port);
-	*port = pport;
+	FREEPTR(pui.path);
+	pui.path = ftp_strdup(url);
 
-	*urltype = purltype;
+	freeurlinfo(ui);
+	*ui = pui;
 
 	return 0;
 }
 
+static void
+print_host(FETCH *fin, const char *host)
+{
+	char *h, *p;
+
+	if (strchr(host, ':') == NULL) {
+		fetch_printf(fin, "Host: %s", host);
+		return;
+	}
+
+	/*
+	 * strip off IPv6 scope identifier, since it is
+	 * local to the node
+	 */
+	h = ftp_strdup(host);
+	if (isipv6addr(h) && (p = strchr(h, '%')) != NULL)
+		*p = '\0';
+
+	fetch_printf(fin, "Host: [%s]", h);
+	free(h);
+}
+
+static void
+print_agent(FETCH *fin)
+{
+	const char *useragent;
+	if ((useragent = getenv("FTPUSERAGENT")) != NULL) {
+		fetch_printf(fin, "User-Agent: %s\r\n", useragent);
+	} else {
+		fetch_printf(fin, "User-Agent: %s/%s\r\n",
+		    FTP_PRODUCT, FTP_VERSION);
+	}
+}
+
+static void
+print_cache(FETCH *fin, int isproxy)
+{
+	fetch_printf(fin, isproxy ?
+	    "Pragma: no-cache\r\n" :
+	    "Cache-Control: no-cache\r\n");
+}
+
+static int
+print_get(FETCH *fin, int isproxy, const struct urlinfo *ui)
+{
+	int hasleading = 0;
+	const char *leading = "  (";
+
+	if (isproxy) {
+		if (verbose) {
+			fprintf(ttyout, "%svia %s:%u", leading,
+			    ui->host, ui->portnum);
+			leading = ", ";
+			hasleading++;
+		}
+		fetch_printf(fin, "GET %s HTTP/1.0\r\n", ui->path);
+		return hasleading;
+	}
+
+	fetch_printf(fin, "GET %s HTTP/1.1\r\n", ui->path);
+	print_host(fin, ui->host);
+
+	if ((ui->utype == HTTP_URL_T && ui->portnum != HTTP_PORT) ||
+	    (ui->utype == HTTPS_URL_T && ui->portnum != HTTPS_PORT))
+		fetch_printf(fin, ":%u", ui->portnum);
+
+	fetch_printf(fin, "\r\n");
+	fetch_printf(fin, "Accept: */*\r\n");
+	fetch_printf(fin, "Connection: close\r\n");
+	if (restart_point) {
+		fputs(leading, ttyout);
+		fetch_printf(fin, "Range: bytes=" LLF "-\r\n",
+		    (LLT)restart_point);
+		fprintf(ttyout, "restarting at " LLF, (LLT)restart_point);
+		hasleading++;
+	}
+	return hasleading;
+}
+
+static void
+getmtime(const char *cp, time_t *mtime)
+{
+	struct tm parsed;
+	const char *t;
+
+	memset(&parsed, 0, sizeof(parsed));
+	t = parse_rfc2616time(&parsed, cp);
+
+	if (t == NULL)
+		return;
+
+	parsed.tm_isdst = -1;
+	if (*t == '\0')
+		*mtime = timegm(&parsed);
+
+#ifndef NO_DEBUG
+	if (ftp_debug && *mtime != -1) {
+		fprintf(ttyout, "parsed time as: %s",
+		    rfc2822time(localtime(mtime)));
+	}
+#endif
+}
+
+static int
+print_proxy(FETCH *fin, const char *leading,
+    const char *wwwauth, const char *proxyauth)
+{
+	int hasleading = 0;
+
+	if (wwwauth) {
+		if (verbose) {
+			fprintf(ttyout, "%swith authorization", leading);
+			hasleading++;
+		}
+		fetch_printf(fin, "Authorization: %s\r\n", wwwauth);
+	}
+	if (proxyauth) {
+		if (verbose) {
+			fprintf(ttyout, "%swith proxy authorization", leading);
+			hasleading++;
+		}
+		fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
+	}
+	return hasleading;
+}
+
+#define C_OK 0
+#define C_CLEANUP 1
+#define C_IMPROPER 2
+
+
+static int
+negotiate_connection(FETCH *fin, const char *url, const char *penv,
+    off_t *rangestart, off_t *rangeend, off_t *entitylen,
+    time_t *mtime, struct authinfo *wauth, struct authinfo *pauth,
+    int *rval, int *ischunked)
+{
+	int			len, hcode, rv;
+	char			buf[FTPBUFLEN], *ep;
+	const char		*errormsg, *cp, *token;
+	char 			*location, *message, *auth;
+
+	auth = message = location = NULL;
+
+	/* Read the response */
+	alarmtimer(quit_time ? quit_time : 60);
+	len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
+	alarmtimer(0);
+	if (len < 0) {
+		if (*errormsg == '\n')
+			errormsg++;
+		warnx("Receiving HTTP reply: %s", errormsg);
+		goto cleanup_fetch_url;
+	}
+	while (len > 0 && (ISLWS(buf[len-1])))
+		buf[--len] = '\0';
+
+	DPRINTF("%s: received `%s'\n", __func__, buf);
+
+	/* Determine HTTP response code */
+	cp = strchr(buf, ' ');
+	if (cp == NULL)
+		goto improper;
+	else
+		cp++;
+
+	hcode = strtol(cp, &ep, 10);
+	if (*ep != '\0' && !isspace((unsigned char)*ep))
+		goto improper;
+
+	message = ftp_strdup(cp);
+
+	/* Read the rest of the header. */
+
+	for (;;) {
+		alarmtimer(quit_time ? quit_time : 60);
+		len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
+		alarmtimer(0);
+		if (len < 0) {
+			if (*errormsg == '\n')
+				errormsg++;
+			warnx("Receiving HTTP reply: %s", errormsg);
+			goto cleanup_fetch_url;
+		}
+		while (len > 0 && (ISLWS(buf[len-1])))
+			buf[--len] = '\0';
+		if (len == 0)
+			break;
+		DPRINTF("%s: received `%s'\n", __func__, buf);
+
+	/*
+	 * Look for some headers
+	 */
+
+		cp = buf;
+
+		if (match_token(&cp, "Content-Length:")) {
+			filesize = STRTOLL(cp, &ep, 10);
+			if (filesize < 0 || *ep != '\0')
+				goto improper;
+			DPRINTF("%s: parsed len as: " LLF "\n",
+			    __func__, (LLT)filesize);
+
+		} else if (match_token(&cp, "Content-Range:")) {
+			if (! match_token(&cp, "bytes"))
+				goto improper;
+
+			if (*cp == '*')
+				cp++;
+			else {
+				*rangestart = STRTOLL(cp, &ep, 10);
+				if (*rangestart < 0 || *ep != '-')
+					goto improper;
+				cp = ep + 1;
+				*rangeend = STRTOLL(cp, &ep, 10);
+				if (*rangeend < 0 || *rangeend < *rangestart)
+					goto improper;
+				cp = ep;
+			}
+			if (*cp != '/')
+				goto improper;
+			cp++;
+			if (*cp == '*')
+				cp++;
+			else {
+				*entitylen = STRTOLL(cp, &ep, 10);
+				if (*entitylen < 0)
+					goto improper;
+				cp = ep;
+			}
+			if (*cp != '\0')
+				goto improper;
+
+#ifndef NO_DEBUG
+			if (ftp_debug) {
+				fprintf(ttyout, "parsed range as: ");
+				if (*rangestart == -1)
+					fprintf(ttyout, "*");
+				else
+					fprintf(ttyout, LLF "-" LLF,
+					    (LLT)*rangestart,
+					    (LLT)*rangeend);
+				fprintf(ttyout, "/" LLF "\n", (LLT)*entitylen);
+			}
+#endif
+			if (! restart_point) {
+				warnx(
+			    "Received unexpected Content-Range header");
+				goto cleanup_fetch_url;
+			}
+
+		} else if (match_token(&cp, "Last-Modified:")) {
+			getmtime(cp, mtime);
+
+		} else if (match_token(&cp, "Location:")) {
+			location = ftp_strdup(cp);
+			DPRINTF("%s: parsed location as `%s'\n",
+			    __func__, cp);
+
+		} else if (match_token(&cp, "Transfer-Encoding:")) {
+			if (match_token(&cp, "binary")) {
+				warnx(
+		"Bogus transfer encoding `binary' (fetching anyway)");
+				continue;
+			}
+			if (! (token = match_token(&cp, "chunked"))) {
+				warnx(
+			    "Unsupported transfer encoding `%s'",
+				    token);
+				goto cleanup_fetch_url;
+			}
+			(*ischunked)++;
+			DPRINTF("%s: using chunked encoding\n",
+			    __func__);
+
+		} else if (match_token(&cp, "Proxy-Authenticate:")
+			|| match_token(&cp, "WWW-Authenticate:")) {
+			if (! (token = match_token(&cp, "Basic"))) {
+				DPRINTF("%s: skipping unknown auth "
+				    "scheme `%s'\n", __func__, token);
+				continue;
+			}
+			FREEPTR(auth);
+			auth = ftp_strdup(token);
+			DPRINTF("%s: parsed auth as `%s'\n",
+			    __func__, cp);
+		}
+
+	}
+			/* finished parsing header */
+
+	switch (hcode) {
+	case 200:
+		break;
+	case 206:
+		if (! restart_point) {
+			warnx("Not expecting partial content header");
+			goto cleanup_fetch_url;
+		}
+		break;
+	case 300:
+	case 301:
+	case 302:
+	case 303:
+	case 305:
+	case 307:
+		if (EMPTYSTRING(location)) {
+			warnx(
+			"No redirection Location provided by server");
+			goto cleanup_fetch_url;
+		}
+		if (redirect_loop++ > 5) {
+			warnx("Too many redirections requested");
+			goto cleanup_fetch_url;
+		}
+		if (hcode == 305) {
+			if (verbose)
+				fprintf(ttyout, "Redirected via %s\n",
+				    location);
+			*rval = fetch_url(url, location,
+			    pauth->auth, wauth->auth);
+		} else {
+			if (verbose)
+				fprintf(ttyout, "Redirected to %s\n",
+				    location);
+			*rval = go_fetch(location);
+		}
+		goto cleanup_fetch_url;
+#ifndef NO_AUTH
+	case 401:
+	case 407:
+	    {
+		struct  authinfo aauth;
+		char **authp;
+
+		if (hcode == 401)
+			aauth = *wauth;
+		else
+			aauth = *pauth;
+		
+		if (verbose || aauth.auth == NULL ||
+		    aauth.user == NULL || aauth.pass == NULL)
+			fprintf(ttyout, "%s\n", message);
+		if (EMPTYSTRING(auth)) {
+			warnx(
+		    "No authentication challenge provided by server");
+			goto cleanup_fetch_url;
+		}
+
+		if (aauth.auth != NULL) {
+			char reply[10];
+
+			fprintf(ttyout,
+			    "Authorization failed. Retry (y/n)? ");
+			if (get_line(stdin, reply, sizeof(reply), NULL)
+			    < 0) {
+				goto cleanup_fetch_url;
+			}
+			if (tolower((unsigned char)reply[0]) != 'y')
+				goto cleanup_fetch_url;
+			aauth.user = NULL;
+			aauth.pass = NULL;
+		}
+
+		authp = &aauth.auth;
+		if (auth_url(auth, authp, &aauth) == 0) {
+			*rval = fetch_url(url, penv,
+			    pauth->auth, wauth->auth);
+			memset(*authp, 0, strlen(*authp));
+			FREEPTR(*authp);
+		}
+		goto cleanup_fetch_url;
+	    }
+#endif
+	default:
+		if (message)
+			warnx("Error retrieving file `%s'", message);
+		else
+			warnx("Unknown error retrieving file");
+		goto cleanup_fetch_url;
+	}
+	rv = C_OK;
+	goto out;
+
+cleanup_fetch_url:
+	rv = C_CLEANUP;
+	goto out;
+improper:
+	rv = C_IMPROPER;
+	goto out;
+out:
+	FREEPTR(auth);
+	FREEPTR(message);
+	FREEPTR(location);
+	return rv;
+}		/* end of ftp:// or http:// specific setup */
+
+
 /*
  * Retrieve URL, via a proxy if necessary, using HTTP.
  * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or
@@ -660,33 +1093,26 @@ fetch_url(const char *url, const char *p
 	sigfunc volatile	oldquit;
 	int volatile		s;
 	struct stat		sb;
-	int volatile		ischunked;
 	int volatile		isproxy;
-	int volatile		rval;
-	int volatile		hcode;
-	int			len;
+	int 			rval, ischunked;
 	size_t			flen;
 	static size_t		bufsize;
 	static char		*xferbuf;
-	const char		*cp, *token;
+	const char		*cp;
 	char			*ep;
-	char			buf[FTPBUFLEN];
-	const char		*errormsg;
 	char			*volatile savefile;
 	char			*volatile auth;
 	char			*volatile location;
 	char			*volatile message;
-	char			*uuser, *pass, *host, *port, *path;
 	char			*volatile decodedpath;
-	char			*puser, *ppass, *useragent;
+	struct authinfo 	wauth, pauth;
 	off_t			hashbytes, rangestart, rangeend, entitylen;
 	int			(*volatile closefunc)(FILE *);
 	FETCH			*volatile fin;
 	FILE			*volatile fout;
 	const char		*volatile penv = proxyenv;
+	struct urlinfo		ui;
 	time_t			mtime;
-	url_t			urltype;
-	in_port_t		portnum;
 	void			*ssl = NULL;
 
 	DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv));
@@ -698,35 +1124,39 @@ fetch_url(const char *url, const char *p
 	s = -1;
 	savefile = NULL;
 	auth = location = message = NULL;
-	ischunked = isproxy = hcode = 0;
+	ischunked = isproxy = 0;
 	rval = 1;
-	uuser = pass = host = path = decodedpath = puser = ppass = NULL;
+
+	initurlinfo(&ui);
+	initauthinfo(&wauth, wwwauth);
+	initauthinfo(&pauth, proxyauth);
+
+	 decodedpath = NULL;
 
 	if (sigsetjmp(httpabort, 1))
 		goto cleanup_fetch_url;
 
-	if (parse_url(url, "URL", &urltype, &uuser, &pass, &host, &port,
-	    &portnum, &path) == -1)
+	if (parse_url(url, "URL", &ui, &wauth) == -1)
 		goto cleanup_fetch_url;
 
-	if (urltype == FILE_URL_T && ! EMPTYSTRING(host)
-	    && strcasecmp(host, "localhost") != 0) {
+	if (ui.utype == FILE_URL_T && ! EMPTYSTRING(ui.host)
+	    && strcasecmp(ui.host, "localhost") != 0) {
 		warnx("No support for non local file URL `%s'", url);
 		goto cleanup_fetch_url;
 	}
 
-	if (EMPTYSTRING(path)) {
-		if (urltype == FTP_URL_T) {
+	if (EMPTYSTRING(ui.path)) {
+		if (ui.utype == FTP_URL_T) {
 			rval = fetch_ftp(url);
 			goto cleanup_fetch_url;
 		}
-		if (!IS_HTTP_TYPE(urltype) || outfile == NULL)  {
+		if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL)  {
 			warnx("Invalid URL (no file after host) `%s'", url);
 			goto cleanup_fetch_url;
 		}
 	}
 
-	decodedpath = ftp_strdup(path);
+	decodedpath = ftp_strdup(ui.path);
 	url_decode(decodedpath);
 
 	if (outfile)
@@ -740,7 +1170,7 @@ fetch_url(const char *url, const char *p
 	}
 	DPRINTF("%s: savefile `%s'\n", __func__, savefile);
 	if (EMPTYSTRING(savefile)) {
-		if (urltype == FTP_URL_T) {
+		if (ui.utype == FTP_URL_T) {
 			rval = fetch_ftp(url);
 			goto cleanup_fetch_url;
 		}
@@ -757,7 +1187,7 @@ fetch_url(const char *url, const char *p
 		if (stat(savefile, &sb) == 0)
 			restart_point = sb.st_size;
 	}
-	if (urltype == FILE_URL_T) {		/* file:// URLs */
+	if (ui.utype == FILE_URL_T) {		/* file:// URLs */
 		direction = "copied";
 		fin = fetch_open(decodedpath, "r");
 		if (fin == NULL) {
@@ -791,20 +1221,20 @@ fetch_url(const char *url, const char *p
 
 		if (penv == NULL) {
 #ifdef WITH_SSL
-			if (urltype == HTTPS_URL_T)
+			if (ui.utype == HTTPS_URL_T)
 				penv = getoptionvalue("https_proxy");
 #endif
-			if (penv == NULL && IS_HTTP_TYPE(urltype))
+			if (penv == NULL && IS_HTTP_TYPE(ui.utype))
 				penv = getoptionvalue("http_proxy");
-			else if (urltype == FTP_URL_T)
+			else if (ui.utype == FTP_URL_T)
 				penv = getoptionvalue("ftp_proxy");
 		}
 		direction = "retrieved";
 		if (! EMPTYSTRING(penv)) {			/* use proxy */
 
-			isproxy = handle_noproxy(host, portnum);
+			isproxy = handle_noproxy(ui.host, ui.portnum);
 
-			if (isproxy == 0 && urltype == FTP_URL_T) {
+			if (isproxy == 0 && ui.utype == FTP_URL_T) {
 				rval = fetch_ftp(url);
 				goto cleanup_fetch_url;
 			}
@@ -816,20 +1246,14 @@ fetch_url(const char *url, const char *p
 					    penv);
 					goto cleanup_fetch_url;
 				}
-				if (handle_proxy(penv, &host, &port,
-				    &puser, &ppass, &urltype) < 0) {
+				if (handle_proxy(url, penv, &ui, &pauth) < 0)
 					goto cleanup_fetch_url;
-				} else {
-				    FREEPTR(path);
-				    path = ftp_strdup(url);
-				}
 			}
 		} /* ! EMPTYSTRING(penv) */
 
-		s = ftp_socket(host, port,
-		    urltype == HTTPS_URL_T ? &ssl : NULL);
+		s = ftp_socket(&ui, &ssl);
 		if (s < 0) {
-			warnx("Can't connect to `%s:%s'", host, port);
+			warnx("Can't connect to `%s:%s'", ui.host, ui.port);
 			goto cleanup_fetch_url;
 		}
 
@@ -845,84 +1269,24 @@ fetch_url(const char *url, const char *p
 		 */
 		if (verbose)
 			fprintf(ttyout, "Requesting %s\n", url);
-		leading = "  (";
-		hasleading = 0;
-		if (isproxy) {
-			if (verbose) {
-				fprintf(ttyout, "%svia %s:%s", leading,
-				    host, port);
-				leading = ", ";
-				hasleading++;
-			}
-			fetch_printf(fin, "GET %s HTTP/1.0\r\n", path);
-			if (flushcache)
-				fetch_printf(fin, "Pragma: no-cache\r\n");
-		} else {
-			fetch_printf(fin, "GET %s HTTP/1.1\r\n", path);
-			if (strchr(host, ':')) {
-				char *h, *p;
 
-				/*
-				 * strip off IPv6 scope identifier, since it is
-				 * local to the node
-				 */
-				h = ftp_strdup(host);
-				if (isipv6addr(h) &&
-				    (p = strchr(h, '%')) != NULL) {
-					*p = '\0';
-				}
-				fetch_printf(fin, "Host: [%s]", h);
-				free(h);
-			} else
-				fetch_printf(fin, "Host: %s", host);
-#ifdef WITH_SSL
-			if ((urltype == HTTP_URL_T && portnum != HTTP_PORT) ||
-			    (urltype == HTTPS_URL_T && portnum != HTTPS_PORT))
-#else
-			if (portnum != HTTP_PORT)
-#endif
-				fetch_printf(fin, ":%u", portnum);
-			fetch_printf(fin, "\r\n");
-			fetch_printf(fin, "Accept: */*\r\n");
-			fetch_printf(fin, "Connection: close\r\n");
-			if (restart_point) {
-				fputs(leading, ttyout);
-				fetch_printf(fin, "Range: bytes=" LLF "-\r\n",
-				    (LLT)restart_point);
-				fprintf(ttyout, "restarting at " LLF,
-				    (LLT)restart_point);
-				leading = ", ";
-				hasleading++;
-			}
-			if (flushcache)
-				fetch_printf(fin, "Cache-Control: no-cache\r\n");
-		}
-		if ((useragent=getenv("FTPUSERAGENT")) != NULL) {
-			fetch_printf(fin, "User-Agent: %s\r\n", useragent);
-		} else {
-			fetch_printf(fin, "User-Agent: %s/%s\r\n",
-			    FTP_PRODUCT, FTP_VERSION);
-		}
-		if (wwwauth) {
-			if (verbose) {
-				fprintf(ttyout, "%swith authorization",
-				    leading);
-				leading = ", ";
-				hasleading++;
-			}
-			fetch_printf(fin, "Authorization: %s\r\n", wwwauth);
-		}
-		if (proxyauth) {
-			if (verbose) {
-				fprintf(ttyout,
-				    "%swith proxy authorization", leading);
-				leading = ", ";
-				hasleading++;
-			}
-			fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
+		hasleading = print_get(fin, isproxy, &ui);
+		if (hasleading)
+			leading = ", ";
+		else
+			leading = "  (";
+
+		if (flushcache)
+			print_cache(fin, isproxy);
+
+		print_agent(fin);
+		hasleading = print_proxy(fin, leading, wauth.auth, pauth.auth);
+		if (hasleading) {
+			leading = ", ";
+			if (verbose)
+				fputs(")\n", ttyout);
 		}
-		if (verbose && hasleading)
-			fputs(")\n", ttyout);
+
 		fetch_printf(fin, "\r\n");
 		if (fetch_flush(fin) == EOF) {
 			warn("Writing HTTP request");
@@ -931,257 +1295,19 @@ fetch_url(const char *url, const char *p
 		}
 		alarmtimer(0);
 
-				/* Read the response */
-		alarmtimer(quit_time ? quit_time : 60);
-		len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
-		alarmtimer(0);
-		if (len < 0) {
-			if (*errormsg == '\n')
-				errormsg++;
-			warnx("Receiving HTTP reply: %s", errormsg);
-			goto cleanup_fetch_url;
-		}
-		while (len > 0 && (ISLWS(buf[len-1])))
-			buf[--len] = '\0';
-		DPRINTF("%s: received `%s'\n", __func__, buf);
-
-				/* Determine HTTP response code */
-		cp = strchr(buf, ' ');
-		if (cp == NULL)
-			goto improper;
-		else
-			cp++;
-		hcode = strtol(cp, &ep, 10);
-		if (*ep != '\0' && !isspace((unsigned char)*ep))
-			goto improper;
-		message = ftp_strdup(cp);
-
-				/* Read the rest of the header. */
-		while (1) {
-			alarmtimer(quit_time ? quit_time : 60);
-			len = fetch_getline(fin, buf, sizeof(buf), &errormsg);
-			alarmtimer(0);
-			if (len < 0) {
-				if (*errormsg == '\n')
-					errormsg++;
-				warnx("Receiving HTTP reply: %s", errormsg);
-				goto cleanup_fetch_url;
-			}
-			while (len > 0 && (ISLWS(buf[len-1])))
-				buf[--len] = '\0';
-			if (len == 0)
-				break;
-			DPRINTF("%s: received `%s'\n", __func__, buf);
-
-		/*
-		 * Look for some headers
-		 */
-
-			cp = buf;
-
-			if (match_token(&cp, "Content-Length:")) {
-				filesize = STRTOLL(cp, &ep, 10);
-				if (filesize < 0 || *ep != '\0')
-					goto improper;
-				DPRINTF("%s: parsed len as: " LLF "\n",
-				    __func__, (LLT)filesize);
-
-			} else if (match_token(&cp, "Content-Range:")) {
-				if (! match_token(&cp, "bytes"))
-					goto improper;
-
-				if (*cp == '*')
-					cp++;
-				else {
-					rangestart = STRTOLL(cp, &ep, 10);
-					if (rangestart < 0 || *ep != '-')
-						goto improper;
-					cp = ep + 1;
-					rangeend = STRTOLL(cp, &ep, 10);
-					if (rangeend < 0 || rangeend < rangestart)
-						goto improper;
-					cp = ep;
-				}
-				if (*cp != '/')
-					goto improper;
-				cp++;
-				if (*cp == '*')
-					cp++;
-				else {
-					entitylen = STRTOLL(cp, &ep, 10);
-					if (entitylen < 0)
-						goto improper;
-					cp = ep;
-				}
-				if (*cp != '\0')
-					goto improper;
-
-#ifndef NO_DEBUG
-				if (ftp_debug) {
-					fprintf(ttyout, "parsed range as: ");
-					if (rangestart == -1)
-						fprintf(ttyout, "*");
-					else
-						fprintf(ttyout, LLF "-" LLF,
-						    (LLT)rangestart,
-						    (LLT)rangeend);
-					fprintf(ttyout, "/" LLF "\n", (LLT)entitylen);
-				}
-#endif
-				if (! restart_point) {
-					warnx(
-				    "Received unexpected Content-Range header");
-					goto cleanup_fetch_url;
-				}
-
-			} else if (match_token(&cp, "Last-Modified:")) {
-				struct tm parsed;
-				const char *t;
-
-				memset(&parsed, 0, sizeof(parsed));
-				t = parse_rfc2616time(&parsed, cp);
-				if (t != NULL) {
-					parsed.tm_isdst = -1;
-					if (*t == '\0')
-						mtime = timegm(&parsed);
-#ifndef NO_DEBUG
-					if (ftp_debug && mtime != -1) {
-						fprintf(ttyout,
-						    "parsed time as: %s",
-						rfc2822time(localtime(&mtime)));
-					}
-#endif
-				}
-
-			} else if (match_token(&cp, "Location:")) {
-				location = ftp_strdup(cp);
-				DPRINTF("%s: parsed location as `%s'\n",
-				    __func__, cp);
-
-			} else if (match_token(&cp, "Transfer-Encoding:")) {
-				if (match_token(&cp, "binary")) {
-					warnx(
-			"Bogus transfer encoding `binary' (fetching anyway)");
-					continue;
-				}
-				if (! (token = match_token(&cp, "chunked"))) {
-					warnx(
-				    "Unsupported transfer encoding `%s'",
-					    token);
-					goto cleanup_fetch_url;
-				}
-				ischunked++;
-				DPRINTF("%s: using chunked encoding\n",
-				    __func__);
-
-			} else if (match_token(&cp, "Proxy-Authenticate:")
-				|| match_token(&cp, "WWW-Authenticate:")) {
-				if (! (token = match_token(&cp, "Basic"))) {
-					DPRINTF("%s: skipping unknown auth "
-					    "scheme `%s'\n", __func__, token);
-					continue;
-				}
-				FREEPTR(auth);
-				auth = ftp_strdup(token);
-				DPRINTF("%s: parsed auth as `%s'\n",
-				    __func__, cp);
-			}
-
-		}
-				/* finished parsing header */
-
-		switch (hcode) {
-		case 200:
-			break;
-		case 206:
-			if (! restart_point) {
-				warnx("Not expecting partial content header");
-				goto cleanup_fetch_url;
-			}
+		switch (negotiate_connection(fin, url, penv,
+		    &rangestart, &rangeend, &entitylen,
+		    &mtime, &wauth, &pauth, &rval, &ischunked)) {
+		case C_OK:
 			break;
-		case 300:
-		case 301:
-		case 302:
-		case 303:
-		case 305:
-		case 307:
-			if (EMPTYSTRING(location)) {
-				warnx(
-				"No redirection Location provided by server");
-				goto cleanup_fetch_url;
-			}
-			if (redirect_loop++ > 5) {
-				warnx("Too many redirections requested");
-				goto cleanup_fetch_url;
-			}
-			if (hcode == 305) {
-				if (verbose)
-					fprintf(ttyout, "Redirected via %s\n",
-					    location);
-				rval = fetch_url(url, location,
-				    proxyauth, wwwauth);
-			} else {
-				if (verbose)
-					fprintf(ttyout, "Redirected to %s\n",
-					    location);
-				rval = go_fetch(location);
-			}
+		case C_CLEANUP:
 			goto cleanup_fetch_url;
-#ifndef NO_AUTH
-		case 401:
-		case 407:
-		    {
-			char **authp;
-			char *auser, *apass;
-
-			if (hcode == 401) {
-				authp = &wwwauth;
-				auser = uuser;
-				apass = pass;
-			} else {
-				authp = &proxyauth;
-				auser = puser;
-				apass = ppass;
-			}
-			if (verbose || *authp == NULL ||
-			    auser == NULL || apass == NULL)
-				fprintf(ttyout, "%s\n", message);
-			if (EMPTYSTRING(auth)) {
-				warnx(
-			    "No authentication challenge provided by server");
-				goto cleanup_fetch_url;
-			}
-			if (*authp != NULL) {
-				char reply[10];
-
-				fprintf(ttyout,
-				    "Authorization failed. Retry (y/n)? ");
-				if (get_line(stdin, reply, sizeof(reply), NULL)
-				    < 0) {
-					goto cleanup_fetch_url;
-				}
-				if (tolower((unsigned char)reply[0]) != 'y')
-					goto cleanup_fetch_url;
-				auser = NULL;
-				apass = NULL;
-			}
-			if (auth_url(auth, authp, auser, apass) == 0) {
-				rval = fetch_url(url, penv,
-				    proxyauth, wwwauth);
-				memset(*authp, 0, strlen(*authp));
-				FREEPTR(*authp);
-			}
-			goto cleanup_fetch_url;
-		    }
-#endif
+		case C_IMPROPER:
+			goto improper;
 		default:
-			if (message)
-				warnx("Error retrieving file `%s'", message);
-			else
-				warnx("Unknown error retrieving file");
-			goto cleanup_fetch_url;
+			abort();
 		}
-	}		/* end of ftp:// or http:// specific setup */
+	}
 
 	/* Open the output file. */
 
@@ -1401,7 +1527,7 @@ fetch_url(const char *url, const char *p
 	goto cleanup_fetch_url;
 
  improper:
-	warnx("Improper response from `%s:%s'", host, port);
+	warnx("Improper response from `%s:%s'", ui.host, ui.port);
 
  cleanup_fetch_url:
 	if (oldint)
@@ -1420,18 +1546,10 @@ fetch_url(const char *url, const char *p
 		(*closefunc)(fout);
 	if (savefile != outfile)
 		FREEPTR(savefile);
-	FREEPTR(uuser);
-	if (pass != NULL)
-		memset(pass, 0, strlen(pass));
-	FREEPTR(pass);
-	FREEPTR(host);
-	FREEPTR(port);
-	FREEPTR(path);
+	freeurlinfo(&ui);
+	freeauthinfo(&wauth);
+	freeauthinfo(&pauth);
 	FREEPTR(decodedpath);
-	FREEPTR(puser);
-	if (ppass != NULL)
-		memset(ppass, 0, strlen(ppass));
-	FREEPTR(ppass);
 	FREEPTR(auth);
 	FREEPTR(location);
 	FREEPTR(message);
@@ -1483,26 +1601,26 @@ static int
 fetch_ftp(const char *url)
 {
 	char		*cp, *xargv[5], rempath[MAXPATHLEN];
-	char		*host, *path, *dir, *file, *uuser, *pass;
-	char		*port;
+	char		*dir, *file;
 	char		 cmdbuf[MAXPATHLEN];
 	char		 dirbuf[4];
 	int		 dirhasglob, filehasglob, rval, transtype, xargc;
 	int		 oanonftp, oautologin;
-	in_port_t	 portnum;
-	url_t		 urltype;
+	struct authinfo  auth;
+	struct urlinfo	 ui;
 
 	DPRINTF("fetch_ftp: `%s'\n", url);
-	host = path = dir = file = uuser = pass = NULL;
-	port = NULL;
+	dir = file = NULL;
 	rval = 1;
 	transtype = TYPE_I;
 
+	initurlinfo(&ui);
+	initauthinfo(&auth, NULL);
+
 	if (STRNEQUAL(url, FTP_URL)) {
-		if ((parse_url(url, "URL", &urltype, &uuser, &pass,
-		    &host, &port, &portnum, &path) == -1) ||
-		    (uuser != NULL && *uuser == '\0') ||
-		    EMPTYSTRING(host)) {
+		if ((parse_url(url, "URL", &ui, &auth) == -1) ||
+		    (auth.user != NULL && *auth.user == '\0') ||
+		    EMPTYSTRING(ui.host)) {
 			warnx("Invalid URL `%s'", url);
 			goto cleanup_fetch_ftp;
 		}
@@ -1512,7 +1630,7 @@ fetch_ftp(const char *url)
 		 */
 
 					/* check for trailing ';type=[aid]' */
-		if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) {
+		if (! EMPTYSTRING(ui.path) && (cp = strrchr(ui.path, ';')) != NULL) {
 			if (strcasecmp(cp, ";type=a") == 0)
 				transtype = TYPE_A;
 			else if (strcasecmp(cp, ";type=i") == 0)
@@ -1529,26 +1647,26 @@ fetch_ftp(const char *url)
 			*cp = 0;
 		}
 	} else {			/* classic style `[user@]host:[file]' */
-		urltype = CLASSIC_URL_T;
-		host = ftp_strdup(url);
-		cp = strchr(host, '@');
+		ui.utype = CLASSIC_URL_T;
+		ui.host = ftp_strdup(url);
+		cp = strchr(ui.host, '@');
 		if (cp != NULL) {
 			*cp = '\0';
-			uuser = host;
+			auth.user = ui.host;
 			anonftp = 0;	/* disable anonftp */
-			host = ftp_strdup(cp + 1);
+			ui.host = ftp_strdup(cp + 1);
 		}
-		cp = strchr(host, ':');
+		cp = strchr(ui.host, ':');
 		if (cp != NULL) {
 			*cp = '\0';
-			path = ftp_strdup(cp + 1);
+			ui.path = ftp_strdup(cp + 1);
 		}
 	}
-	if (EMPTYSTRING(host))
+	if (EMPTYSTRING(ui.host))
 		goto cleanup_fetch_ftp;
 
 			/* Extract the file and (if present) directory name. */
-	dir = path;
+	dir = ui.path;
 	if (! EMPTYSTRING(dir)) {
 		/*
 		 * If we are dealing with classic `[user@]host:[path]' syntax,
@@ -1573,7 +1691,7 @@ fetch_ftp(const char *url)
 		 * resulting from an URL of the form `ftp://host//file'.)
 		 */
 		cp = strrchr(dir, '/');
-		if (cp == dir && urltype == CLASSIC_URL_T) {
+		if (cp == dir && ui.utype == CLASSIC_URL_T) {
 			file = cp + 1;
 			(void)strlcpy(dirbuf, "/", sizeof(dirbuf));
 			dir = dirbuf;
@@ -1586,18 +1704,18 @@ fetch_ftp(const char *url)
 		}
 	} else
 		dir = NULL;
-	if (urltype == FTP_URL_T && file != NULL) {
+	if (ui.utype == FTP_URL_T && file != NULL) {
 		url_decode(file);
 		/* but still don't url_decode(dir) */
 	}
 	DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s "
 	    "path `%s' dir `%s' file `%s'\n",
-	    STRorNULL(uuser), STRorNULL(pass),
-	    STRorNULL(host), STRorNULL(port),
-	    STRorNULL(path), STRorNULL(dir), STRorNULL(file));
+	    STRorNULL(auth.user), STRorNULL(auth.pass),
+	    STRorNULL(ui.host), STRorNULL(ui.port),
+	    STRorNULL(ui.path), STRorNULL(dir), STRorNULL(file));
 
 	dirhasglob = filehasglob = 0;
-	if (doglob && urltype == CLASSIC_URL_T) {
+	if (doglob && ui.utype == CLASSIC_URL_T) {
 		if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL)
 			dirhasglob = 1;
 		if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL)
@@ -1611,11 +1729,11 @@ fetch_ftp(const char *url)
 	anonftp = oanonftp;
 	(void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf));
 	xargv[0] = cmdbuf;
-	xargv[1] = host;
+	xargv[1] = ui.host;
 	xargv[2] = NULL;
 	xargc = 2;
-	if (port) {
-		xargv[2] = port;
+	if (ui.port) {
+		xargv[2] = ui.port;
 		xargv[3] = NULL;
 		xargc = 3;
 	}
@@ -1625,9 +1743,9 @@ fetch_ftp(const char *url)
 	setpeer(xargc, xargv);
 	autologin = oautologin;
 	if ((connected == 0) ||
-	    (connected == 1 && !ftp_login(host, uuser, pass))) {
+	    (connected == 1 && !ftp_login(ui.host, auth.user, auth.pass))) {
 		warnx("Can't connect or login to host `%s:%s'",
-			host, port ? port : "?");
+			ui.host, ui.port ? ui.port : "?");
 		goto cleanup_fetch_ftp;
 	}
 
@@ -1712,7 +1830,7 @@ fetch_ftp(const char *url)
 		 * Note that we don't need `dir' after this point.
 		 */
 		do {
-			if (urltype == FTP_URL_T) {
+			if (ui.utype == FTP_URL_T) {
 				nextpart = strchr(dir, '/');
 				if (nextpart) {
 					*nextpart = '\0';
@@ -1723,7 +1841,7 @@ fetch_ftp(const char *url)
 				nextpart = NULL;
 			DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n",
 			    STRorNULL(dir), STRorNULL(nextpart));
-			if (urltype == FTP_URL_T || *dir != '\0') {
+			if (ui.utype == FTP_URL_T || *dir != '\0') {
 				(void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf));
 				xargv[0] = cmdbuf;
 				xargv[1] = dir;
@@ -1797,13 +1915,8 @@ fetch_ftp(const char *url)
 		rval = 0;
 
  cleanup_fetch_ftp:
-	FREEPTR(port);
-	FREEPTR(host);
-	FREEPTR(path);
-	FREEPTR(uuser);
-	if (pass)
-		memset(pass, 0, strlen(pass));
-	FREEPTR(pass);
+	freeurlinfo(&ui);
+	freeauthinfo(&auth);
 	return (rval);
 }
 

Reply via email to