xref: /dflybsd-src/contrib/tnftp/src/fetch.c (revision 1612048d3f35192eeb39f3a28148604bef0d5b6e)
1*3a184c67SAntonio Huete Jimenez /*	$NetBSD: fetch.c,v 1.26 2021/08/27 01:38:49 lukem Exp $	*/
2*3a184c67SAntonio Huete Jimenez /*	from	NetBSD: fetch.c,v 1.234 2021/08/01 15:29:30 andvar Exp	*/
36cdfca03SJohn Marino 
46cdfca03SJohn Marino /*-
56cdfca03SJohn Marino  * Copyright (c) 1997-2015 The NetBSD Foundation, Inc.
66cdfca03SJohn Marino  * All rights reserved.
76cdfca03SJohn Marino  *
86cdfca03SJohn Marino  * This code is derived from software contributed to The NetBSD Foundation
96cdfca03SJohn Marino  * by Luke Mewburn.
106cdfca03SJohn Marino  *
116cdfca03SJohn Marino  * This code is derived from software contributed to The NetBSD Foundation
126cdfca03SJohn Marino  * by Scott Aaron Bamford.
136cdfca03SJohn Marino  *
146cdfca03SJohn Marino  * This code is derived from software contributed to The NetBSD Foundation
156cdfca03SJohn Marino  * by Thomas Klausner.
166cdfca03SJohn Marino  *
176cdfca03SJohn Marino  * Redistribution and use in source and binary forms, with or without
186cdfca03SJohn Marino  * modification, are permitted provided that the following conditions
196cdfca03SJohn Marino  * are met:
206cdfca03SJohn Marino  * 1. Redistributions of source code must retain the above copyright
216cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer.
226cdfca03SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
236cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
246cdfca03SJohn Marino  *    documentation and/or other materials provided with the distribution.
256cdfca03SJohn Marino  *
266cdfca03SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
276cdfca03SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
286cdfca03SJohn Marino  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
296cdfca03SJohn Marino  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
306cdfca03SJohn Marino  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
316cdfca03SJohn Marino  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
326cdfca03SJohn Marino  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
336cdfca03SJohn Marino  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
346cdfca03SJohn Marino  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
356cdfca03SJohn Marino  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
366cdfca03SJohn Marino  * POSSIBILITY OF SUCH DAMAGE.
376cdfca03SJohn Marino  */
386cdfca03SJohn Marino 
396cdfca03SJohn Marino #include "tnftp.h"
406cdfca03SJohn Marino 
416cdfca03SJohn Marino #if 0	/* tnftp */
426cdfca03SJohn Marino 
436cdfca03SJohn Marino #include <sys/cdefs.h>
446cdfca03SJohn Marino #ifndef lint
45*3a184c67SAntonio Huete Jimenez __RCSID(" NetBSD: fetch.c,v 1.234 2021/08/01 15:29:30 andvar Exp  ");
466cdfca03SJohn Marino #endif /* not lint */
476cdfca03SJohn Marino 
486cdfca03SJohn Marino /*
496cdfca03SJohn Marino  * FTP User Program -- Command line file retrieval
506cdfca03SJohn Marino  */
516cdfca03SJohn Marino 
526cdfca03SJohn Marino #include <sys/types.h>
536cdfca03SJohn Marino #include <sys/param.h>
546cdfca03SJohn Marino #include <sys/socket.h>
556cdfca03SJohn Marino #include <sys/stat.h>
566cdfca03SJohn Marino #include <sys/time.h>
576cdfca03SJohn Marino 
586cdfca03SJohn Marino #include <netinet/in.h>
596cdfca03SJohn Marino 
606cdfca03SJohn Marino #include <arpa/ftp.h>
616cdfca03SJohn Marino #include <arpa/inet.h>
626cdfca03SJohn Marino 
636cdfca03SJohn Marino #include <assert.h>
646cdfca03SJohn Marino #include <ctype.h>
656cdfca03SJohn Marino #include <err.h>
666cdfca03SJohn Marino #include <errno.h>
676cdfca03SJohn Marino #include <netdb.h>
686cdfca03SJohn Marino #include <fcntl.h>
696cdfca03SJohn Marino #include <stdio.h>
706cdfca03SJohn Marino #include <stdlib.h>
716cdfca03SJohn Marino #include <string.h>
726cdfca03SJohn Marino #include <unistd.h>
736cdfca03SJohn Marino #include <time.h>
746cdfca03SJohn Marino 
756cdfca03SJohn Marino #endif	/* tnftp */
766cdfca03SJohn Marino 
776cdfca03SJohn Marino #include "ssl.h"
786cdfca03SJohn Marino #include "ftp_var.h"
796cdfca03SJohn Marino #include "version.h"
806cdfca03SJohn Marino 
816cdfca03SJohn Marino typedef enum {
826cdfca03SJohn Marino 	UNKNOWN_URL_T=-1,
836cdfca03SJohn Marino 	HTTP_URL_T,
846cdfca03SJohn Marino 	HTTPS_URL_T,
856cdfca03SJohn Marino 	FTP_URL_T,
866cdfca03SJohn Marino 	FILE_URL_T,
876cdfca03SJohn Marino 	CLASSIC_URL_T
886cdfca03SJohn Marino } url_t;
896cdfca03SJohn Marino 
90*3a184c67SAntonio Huete Jimenez struct authinfo {
91*3a184c67SAntonio Huete Jimenez 	char *auth;
92*3a184c67SAntonio Huete Jimenez 	char *user;
93*3a184c67SAntonio Huete Jimenez 	char *pass;
94*3a184c67SAntonio Huete Jimenez };
95*3a184c67SAntonio Huete Jimenez 
96*3a184c67SAntonio Huete Jimenez struct urlinfo {
97*3a184c67SAntonio Huete Jimenez 	char *host;
98*3a184c67SAntonio Huete Jimenez 	char *port;
99*3a184c67SAntonio Huete Jimenez 	char *path;
100*3a184c67SAntonio Huete Jimenez 	url_t utype;
101*3a184c67SAntonio Huete Jimenez 	in_port_t portnum;
102*3a184c67SAntonio Huete Jimenez };
103*3a184c67SAntonio Huete Jimenez 
104*3a184c67SAntonio Huete Jimenez struct posinfo {
105*3a184c67SAntonio Huete Jimenez 	off_t rangestart;
106*3a184c67SAntonio Huete Jimenez 	off_t rangeend;
107*3a184c67SAntonio Huete Jimenez 	off_t entitylen;
108*3a184c67SAntonio Huete Jimenez };
109*3a184c67SAntonio Huete Jimenez 
1106cdfca03SJohn Marino __dead static void	aborthttp(int);
1116cdfca03SJohn Marino __dead static void	timeouthttp(int);
1126cdfca03SJohn Marino #ifndef NO_AUTH
113*3a184c67SAntonio Huete Jimenez static int	auth_url(const char *, char **, const struct authinfo *);
1146cdfca03SJohn Marino static void	base64_encode(const unsigned char *, size_t, unsigned char *);
1156cdfca03SJohn Marino #endif
1166cdfca03SJohn Marino static int	go_fetch(const char *);
1176cdfca03SJohn Marino static int	fetch_ftp(const char *);
1186cdfca03SJohn Marino static int	fetch_url(const char *, const char *, char *, char *);
1196cdfca03SJohn Marino static const char *match_token(const char **, const char *);
120*3a184c67SAntonio Huete Jimenez static int	parse_url(const char *, const char *, struct urlinfo *,
121*3a184c67SAntonio Huete Jimenez     struct authinfo *);
1226cdfca03SJohn Marino static void	url_decode(char *);
123*3a184c67SAntonio Huete Jimenez static void	freeauthinfo(struct authinfo *);
124*3a184c67SAntonio Huete Jimenez static void	freeurlinfo(struct urlinfo *);
1256cdfca03SJohn Marino 
1266cdfca03SJohn Marino static int	redirect_loop;
1276cdfca03SJohn Marino 
1286cdfca03SJohn Marino 
1296cdfca03SJohn Marino #define	STRNEQUAL(a,b)	(strncasecmp((a), (b), sizeof((b))-1) == 0)
1306cdfca03SJohn Marino #define	ISLWS(x)	((x)=='\r' || (x)=='\n' || (x)==' ' || (x)=='\t')
1316cdfca03SJohn Marino #define	SKIPLWS(x)	do { while (ISLWS((*x))) x++; } while (0)
1326cdfca03SJohn Marino 
1336cdfca03SJohn Marino 
1346cdfca03SJohn Marino #define	ABOUT_URL	"about:"	/* propaganda */
1356cdfca03SJohn Marino #define	FILE_URL	"file://"	/* file URL prefix */
1366cdfca03SJohn Marino #define	FTP_URL		"ftp://"	/* ftp URL prefix */
1376cdfca03SJohn Marino #define	HTTP_URL	"http://"	/* http URL prefix */
1386cdfca03SJohn Marino #ifdef WITH_SSL
1396cdfca03SJohn Marino #define	HTTPS_URL	"https://"	/* https URL prefix */
1406cdfca03SJohn Marino 
1416cdfca03SJohn Marino #define	IS_HTTP_TYPE(urltype) \
1426cdfca03SJohn Marino 	(((urltype) == HTTP_URL_T) || ((urltype) == HTTPS_URL_T))
1436cdfca03SJohn Marino #else
1446cdfca03SJohn Marino #define	IS_HTTP_TYPE(urltype) \
1456cdfca03SJohn Marino 	((urltype) == HTTP_URL_T)
1466cdfca03SJohn Marino #endif
1476cdfca03SJohn Marino 
148*3a184c67SAntonio Huete Jimenez /**
149*3a184c67SAntonio Huete Jimenez  * fwrite(3) replacement that just uses write(2). Many stdio implementations
150*3a184c67SAntonio Huete Jimenez  * don't handle interrupts properly and corrupt the output. We are taking
151*3a184c67SAntonio Huete Jimenez  * alarm interrupts because of the progress bar.
152*3a184c67SAntonio Huete Jimenez  *
153*3a184c67SAntonio Huete Jimenez  * Assumes `fp' is pristine with no prior I/O calls on it.
154*3a184c67SAntonio Huete Jimenez  */
155*3a184c67SAntonio Huete Jimenez static size_t
maxwrite(const void * buf,size_t size,size_t nmemb,FILE * fp)156*3a184c67SAntonio Huete Jimenez maxwrite(const void *buf, size_t size, size_t nmemb, FILE *fp)
157*3a184c67SAntonio Huete Jimenez {
158*3a184c67SAntonio Huete Jimenez 	const char *p = buf;
159*3a184c67SAntonio Huete Jimenez 	ssize_t nwr = 0;
160*3a184c67SAntonio Huete Jimenez 	ssize_t n;
161*3a184c67SAntonio Huete Jimenez 	int fd = fileno(fp);
162*3a184c67SAntonio Huete Jimenez 
163*3a184c67SAntonio Huete Jimenez 	size *= nmemb;	/* assume no overflow */
164*3a184c67SAntonio Huete Jimenez 
165*3a184c67SAntonio Huete Jimenez 	while (size > 0) {
166*3a184c67SAntonio Huete Jimenez 		if ((n = write(fd, p, size)) == -1) {
167*3a184c67SAntonio Huete Jimenez 			switch (errno) {
168*3a184c67SAntonio Huete Jimenez 			case EINTR:
169*3a184c67SAntonio Huete Jimenez 			case EAGAIN:
170*3a184c67SAntonio Huete Jimenez #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
171*3a184c67SAntonio Huete Jimenez 			case EWOULDBLOCK:
172*3a184c67SAntonio Huete Jimenez #endif
173*3a184c67SAntonio Huete Jimenez 				continue;
174*3a184c67SAntonio Huete Jimenez 			default:
175*3a184c67SAntonio Huete Jimenez 				return nwr;
176*3a184c67SAntonio Huete Jimenez 			}
177*3a184c67SAntonio Huete Jimenez 		}
178*3a184c67SAntonio Huete Jimenez 		p += n;
179*3a184c67SAntonio Huete Jimenez 		nwr += n;
180*3a184c67SAntonio Huete Jimenez 		size -= n;
181*3a184c67SAntonio Huete Jimenez 	}
182*3a184c67SAntonio Huete Jimenez 	return nwr;
183*3a184c67SAntonio Huete Jimenez }
184*3a184c67SAntonio Huete Jimenez 
1856cdfca03SJohn Marino /*
1866cdfca03SJohn Marino  * Determine if token is the next word in buf (case insensitive).
1876cdfca03SJohn Marino  * If so, advance buf past the token and any trailing LWS, and
1886cdfca03SJohn Marino  * return a pointer to the token (in buf).  Otherwise, return NULL.
1896cdfca03SJohn Marino  * token may be preceded by LWS.
1906cdfca03SJohn Marino  * token must be followed by LWS or NUL.  (I.e, don't partial match).
1916cdfca03SJohn Marino  */
1926cdfca03SJohn Marino static const char *
match_token(const char ** buf,const char * token)1936cdfca03SJohn Marino match_token(const char **buf, const char *token)
1946cdfca03SJohn Marino {
1956cdfca03SJohn Marino 	const char	*p, *orig;
1966cdfca03SJohn Marino 	size_t		tlen;
1976cdfca03SJohn Marino 
1986cdfca03SJohn Marino 	tlen = strlen(token);
1996cdfca03SJohn Marino 	p = *buf;
2006cdfca03SJohn Marino 	SKIPLWS(p);
2016cdfca03SJohn Marino 	orig = p;
2026cdfca03SJohn Marino 	if (strncasecmp(p, token, tlen) != 0)
2036cdfca03SJohn Marino 		return NULL;
2046cdfca03SJohn Marino 	p += tlen;
2056cdfca03SJohn Marino 	if (*p != '\0' && !ISLWS(*p))
2066cdfca03SJohn Marino 		return NULL;
2076cdfca03SJohn Marino 	SKIPLWS(p);
2086cdfca03SJohn Marino 	orig = *buf;
2096cdfca03SJohn Marino 	*buf = p;
2106cdfca03SJohn Marino 	return orig;
2116cdfca03SJohn Marino }
2126cdfca03SJohn Marino 
213*3a184c67SAntonio Huete Jimenez static void
initposinfo(struct posinfo * pi)214*3a184c67SAntonio Huete Jimenez initposinfo(struct posinfo *pi)
215*3a184c67SAntonio Huete Jimenez {
216*3a184c67SAntonio Huete Jimenez 	pi->rangestart = pi->rangeend = pi->entitylen = -1;
217*3a184c67SAntonio Huete Jimenez }
218*3a184c67SAntonio Huete Jimenez 
219*3a184c67SAntonio Huete Jimenez static void
initauthinfo(struct authinfo * ai,char * auth)220*3a184c67SAntonio Huete Jimenez initauthinfo(struct authinfo *ai, char *auth)
221*3a184c67SAntonio Huete Jimenez {
222*3a184c67SAntonio Huete Jimenez 	ai->auth = auth;
223*3a184c67SAntonio Huete Jimenez 	ai->user = ai->pass = 0;
224*3a184c67SAntonio Huete Jimenez }
225*3a184c67SAntonio Huete Jimenez 
226*3a184c67SAntonio Huete Jimenez static void
freeauthinfo(struct authinfo * a)227*3a184c67SAntonio Huete Jimenez freeauthinfo(struct authinfo *a)
228*3a184c67SAntonio Huete Jimenez {
229*3a184c67SAntonio Huete Jimenez 	FREEPTR(a->user);
230*3a184c67SAntonio Huete Jimenez 	if (a->pass != NULL)
231*3a184c67SAntonio Huete Jimenez 		memset(a->pass, 0, strlen(a->pass));
232*3a184c67SAntonio Huete Jimenez 	FREEPTR(a->pass);
233*3a184c67SAntonio Huete Jimenez }
234*3a184c67SAntonio Huete Jimenez 
235*3a184c67SAntonio Huete Jimenez static void
initurlinfo(struct urlinfo * ui)236*3a184c67SAntonio Huete Jimenez initurlinfo(struct urlinfo *ui)
237*3a184c67SAntonio Huete Jimenez {
238*3a184c67SAntonio Huete Jimenez 	ui->host = ui->port = ui->path = 0;
239*3a184c67SAntonio Huete Jimenez 	ui->utype = UNKNOWN_URL_T;
240*3a184c67SAntonio Huete Jimenez 	ui->portnum = 0;
241*3a184c67SAntonio Huete Jimenez }
242*3a184c67SAntonio Huete Jimenez 
243*3a184c67SAntonio Huete Jimenez static void
copyurlinfo(struct urlinfo * dui,struct urlinfo * sui)244*3a184c67SAntonio Huete Jimenez copyurlinfo(struct urlinfo *dui, struct urlinfo *sui)
245*3a184c67SAntonio Huete Jimenez {
246*3a184c67SAntonio Huete Jimenez 	dui->host = ftp_strdup(sui->host);
247*3a184c67SAntonio Huete Jimenez 	dui->port = ftp_strdup(sui->port);
248*3a184c67SAntonio Huete Jimenez 	dui->path = ftp_strdup(sui->path);
249*3a184c67SAntonio Huete Jimenez 	dui->utype = sui->utype;
250*3a184c67SAntonio Huete Jimenez 	dui->portnum = sui->portnum;
251*3a184c67SAntonio Huete Jimenez }
252*3a184c67SAntonio Huete Jimenez 
253*3a184c67SAntonio Huete Jimenez static void
freeurlinfo(struct urlinfo * ui)254*3a184c67SAntonio Huete Jimenez freeurlinfo(struct urlinfo *ui)
255*3a184c67SAntonio Huete Jimenez {
256*3a184c67SAntonio Huete Jimenez 	FREEPTR(ui->host);
257*3a184c67SAntonio Huete Jimenez 	FREEPTR(ui->port);
258*3a184c67SAntonio Huete Jimenez 	FREEPTR(ui->path);
259*3a184c67SAntonio Huete Jimenez }
260*3a184c67SAntonio Huete Jimenez 
2616cdfca03SJohn Marino #ifndef NO_AUTH
2626cdfca03SJohn Marino /*
2636cdfca03SJohn Marino  * Generate authorization response based on given authentication challenge.
2646cdfca03SJohn Marino  * Returns -1 if an error occurred, otherwise 0.
2656cdfca03SJohn Marino  * Sets response to a malloc(3)ed string; caller should free.
2666cdfca03SJohn Marino  */
2676cdfca03SJohn Marino static int
auth_url(const char * challenge,char ** response,const struct authinfo * auth)268*3a184c67SAntonio Huete Jimenez auth_url(const char *challenge, char **response, const struct authinfo *auth)
2696cdfca03SJohn Marino {
2706cdfca03SJohn Marino 	const char	*cp, *scheme, *errormsg;
2716cdfca03SJohn Marino 	char		*ep, *clear, *realm;
2726cdfca03SJohn Marino 	char		 uuser[BUFSIZ], *gotpass;
2736cdfca03SJohn Marino 	const char	*upass;
2746cdfca03SJohn Marino 	int		 rval;
2756cdfca03SJohn Marino 	size_t		 len, clen, rlen;
2766cdfca03SJohn Marino 
2776cdfca03SJohn Marino 	*response = NULL;
2786cdfca03SJohn Marino 	clear = realm = NULL;
2796cdfca03SJohn Marino 	rval = -1;
2806cdfca03SJohn Marino 	cp = challenge;
2816cdfca03SJohn Marino 	scheme = "Basic";	/* only support Basic authentication */
2826cdfca03SJohn Marino 	gotpass = NULL;
2836cdfca03SJohn Marino 
2846cdfca03SJohn Marino 	DPRINTF("auth_url: challenge `%s'\n", challenge);
2856cdfca03SJohn Marino 
2866cdfca03SJohn Marino 	if (! match_token(&cp, scheme)) {
2876cdfca03SJohn Marino 		warnx("Unsupported authentication challenge `%s'",
2886cdfca03SJohn Marino 		    challenge);
2896cdfca03SJohn Marino 		goto cleanup_auth_url;
2906cdfca03SJohn Marino 	}
2916cdfca03SJohn Marino 
2926cdfca03SJohn Marino #define	REALM "realm=\""
2936cdfca03SJohn Marino 	if (STRNEQUAL(cp, REALM))
2946cdfca03SJohn Marino 		cp += sizeof(REALM) - 1;
2956cdfca03SJohn Marino 	else {
2966cdfca03SJohn Marino 		warnx("Unsupported authentication challenge `%s'",
2976cdfca03SJohn Marino 		    challenge);
2986cdfca03SJohn Marino 		goto cleanup_auth_url;
2996cdfca03SJohn Marino 	}
3006cdfca03SJohn Marino /* XXX: need to improve quoted-string parsing to support \ quoting, etc. */
3016cdfca03SJohn Marino 	if ((ep = strchr(cp, '\"')) != NULL) {
3026cdfca03SJohn Marino 		len = ep - cp;
3036cdfca03SJohn Marino 		realm = (char *)ftp_malloc(len + 1);
3046cdfca03SJohn Marino 		(void)strlcpy(realm, cp, len + 1);
3056cdfca03SJohn Marino 	} else {
3066cdfca03SJohn Marino 		warnx("Unsupported authentication challenge `%s'",
3076cdfca03SJohn Marino 		    challenge);
3086cdfca03SJohn Marino 		goto cleanup_auth_url;
3096cdfca03SJohn Marino 	}
3106cdfca03SJohn Marino 
3116cdfca03SJohn Marino 	fprintf(ttyout, "Username for `%s': ", realm);
312*3a184c67SAntonio Huete Jimenez 	if (auth->user != NULL) {
313*3a184c67SAntonio Huete Jimenez 		(void)strlcpy(uuser, auth->user, sizeof(uuser));
3146cdfca03SJohn Marino 		fprintf(ttyout, "%s\n", uuser);
3156cdfca03SJohn Marino 	} else {
3166cdfca03SJohn Marino 		(void)fflush(ttyout);
3176cdfca03SJohn Marino 		if (get_line(stdin, uuser, sizeof(uuser), &errormsg) < 0) {
3186cdfca03SJohn Marino 			warnx("%s; can't authenticate", errormsg);
3196cdfca03SJohn Marino 			goto cleanup_auth_url;
3206cdfca03SJohn Marino 		}
3216cdfca03SJohn Marino 	}
322*3a184c67SAntonio Huete Jimenez 	if (auth->pass != NULL)
323*3a184c67SAntonio Huete Jimenez 		upass = auth->pass;
3246cdfca03SJohn Marino 	else {
3256cdfca03SJohn Marino 		gotpass = getpass("Password: ");
3266cdfca03SJohn Marino 		if (gotpass == NULL) {
3276cdfca03SJohn Marino 			warnx("Can't read password");
3286cdfca03SJohn Marino 			goto cleanup_auth_url;
3296cdfca03SJohn Marino 		}
3306cdfca03SJohn Marino 		upass = gotpass;
3316cdfca03SJohn Marino 	}
3326cdfca03SJohn Marino 
3336cdfca03SJohn Marino 	clen = strlen(uuser) + strlen(upass) + 2;	/* user + ":" + pass + "\0" */
3346cdfca03SJohn Marino 	clear = (char *)ftp_malloc(clen);
3356cdfca03SJohn Marino 	(void)strlcpy(clear, uuser, clen);
3366cdfca03SJohn Marino 	(void)strlcat(clear, ":", clen);
3376cdfca03SJohn Marino 	(void)strlcat(clear, upass, clen);
3386cdfca03SJohn Marino 	if (gotpass)
3396cdfca03SJohn Marino 		memset(gotpass, 0, strlen(gotpass));
3406cdfca03SJohn Marino 
3416cdfca03SJohn Marino 						/* scheme + " " + enc + "\0" */
3426cdfca03SJohn Marino 	rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1;
343*3a184c67SAntonio Huete Jimenez 	*response = ftp_malloc(rlen);
3446cdfca03SJohn Marino 	(void)strlcpy(*response, scheme, rlen);
3456cdfca03SJohn Marino 	len = strlcat(*response, " ", rlen);
3466cdfca03SJohn Marino 			/* use  `clen - 1'  to not encode the trailing NUL */
3476cdfca03SJohn Marino 	base64_encode((unsigned char *)clear, clen - 1,
3486cdfca03SJohn Marino 	    (unsigned char *)*response + len);
3496cdfca03SJohn Marino 	memset(clear, 0, clen);
3506cdfca03SJohn Marino 	rval = 0;
3516cdfca03SJohn Marino 
3526cdfca03SJohn Marino  cleanup_auth_url:
3536cdfca03SJohn Marino 	FREEPTR(clear);
3546cdfca03SJohn Marino 	FREEPTR(realm);
3556cdfca03SJohn Marino 	return (rval);
3566cdfca03SJohn Marino }
3576cdfca03SJohn Marino 
3586cdfca03SJohn Marino /*
3596cdfca03SJohn Marino  * Encode len bytes starting at clear using base64 encoding into encoded,
3606cdfca03SJohn Marino  * which should be at least ((len + 2) * 4 / 3 + 1) in size.
3616cdfca03SJohn Marino  */
3626cdfca03SJohn Marino static void
base64_encode(const unsigned char * clear,size_t len,unsigned char * encoded)3636cdfca03SJohn Marino base64_encode(const unsigned char *clear, size_t len, unsigned char *encoded)
3646cdfca03SJohn Marino {
3656cdfca03SJohn Marino 	static const unsigned char enc[] =
3666cdfca03SJohn Marino 	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3676cdfca03SJohn Marino 	unsigned char	*cp;
3686cdfca03SJohn Marino 	size_t	 i;
3696cdfca03SJohn Marino 
3706cdfca03SJohn Marino 	cp = encoded;
3716cdfca03SJohn Marino 	for (i = 0; i < len; i += 3) {
3726cdfca03SJohn Marino 		*(cp++) = enc[((clear[i + 0] >> 2))];
3736cdfca03SJohn Marino 		*(cp++) = enc[((clear[i + 0] << 4) & 0x30)
3746cdfca03SJohn Marino 			    | ((clear[i + 1] >> 4) & 0x0f)];
3756cdfca03SJohn Marino 		*(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
3766cdfca03SJohn Marino 			    | ((clear[i + 2] >> 6) & 0x03)];
3776cdfca03SJohn Marino 		*(cp++) = enc[((clear[i + 2]     ) & 0x3f)];
3786cdfca03SJohn Marino 	}
3796cdfca03SJohn Marino 	*cp = '\0';
3806cdfca03SJohn Marino 	while (i-- > len)
3816cdfca03SJohn Marino 		*(--cp) = '=';
3826cdfca03SJohn Marino }
3836cdfca03SJohn Marino #endif
3846cdfca03SJohn Marino 
3856cdfca03SJohn Marino /*
3866cdfca03SJohn Marino  * Decode %xx escapes in given string, `in-place'.
3876cdfca03SJohn Marino  */
3886cdfca03SJohn Marino static void
url_decode(char * url)3896cdfca03SJohn Marino url_decode(char *url)
3906cdfca03SJohn Marino {
3916cdfca03SJohn Marino 	unsigned char *p, *q;
3926cdfca03SJohn Marino 
3936cdfca03SJohn Marino 	if (EMPTYSTRING(url))
3946cdfca03SJohn Marino 		return;
3956cdfca03SJohn Marino 	p = q = (unsigned char *)url;
3966cdfca03SJohn Marino 
3976cdfca03SJohn Marino #define	HEXTOINT(x) (x - (isdigit(x) ? '0' : (islower(x) ? 'a' : 'A') - 10))
3986cdfca03SJohn Marino 	while (*p) {
3996cdfca03SJohn Marino 		if (p[0] == '%'
4006cdfca03SJohn Marino 		    && p[1] && isxdigit((unsigned char)p[1])
4016cdfca03SJohn Marino 		    && p[2] && isxdigit((unsigned char)p[2])) {
4026cdfca03SJohn Marino 			*q++ = HEXTOINT(p[1]) * 16 + HEXTOINT(p[2]);
4036cdfca03SJohn Marino 			p+=3;
4046cdfca03SJohn Marino 		} else
4056cdfca03SJohn Marino 			*q++ = *p++;
4066cdfca03SJohn Marino 	}
4076cdfca03SJohn Marino 	*q = '\0';
4086cdfca03SJohn Marino }
4096cdfca03SJohn Marino 
4106cdfca03SJohn Marino 
4116cdfca03SJohn Marino /*
4126cdfca03SJohn Marino  * Parse URL of form (per RFC 3986):
4136cdfca03SJohn Marino  *	<type>://[<user>[:<password>]@]<host>[:<port>][/<path>]
4146cdfca03SJohn Marino  * Returns -1 if a parse error occurred, otherwise 0.
4156cdfca03SJohn Marino  * It's the caller's responsibility to url_decode() the returned
4166cdfca03SJohn Marino  * user, pass and path.
4176cdfca03SJohn Marino  *
4186cdfca03SJohn Marino  * Sets type to url_t, each of the given char ** pointers to a
4196cdfca03SJohn Marino  * malloc(3)ed strings of the relevant section, and port to
4206cdfca03SJohn Marino  * the number given, or ftpport if ftp://, or httpport if http://.
4216cdfca03SJohn Marino  *
4226cdfca03SJohn Marino  * XXX: this is not totally RFC 3986 compliant; <path> will have the
4236cdfca03SJohn Marino  * leading `/' unless it's an ftp:// URL, as this makes things easier
4246cdfca03SJohn Marino  * for file:// and http:// URLs.  ftp:// URLs have the `/' between the
4256cdfca03SJohn Marino  * host and the URL-path removed, but any additional leading slashes
4266cdfca03SJohn Marino  * in the URL-path are retained (because they imply that we should
4276cdfca03SJohn Marino  * later do "CWD" with a null argument).
4286cdfca03SJohn Marino  *
4296cdfca03SJohn Marino  * Examples:
4306cdfca03SJohn Marino  *	 input URL			 output path
4316cdfca03SJohn Marino  *	 ---------			 -----------
4326cdfca03SJohn Marino  *	"http://host"			"/"
4336cdfca03SJohn Marino  *	"http://host/"			"/"
4346cdfca03SJohn Marino  *	"http://host/path"		"/path"
4356cdfca03SJohn Marino  *	"file://host/dir/file"		"dir/file"
4366cdfca03SJohn Marino  *	"ftp://host"			""
4376cdfca03SJohn Marino  *	"ftp://host/"			""
4386cdfca03SJohn Marino  *	"ftp://host//"			"/"
4396cdfca03SJohn Marino  *	"ftp://host/dir/file"		"dir/file"
4406cdfca03SJohn Marino  *	"ftp://host//dir/file"		"/dir/file"
4416cdfca03SJohn Marino  */
442*3a184c67SAntonio Huete Jimenez 
4436cdfca03SJohn Marino static int
parse_url(const char * url,const char * desc,struct urlinfo * ui,struct authinfo * auth)444*3a184c67SAntonio Huete Jimenez parse_url(const char *url, const char *desc, struct urlinfo *ui,
445*3a184c67SAntonio Huete Jimenez     struct authinfo *auth)
4466cdfca03SJohn Marino {
4476cdfca03SJohn Marino 	const char	*origurl, *tport;
4486cdfca03SJohn Marino 	char		*cp, *ep, *thost;
4496cdfca03SJohn Marino 	size_t		 len;
4506cdfca03SJohn Marino 
451*3a184c67SAntonio Huete Jimenez 	if (url == NULL || desc == NULL || ui == NULL || auth == NULL)
4526cdfca03SJohn Marino 		errx(1, "parse_url: invoked with NULL argument!");
4536cdfca03SJohn Marino 	DPRINTF("parse_url: %s `%s'\n", desc, url);
4546cdfca03SJohn Marino 
4556cdfca03SJohn Marino 	origurl = url;
4566cdfca03SJohn Marino 	tport = NULL;
4576cdfca03SJohn Marino 
4586cdfca03SJohn Marino 	if (STRNEQUAL(url, HTTP_URL)) {
4596cdfca03SJohn Marino 		url += sizeof(HTTP_URL) - 1;
460*3a184c67SAntonio Huete Jimenez 		ui->utype = HTTP_URL_T;
461*3a184c67SAntonio Huete Jimenez 		ui->portnum = HTTP_PORT;
4626cdfca03SJohn Marino 		tport = httpport;
4636cdfca03SJohn Marino 	} else if (STRNEQUAL(url, FTP_URL)) {
4646cdfca03SJohn Marino 		url += sizeof(FTP_URL) - 1;
465*3a184c67SAntonio Huete Jimenez 		ui->utype = FTP_URL_T;
466*3a184c67SAntonio Huete Jimenez 		ui->portnum = FTP_PORT;
4676cdfca03SJohn Marino 		tport = ftpport;
4686cdfca03SJohn Marino 	} else if (STRNEQUAL(url, FILE_URL)) {
4696cdfca03SJohn Marino 		url += sizeof(FILE_URL) - 1;
470*3a184c67SAntonio Huete Jimenez 		ui->utype = FILE_URL_T;
471*3a184c67SAntonio Huete Jimenez 		tport = "";
4726cdfca03SJohn Marino #ifdef WITH_SSL
4736cdfca03SJohn Marino 	} else if (STRNEQUAL(url, HTTPS_URL)) {
4746cdfca03SJohn Marino 		url += sizeof(HTTPS_URL) - 1;
475*3a184c67SAntonio Huete Jimenez 		ui->utype = HTTPS_URL_T;
476*3a184c67SAntonio Huete Jimenez 		ui->portnum = HTTPS_PORT;
4776cdfca03SJohn Marino 		tport = httpsport;
4786cdfca03SJohn Marino #endif
4796cdfca03SJohn Marino 	} else {
4806cdfca03SJohn Marino 		warnx("Invalid %s `%s'", desc, url);
4816cdfca03SJohn Marino  cleanup_parse_url:
482*3a184c67SAntonio Huete Jimenez 		freeauthinfo(auth);
483*3a184c67SAntonio Huete Jimenez 		freeurlinfo(ui);
4846cdfca03SJohn Marino 		return (-1);
4856cdfca03SJohn Marino 	}
4866cdfca03SJohn Marino 
4876cdfca03SJohn Marino 	if (*url == '\0')
4886cdfca03SJohn Marino 		return (0);
4896cdfca03SJohn Marino 
4906cdfca03SJohn Marino 			/* find [user[:pass]@]host[:port] */
4916cdfca03SJohn Marino 	ep = strchr(url, '/');
4926cdfca03SJohn Marino 	if (ep == NULL)
4936cdfca03SJohn Marino 		thost = ftp_strdup(url);
4946cdfca03SJohn Marino 	else {
4956cdfca03SJohn Marino 		len = ep - url;
4966cdfca03SJohn Marino 		thost = (char *)ftp_malloc(len + 1);
4976cdfca03SJohn Marino 		(void)strlcpy(thost, url, len + 1);
498*3a184c67SAntonio Huete Jimenez 		if (ui->utype == FTP_URL_T)	/* skip first / for ftp URLs */
4996cdfca03SJohn Marino 			ep++;
500*3a184c67SAntonio Huete Jimenez 		ui->path = ftp_strdup(ep);
5016cdfca03SJohn Marino 	}
5026cdfca03SJohn Marino 
5036cdfca03SJohn Marino 	cp = strchr(thost, '@');	/* look for user[:pass]@ in URLs */
5046cdfca03SJohn Marino 	if (cp != NULL) {
505*3a184c67SAntonio Huete Jimenez 		if (ui->utype == FTP_URL_T)
5066cdfca03SJohn Marino 			anonftp = 0;	/* disable anonftp */
507*3a184c67SAntonio Huete Jimenez 		auth->user = thost;
5086cdfca03SJohn Marino 		*cp = '\0';
5096cdfca03SJohn Marino 		thost = ftp_strdup(cp + 1);
510*3a184c67SAntonio Huete Jimenez 		cp = strchr(auth->user, ':');
5116cdfca03SJohn Marino 		if (cp != NULL) {
5126cdfca03SJohn Marino 			*cp = '\0';
513*3a184c67SAntonio Huete Jimenez 			auth->pass = ftp_strdup(cp + 1);
5146cdfca03SJohn Marino 		}
515*3a184c67SAntonio Huete Jimenez 		url_decode(auth->user);
516*3a184c67SAntonio Huete Jimenez 		if (auth->pass)
517*3a184c67SAntonio Huete Jimenez 			url_decode(auth->pass);
5186cdfca03SJohn Marino 	}
5196cdfca03SJohn Marino 
5206cdfca03SJohn Marino #ifdef INET6
5216cdfca03SJohn Marino 			/*
5226cdfca03SJohn Marino 			 * Check if thost is an encoded IPv6 address, as per
5236cdfca03SJohn Marino 			 * RFC 3986:
5246cdfca03SJohn Marino 			 *	`[' ipv6-address ']'
5256cdfca03SJohn Marino 			 */
5266cdfca03SJohn Marino 	if (*thost == '[') {
5276cdfca03SJohn Marino 		cp = thost + 1;
5286cdfca03SJohn Marino 		if ((ep = strchr(cp, ']')) == NULL ||
5296cdfca03SJohn Marino 		    (ep[1] != '\0' && ep[1] != ':')) {
5306cdfca03SJohn Marino 			warnx("Invalid address `%s' in %s `%s'",
5316cdfca03SJohn Marino 			    thost, desc, origurl);
5326cdfca03SJohn Marino 			goto cleanup_parse_url;
5336cdfca03SJohn Marino 		}
5346cdfca03SJohn Marino 		len = ep - cp;		/* change `[xyz]' -> `xyz' */
5356cdfca03SJohn Marino 		memmove(thost, thost + 1, len);
5366cdfca03SJohn Marino 		thost[len] = '\0';
5376cdfca03SJohn Marino 		if (! isipv6addr(thost)) {
5386cdfca03SJohn Marino 			warnx("Invalid IPv6 address `%s' in %s `%s'",
5396cdfca03SJohn Marino 			    thost, desc, origurl);
5406cdfca03SJohn Marino 			goto cleanup_parse_url;
5416cdfca03SJohn Marino 		}
5426cdfca03SJohn Marino 		cp = ep + 1;
5436cdfca03SJohn Marino 		if (*cp == ':')
5446cdfca03SJohn Marino 			cp++;
5456cdfca03SJohn Marino 		else
5466cdfca03SJohn Marino 			cp = NULL;
5476cdfca03SJohn Marino 	} else
5486cdfca03SJohn Marino #endif /* INET6 */
5496cdfca03SJohn Marino 		if ((cp = strchr(thost, ':')) != NULL)
5506cdfca03SJohn Marino 			*cp++ = '\0';
551*3a184c67SAntonio Huete Jimenez 	ui->host = thost;
5526cdfca03SJohn Marino 
5536cdfca03SJohn Marino 			/* look for [:port] */
5546cdfca03SJohn Marino 	if (cp != NULL) {
5556cdfca03SJohn Marino 		unsigned long	nport;
5566cdfca03SJohn Marino 
5576cdfca03SJohn Marino 		nport = strtoul(cp, &ep, 10);
5586cdfca03SJohn Marino 		if (*cp == '\0' || *ep != '\0' ||
5596cdfca03SJohn Marino 		    nport < 1 || nport > MAX_IN_PORT_T) {
5606cdfca03SJohn Marino 			warnx("Unknown port `%s' in %s `%s'",
5616cdfca03SJohn Marino 			    cp, desc, origurl);
5626cdfca03SJohn Marino 			goto cleanup_parse_url;
5636cdfca03SJohn Marino 		}
564*3a184c67SAntonio Huete Jimenez 		ui->portnum = nport;
5656cdfca03SJohn Marino 		tport = cp;
5666cdfca03SJohn Marino 	}
5676cdfca03SJohn Marino 
5686cdfca03SJohn Marino 	if (tport != NULL)
569*3a184c67SAntonio Huete Jimenez 		ui->port = ftp_strdup(tport);
570*3a184c67SAntonio Huete Jimenez 	if (ui->path == NULL) {
5716cdfca03SJohn Marino 		const char *emptypath = "/";
572*3a184c67SAntonio Huete Jimenez 		if (ui->utype == FTP_URL_T)	/* skip first / for ftp URLs */
5736cdfca03SJohn Marino 			emptypath++;
574*3a184c67SAntonio Huete Jimenez 		ui->path = ftp_strdup(emptypath);
5756cdfca03SJohn Marino 	}
5766cdfca03SJohn Marino 
5776cdfca03SJohn Marino 	DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) "
5786cdfca03SJohn Marino 	    "path `%s'\n",
579*3a184c67SAntonio Huete Jimenez 	    STRorNULL(auth->user), STRorNULL(auth->pass),
580*3a184c67SAntonio Huete Jimenez 	    STRorNULL(ui->host), STRorNULL(ui->port),
581*3a184c67SAntonio Huete Jimenez 	    ui->portnum ? ui->portnum : -1, STRorNULL(ui->path));
5826cdfca03SJohn Marino 
5836cdfca03SJohn Marino 	return (0);
5846cdfca03SJohn Marino }
5856cdfca03SJohn Marino 
5866cdfca03SJohn Marino sigjmp_buf	httpabort;
5876cdfca03SJohn Marino 
5886cdfca03SJohn Marino static int
ftp_socket(const struct urlinfo * ui,void ** ssl)589*3a184c67SAntonio Huete Jimenez ftp_socket(const struct urlinfo *ui, void **ssl)
5906cdfca03SJohn Marino {
5916cdfca03SJohn Marino 	struct addrinfo	hints, *res, *res0 = NULL;
5926cdfca03SJohn Marino 	int error;
593*3a184c67SAntonio Huete Jimenez 	int s;
594*3a184c67SAntonio Huete Jimenez 	const char *host = ui->host;
595*3a184c67SAntonio Huete Jimenez 	const char *port = ui->port;
5966cdfca03SJohn Marino 
597*3a184c67SAntonio Huete Jimenez 	if (ui->utype != HTTPS_URL_T)
598*3a184c67SAntonio Huete Jimenez 		ssl = NULL;
5996cdfca03SJohn Marino 
600*3a184c67SAntonio Huete Jimenez 	memset(&hints, 0, sizeof(hints));
601*3a184c67SAntonio Huete Jimenez 	hints.ai_flags = 0;
602*3a184c67SAntonio Huete Jimenez 	hints.ai_family = family;
603*3a184c67SAntonio Huete Jimenez 	hints.ai_socktype = SOCK_STREAM;
604*3a184c67SAntonio Huete Jimenez 	hints.ai_protocol = 0;
605*3a184c67SAntonio Huete Jimenez 
606*3a184c67SAntonio Huete Jimenez 	error = getaddrinfo(host, port, &hints, &res0);
607*3a184c67SAntonio Huete Jimenez 	if (error) {
608*3a184c67SAntonio Huete Jimenez 		warnx("Can't LOOKUP `%s:%s': %s", host, port,
609*3a184c67SAntonio Huete Jimenez 		    (error == EAI_SYSTEM) ? strerror(errno)
610*3a184c67SAntonio Huete Jimenez 					  : gai_strerror(error));
611*3a184c67SAntonio Huete Jimenez 		return -1;
612*3a184c67SAntonio Huete Jimenez 	}
613*3a184c67SAntonio Huete Jimenez 
614*3a184c67SAntonio Huete Jimenez 	if (res0->ai_canonname)
615*3a184c67SAntonio Huete Jimenez 		host = res0->ai_canonname;
616*3a184c67SAntonio Huete Jimenez 
6176cdfca03SJohn Marino 	s = -1;
618*3a184c67SAntonio Huete Jimenez 	if (ssl)
619*3a184c67SAntonio Huete Jimenez 		*ssl = NULL;
620*3a184c67SAntonio Huete Jimenez 	for (res = res0; res; res = res->ai_next) {
621*3a184c67SAntonio Huete Jimenez 		char	hname[NI_MAXHOST], sname[NI_MAXSERV];
6226cdfca03SJohn Marino 
623*3a184c67SAntonio Huete Jimenez 		ai_unmapped(res);
624*3a184c67SAntonio Huete Jimenez 		if (getnameinfo(res->ai_addr, res->ai_addrlen,
625*3a184c67SAntonio Huete Jimenez 		    hname, sizeof(hname), sname, sizeof(sname),
626*3a184c67SAntonio Huete Jimenez 		    NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
627*3a184c67SAntonio Huete Jimenez 			strlcpy(hname, "?", sizeof(hname));
628*3a184c67SAntonio Huete Jimenez 			strlcpy(sname, "?", sizeof(sname));
6296cdfca03SJohn Marino 		}
6306cdfca03SJohn Marino 
631*3a184c67SAntonio Huete Jimenez 		if (verbose && res0->ai_next) {
632*3a184c67SAntonio Huete Jimenez #ifdef INET6
633*3a184c67SAntonio Huete Jimenez 			if(res->ai_family == AF_INET6) {
634*3a184c67SAntonio Huete Jimenez 				fprintf(ttyout, "Trying [%s]:%s ...\n",
635*3a184c67SAntonio Huete Jimenez 				    hname, sname);
636*3a184c67SAntonio Huete Jimenez 			} else {
6376cdfca03SJohn Marino #endif
638*3a184c67SAntonio Huete Jimenez 				fprintf(ttyout, "Trying %s:%s ...\n",
639*3a184c67SAntonio Huete Jimenez 				    hname, sname);
640*3a184c67SAntonio Huete Jimenez #ifdef INET6
6416cdfca03SJohn Marino 			}
642*3a184c67SAntonio Huete Jimenez #endif
643*3a184c67SAntonio Huete Jimenez 		}
6446cdfca03SJohn Marino 
645*3a184c67SAntonio Huete Jimenez 		s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
646*3a184c67SAntonio Huete Jimenez 		if (s < 0) {
647*3a184c67SAntonio Huete Jimenez 			warn(
648*3a184c67SAntonio Huete Jimenez 			    "Can't create socket for connection to "
649*3a184c67SAntonio Huete Jimenez 			    "`%s:%s'", hname, sname);
650*3a184c67SAntonio Huete Jimenez 			continue;
651*3a184c67SAntonio Huete Jimenez 		}
652*3a184c67SAntonio Huete Jimenez 
653*3a184c67SAntonio Huete Jimenez 		if (ftp_connect(s, res->ai_addr, res->ai_addrlen,
654*3a184c67SAntonio Huete Jimenez 		    verbose || !res->ai_next) < 0) {
655*3a184c67SAntonio Huete Jimenez 			close(s);
656*3a184c67SAntonio Huete Jimenez 			s = -1;
657*3a184c67SAntonio Huete Jimenez 			continue;
658*3a184c67SAntonio Huete Jimenez 		}
659*3a184c67SAntonio Huete Jimenez 
660*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
661*3a184c67SAntonio Huete Jimenez 		if (ssl) {
662*3a184c67SAntonio Huete Jimenez 			if ((*ssl = fetch_start_ssl(s, host)) == NULL) {
663*3a184c67SAntonio Huete Jimenez 				close(s);
664*3a184c67SAntonio Huete Jimenez 				s = -1;
665*3a184c67SAntonio Huete Jimenez 				continue;
666*3a184c67SAntonio Huete Jimenez 			}
667*3a184c67SAntonio Huete Jimenez 		}
668*3a184c67SAntonio Huete Jimenez #endif
669*3a184c67SAntonio Huete Jimenez 		break;
670*3a184c67SAntonio Huete Jimenez 	}
671*3a184c67SAntonio Huete Jimenez 	if (res0)
672*3a184c67SAntonio Huete Jimenez 		freeaddrinfo(res0);
673*3a184c67SAntonio Huete Jimenez 	return s;
674*3a184c67SAntonio Huete Jimenez }
675*3a184c67SAntonio Huete Jimenez 
676*3a184c67SAntonio Huete Jimenez static int
handle_noproxy(const char * host,in_port_t portnum)677*3a184c67SAntonio Huete Jimenez handle_noproxy(const char *host, in_port_t portnum)
678*3a184c67SAntonio Huete Jimenez {
679*3a184c67SAntonio Huete Jimenez 
680*3a184c67SAntonio Huete Jimenez 	char *cp, *ep, *np, *np_copy, *np_iter, *no_proxy;
681*3a184c67SAntonio Huete Jimenez 	unsigned long np_port;
682*3a184c67SAntonio Huete Jimenez 	size_t hlen, plen;
683*3a184c67SAntonio Huete Jimenez 	int isproxy = 1;
6846cdfca03SJohn Marino 
6856cdfca03SJohn Marino 	/* check URL against list of no_proxied sites */
6866cdfca03SJohn Marino 	no_proxy = getoptionvalue("no_proxy");
687*3a184c67SAntonio Huete Jimenez 	if (EMPTYSTRING(no_proxy))
688*3a184c67SAntonio Huete Jimenez 		return isproxy;
6896cdfca03SJohn Marino 
6906cdfca03SJohn Marino 	np_iter = np_copy = ftp_strdup(no_proxy);
6916cdfca03SJohn Marino 	hlen = strlen(host);
6926cdfca03SJohn Marino 	while ((cp = strsep(&np_iter, " ,")) != NULL) {
6936cdfca03SJohn Marino 		if (*cp == '\0')
6946cdfca03SJohn Marino 			continue;
6956cdfca03SJohn Marino 		if ((np = strrchr(cp, ':')) != NULL) {
6966cdfca03SJohn Marino 			*np++ =  '\0';
6976cdfca03SJohn Marino 			np_port = strtoul(np, &ep, 10);
6986cdfca03SJohn Marino 			if (*np == '\0' || *ep != '\0')
6996cdfca03SJohn Marino 				continue;
7006cdfca03SJohn Marino 			if (np_port != portnum)
7016cdfca03SJohn Marino 				continue;
7026cdfca03SJohn Marino 		}
7036cdfca03SJohn Marino 		plen = strlen(cp);
7046cdfca03SJohn Marino 		if (hlen < plen)
7056cdfca03SJohn Marino 			continue;
706*3a184c67SAntonio Huete Jimenez 		if (strncasecmp(host + hlen - plen, cp, plen) == 0) {
7076cdfca03SJohn Marino 			isproxy = 0;
7086cdfca03SJohn Marino 			break;
7096cdfca03SJohn Marino 		}
7106cdfca03SJohn Marino 	}
7116cdfca03SJohn Marino 	FREEPTR(np_copy);
712*3a184c67SAntonio Huete Jimenez 	return isproxy;
7136cdfca03SJohn Marino }
7146cdfca03SJohn Marino 
715*3a184c67SAntonio Huete Jimenez static int
handle_proxy(const char * url,const char * penv,struct urlinfo * ui,struct authinfo * pauth)716*3a184c67SAntonio Huete Jimenez handle_proxy(const char *url, const char *penv, struct urlinfo *ui,
717*3a184c67SAntonio Huete Jimenez     struct authinfo *pauth)
718*3a184c67SAntonio Huete Jimenez {
719*3a184c67SAntonio Huete Jimenez 	struct urlinfo pui;
7206cdfca03SJohn Marino 
721*3a184c67SAntonio Huete Jimenez 	if (isipv6addr(ui->host) && strchr(ui->host, '%') != NULL) {
722*3a184c67SAntonio Huete Jimenez 		warnx("Scoped address notation `%s' disallowed via web proxy",
723*3a184c67SAntonio Huete Jimenez 		    ui->host);
724*3a184c67SAntonio Huete Jimenez 		return -1;
725*3a184c67SAntonio Huete Jimenez 	}
726*3a184c67SAntonio Huete Jimenez 
727*3a184c67SAntonio Huete Jimenez 	initurlinfo(&pui);
728*3a184c67SAntonio Huete Jimenez 	if (parse_url(penv, "proxy URL", &pui, pauth) == -1)
729*3a184c67SAntonio Huete Jimenez 		return -1;
730*3a184c67SAntonio Huete Jimenez 
731*3a184c67SAntonio Huete Jimenez 	if ((!IS_HTTP_TYPE(pui.utype) && pui.utype != FTP_URL_T) ||
732*3a184c67SAntonio Huete Jimenez 	    EMPTYSTRING(pui.host) ||
733*3a184c67SAntonio Huete Jimenez 	    (! EMPTYSTRING(pui.path) && strcmp(pui.path, "/") != 0)) {
7346cdfca03SJohn Marino 		warnx("Malformed proxy URL `%s'", penv);
735*3a184c67SAntonio Huete Jimenez 		freeurlinfo(&pui);
736*3a184c67SAntonio Huete Jimenez 		return -1;
7376cdfca03SJohn Marino 	}
7386cdfca03SJohn Marino 
739*3a184c67SAntonio Huete Jimenez 	FREEPTR(pui.path);
740*3a184c67SAntonio Huete Jimenez 	pui.path = ftp_strdup(url);
7416cdfca03SJohn Marino 
742*3a184c67SAntonio Huete Jimenez 	freeurlinfo(ui);
743*3a184c67SAntonio Huete Jimenez 	*ui = pui;
7446cdfca03SJohn Marino 
745*3a184c67SAntonio Huete Jimenez 	return 0;
7466cdfca03SJohn Marino }
7476cdfca03SJohn Marino 
748*3a184c67SAntonio Huete Jimenez static void
print_host(FETCH * fin,const struct urlinfo * ui)749*3a184c67SAntonio Huete Jimenez print_host(FETCH *fin, const struct urlinfo *ui)
750*3a184c67SAntonio Huete Jimenez {
7516cdfca03SJohn Marino 	char *h, *p;
7526cdfca03SJohn Marino 
753*3a184c67SAntonio Huete Jimenez 	if (strchr(ui->host, ':') == NULL) {
754*3a184c67SAntonio Huete Jimenez 		fetch_printf(fin, "Host: %s", ui->host);
755*3a184c67SAntonio Huete Jimenez 	} else {
7566cdfca03SJohn Marino 		/*
7576cdfca03SJohn Marino 		 * strip off IPv6 scope identifier, since it is
7586cdfca03SJohn Marino 		 * local to the node
7596cdfca03SJohn Marino 		 */
760*3a184c67SAntonio Huete Jimenez 		h = ftp_strdup(ui->host);
761*3a184c67SAntonio Huete Jimenez 		if (isipv6addr(h) && (p = strchr(h, '%')) != NULL)
7626cdfca03SJohn Marino 			*p = '\0';
763*3a184c67SAntonio Huete Jimenez 
7646cdfca03SJohn Marino 		fetch_printf(fin, "Host: [%s]", h);
7656cdfca03SJohn Marino 		free(h);
766*3a184c67SAntonio Huete Jimenez 	}
767*3a184c67SAntonio Huete Jimenez 
768*3a184c67SAntonio Huete Jimenez 	if ((ui->utype == HTTP_URL_T && ui->portnum != HTTP_PORT) ||
769*3a184c67SAntonio Huete Jimenez 	    (ui->utype == HTTPS_URL_T && ui->portnum != HTTPS_PORT))
770*3a184c67SAntonio Huete Jimenez 		fetch_printf(fin, ":%u", ui->portnum);
7716cdfca03SJohn Marino 	fetch_printf(fin, "\r\n");
7726cdfca03SJohn Marino }
773*3a184c67SAntonio Huete Jimenez 
774*3a184c67SAntonio Huete Jimenez static void
print_agent(FETCH * fin)775*3a184c67SAntonio Huete Jimenez print_agent(FETCH *fin)
776*3a184c67SAntonio Huete Jimenez {
777*3a184c67SAntonio Huete Jimenez 	const char *useragent;
7786cdfca03SJohn Marino 	if ((useragent = getenv("FTPUSERAGENT")) != NULL) {
7796cdfca03SJohn Marino 		fetch_printf(fin, "User-Agent: %s\r\n", useragent);
7806cdfca03SJohn Marino 	} else {
7816cdfca03SJohn Marino 		fetch_printf(fin, "User-Agent: %s/%s\r\n",
7826cdfca03SJohn Marino 		    FTP_PRODUCT, FTP_VERSION);
7836cdfca03SJohn Marino 	}
784*3a184c67SAntonio Huete Jimenez }
785*3a184c67SAntonio Huete Jimenez 
786*3a184c67SAntonio Huete Jimenez static void
print_cache(FETCH * fin,int isproxy)787*3a184c67SAntonio Huete Jimenez print_cache(FETCH *fin, int isproxy)
788*3a184c67SAntonio Huete Jimenez {
789*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, isproxy ?
790*3a184c67SAntonio Huete Jimenez 	    "Pragma: no-cache\r\n" :
791*3a184c67SAntonio Huete Jimenez 	    "Cache-Control: no-cache\r\n");
792*3a184c67SAntonio Huete Jimenez }
793*3a184c67SAntonio Huete Jimenez 
794*3a184c67SAntonio Huete Jimenez static int
print_get(FETCH * fin,int hasleading,int isproxy,const struct urlinfo * oui,const struct urlinfo * ui)795*3a184c67SAntonio Huete Jimenez print_get(FETCH *fin, int hasleading, int isproxy, const struct urlinfo *oui,
796*3a184c67SAntonio Huete Jimenez     const struct urlinfo *ui)
797*3a184c67SAntonio Huete Jimenez {
798*3a184c67SAntonio Huete Jimenez 	const char *leading = hasleading ? ", " : "  (";
799*3a184c67SAntonio Huete Jimenez 
800*3a184c67SAntonio Huete Jimenez 	if (isproxy) {
801*3a184c67SAntonio Huete Jimenez 		if (verbose) {
802*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, "%svia %s:%u", leading,
803*3a184c67SAntonio Huete Jimenez 			    ui->host, ui->portnum);
804*3a184c67SAntonio Huete Jimenez 			leading = ", ";
805*3a184c67SAntonio Huete Jimenez 			hasleading++;
806*3a184c67SAntonio Huete Jimenez 		}
807*3a184c67SAntonio Huete Jimenez 		fetch_printf(fin, "GET %s HTTP/1.0\r\n", ui->path);
808*3a184c67SAntonio Huete Jimenez 		print_host(fin, oui);
809*3a184c67SAntonio Huete Jimenez 		return hasleading;
810*3a184c67SAntonio Huete Jimenez 	}
811*3a184c67SAntonio Huete Jimenez 
812*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, "GET %s HTTP/1.1\r\n", ui->path);
813*3a184c67SAntonio Huete Jimenez 	print_host(fin, ui);
814*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, "Accept: */*\r\n");
815*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, "Connection: close\r\n");
816*3a184c67SAntonio Huete Jimenez 	if (restart_point) {
817*3a184c67SAntonio Huete Jimenez 		fputs(leading, ttyout);
818*3a184c67SAntonio Huete Jimenez 		fetch_printf(fin, "Range: bytes=" LLF "-\r\n",
819*3a184c67SAntonio Huete Jimenez 		    (LLT)restart_point);
820*3a184c67SAntonio Huete Jimenez 		fprintf(ttyout, "restarting at " LLF, (LLT)restart_point);
821*3a184c67SAntonio Huete Jimenez 		hasleading++;
822*3a184c67SAntonio Huete Jimenez 	}
823*3a184c67SAntonio Huete Jimenez 	return hasleading;
824*3a184c67SAntonio Huete Jimenez }
825*3a184c67SAntonio Huete Jimenez 
826*3a184c67SAntonio Huete Jimenez static void
getmtime(const char * cp,time_t * mtime)827*3a184c67SAntonio Huete Jimenez getmtime(const char *cp, time_t *mtime)
828*3a184c67SAntonio Huete Jimenez {
829*3a184c67SAntonio Huete Jimenez 	struct tm parsed;
830*3a184c67SAntonio Huete Jimenez 	const char *t;
831*3a184c67SAntonio Huete Jimenez 
832*3a184c67SAntonio Huete Jimenez 	memset(&parsed, 0, sizeof(parsed));
833*3a184c67SAntonio Huete Jimenez 	t = parse_rfc2616time(&parsed, cp);
834*3a184c67SAntonio Huete Jimenez 
835*3a184c67SAntonio Huete Jimenez 	if (t == NULL)
836*3a184c67SAntonio Huete Jimenez 		return;
837*3a184c67SAntonio Huete Jimenez 
838*3a184c67SAntonio Huete Jimenez 	parsed.tm_isdst = -1;
839*3a184c67SAntonio Huete Jimenez 	if (*t == '\0')
840*3a184c67SAntonio Huete Jimenez 		*mtime = timegm(&parsed);
841*3a184c67SAntonio Huete Jimenez 
842*3a184c67SAntonio Huete Jimenez #ifndef NO_DEBUG
843*3a184c67SAntonio Huete Jimenez 	if (ftp_debug && *mtime != -1) {
844*3a184c67SAntonio Huete Jimenez 		fprintf(ttyout, "parsed time as: %s",
845*3a184c67SAntonio Huete Jimenez 		    rfc2822time(localtime(mtime)));
846*3a184c67SAntonio Huete Jimenez 	}
847*3a184c67SAntonio Huete Jimenez #endif
848*3a184c67SAntonio Huete Jimenez }
849*3a184c67SAntonio Huete Jimenez 
850*3a184c67SAntonio Huete Jimenez static int
print_proxy(FETCH * fin,int hasleading,const char * wwwauth,const char * proxyauth)851*3a184c67SAntonio Huete Jimenez print_proxy(FETCH *fin, int hasleading, const char *wwwauth,
852*3a184c67SAntonio Huete Jimenez     const char *proxyauth)
853*3a184c67SAntonio Huete Jimenez {
854*3a184c67SAntonio Huete Jimenez 	const char *leading = hasleading ? ", " : "  (";
855*3a184c67SAntonio Huete Jimenez 
8566cdfca03SJohn Marino 	if (wwwauth) {
8576cdfca03SJohn Marino 		if (verbose) {
858*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, "%swith authorization", leading);
8596cdfca03SJohn Marino 			hasleading++;
8606cdfca03SJohn Marino 		}
8616cdfca03SJohn Marino 		fetch_printf(fin, "Authorization: %s\r\n", wwwauth);
8626cdfca03SJohn Marino 	}
8636cdfca03SJohn Marino 	if (proxyauth) {
8646cdfca03SJohn Marino 		if (verbose) {
865*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, "%swith proxy authorization", leading);
8666cdfca03SJohn Marino 			hasleading++;
8676cdfca03SJohn Marino 		}
8686cdfca03SJohn Marino 		fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
8696cdfca03SJohn Marino 	}
870*3a184c67SAntonio Huete Jimenez 	return hasleading;
8716cdfca03SJohn Marino }
8726cdfca03SJohn Marino 
873*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
874*3a184c67SAntonio Huete Jimenez static void
print_connect(FETCH * fin,const struct urlinfo * ui)875*3a184c67SAntonio Huete Jimenez print_connect(FETCH *fin, const struct urlinfo *ui)
876*3a184c67SAntonio Huete Jimenez {
877*3a184c67SAntonio Huete Jimenez 	char hname[NI_MAXHOST], *p;
878*3a184c67SAntonio Huete Jimenez 	const char *h;
879*3a184c67SAntonio Huete Jimenez 
880*3a184c67SAntonio Huete Jimenez 	if (isipv6addr(ui->host)) {
881*3a184c67SAntonio Huete Jimenez 		/*
882*3a184c67SAntonio Huete Jimenez 		 * strip off IPv6 scope identifier,
883*3a184c67SAntonio Huete Jimenez 		 * since it is local to the node
884*3a184c67SAntonio Huete Jimenez 		 */
885*3a184c67SAntonio Huete Jimenez 		if ((p = strchr(ui->host, '%')) == NULL)
886*3a184c67SAntonio Huete Jimenez 			snprintf(hname, sizeof(hname), "[%s]", ui->host);
887*3a184c67SAntonio Huete Jimenez 		else
888*3a184c67SAntonio Huete Jimenez 			snprintf(hname, sizeof(hname), "[%.*s]",
889*3a184c67SAntonio Huete Jimenez 			    (int)(p - ui->host), ui->host);
890*3a184c67SAntonio Huete Jimenez 		h = hname;
891*3a184c67SAntonio Huete Jimenez 	} else
892*3a184c67SAntonio Huete Jimenez 		h = ui->host;
893*3a184c67SAntonio Huete Jimenez 
894*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, "CONNECT %s:%d HTTP/1.1\r\n", h, ui->portnum);
895*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, "Host: %s:%d\r\n", h, ui->portnum);
896*3a184c67SAntonio Huete Jimenez }
897*3a184c67SAntonio Huete Jimenez #endif
898*3a184c67SAntonio Huete Jimenez 
899*3a184c67SAntonio Huete Jimenez #define C_OK 0
900*3a184c67SAntonio Huete Jimenez #define C_CLEANUP 1
901*3a184c67SAntonio Huete Jimenez #define C_IMPROPER 2
902*3a184c67SAntonio Huete Jimenez 
903*3a184c67SAntonio Huete Jimenez static int
getresponseline(FETCH * fin,char * buf,size_t buflen,int * len)904*3a184c67SAntonio Huete Jimenez getresponseline(FETCH *fin, char *buf, size_t buflen, int *len)
905*3a184c67SAntonio Huete Jimenez {
906*3a184c67SAntonio Huete Jimenez 	const char *errormsg;
907*3a184c67SAntonio Huete Jimenez 
9086cdfca03SJohn Marino 	alarmtimer(quit_time ? quit_time : 60);
909*3a184c67SAntonio Huete Jimenez 	*len = fetch_getline(fin, buf, buflen, &errormsg);
9106cdfca03SJohn Marino 	alarmtimer(0);
911*3a184c67SAntonio Huete Jimenez 	if (*len < 0) {
9126cdfca03SJohn Marino 		if (*errormsg == '\n')
9136cdfca03SJohn Marino 			errormsg++;
9146cdfca03SJohn Marino 		warnx("Receiving HTTP reply: %s", errormsg);
915*3a184c67SAntonio Huete Jimenez 		return C_CLEANUP;
9166cdfca03SJohn Marino 	}
917*3a184c67SAntonio Huete Jimenez 	while (*len > 0 && (ISLWS(buf[*len-1])))
918*3a184c67SAntonio Huete Jimenez 		buf[--*len] = '\0';
919*3a184c67SAntonio Huete Jimenez 
920*3a184c67SAntonio Huete Jimenez 	if (*len)
9216cdfca03SJohn Marino 		DPRINTF("%s: received `%s'\n", __func__, buf);
922*3a184c67SAntonio Huete Jimenez 	return C_OK;
923*3a184c67SAntonio Huete Jimenez }
924*3a184c67SAntonio Huete Jimenez 
925*3a184c67SAntonio Huete Jimenez static int
getresponse(FETCH * fin,char ** cp,size_t buflen,int * hcode)926*3a184c67SAntonio Huete Jimenez getresponse(FETCH *fin, char **cp, size_t buflen, int *hcode)
927*3a184c67SAntonio Huete Jimenez {
928*3a184c67SAntonio Huete Jimenez 	int len, rv;
929*3a184c67SAntonio Huete Jimenez 	char *ep, *buf = *cp;
930*3a184c67SAntonio Huete Jimenez 
931*3a184c67SAntonio Huete Jimenez 	*hcode = 0;
932*3a184c67SAntonio Huete Jimenez 	if ((rv = getresponseline(fin, buf, buflen, &len)) != C_OK)
933*3a184c67SAntonio Huete Jimenez 		return rv;
9346cdfca03SJohn Marino 
9356cdfca03SJohn Marino 	/* Determine HTTP response code */
936*3a184c67SAntonio Huete Jimenez 	*cp = strchr(buf, ' ');
937*3a184c67SAntonio Huete Jimenez 	if (*cp == NULL)
938*3a184c67SAntonio Huete Jimenez 		return C_IMPROPER;
939*3a184c67SAntonio Huete Jimenez 
940*3a184c67SAntonio Huete Jimenez 	(*cp)++;
941*3a184c67SAntonio Huete Jimenez 
942*3a184c67SAntonio Huete Jimenez 	*hcode = strtol(*cp, &ep, 10);
9436cdfca03SJohn Marino 	if (*ep != '\0' && !isspace((unsigned char)*ep))
944*3a184c67SAntonio Huete Jimenez 		return C_IMPROPER;
945*3a184c67SAntonio Huete Jimenez 
946*3a184c67SAntonio Huete Jimenez 	return C_OK;
947*3a184c67SAntonio Huete Jimenez }
948*3a184c67SAntonio Huete Jimenez 
949*3a184c67SAntonio Huete Jimenez static int
parse_posinfo(const char ** cp,struct posinfo * pi)950*3a184c67SAntonio Huete Jimenez parse_posinfo(const char **cp, struct posinfo *pi)
951*3a184c67SAntonio Huete Jimenez {
952*3a184c67SAntonio Huete Jimenez 	char *ep;
953*3a184c67SAntonio Huete Jimenez 	if (!match_token(cp, "bytes"))
954*3a184c67SAntonio Huete Jimenez 		return -1;
955*3a184c67SAntonio Huete Jimenez 
956*3a184c67SAntonio Huete Jimenez 	if (**cp == '*')
957*3a184c67SAntonio Huete Jimenez 		(*cp)++;
958*3a184c67SAntonio Huete Jimenez 	else {
959*3a184c67SAntonio Huete Jimenez 		pi->rangestart = STRTOLL(*cp, &ep, 10);
960*3a184c67SAntonio Huete Jimenez 		if (pi->rangestart < 0 || *ep != '-')
961*3a184c67SAntonio Huete Jimenez 			return -1;
962*3a184c67SAntonio Huete Jimenez 		*cp = ep + 1;
963*3a184c67SAntonio Huete Jimenez 		pi->rangeend = STRTOLL(*cp, &ep, 10);
964*3a184c67SAntonio Huete Jimenez 		if (pi->rangeend < 0 || pi->rangeend < pi->rangestart)
965*3a184c67SAntonio Huete Jimenez 			return -1;
966*3a184c67SAntonio Huete Jimenez 		*cp = ep;
967*3a184c67SAntonio Huete Jimenez 	}
968*3a184c67SAntonio Huete Jimenez 	if (**cp != '/')
969*3a184c67SAntonio Huete Jimenez 		return -1;
970*3a184c67SAntonio Huete Jimenez 	(*cp)++;
971*3a184c67SAntonio Huete Jimenez 	if (**cp == '*')
972*3a184c67SAntonio Huete Jimenez 		(*cp)++;
973*3a184c67SAntonio Huete Jimenez 	else {
974*3a184c67SAntonio Huete Jimenez 		pi->entitylen = STRTOLL(*cp, &ep, 10);
975*3a184c67SAntonio Huete Jimenez 		if (pi->entitylen < 0)
976*3a184c67SAntonio Huete Jimenez 			return -1;
977*3a184c67SAntonio Huete Jimenez 		*cp = ep;
978*3a184c67SAntonio Huete Jimenez 	}
979*3a184c67SAntonio Huete Jimenez 	if (**cp != '\0')
980*3a184c67SAntonio Huete Jimenez 		return -1;
981*3a184c67SAntonio Huete Jimenez 
982*3a184c67SAntonio Huete Jimenez #ifndef NO_DEBUG
983*3a184c67SAntonio Huete Jimenez 	if (ftp_debug) {
984*3a184c67SAntonio Huete Jimenez 		fprintf(ttyout, "parsed range as: ");
985*3a184c67SAntonio Huete Jimenez 		if (pi->rangestart == -1)
986*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, "*");
987*3a184c67SAntonio Huete Jimenez 		else
988*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, LLF "-" LLF, (LLT)pi->rangestart,
989*3a184c67SAntonio Huete Jimenez 			    (LLT)pi->rangeend);
990*3a184c67SAntonio Huete Jimenez 		fprintf(ttyout, "/" LLF "\n", (LLT)pi->entitylen);
991*3a184c67SAntonio Huete Jimenez 	}
992*3a184c67SAntonio Huete Jimenez #endif
993*3a184c67SAntonio Huete Jimenez 	return 0;
994*3a184c67SAntonio Huete Jimenez }
995*3a184c67SAntonio Huete Jimenez 
996*3a184c67SAntonio Huete Jimenez #ifndef NO_AUTH
997*3a184c67SAntonio Huete Jimenez static void
do_auth(int hcode,const char * url,const char * penv,struct authinfo * wauth,struct authinfo * pauth,char ** auth,const char * message,volatile int * rval)998*3a184c67SAntonio Huete Jimenez do_auth(int hcode, const char *url, const char *penv, struct authinfo *wauth,
999*3a184c67SAntonio Huete Jimenez     struct authinfo *pauth, char **auth, const char *message,
1000*3a184c67SAntonio Huete Jimenez     volatile int *rval)
1001*3a184c67SAntonio Huete Jimenez {
1002*3a184c67SAntonio Huete Jimenez 	struct authinfo aauth;
1003*3a184c67SAntonio Huete Jimenez 	char *response;
1004*3a184c67SAntonio Huete Jimenez 
1005*3a184c67SAntonio Huete Jimenez 	if (hcode == 401)
1006*3a184c67SAntonio Huete Jimenez 		aauth = *wauth;
1007*3a184c67SAntonio Huete Jimenez 	else
1008*3a184c67SAntonio Huete Jimenez 		aauth = *pauth;
1009*3a184c67SAntonio Huete Jimenez 
1010*3a184c67SAntonio Huete Jimenez 	if (verbose || aauth.auth == NULL ||
1011*3a184c67SAntonio Huete Jimenez 	    aauth.user == NULL || aauth.pass == NULL)
1012*3a184c67SAntonio Huete Jimenez 		fprintf(ttyout, "%s\n", message);
1013*3a184c67SAntonio Huete Jimenez 	if (EMPTYSTRING(*auth)) {
1014*3a184c67SAntonio Huete Jimenez 		warnx("No authentication challenge provided by server");
1015*3a184c67SAntonio Huete Jimenez 		return;
1016*3a184c67SAntonio Huete Jimenez 	}
1017*3a184c67SAntonio Huete Jimenez 
1018*3a184c67SAntonio Huete Jimenez 	if (aauth.auth != NULL) {
1019*3a184c67SAntonio Huete Jimenez 		char reply[10];
1020*3a184c67SAntonio Huete Jimenez 
1021*3a184c67SAntonio Huete Jimenez 		fprintf(ttyout, "Authorization failed. Retry (y/n)? ");
1022*3a184c67SAntonio Huete Jimenez 		if (get_line(stdin, reply, sizeof(reply), NULL) < 0) {
1023*3a184c67SAntonio Huete Jimenez 			return;
1024*3a184c67SAntonio Huete Jimenez 		}
1025*3a184c67SAntonio Huete Jimenez 		if (tolower((unsigned char)reply[0]) != 'y')
1026*3a184c67SAntonio Huete Jimenez 			return;
1027*3a184c67SAntonio Huete Jimenez 
1028*3a184c67SAntonio Huete Jimenez 		aauth.user = NULL;
1029*3a184c67SAntonio Huete Jimenez 		aauth.pass = NULL;
1030*3a184c67SAntonio Huete Jimenez 	}
1031*3a184c67SAntonio Huete Jimenez 
1032*3a184c67SAntonio Huete Jimenez 	if (auth_url(*auth, &response, &aauth) == 0) {
1033*3a184c67SAntonio Huete Jimenez 		*rval = fetch_url(url, penv,
1034*3a184c67SAntonio Huete Jimenez 		    hcode == 401 ? pauth->auth : response,
1035*3a184c67SAntonio Huete Jimenez 		    hcode == 401 ? response: wauth->auth);
1036*3a184c67SAntonio Huete Jimenez 		memset(response, 0, strlen(response));
1037*3a184c67SAntonio Huete Jimenez 		FREEPTR(response);
1038*3a184c67SAntonio Huete Jimenez 	}
1039*3a184c67SAntonio Huete Jimenez }
1040*3a184c67SAntonio Huete Jimenez #endif
1041*3a184c67SAntonio Huete Jimenez 
1042*3a184c67SAntonio Huete Jimenez static int
negotiate_connection(FETCH * fin,const char * url,const char * penv,struct posinfo * pi,time_t * mtime,struct authinfo * wauth,struct authinfo * pauth,volatile int * rval,volatile int * ischunked,char ** auth)1043*3a184c67SAntonio Huete Jimenez negotiate_connection(FETCH *fin, const char *url, const char *penv,
1044*3a184c67SAntonio Huete Jimenez     struct posinfo *pi, time_t *mtime, struct authinfo *wauth,
1045*3a184c67SAntonio Huete Jimenez     struct authinfo *pauth, volatile int *rval, volatile int *ischunked,
1046*3a184c67SAntonio Huete Jimenez     char **auth)
1047*3a184c67SAntonio Huete Jimenez {
1048*3a184c67SAntonio Huete Jimenez 	int			len, hcode, rv;
1049*3a184c67SAntonio Huete Jimenez 	char			buf[FTPBUFLEN], *ep;
1050*3a184c67SAntonio Huete Jimenez 	const char		*cp, *token;
1051*3a184c67SAntonio Huete Jimenez 	char 			*location, *message;
1052*3a184c67SAntonio Huete Jimenez 
1053*3a184c67SAntonio Huete Jimenez 	*auth = message = location = NULL;
1054*3a184c67SAntonio Huete Jimenez 
1055*3a184c67SAntonio Huete Jimenez 	/* Read the response */
1056*3a184c67SAntonio Huete Jimenez 	ep = buf;
1057*3a184c67SAntonio Huete Jimenez 	switch (getresponse(fin, &ep, sizeof(buf), &hcode)) {
1058*3a184c67SAntonio Huete Jimenez 	case C_CLEANUP:
1059*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1060*3a184c67SAntonio Huete Jimenez 	case C_IMPROPER:
10616cdfca03SJohn Marino 		goto improper;
1062*3a184c67SAntonio Huete Jimenez 	case C_OK:
1063*3a184c67SAntonio Huete Jimenez 		message = ftp_strdup(ep);
1064*3a184c67SAntonio Huete Jimenez 		break;
1065*3a184c67SAntonio Huete Jimenez 	}
10666cdfca03SJohn Marino 
10676cdfca03SJohn Marino 	/* Read the rest of the header. */
1068*3a184c67SAntonio Huete Jimenez 
1069*3a184c67SAntonio Huete Jimenez 	for (;;) {
1070*3a184c67SAntonio Huete Jimenez 		if ((rv = getresponseline(fin, buf, sizeof(buf), &len)) != C_OK)
10716cdfca03SJohn Marino 			goto cleanup_fetch_url;
10726cdfca03SJohn Marino 		if (len == 0)
10736cdfca03SJohn Marino 			break;
10746cdfca03SJohn Marino 
10756cdfca03SJohn Marino 	/*
10766cdfca03SJohn Marino 	 * Look for some headers
10776cdfca03SJohn Marino 	 */
10786cdfca03SJohn Marino 
10796cdfca03SJohn Marino 		cp = buf;
10806cdfca03SJohn Marino 
10816cdfca03SJohn Marino 		if (match_token(&cp, "Content-Length:")) {
10826cdfca03SJohn Marino 			filesize = STRTOLL(cp, &ep, 10);
10836cdfca03SJohn Marino 			if (filesize < 0 || *ep != '\0')
10846cdfca03SJohn Marino 				goto improper;
10856cdfca03SJohn Marino 			DPRINTF("%s: parsed len as: " LLF "\n",
10866cdfca03SJohn Marino 			    __func__, (LLT)filesize);
10876cdfca03SJohn Marino 
10886cdfca03SJohn Marino 		} else if (match_token(&cp, "Content-Range:")) {
1089*3a184c67SAntonio Huete Jimenez 			if (parse_posinfo(&cp, pi) == -1)
10906cdfca03SJohn Marino 				goto improper;
10916cdfca03SJohn Marino 			if (! restart_point) {
10926cdfca03SJohn Marino 				warnx(
10936cdfca03SJohn Marino 			    "Received unexpected Content-Range header");
10946cdfca03SJohn Marino 				goto cleanup_fetch_url;
10956cdfca03SJohn Marino 			}
10966cdfca03SJohn Marino 
10976cdfca03SJohn Marino 		} else if (match_token(&cp, "Last-Modified:")) {
1098*3a184c67SAntonio Huete Jimenez 			getmtime(cp, mtime);
10996cdfca03SJohn Marino 
11006cdfca03SJohn Marino 		} else if (match_token(&cp, "Location:")) {
11016cdfca03SJohn Marino 			location = ftp_strdup(cp);
11026cdfca03SJohn Marino 			DPRINTF("%s: parsed location as `%s'\n",
11036cdfca03SJohn Marino 			    __func__, cp);
11046cdfca03SJohn Marino 
11056cdfca03SJohn Marino 		} else if (match_token(&cp, "Transfer-Encoding:")) {
11066cdfca03SJohn Marino 			if (match_token(&cp, "binary")) {
11076cdfca03SJohn Marino 				warnx(
11086cdfca03SJohn Marino 		"Bogus transfer encoding `binary' (fetching anyway)");
11096cdfca03SJohn Marino 				continue;
11106cdfca03SJohn Marino 			}
11116cdfca03SJohn Marino 			if (! (token = match_token(&cp, "chunked"))) {
11126cdfca03SJohn Marino 				warnx(
11136cdfca03SJohn Marino 			    "Unsupported transfer encoding `%s'",
11146cdfca03SJohn Marino 				    token);
11156cdfca03SJohn Marino 				goto cleanup_fetch_url;
11166cdfca03SJohn Marino 			}
1117*3a184c67SAntonio Huete Jimenez 			(*ischunked)++;
11186cdfca03SJohn Marino 			DPRINTF("%s: using chunked encoding\n",
11196cdfca03SJohn Marino 			    __func__);
11206cdfca03SJohn Marino 
11216cdfca03SJohn Marino 		} else if (match_token(&cp, "Proxy-Authenticate:")
11226cdfca03SJohn Marino 			|| match_token(&cp, "WWW-Authenticate:")) {
11236cdfca03SJohn Marino 			if (! (token = match_token(&cp, "Basic"))) {
11246cdfca03SJohn Marino 				DPRINTF("%s: skipping unknown auth "
11256cdfca03SJohn Marino 				    "scheme `%s'\n", __func__, token);
11266cdfca03SJohn Marino 				continue;
11276cdfca03SJohn Marino 			}
1128*3a184c67SAntonio Huete Jimenez 			FREEPTR(*auth);
1129*3a184c67SAntonio Huete Jimenez 			*auth = ftp_strdup(token);
11306cdfca03SJohn Marino 			DPRINTF("%s: parsed auth as `%s'\n",
11316cdfca03SJohn Marino 			    __func__, cp);
11326cdfca03SJohn Marino 		}
11336cdfca03SJohn Marino 
11346cdfca03SJohn Marino 	}
11356cdfca03SJohn Marino 			/* finished parsing header */
11366cdfca03SJohn Marino 
11376cdfca03SJohn Marino 	switch (hcode) {
11386cdfca03SJohn Marino 	case 200:
11396cdfca03SJohn Marino 		break;
11406cdfca03SJohn Marino 	case 206:
11416cdfca03SJohn Marino 		if (! restart_point) {
11426cdfca03SJohn Marino 			warnx("Not expecting partial content header");
11436cdfca03SJohn Marino 			goto cleanup_fetch_url;
11446cdfca03SJohn Marino 		}
11456cdfca03SJohn Marino 		break;
11466cdfca03SJohn Marino 	case 300:
11476cdfca03SJohn Marino 	case 301:
11486cdfca03SJohn Marino 	case 302:
11496cdfca03SJohn Marino 	case 303:
11506cdfca03SJohn Marino 	case 305:
11516cdfca03SJohn Marino 	case 307:
11526cdfca03SJohn Marino 		if (EMPTYSTRING(location)) {
11536cdfca03SJohn Marino 			warnx(
11546cdfca03SJohn Marino 			"No redirection Location provided by server");
11556cdfca03SJohn Marino 			goto cleanup_fetch_url;
11566cdfca03SJohn Marino 		}
11576cdfca03SJohn Marino 		if (redirect_loop++ > 5) {
11586cdfca03SJohn Marino 			warnx("Too many redirections requested");
11596cdfca03SJohn Marino 			goto cleanup_fetch_url;
11606cdfca03SJohn Marino 		}
11616cdfca03SJohn Marino 		if (hcode == 305) {
11626cdfca03SJohn Marino 			if (verbose)
11636cdfca03SJohn Marino 				fprintf(ttyout, "Redirected via %s\n",
11646cdfca03SJohn Marino 				    location);
1165*3a184c67SAntonio Huete Jimenez 			*rval = fetch_url(url, location,
1166*3a184c67SAntonio Huete Jimenez 			    pauth->auth, wauth->auth);
11676cdfca03SJohn Marino 		} else {
11686cdfca03SJohn Marino 			if (verbose)
11696cdfca03SJohn Marino 				fprintf(ttyout, "Redirected to %s\n",
11706cdfca03SJohn Marino 				    location);
1171*3a184c67SAntonio Huete Jimenez 			*rval = go_fetch(location);
11726cdfca03SJohn Marino 		}
11736cdfca03SJohn Marino 		goto cleanup_fetch_url;
11746cdfca03SJohn Marino #ifndef NO_AUTH
11756cdfca03SJohn Marino 	case 401:
11766cdfca03SJohn Marino 	case 407:
1177*3a184c67SAntonio Huete Jimenez 		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval);
11786cdfca03SJohn Marino 		goto cleanup_fetch_url;
11796cdfca03SJohn Marino #endif
11806cdfca03SJohn Marino 	default:
11816cdfca03SJohn Marino 		if (message)
11826cdfca03SJohn Marino 			warnx("Error retrieving file `%s'", message);
11836cdfca03SJohn Marino 		else
11846cdfca03SJohn Marino 			warnx("Unknown error retrieving file");
11856cdfca03SJohn Marino 		goto cleanup_fetch_url;
11866cdfca03SJohn Marino 	}
1187*3a184c67SAntonio Huete Jimenez 	rv = C_OK;
1188*3a184c67SAntonio Huete Jimenez 	goto out;
1189*3a184c67SAntonio Huete Jimenez 
1190*3a184c67SAntonio Huete Jimenez cleanup_fetch_url:
1191*3a184c67SAntonio Huete Jimenez 	rv = C_CLEANUP;
1192*3a184c67SAntonio Huete Jimenez 	goto out;
1193*3a184c67SAntonio Huete Jimenez improper:
1194*3a184c67SAntonio Huete Jimenez 	rv = C_IMPROPER;
1195*3a184c67SAntonio Huete Jimenez 	goto out;
1196*3a184c67SAntonio Huete Jimenez out:
1197*3a184c67SAntonio Huete Jimenez 	FREEPTR(message);
1198*3a184c67SAntonio Huete Jimenez 	FREEPTR(location);
1199*3a184c67SAntonio Huete Jimenez 	return rv;
12006cdfca03SJohn Marino }		/* end of ftp:// or http:// specific setup */
12016cdfca03SJohn Marino 
1202*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
1203*3a184c67SAntonio Huete Jimenez static int
connectmethod(FETCH * fin,const char * url,const char * penv,struct urlinfo * oui,struct urlinfo * ui,struct authinfo * wauth,struct authinfo * pauth,char ** auth,int * hasleading,volatile int * rval)1204*3a184c67SAntonio Huete Jimenez connectmethod(FETCH *fin, const char *url, const char *penv,
1205*3a184c67SAntonio Huete Jimenez     struct urlinfo *oui, struct urlinfo *ui, struct authinfo *wauth,
1206*3a184c67SAntonio Huete Jimenez     struct authinfo *pauth, char **auth, int *hasleading, volatile int *rval)
1207*3a184c67SAntonio Huete Jimenez {
1208*3a184c67SAntonio Huete Jimenez 	void *ssl;
1209*3a184c67SAntonio Huete Jimenez 	int hcode, rv;
1210*3a184c67SAntonio Huete Jimenez 	const char *cp;
1211*3a184c67SAntonio Huete Jimenez 	char buf[FTPBUFLEN], *ep;
1212*3a184c67SAntonio Huete Jimenez 	char *message = NULL;
1213*3a184c67SAntonio Huete Jimenez 
1214*3a184c67SAntonio Huete Jimenez 	print_connect(fin, oui);
1215*3a184c67SAntonio Huete Jimenez 
1216*3a184c67SAntonio Huete Jimenez 	print_agent(fin);
1217*3a184c67SAntonio Huete Jimenez 	*hasleading = print_proxy(fin, *hasleading, NULL, pauth->auth);
1218*3a184c67SAntonio Huete Jimenez 
1219*3a184c67SAntonio Huete Jimenez 	if (verbose && *hasleading)
1220*3a184c67SAntonio Huete Jimenez 		fputs(")\n", ttyout);
1221*3a184c67SAntonio Huete Jimenez 	*hasleading = 0;
1222*3a184c67SAntonio Huete Jimenez 
1223*3a184c67SAntonio Huete Jimenez 	fetch_printf(fin, "\r\n");
1224*3a184c67SAntonio Huete Jimenez 	if (fetch_flush(fin) == EOF) {
1225*3a184c67SAntonio Huete Jimenez 		warn("Writing HTTP request");
1226*3a184c67SAntonio Huete Jimenez 		alarmtimer(0);
1227*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1228*3a184c67SAntonio Huete Jimenez 	}
1229*3a184c67SAntonio Huete Jimenez 	alarmtimer(0);
1230*3a184c67SAntonio Huete Jimenez 
1231*3a184c67SAntonio Huete Jimenez 	/* Read the response */
1232*3a184c67SAntonio Huete Jimenez 	ep = buf;
1233*3a184c67SAntonio Huete Jimenez 	switch (getresponse(fin, &ep, sizeof(buf), &hcode)) {
1234*3a184c67SAntonio Huete Jimenez 	case C_CLEANUP:
1235*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1236*3a184c67SAntonio Huete Jimenez 	case C_IMPROPER:
1237*3a184c67SAntonio Huete Jimenez 		goto improper;
1238*3a184c67SAntonio Huete Jimenez 	case C_OK:
1239*3a184c67SAntonio Huete Jimenez 		message = ftp_strdup(ep);
1240*3a184c67SAntonio Huete Jimenez 		break;
1241*3a184c67SAntonio Huete Jimenez 	}
1242*3a184c67SAntonio Huete Jimenez 
1243*3a184c67SAntonio Huete Jimenez 	for (;;) {
1244*3a184c67SAntonio Huete Jimenez 		int len;
1245*3a184c67SAntonio Huete Jimenez 		if (getresponseline(fin, buf, sizeof(buf), &len) != C_OK)
1246*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1247*3a184c67SAntonio Huete Jimenez 		if (len == 0)
1248*3a184c67SAntonio Huete Jimenez 			break;
1249*3a184c67SAntonio Huete Jimenez 
1250*3a184c67SAntonio Huete Jimenez 		cp = buf;
1251*3a184c67SAntonio Huete Jimenez 		if (match_token(&cp, "Proxy-Authenticate:")) {
1252*3a184c67SAntonio Huete Jimenez 			const char *token;
1253*3a184c67SAntonio Huete Jimenez 			if (!(token = match_token(&cp, "Basic"))) {
1254*3a184c67SAntonio Huete Jimenez 				DPRINTF(
1255*3a184c67SAntonio Huete Jimenez 				    "%s: skipping unknown auth scheme `%s'\n",
1256*3a184c67SAntonio Huete Jimenez 				    __func__, token);
1257*3a184c67SAntonio Huete Jimenez 				continue;
1258*3a184c67SAntonio Huete Jimenez 			}
1259*3a184c67SAntonio Huete Jimenez 			FREEPTR(*auth);
1260*3a184c67SAntonio Huete Jimenez 			*auth = ftp_strdup(token);
1261*3a184c67SAntonio Huete Jimenez 			DPRINTF("%s: parsed auth as " "`%s'\n", __func__, cp);
1262*3a184c67SAntonio Huete Jimenez 		}
1263*3a184c67SAntonio Huete Jimenez 	}
1264*3a184c67SAntonio Huete Jimenez 
1265*3a184c67SAntonio Huete Jimenez 	/* finished parsing header */
1266*3a184c67SAntonio Huete Jimenez 	switch (hcode) {
1267*3a184c67SAntonio Huete Jimenez 	case 200:
1268*3a184c67SAntonio Huete Jimenez 		break;
1269*3a184c67SAntonio Huete Jimenez #ifndef NO_AUTH
1270*3a184c67SAntonio Huete Jimenez 	case 407:
1271*3a184c67SAntonio Huete Jimenez 		do_auth(hcode, url, penv, wauth, pauth, auth, message, rval);
1272*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1273*3a184c67SAntonio Huete Jimenez #endif
1274*3a184c67SAntonio Huete Jimenez 	default:
1275*3a184c67SAntonio Huete Jimenez 		if (message)
1276*3a184c67SAntonio Huete Jimenez 			warnx("Error proxy connect " "`%s'", message);
1277*3a184c67SAntonio Huete Jimenez 		else
1278*3a184c67SAntonio Huete Jimenez 			warnx("Unknown error proxy " "connect");
1279*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1280*3a184c67SAntonio Huete Jimenez 	}
1281*3a184c67SAntonio Huete Jimenez 
1282*3a184c67SAntonio Huete Jimenez 	if ((ssl = fetch_start_ssl(fetch_fileno(fin), oui->host)) == NULL)
1283*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1284*3a184c67SAntonio Huete Jimenez 	fetch_set_ssl(fin, ssl);
1285*3a184c67SAntonio Huete Jimenez 
1286*3a184c67SAntonio Huete Jimenez 	rv = C_OK;
1287*3a184c67SAntonio Huete Jimenez 	goto out;
1288*3a184c67SAntonio Huete Jimenez improper:
1289*3a184c67SAntonio Huete Jimenez 	rv = C_IMPROPER;
1290*3a184c67SAntonio Huete Jimenez 	goto out;
1291*3a184c67SAntonio Huete Jimenez cleanup_fetch_url:
1292*3a184c67SAntonio Huete Jimenez 	rv = C_CLEANUP;
1293*3a184c67SAntonio Huete Jimenez 	goto out;
1294*3a184c67SAntonio Huete Jimenez out:
1295*3a184c67SAntonio Huete Jimenez 	FREEPTR(message);
1296*3a184c67SAntonio Huete Jimenez 	return rv;
1297*3a184c67SAntonio Huete Jimenez }
1298*3a184c67SAntonio Huete Jimenez #endif
1299*3a184c67SAntonio Huete Jimenez 
1300*3a184c67SAntonio Huete Jimenez /*
1301*3a184c67SAntonio Huete Jimenez  * Retrieve URL, via a proxy if necessary, using HTTP.
1302*3a184c67SAntonio Huete Jimenez  * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or
1303*3a184c67SAntonio Huete Jimenez  * http_proxy/https_proxy as appropriate.
1304*3a184c67SAntonio Huete Jimenez  * Supports HTTP redirects.
1305*3a184c67SAntonio Huete Jimenez  * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1306*3a184c67SAntonio Huete Jimenez  * is still open (e.g, ftp xfer with trailing /)
1307*3a184c67SAntonio Huete Jimenez  */
1308*3a184c67SAntonio Huete Jimenez static int
fetch_url(const char * url,const char * proxyenv,char * proxyauth,char * wwwauth)1309*3a184c67SAntonio Huete Jimenez fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
1310*3a184c67SAntonio Huete Jimenez {
1311*3a184c67SAntonio Huete Jimenez 	sigfunc volatile	oldint;
1312*3a184c67SAntonio Huete Jimenez 	sigfunc volatile	oldpipe;
1313*3a184c67SAntonio Huete Jimenez 	sigfunc volatile	oldalrm;
1314*3a184c67SAntonio Huete Jimenez 	sigfunc volatile	oldquit;
1315*3a184c67SAntonio Huete Jimenez 	int volatile		s;
1316*3a184c67SAntonio Huete Jimenez 	struct stat		sb;
1317*3a184c67SAntonio Huete Jimenez 	int volatile		isproxy;
1318*3a184c67SAntonio Huete Jimenez 	int volatile 		rval, ischunked;
1319*3a184c67SAntonio Huete Jimenez 	size_t			flen;
1320*3a184c67SAntonio Huete Jimenez 	static size_t		bufsize;
1321*3a184c67SAntonio Huete Jimenez 	static char		*xferbuf;
1322*3a184c67SAntonio Huete Jimenez 	const char		*cp;
1323*3a184c67SAntonio Huete Jimenez 	char			*ep;
1324*3a184c67SAntonio Huete Jimenez 	char			*volatile auth;
1325*3a184c67SAntonio Huete Jimenez 	char			*volatile savefile;
1326*3a184c67SAntonio Huete Jimenez 	char			*volatile location;
1327*3a184c67SAntonio Huete Jimenez 	char			*volatile message;
1328*3a184c67SAntonio Huete Jimenez 	char			*volatile decodedpath;
1329*3a184c67SAntonio Huete Jimenez 	struct authinfo 	wauth, pauth;
1330*3a184c67SAntonio Huete Jimenez 	struct posinfo		pi;
1331*3a184c67SAntonio Huete Jimenez 	off_t			hashbytes;
1332*3a184c67SAntonio Huete Jimenez 	int			(*volatile closefunc)(FILE *);
1333*3a184c67SAntonio Huete Jimenez 	FETCH			*volatile fin;
1334*3a184c67SAntonio Huete Jimenez 	FILE			*volatile fout;
1335*3a184c67SAntonio Huete Jimenez 	const char		*volatile penv = proxyenv;
1336*3a184c67SAntonio Huete Jimenez 	struct urlinfo		ui, oui;
1337*3a184c67SAntonio Huete Jimenez 	time_t			mtime;
1338*3a184c67SAntonio Huete Jimenez 	void			*ssl = NULL;
1339*3a184c67SAntonio Huete Jimenez 
1340*3a184c67SAntonio Huete Jimenez 	DPRINTF("%s: `%s' proxyenv `%s'\n", __func__, url, STRorNULL(penv));
1341*3a184c67SAntonio Huete Jimenez 
1342*3a184c67SAntonio Huete Jimenez 	oldquit = oldalrm = oldint = oldpipe = SIG_ERR;
1343*3a184c67SAntonio Huete Jimenez 	closefunc = NULL;
1344*3a184c67SAntonio Huete Jimenez 	fin = NULL;
1345*3a184c67SAntonio Huete Jimenez 	fout = NULL;
1346*3a184c67SAntonio Huete Jimenez 	s = -1;
1347*3a184c67SAntonio Huete Jimenez 	savefile = NULL;
1348*3a184c67SAntonio Huete Jimenez 	auth = location = message = NULL;
1349*3a184c67SAntonio Huete Jimenez 	ischunked = isproxy = 0;
1350*3a184c67SAntonio Huete Jimenez 	rval = 1;
1351*3a184c67SAntonio Huete Jimenez 
1352*3a184c67SAntonio Huete Jimenez 	initurlinfo(&ui);
1353*3a184c67SAntonio Huete Jimenez 	initurlinfo(&oui);
1354*3a184c67SAntonio Huete Jimenez 	initauthinfo(&wauth, wwwauth);
1355*3a184c67SAntonio Huete Jimenez 	initauthinfo(&pauth, proxyauth);
1356*3a184c67SAntonio Huete Jimenez 
1357*3a184c67SAntonio Huete Jimenez 	decodedpath = NULL;
1358*3a184c67SAntonio Huete Jimenez 
1359*3a184c67SAntonio Huete Jimenez 	if (sigsetjmp(httpabort, 1))
1360*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1361*3a184c67SAntonio Huete Jimenez 
1362*3a184c67SAntonio Huete Jimenez 	if (parse_url(url, "URL", &ui, &wauth) == -1)
1363*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1364*3a184c67SAntonio Huete Jimenez 
1365*3a184c67SAntonio Huete Jimenez 	copyurlinfo(&oui, &ui);
1366*3a184c67SAntonio Huete Jimenez 
1367*3a184c67SAntonio Huete Jimenez 	if (ui.utype == FILE_URL_T && ! EMPTYSTRING(ui.host)
1368*3a184c67SAntonio Huete Jimenez 	    && strcasecmp(ui.host, "localhost") != 0) {
1369*3a184c67SAntonio Huete Jimenez 		warnx("No support for non local file URL `%s'", url);
1370*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1371*3a184c67SAntonio Huete Jimenez 	}
1372*3a184c67SAntonio Huete Jimenez 
1373*3a184c67SAntonio Huete Jimenez 	if (EMPTYSTRING(ui.path)) {
1374*3a184c67SAntonio Huete Jimenez 		if (ui.utype == FTP_URL_T) {
1375*3a184c67SAntonio Huete Jimenez 			rval = fetch_ftp(url);
1376*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1377*3a184c67SAntonio Huete Jimenez 		}
1378*3a184c67SAntonio Huete Jimenez 		if (!IS_HTTP_TYPE(ui.utype) || outfile == NULL)  {
1379*3a184c67SAntonio Huete Jimenez 			warnx("Invalid URL (no file after host) `%s'", url);
1380*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1381*3a184c67SAntonio Huete Jimenez 		}
1382*3a184c67SAntonio Huete Jimenez 	}
1383*3a184c67SAntonio Huete Jimenez 
1384*3a184c67SAntonio Huete Jimenez 	decodedpath = ftp_strdup(ui.path);
1385*3a184c67SAntonio Huete Jimenez 	url_decode(decodedpath);
1386*3a184c67SAntonio Huete Jimenez 
1387*3a184c67SAntonio Huete Jimenez 	if (outfile)
1388*3a184c67SAntonio Huete Jimenez 		savefile = outfile;
1389*3a184c67SAntonio Huete Jimenez 	else {
1390*3a184c67SAntonio Huete Jimenez 		cp = strrchr(decodedpath, '/');		/* find savefile */
1391*3a184c67SAntonio Huete Jimenez 		if (cp != NULL)
1392*3a184c67SAntonio Huete Jimenez 			savefile = ftp_strdup(cp + 1);
1393*3a184c67SAntonio Huete Jimenez 		else
1394*3a184c67SAntonio Huete Jimenez 			savefile = ftp_strdup(decodedpath);
1395*3a184c67SAntonio Huete Jimenez 		/*
1396*3a184c67SAntonio Huete Jimenez 		 * Use the first URL we requested not the name after a
1397*3a184c67SAntonio Huete Jimenez 		 * possible redirect, but careful to save it because our
1398*3a184c67SAntonio Huete Jimenez 		 * "safety" check is the match to outfile.
1399*3a184c67SAntonio Huete Jimenez 		 */
1400*3a184c67SAntonio Huete Jimenez 		outfile = ftp_strdup(savefile);
1401*3a184c67SAntonio Huete Jimenez 	}
1402*3a184c67SAntonio Huete Jimenez 	DPRINTF("%s: savefile `%s'\n", __func__, savefile);
1403*3a184c67SAntonio Huete Jimenez 	if (EMPTYSTRING(savefile)) {
1404*3a184c67SAntonio Huete Jimenez 		if (ui.utype == FTP_URL_T) {
1405*3a184c67SAntonio Huete Jimenez 			rval = fetch_ftp(url);
1406*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1407*3a184c67SAntonio Huete Jimenez 		}
1408*3a184c67SAntonio Huete Jimenez 		warnx("No file after directory (you must specify an "
1409*3a184c67SAntonio Huete Jimenez 		    "output file) `%s'", url);
1410*3a184c67SAntonio Huete Jimenez 		goto cleanup_fetch_url;
1411*3a184c67SAntonio Huete Jimenez 	}
1412*3a184c67SAntonio Huete Jimenez 
1413*3a184c67SAntonio Huete Jimenez 	restart_point = 0;
1414*3a184c67SAntonio Huete Jimenez 	filesize = -1;
1415*3a184c67SAntonio Huete Jimenez 	initposinfo(&pi);
1416*3a184c67SAntonio Huete Jimenez 	mtime = -1;
1417*3a184c67SAntonio Huete Jimenez 	if (restartautofetch) {
1418*3a184c67SAntonio Huete Jimenez 		if (stat(savefile, &sb) == 0)
1419*3a184c67SAntonio Huete Jimenez 			restart_point = sb.st_size;
1420*3a184c67SAntonio Huete Jimenez 	}
1421*3a184c67SAntonio Huete Jimenez 	if (ui.utype == FILE_URL_T) {		/* file:// URLs */
1422*3a184c67SAntonio Huete Jimenez 		direction = "copied";
1423*3a184c67SAntonio Huete Jimenez 		fin = fetch_open(decodedpath, "r");
1424*3a184c67SAntonio Huete Jimenez 		if (fin == NULL) {
1425*3a184c67SAntonio Huete Jimenez 			warn("Can't open `%s'", decodedpath);
1426*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1427*3a184c67SAntonio Huete Jimenez 		}
1428*3a184c67SAntonio Huete Jimenez 		if (fstat(fetch_fileno(fin), &sb) == 0) {
1429*3a184c67SAntonio Huete Jimenez 			mtime = sb.st_mtime;
1430*3a184c67SAntonio Huete Jimenez 			filesize = sb.st_size;
1431*3a184c67SAntonio Huete Jimenez 		}
1432*3a184c67SAntonio Huete Jimenez 		if (restart_point) {
1433*3a184c67SAntonio Huete Jimenez 			if (lseek(fetch_fileno(fin), restart_point, SEEK_SET) < 0) {
1434*3a184c67SAntonio Huete Jimenez 				warn("Can't seek to restart `%s'",
1435*3a184c67SAntonio Huete Jimenez 				    decodedpath);
1436*3a184c67SAntonio Huete Jimenez 				goto cleanup_fetch_url;
1437*3a184c67SAntonio Huete Jimenez 			}
1438*3a184c67SAntonio Huete Jimenez 		}
1439*3a184c67SAntonio Huete Jimenez 		if (verbose) {
1440*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, "Copying %s", decodedpath);
1441*3a184c67SAntonio Huete Jimenez 			if (restart_point)
1442*3a184c67SAntonio Huete Jimenez 				fprintf(ttyout, " (restarting at " LLF ")",
1443*3a184c67SAntonio Huete Jimenez 				    (LLT)restart_point);
1444*3a184c67SAntonio Huete Jimenez 			fputs("\n", ttyout);
1445*3a184c67SAntonio Huete Jimenez 		}
1446*3a184c67SAntonio Huete Jimenez 		if (0 == rcvbuf_size) {
1447*3a184c67SAntonio Huete Jimenez 			rcvbuf_size = 8 * 1024; /* XXX */
1448*3a184c67SAntonio Huete Jimenez 		}
1449*3a184c67SAntonio Huete Jimenez 	} else {				/* ftp:// or http:// URLs */
1450*3a184c67SAntonio Huete Jimenez 		int hasleading;
1451*3a184c67SAntonio Huete Jimenez 
1452*3a184c67SAntonio Huete Jimenez 		if (penv == NULL) {
1453*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
1454*3a184c67SAntonio Huete Jimenez 			if (ui.utype == HTTPS_URL_T)
1455*3a184c67SAntonio Huete Jimenez 				penv = getoptionvalue("https_proxy");
1456*3a184c67SAntonio Huete Jimenez #endif
1457*3a184c67SAntonio Huete Jimenez 			if (penv == NULL && IS_HTTP_TYPE(ui.utype))
1458*3a184c67SAntonio Huete Jimenez 				penv = getoptionvalue("http_proxy");
1459*3a184c67SAntonio Huete Jimenez 			else if (ui.utype == FTP_URL_T)
1460*3a184c67SAntonio Huete Jimenez 				penv = getoptionvalue("ftp_proxy");
1461*3a184c67SAntonio Huete Jimenez 		}
1462*3a184c67SAntonio Huete Jimenez 		direction = "retrieved";
1463*3a184c67SAntonio Huete Jimenez 		if (! EMPTYSTRING(penv)) {			/* use proxy */
1464*3a184c67SAntonio Huete Jimenez 
1465*3a184c67SAntonio Huete Jimenez 			isproxy = handle_noproxy(ui.host, ui.portnum);
1466*3a184c67SAntonio Huete Jimenez 
1467*3a184c67SAntonio Huete Jimenez 			if (isproxy == 0 && ui.utype == FTP_URL_T) {
1468*3a184c67SAntonio Huete Jimenez 				rval = fetch_ftp(url);
1469*3a184c67SAntonio Huete Jimenez 				goto cleanup_fetch_url;
1470*3a184c67SAntonio Huete Jimenez 			}
1471*3a184c67SAntonio Huete Jimenez 
1472*3a184c67SAntonio Huete Jimenez 			if (isproxy) {
1473*3a184c67SAntonio Huete Jimenez 				if (restart_point) {
1474*3a184c67SAntonio Huete Jimenez 					warnx(
1475*3a184c67SAntonio Huete Jimenez 					    "Can't restart via proxy URL `%s'",
1476*3a184c67SAntonio Huete Jimenez 					    penv);
1477*3a184c67SAntonio Huete Jimenez 					goto cleanup_fetch_url;
1478*3a184c67SAntonio Huete Jimenez 				}
1479*3a184c67SAntonio Huete Jimenez 				if (handle_proxy(url, penv, &ui, &pauth) < 0)
1480*3a184c67SAntonio Huete Jimenez 					goto cleanup_fetch_url;
1481*3a184c67SAntonio Huete Jimenez 			}
1482*3a184c67SAntonio Huete Jimenez 		} /* ! EMPTYSTRING(penv) */
1483*3a184c67SAntonio Huete Jimenez 
1484*3a184c67SAntonio Huete Jimenez 		s = ftp_socket(&ui, &ssl);
1485*3a184c67SAntonio Huete Jimenez 		if (s < 0) {
1486*3a184c67SAntonio Huete Jimenez 			warnx("Can't connect to `%s:%s'", ui.host, ui.port);
1487*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1488*3a184c67SAntonio Huete Jimenez 		}
1489*3a184c67SAntonio Huete Jimenez 
1490*3a184c67SAntonio Huete Jimenez 		oldalrm = xsignal(SIGALRM, timeouthttp);
1491*3a184c67SAntonio Huete Jimenez 		alarmtimer(quit_time ? quit_time : 60);
1492*3a184c67SAntonio Huete Jimenez 		fin = fetch_fdopen(s, "r+");
1493*3a184c67SAntonio Huete Jimenez 		fetch_set_ssl(fin, ssl);
1494*3a184c67SAntonio Huete Jimenez 		alarmtimer(0);
1495*3a184c67SAntonio Huete Jimenez 
1496*3a184c67SAntonio Huete Jimenez 		alarmtimer(quit_time ? quit_time : 60);
1497*3a184c67SAntonio Huete Jimenez 		/*
1498*3a184c67SAntonio Huete Jimenez 		 * Construct and send the request.
1499*3a184c67SAntonio Huete Jimenez 		 */
1500*3a184c67SAntonio Huete Jimenez 		if (verbose)
1501*3a184c67SAntonio Huete Jimenez 			fprintf(ttyout, "Requesting %s\n", url);
1502*3a184c67SAntonio Huete Jimenez 
1503*3a184c67SAntonio Huete Jimenez 		hasleading = 0;
1504*3a184c67SAntonio Huete Jimenez #ifdef WITH_SSL
1505*3a184c67SAntonio Huete Jimenez 		if (isproxy && oui.utype == HTTPS_URL_T) {
1506*3a184c67SAntonio Huete Jimenez 			switch (connectmethod(fin, url, penv, &oui, &ui,
1507*3a184c67SAntonio Huete Jimenez 			    &wauth, &pauth, __UNVOLATILE(&auth), &hasleading,
1508*3a184c67SAntonio Huete Jimenez 			    &rval)) {
1509*3a184c67SAntonio Huete Jimenez 			case C_CLEANUP:
1510*3a184c67SAntonio Huete Jimenez 				goto cleanup_fetch_url;
1511*3a184c67SAntonio Huete Jimenez 			case C_IMPROPER:
1512*3a184c67SAntonio Huete Jimenez 				goto improper;
1513*3a184c67SAntonio Huete Jimenez 			case C_OK:
1514*3a184c67SAntonio Huete Jimenez 				break;
1515*3a184c67SAntonio Huete Jimenez 			default:
1516*3a184c67SAntonio Huete Jimenez 				abort();
1517*3a184c67SAntonio Huete Jimenez 			}
1518*3a184c67SAntonio Huete Jimenez 		}
1519*3a184c67SAntonio Huete Jimenez #endif
1520*3a184c67SAntonio Huete Jimenez 
1521*3a184c67SAntonio Huete Jimenez 		hasleading = print_get(fin, hasleading, isproxy, &oui, &ui);
1522*3a184c67SAntonio Huete Jimenez 
1523*3a184c67SAntonio Huete Jimenez 		if (flushcache)
1524*3a184c67SAntonio Huete Jimenez 			print_cache(fin, isproxy);
1525*3a184c67SAntonio Huete Jimenez 
1526*3a184c67SAntonio Huete Jimenez 		print_agent(fin);
1527*3a184c67SAntonio Huete Jimenez 		hasleading = print_proxy(fin, hasleading, wauth.auth,
1528*3a184c67SAntonio Huete Jimenez 		     auth ? NULL : pauth.auth);
1529*3a184c67SAntonio Huete Jimenez 		if (hasleading) {
1530*3a184c67SAntonio Huete Jimenez 			hasleading = 0;
1531*3a184c67SAntonio Huete Jimenez 			if (verbose)
1532*3a184c67SAntonio Huete Jimenez 				fputs(")\n", ttyout);
1533*3a184c67SAntonio Huete Jimenez 		}
1534*3a184c67SAntonio Huete Jimenez 
1535*3a184c67SAntonio Huete Jimenez 		fetch_printf(fin, "\r\n");
1536*3a184c67SAntonio Huete Jimenez 		if (fetch_flush(fin) == EOF) {
1537*3a184c67SAntonio Huete Jimenez 			warn("Writing HTTP request");
1538*3a184c67SAntonio Huete Jimenez 			alarmtimer(0);
1539*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1540*3a184c67SAntonio Huete Jimenez 		}
1541*3a184c67SAntonio Huete Jimenez 		alarmtimer(0);
1542*3a184c67SAntonio Huete Jimenez 
1543*3a184c67SAntonio Huete Jimenez 		switch (negotiate_connection(fin, url, penv, &pi,
1544*3a184c67SAntonio Huete Jimenez 		    &mtime, &wauth, &pauth, &rval, &ischunked,
1545*3a184c67SAntonio Huete Jimenez 		    __UNVOLATILE(&auth))) {
1546*3a184c67SAntonio Huete Jimenez 		case C_OK:
1547*3a184c67SAntonio Huete Jimenez 			break;
1548*3a184c67SAntonio Huete Jimenez 		case C_CLEANUP:
1549*3a184c67SAntonio Huete Jimenez 			goto cleanup_fetch_url;
1550*3a184c67SAntonio Huete Jimenez 		case C_IMPROPER:
1551*3a184c67SAntonio Huete Jimenez 			goto improper;
1552*3a184c67SAntonio Huete Jimenez 		default:
1553*3a184c67SAntonio Huete Jimenez 			abort();
1554*3a184c67SAntonio Huete Jimenez 		}
1555*3a184c67SAntonio Huete Jimenez 	}
1556*3a184c67SAntonio Huete Jimenez 
15576cdfca03SJohn Marino 	/* Open the output file. */
15586cdfca03SJohn Marino 
15596cdfca03SJohn Marino 	/*
15606cdfca03SJohn Marino 	 * Only trust filenames with special meaning if they came from
15616cdfca03SJohn Marino 	 * the command line
15626cdfca03SJohn Marino 	 */
15636cdfca03SJohn Marino 	if (outfile == savefile) {
15646cdfca03SJohn Marino 		if (strcmp(savefile, "-") == 0) {
15656cdfca03SJohn Marino 			fout = stdout;
15666cdfca03SJohn Marino 		} else if (*savefile == '|') {
1567d9574910SJohn Marino #if 0
15686cdfca03SJohn Marino 			oldpipe = xsignal(SIGPIPE, SIG_IGN);
15696cdfca03SJohn Marino 			fout = popen(savefile + 1, "w");
15706cdfca03SJohn Marino 			if (fout == NULL) {
15716cdfca03SJohn Marino 				warn("Can't execute `%s'", savefile + 1);
15726cdfca03SJohn Marino 				goto cleanup_fetch_url;
15736cdfca03SJohn Marino 			}
15746cdfca03SJohn Marino 			closefunc = pclose;
1575d9574910SJohn Marino #endif
15766cdfca03SJohn Marino 		}
15776cdfca03SJohn Marino 	}
15786cdfca03SJohn Marino 	if (fout == NULL) {
1579*3a184c67SAntonio Huete Jimenez 		if ((pi.rangeend != -1 && pi.rangeend <= restart_point) ||
1580*3a184c67SAntonio Huete Jimenez 		    (pi.rangestart == -1 &&
1581*3a184c67SAntonio Huete Jimenez 		    filesize != -1 && filesize <= restart_point)) {
15826cdfca03SJohn Marino 			/* already done */
15836cdfca03SJohn Marino 			if (verbose)
15846cdfca03SJohn Marino 				fprintf(ttyout, "already done\n");
15856cdfca03SJohn Marino 			rval = 0;
15866cdfca03SJohn Marino 			goto cleanup_fetch_url;
15876cdfca03SJohn Marino 		}
1588*3a184c67SAntonio Huete Jimenez 		if (restart_point && pi.rangestart != -1) {
1589*3a184c67SAntonio Huete Jimenez 			if (pi.entitylen != -1)
1590*3a184c67SAntonio Huete Jimenez 				filesize = pi.entitylen;
1591*3a184c67SAntonio Huete Jimenez 			if (pi.rangestart != restart_point) {
15926cdfca03SJohn Marino 				warnx(
15936cdfca03SJohn Marino 				    "Size of `%s' differs from save file `%s'",
15946cdfca03SJohn Marino 				    url, savefile);
15956cdfca03SJohn Marino 				goto cleanup_fetch_url;
15966cdfca03SJohn Marino 			}
15976cdfca03SJohn Marino 			fout = fopen(savefile, "a");
15986cdfca03SJohn Marino 		} else
15996cdfca03SJohn Marino 			fout = fopen(savefile, "w");
16006cdfca03SJohn Marino 		if (fout == NULL) {
16016cdfca03SJohn Marino 			warn("Can't open `%s'", savefile);
16026cdfca03SJohn Marino 			goto cleanup_fetch_url;
16036cdfca03SJohn Marino 		}
16046cdfca03SJohn Marino 		closefunc = fclose;
16056cdfca03SJohn Marino 	}
16066cdfca03SJohn Marino 
16076cdfca03SJohn Marino 			/* Trap signals */
16086cdfca03SJohn Marino 	oldquit = xsignal(SIGQUIT, psummary);
16096cdfca03SJohn Marino 	oldint = xsignal(SIGINT, aborthttp);
16106cdfca03SJohn Marino 
16116cdfca03SJohn Marino 	assert(rcvbuf_size > 0);
16126cdfca03SJohn Marino 	if ((size_t)rcvbuf_size > bufsize) {
16136cdfca03SJohn Marino 		if (xferbuf)
16146cdfca03SJohn Marino 			(void)free(xferbuf);
16156cdfca03SJohn Marino 		bufsize = rcvbuf_size;
16166cdfca03SJohn Marino 		xferbuf = ftp_malloc(bufsize);
16176cdfca03SJohn Marino 	}
16186cdfca03SJohn Marino 
16196cdfca03SJohn Marino 	bytes = 0;
16206cdfca03SJohn Marino 	hashbytes = mark;
1621*3a184c67SAntonio Huete Jimenez 	if (oldalrm != SIG_ERR) {
16226cdfca03SJohn Marino 		(void)xsignal(SIGALRM, oldalrm);
1623*3a184c67SAntonio Huete Jimenez 		oldalrm = SIG_ERR;
16246cdfca03SJohn Marino 	}
16256cdfca03SJohn Marino 	progressmeter(-1);
16266cdfca03SJohn Marino 
16276cdfca03SJohn Marino 			/* Finally, suck down the file. */
16286cdfca03SJohn Marino 	do {
16296cdfca03SJohn Marino 		long chunksize;
16306cdfca03SJohn Marino 		short lastchunk;
16316cdfca03SJohn Marino 
16326cdfca03SJohn Marino 		chunksize = 0;
16336cdfca03SJohn Marino 		lastchunk = 0;
16346cdfca03SJohn Marino 					/* read chunk-size */
16356cdfca03SJohn Marino 		if (ischunked) {
16366cdfca03SJohn Marino 			if (fetch_getln(xferbuf, bufsize, fin) == NULL) {
16376cdfca03SJohn Marino 				warnx("Unexpected EOF reading chunk-size");
16386cdfca03SJohn Marino 				goto cleanup_fetch_url;
16396cdfca03SJohn Marino 			}
16406cdfca03SJohn Marino 			errno = 0;
16416cdfca03SJohn Marino 			chunksize = strtol(xferbuf, &ep, 16);
16426cdfca03SJohn Marino 			if (ep == xferbuf) {
16436cdfca03SJohn Marino 				warnx("Invalid chunk-size");
16446cdfca03SJohn Marino 				goto cleanup_fetch_url;
16456cdfca03SJohn Marino 			}
16466cdfca03SJohn Marino 			if (errno == ERANGE || chunksize < 0) {
16476cdfca03SJohn Marino 				errno = ERANGE;
16486cdfca03SJohn Marino 				warn("Chunk-size `%.*s'",
16496cdfca03SJohn Marino 				    (int)(ep-xferbuf), xferbuf);
16506cdfca03SJohn Marino 				goto cleanup_fetch_url;
16516cdfca03SJohn Marino 			}
16526cdfca03SJohn Marino 
16536cdfca03SJohn Marino 				/*
16546cdfca03SJohn Marino 				 * XXX:	Work around bug in Apache 1.3.9 and
16556cdfca03SJohn Marino 				 *	1.3.11, which incorrectly put trailing
16566cdfca03SJohn Marino 				 *	space after the chunk-size.
16576cdfca03SJohn Marino 				 */
16586cdfca03SJohn Marino 			while (*ep == ' ')
16596cdfca03SJohn Marino 				ep++;
16606cdfca03SJohn Marino 
16616cdfca03SJohn Marino 					/* skip [ chunk-ext ] */
16626cdfca03SJohn Marino 			if (*ep == ';') {
16636cdfca03SJohn Marino 				while (*ep && *ep != '\r')
16646cdfca03SJohn Marino 					ep++;
16656cdfca03SJohn Marino 			}
16666cdfca03SJohn Marino 
16676cdfca03SJohn Marino 			if (strcmp(ep, "\r\n") != 0) {
16686cdfca03SJohn Marino 				warnx("Unexpected data following chunk-size");
16696cdfca03SJohn Marino 				goto cleanup_fetch_url;
16706cdfca03SJohn Marino 			}
16716cdfca03SJohn Marino 			DPRINTF("%s: got chunk-size of " LLF "\n", __func__,
16726cdfca03SJohn Marino 			    (LLT)chunksize);
16736cdfca03SJohn Marino 			if (chunksize == 0) {
16746cdfca03SJohn Marino 				lastchunk = 1;
16756cdfca03SJohn Marino 				goto chunkdone;
16766cdfca03SJohn Marino 			}
16776cdfca03SJohn Marino 		}
16786cdfca03SJohn Marino 					/* transfer file or chunk */
16796cdfca03SJohn Marino 		while (1) {
16806cdfca03SJohn Marino 			struct timeval then, now, td;
16816cdfca03SJohn Marino 			volatile off_t bufrem;
16826cdfca03SJohn Marino 
16836cdfca03SJohn Marino 			if (rate_get)
16846cdfca03SJohn Marino 				(void)gettimeofday(&then, NULL);
16856cdfca03SJohn Marino 			bufrem = rate_get ? rate_get : (off_t)bufsize;
16866cdfca03SJohn Marino 			if (ischunked)
16876cdfca03SJohn Marino 				bufrem = MIN(chunksize, bufrem);
16886cdfca03SJohn Marino 			while (bufrem > 0) {
1689*3a184c67SAntonio Huete Jimenez 				size_t nr = MIN((off_t)bufsize, bufrem);
16906cdfca03SJohn Marino 				flen = fetch_read(xferbuf, sizeof(char),
1691*3a184c67SAntonio Huete Jimenez 				    nr, fin);
1692*3a184c67SAntonio Huete Jimenez 				if (flen == 0) {
1693*3a184c67SAntonio Huete Jimenez 					if (fetch_error(fin))
1694*3a184c67SAntonio Huete Jimenez 						goto chunkerror;
16956cdfca03SJohn Marino 					goto chunkdone;
1696*3a184c67SAntonio Huete Jimenez 				}
16976cdfca03SJohn Marino 				bytes += flen;
16986cdfca03SJohn Marino 				bufrem -= flen;
1699*3a184c67SAntonio Huete Jimenez 				if (maxwrite(xferbuf, sizeof(char), flen, fout)
17006cdfca03SJohn Marino 				    != flen) {
17016cdfca03SJohn Marino 					warn("Writing `%s'", savefile);
17026cdfca03SJohn Marino 					goto cleanup_fetch_url;
17036cdfca03SJohn Marino 				}
17046cdfca03SJohn Marino 				if (hash && !progress) {
17056cdfca03SJohn Marino 					while (bytes >= hashbytes) {
17066cdfca03SJohn Marino 						(void)putc('#', ttyout);
17076cdfca03SJohn Marino 						hashbytes += mark;
17086cdfca03SJohn Marino 					}
17096cdfca03SJohn Marino 					(void)fflush(ttyout);
17106cdfca03SJohn Marino 				}
17116cdfca03SJohn Marino 				if (ischunked) {
17126cdfca03SJohn Marino 					chunksize -= flen;
17136cdfca03SJohn Marino 					if (chunksize <= 0)
17146cdfca03SJohn Marino 						break;
17156cdfca03SJohn Marino 				}
17166cdfca03SJohn Marino 			}
17176cdfca03SJohn Marino 			if (rate_get) {
17186cdfca03SJohn Marino 				while (1) {
17196cdfca03SJohn Marino 					(void)gettimeofday(&now, NULL);
17206cdfca03SJohn Marino 					timersub(&now, &then, &td);
17216cdfca03SJohn Marino 					if (td.tv_sec > 0)
17226cdfca03SJohn Marino 						break;
17236cdfca03SJohn Marino 					usleep(1000000 - td.tv_usec);
17246cdfca03SJohn Marino 				}
17256cdfca03SJohn Marino 			}
17266cdfca03SJohn Marino 			if (ischunked && chunksize <= 0)
17276cdfca03SJohn Marino 				break;
17286cdfca03SJohn Marino 		}
17296cdfca03SJohn Marino 					/* read CRLF after chunk*/
17306cdfca03SJohn Marino  chunkdone:
17316cdfca03SJohn Marino 		if (ischunked) {
17326cdfca03SJohn Marino 			if (fetch_getln(xferbuf, bufsize, fin) == NULL) {
17336cdfca03SJohn Marino 				alarmtimer(0);
17346cdfca03SJohn Marino 				warnx("Unexpected EOF reading chunk CRLF");
17356cdfca03SJohn Marino 				goto cleanup_fetch_url;
17366cdfca03SJohn Marino 			}
17376cdfca03SJohn Marino 			if (strcmp(xferbuf, "\r\n") != 0) {
17386cdfca03SJohn Marino 				warnx("Unexpected data following chunk");
17396cdfca03SJohn Marino 				goto cleanup_fetch_url;
17406cdfca03SJohn Marino 			}
17416cdfca03SJohn Marino 			if (lastchunk)
17426cdfca03SJohn Marino 				break;
17436cdfca03SJohn Marino 		}
17446cdfca03SJohn Marino 	} while (ischunked);
17456cdfca03SJohn Marino 
17466cdfca03SJohn Marino /* XXX: deal with optional trailer & CRLF here? */
1747*3a184c67SAntonio Huete Jimenez chunkerror:
17486cdfca03SJohn Marino 	if (hash && !progress && bytes > 0) {
17496cdfca03SJohn Marino 		if (bytes < mark)
17506cdfca03SJohn Marino 			(void)putc('#', ttyout);
17516cdfca03SJohn Marino 		(void)putc('\n', ttyout);
17526cdfca03SJohn Marino 	}
17536cdfca03SJohn Marino 	if (fetch_error(fin)) {
17546cdfca03SJohn Marino 		warn("Reading file");
17556cdfca03SJohn Marino 		goto cleanup_fetch_url;
17566cdfca03SJohn Marino 	}
17576cdfca03SJohn Marino 	progressmeter(1);
17586cdfca03SJohn Marino 	(void)fflush(fout);
17596cdfca03SJohn Marino 	if (closefunc == fclose && mtime != -1) {
17606cdfca03SJohn Marino 		struct timeval tval[2];
17616cdfca03SJohn Marino 
17626cdfca03SJohn Marino 		(void)gettimeofday(&tval[0], NULL);
17636cdfca03SJohn Marino 		tval[1].tv_sec = mtime;
17646cdfca03SJohn Marino 		tval[1].tv_usec = 0;
17656cdfca03SJohn Marino 		(*closefunc)(fout);
17666cdfca03SJohn Marino 		fout = NULL;
17676cdfca03SJohn Marino 
17686cdfca03SJohn Marino 		if (utimes(savefile, tval) == -1) {
17696cdfca03SJohn Marino 			fprintf(ttyout,
17706cdfca03SJohn Marino 			    "Can't change modification time to %s",
17716cdfca03SJohn Marino 			    rfc2822time(localtime(&mtime)));
17726cdfca03SJohn Marino 		}
17736cdfca03SJohn Marino 	}
17746cdfca03SJohn Marino 	if (bytes > 0)
17756cdfca03SJohn Marino 		ptransfer(0);
17766cdfca03SJohn Marino 	bytes = 0;
17776cdfca03SJohn Marino 
17786cdfca03SJohn Marino 	rval = 0;
17796cdfca03SJohn Marino 	goto cleanup_fetch_url;
17806cdfca03SJohn Marino 
17816cdfca03SJohn Marino  improper:
1782*3a184c67SAntonio Huete Jimenez 	warnx("Improper response from `%s:%s'", ui.host, ui.port);
17836cdfca03SJohn Marino 
17846cdfca03SJohn Marino  cleanup_fetch_url:
1785*3a184c67SAntonio Huete Jimenez 	if (oldint != SIG_ERR)
17866cdfca03SJohn Marino 		(void)xsignal(SIGINT, oldint);
1787*3a184c67SAntonio Huete Jimenez 	if (oldpipe != SIG_ERR)
17886cdfca03SJohn Marino 		(void)xsignal(SIGPIPE, oldpipe);
1789*3a184c67SAntonio Huete Jimenez 	if (oldalrm != SIG_ERR)
17906cdfca03SJohn Marino 		(void)xsignal(SIGALRM, oldalrm);
1791*3a184c67SAntonio Huete Jimenez 	if (oldquit != SIG_ERR)
1792*3a184c67SAntonio Huete Jimenez 		(void)xsignal(SIGQUIT, oldquit);
17936cdfca03SJohn Marino 	if (fin != NULL)
17946cdfca03SJohn Marino 		fetch_close(fin);
17956cdfca03SJohn Marino 	else if (s != -1)
17966cdfca03SJohn Marino 		close(s);
17976cdfca03SJohn Marino 	if (closefunc != NULL && fout != NULL)
17986cdfca03SJohn Marino 		(*closefunc)(fout);
17996cdfca03SJohn Marino 	if (savefile != outfile)
18006cdfca03SJohn Marino 		FREEPTR(savefile);
1801*3a184c67SAntonio Huete Jimenez 	freeurlinfo(&ui);
1802*3a184c67SAntonio Huete Jimenez 	freeurlinfo(&oui);
1803*3a184c67SAntonio Huete Jimenez 	freeauthinfo(&wauth);
1804*3a184c67SAntonio Huete Jimenez 	freeauthinfo(&pauth);
18056cdfca03SJohn Marino 	FREEPTR(decodedpath);
18066cdfca03SJohn Marino 	FREEPTR(auth);
18076cdfca03SJohn Marino 	FREEPTR(location);
18086cdfca03SJohn Marino 	FREEPTR(message);
18096cdfca03SJohn Marino 	return (rval);
18106cdfca03SJohn Marino }
18116cdfca03SJohn Marino 
18126cdfca03SJohn Marino /*
18136cdfca03SJohn Marino  * Abort a HTTP retrieval
18146cdfca03SJohn Marino  */
18156cdfca03SJohn Marino static void
aborthttp(int notused)18166cdfca03SJohn Marino aborthttp(int notused)
18176cdfca03SJohn Marino {
18186cdfca03SJohn Marino 	char msgbuf[100];
18196cdfca03SJohn Marino 	int len;
18206cdfca03SJohn Marino 
18216cdfca03SJohn Marino 	sigint_raised = 1;
18226cdfca03SJohn Marino 	alarmtimer(0);
18236cdfca03SJohn Marino 	if (fromatty) {
18246cdfca03SJohn Marino 		len = snprintf(msgbuf, sizeof(msgbuf),
18256cdfca03SJohn Marino 		    "\n%s: HTTP fetch aborted.\n", getprogname());
18266cdfca03SJohn Marino 		if (len > 0)
18276cdfca03SJohn Marino 			write(fileno(ttyout), msgbuf, len);
18286cdfca03SJohn Marino 	}
18296cdfca03SJohn Marino 	siglongjmp(httpabort, 1);
18306cdfca03SJohn Marino }
18316cdfca03SJohn Marino 
18326cdfca03SJohn Marino static void
timeouthttp(int notused)18336cdfca03SJohn Marino timeouthttp(int notused)
18346cdfca03SJohn Marino {
18356cdfca03SJohn Marino 	char msgbuf[100];
18366cdfca03SJohn Marino 	int len;
18376cdfca03SJohn Marino 
18386cdfca03SJohn Marino 	alarmtimer(0);
18396cdfca03SJohn Marino 	if (fromatty) {
18406cdfca03SJohn Marino 		len = snprintf(msgbuf, sizeof(msgbuf),
18416cdfca03SJohn Marino 		    "\n%s: HTTP fetch timeout.\n", getprogname());
18426cdfca03SJohn Marino 		if (len > 0)
18436cdfca03SJohn Marino 			write(fileno(ttyout), msgbuf, len);
18446cdfca03SJohn Marino 	}
18456cdfca03SJohn Marino 	siglongjmp(httpabort, 1);
18466cdfca03SJohn Marino }
18476cdfca03SJohn Marino 
18486cdfca03SJohn Marino /*
18496cdfca03SJohn Marino  * Retrieve ftp URL or classic ftp argument using FTP.
18506cdfca03SJohn Marino  * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
18516cdfca03SJohn Marino  * is still open (e.g, ftp xfer with trailing /)
18526cdfca03SJohn Marino  */
18536cdfca03SJohn Marino static int
fetch_ftp(const char * url)18546cdfca03SJohn Marino fetch_ftp(const char *url)
18556cdfca03SJohn Marino {
18566cdfca03SJohn Marino 	char		*cp, *xargv[5], rempath[MAXPATHLEN];
1857*3a184c67SAntonio Huete Jimenez 	char		*dir, *file;
18586cdfca03SJohn Marino 	char		 cmdbuf[MAXPATHLEN];
18596cdfca03SJohn Marino 	char		 dirbuf[4];
18606cdfca03SJohn Marino 	int		 dirhasglob, filehasglob, rval, transtype, xargc;
18616cdfca03SJohn Marino 	int		 oanonftp, oautologin;
1862*3a184c67SAntonio Huete Jimenez 	struct authinfo  auth;
1863*3a184c67SAntonio Huete Jimenez 	struct urlinfo	 ui;
18646cdfca03SJohn Marino 
18656cdfca03SJohn Marino 	DPRINTF("fetch_ftp: `%s'\n", url);
1866*3a184c67SAntonio Huete Jimenez 	dir = file = NULL;
18676cdfca03SJohn Marino 	rval = 1;
18686cdfca03SJohn Marino 	transtype = TYPE_I;
18696cdfca03SJohn Marino 
1870*3a184c67SAntonio Huete Jimenez 	initurlinfo(&ui);
1871*3a184c67SAntonio Huete Jimenez 	initauthinfo(&auth, NULL);
1872*3a184c67SAntonio Huete Jimenez 
18736cdfca03SJohn Marino 	if (STRNEQUAL(url, FTP_URL)) {
1874*3a184c67SAntonio Huete Jimenez 		if ((parse_url(url, "URL", &ui, &auth) == -1) ||
1875*3a184c67SAntonio Huete Jimenez 		    (auth.user != NULL && *auth.user == '\0') ||
1876*3a184c67SAntonio Huete Jimenez 		    EMPTYSTRING(ui.host)) {
18776cdfca03SJohn Marino 			warnx("Invalid URL `%s'", url);
18786cdfca03SJohn Marino 			goto cleanup_fetch_ftp;
18796cdfca03SJohn Marino 		}
18806cdfca03SJohn Marino 		/*
18816cdfca03SJohn Marino 		 * Note: Don't url_decode(path) here.  We need to keep the
18826cdfca03SJohn Marino 		 * distinction between "/" and "%2F" until later.
18836cdfca03SJohn Marino 		 */
18846cdfca03SJohn Marino 
18856cdfca03SJohn Marino 					/* check for trailing ';type=[aid]' */
1886*3a184c67SAntonio Huete Jimenez 		if (! EMPTYSTRING(ui.path) && (cp = strrchr(ui.path, ';')) != NULL) {
18876cdfca03SJohn Marino 			if (strcasecmp(cp, ";type=a") == 0)
18886cdfca03SJohn Marino 				transtype = TYPE_A;
18896cdfca03SJohn Marino 			else if (strcasecmp(cp, ";type=i") == 0)
18906cdfca03SJohn Marino 				transtype = TYPE_I;
18916cdfca03SJohn Marino 			else if (strcasecmp(cp, ";type=d") == 0) {
18926cdfca03SJohn Marino 				warnx(
18936cdfca03SJohn Marino 			    "Directory listing via a URL is not supported");
18946cdfca03SJohn Marino 				goto cleanup_fetch_ftp;
18956cdfca03SJohn Marino 			} else {
18966cdfca03SJohn Marino 				warnx("Invalid suffix `%s' in URL `%s'", cp,
18976cdfca03SJohn Marino 				    url);
18986cdfca03SJohn Marino 				goto cleanup_fetch_ftp;
18996cdfca03SJohn Marino 			}
19006cdfca03SJohn Marino 			*cp = 0;
19016cdfca03SJohn Marino 		}
19026cdfca03SJohn Marino 	} else {			/* classic style `[user@]host:[file]' */
1903*3a184c67SAntonio Huete Jimenez 		ui.utype = CLASSIC_URL_T;
1904*3a184c67SAntonio Huete Jimenez 		ui.host = ftp_strdup(url);
1905*3a184c67SAntonio Huete Jimenez 		cp = strchr(ui.host, '@');
19066cdfca03SJohn Marino 		if (cp != NULL) {
19076cdfca03SJohn Marino 			*cp = '\0';
1908*3a184c67SAntonio Huete Jimenez 			auth.user = ui.host;
19096cdfca03SJohn Marino 			anonftp = 0;	/* disable anonftp */
1910*3a184c67SAntonio Huete Jimenez 			ui.host = ftp_strdup(cp + 1);
19116cdfca03SJohn Marino 		}
1912*3a184c67SAntonio Huete Jimenez 		cp = strchr(ui.host, ':');
19136cdfca03SJohn Marino 		if (cp != NULL) {
19146cdfca03SJohn Marino 			*cp = '\0';
1915*3a184c67SAntonio Huete Jimenez 			ui.path = ftp_strdup(cp + 1);
19166cdfca03SJohn Marino 		}
19176cdfca03SJohn Marino 	}
1918*3a184c67SAntonio Huete Jimenez 	if (EMPTYSTRING(ui.host))
19196cdfca03SJohn Marino 		goto cleanup_fetch_ftp;
19206cdfca03SJohn Marino 
19216cdfca03SJohn Marino 			/* Extract the file and (if present) directory name. */
1922*3a184c67SAntonio Huete Jimenez 	dir = ui.path;
19236cdfca03SJohn Marino 	if (! EMPTYSTRING(dir)) {
19246cdfca03SJohn Marino 		/*
19256cdfca03SJohn Marino 		 * If we are dealing with classic `[user@]host:[path]' syntax,
19266cdfca03SJohn Marino 		 * then a path of the form `/file' (resulting from input of the
19276cdfca03SJohn Marino 		 * form `host:/file') means that we should do "CWD /" before
19286cdfca03SJohn Marino 		 * retrieving the file.  So we set dir="/" and file="file".
19296cdfca03SJohn Marino 		 *
19306cdfca03SJohn Marino 		 * But if we are dealing with URLs like `ftp://host/path' then
19316cdfca03SJohn Marino 		 * a path of the form `/file' (resulting from a URL of the form
19326cdfca03SJohn Marino 		 * `ftp://host//file') means that we should do `CWD ' (with an
19336cdfca03SJohn Marino 		 * empty argument) before retrieving the file.  So we set
19346cdfca03SJohn Marino 		 * dir="" and file="file".
19356cdfca03SJohn Marino 		 *
19366cdfca03SJohn Marino 		 * If the path does not contain / at all, we set dir=NULL.
19376cdfca03SJohn Marino 		 * (We get a path without any slashes if we are dealing with
19386cdfca03SJohn Marino 		 * classic `[user@]host:[file]' or URL `ftp://host/file'.)
19396cdfca03SJohn Marino 		 *
19406cdfca03SJohn Marino 		 * In all other cases, we set dir to a string that does not
19416cdfca03SJohn Marino 		 * include the final '/' that separates the dir part from the
19426cdfca03SJohn Marino 		 * file part of the path.  (This will be the empty string if
19436cdfca03SJohn Marino 		 * and only if we are dealing with a path of the form `/file'
19446cdfca03SJohn Marino 		 * resulting from an URL of the form `ftp://host//file'.)
19456cdfca03SJohn Marino 		 */
19466cdfca03SJohn Marino 		cp = strrchr(dir, '/');
1947*3a184c67SAntonio Huete Jimenez 		if (cp == dir && ui.utype == CLASSIC_URL_T) {
19486cdfca03SJohn Marino 			file = cp + 1;
19496cdfca03SJohn Marino 			(void)strlcpy(dirbuf, "/", sizeof(dirbuf));
19506cdfca03SJohn Marino 			dir = dirbuf;
19516cdfca03SJohn Marino 		} else if (cp != NULL) {
19526cdfca03SJohn Marino 			*cp++ = '\0';
19536cdfca03SJohn Marino 			file = cp;
19546cdfca03SJohn Marino 		} else {
19556cdfca03SJohn Marino 			file = dir;
19566cdfca03SJohn Marino 			dir = NULL;
19576cdfca03SJohn Marino 		}
19586cdfca03SJohn Marino 	} else
19596cdfca03SJohn Marino 		dir = NULL;
1960*3a184c67SAntonio Huete Jimenez 	if (ui.utype == FTP_URL_T && file != NULL) {
19616cdfca03SJohn Marino 		url_decode(file);
19626cdfca03SJohn Marino 		/* but still don't url_decode(dir) */
19636cdfca03SJohn Marino 	}
19646cdfca03SJohn Marino 	DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s "
19656cdfca03SJohn Marino 	    "path `%s' dir `%s' file `%s'\n",
1966*3a184c67SAntonio Huete Jimenez 	    STRorNULL(auth.user), STRorNULL(auth.pass),
1967*3a184c67SAntonio Huete Jimenez 	    STRorNULL(ui.host), STRorNULL(ui.port),
1968*3a184c67SAntonio Huete Jimenez 	    STRorNULL(ui.path), STRorNULL(dir), STRorNULL(file));
19696cdfca03SJohn Marino 
19706cdfca03SJohn Marino 	dirhasglob = filehasglob = 0;
1971*3a184c67SAntonio Huete Jimenez 	if (doglob &&
1972*3a184c67SAntonio Huete Jimenez 	    (ui.utype == CLASSIC_URL_T || ui.utype == FTP_URL_T)) {
19736cdfca03SJohn Marino 		if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL)
19746cdfca03SJohn Marino 			dirhasglob = 1;
19756cdfca03SJohn Marino 		if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL)
19766cdfca03SJohn Marino 			filehasglob = 1;
19776cdfca03SJohn Marino 	}
19786cdfca03SJohn Marino 
19796cdfca03SJohn Marino 			/* Set up the connection */
19806cdfca03SJohn Marino 	oanonftp = anonftp;
19816cdfca03SJohn Marino 	if (connected)
19826cdfca03SJohn Marino 		disconnect(0, NULL);
19836cdfca03SJohn Marino 	anonftp = oanonftp;
19846cdfca03SJohn Marino 	(void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf));
19856cdfca03SJohn Marino 	xargv[0] = cmdbuf;
1986*3a184c67SAntonio Huete Jimenez 	xargv[1] = ui.host;
19876cdfca03SJohn Marino 	xargv[2] = NULL;
19886cdfca03SJohn Marino 	xargc = 2;
1989*3a184c67SAntonio Huete Jimenez 	if (ui.port) {
1990*3a184c67SAntonio Huete Jimenez 		xargv[2] = ui.port;
19916cdfca03SJohn Marino 		xargv[3] = NULL;
19926cdfca03SJohn Marino 		xargc = 3;
19936cdfca03SJohn Marino 	}
19946cdfca03SJohn Marino 	oautologin = autologin;
19956cdfca03SJohn Marino 		/* don't autologin in setpeer(), use ftp_login() below */
19966cdfca03SJohn Marino 	autologin = 0;
19976cdfca03SJohn Marino 	setpeer(xargc, xargv);
19986cdfca03SJohn Marino 	autologin = oautologin;
19996cdfca03SJohn Marino 	if ((connected == 0) ||
2000*3a184c67SAntonio Huete Jimenez 	    (connected == 1 && !ftp_login(ui.host, auth.user, auth.pass))) {
20016cdfca03SJohn Marino 		warnx("Can't connect or login to host `%s:%s'",
2002*3a184c67SAntonio Huete Jimenez 			ui.host, ui.port ? ui.port : "?");
20036cdfca03SJohn Marino 		goto cleanup_fetch_ftp;
20046cdfca03SJohn Marino 	}
20056cdfca03SJohn Marino 
20066cdfca03SJohn Marino 	switch (transtype) {
20076cdfca03SJohn Marino 	case TYPE_A:
20086cdfca03SJohn Marino 		setascii(1, xargv);
20096cdfca03SJohn Marino 		break;
20106cdfca03SJohn Marino 	case TYPE_I:
20116cdfca03SJohn Marino 		setbinary(1, xargv);
20126cdfca03SJohn Marino 		break;
20136cdfca03SJohn Marino 	default:
20146cdfca03SJohn Marino 		errx(1, "fetch_ftp: unknown transfer type %d", transtype);
20156cdfca03SJohn Marino 	}
20166cdfca03SJohn Marino 
20176cdfca03SJohn Marino 		/*
20186cdfca03SJohn Marino 		 * Change directories, if necessary.
20196cdfca03SJohn Marino 		 *
20206cdfca03SJohn Marino 		 * Note: don't use EMPTYSTRING(dir) below, because
20216cdfca03SJohn Marino 		 * dir=="" means something different from dir==NULL.
20226cdfca03SJohn Marino 		 */
20236cdfca03SJohn Marino 	if (dir != NULL && !dirhasglob) {
20246cdfca03SJohn Marino 		char *nextpart;
20256cdfca03SJohn Marino 
20266cdfca03SJohn Marino 		/*
20276cdfca03SJohn Marino 		 * If we are dealing with a classic `[user@]host:[path]'
20286cdfca03SJohn Marino 		 * (urltype is CLASSIC_URL_T) then we have a raw directory
20296cdfca03SJohn Marino 		 * name (not encoded in any way) and we can change
20306cdfca03SJohn Marino 		 * directories in one step.
20316cdfca03SJohn Marino 		 *
20326cdfca03SJohn Marino 		 * If we are dealing with an `ftp://host/path' URL
20336cdfca03SJohn Marino 		 * (urltype is FTP_URL_T), then RFC 3986 says we need to
20346cdfca03SJohn Marino 		 * send a separate CWD command for each unescaped "/"
20356cdfca03SJohn Marino 		 * in the path, and we have to interpret %hex escaping
20366cdfca03SJohn Marino 		 * *after* we find the slashes.  It's possible to get
20376cdfca03SJohn Marino 		 * empty components here, (from multiple adjacent
20386cdfca03SJohn Marino 		 * slashes in the path) and RFC 3986 says that we should
20396cdfca03SJohn Marino 		 * still do `CWD ' (with a null argument) in such cases.
20406cdfca03SJohn Marino 		 *
20416cdfca03SJohn Marino 		 * Many ftp servers don't support `CWD ', so if there's an
20426cdfca03SJohn Marino 		 * error performing that command, bail out with a descriptive
20436cdfca03SJohn Marino 		 * message.
20446cdfca03SJohn Marino 		 *
20456cdfca03SJohn Marino 		 * Examples:
20466cdfca03SJohn Marino 		 *
20476cdfca03SJohn Marino 		 * host:			dir="", urltype=CLASSIC_URL_T
20486cdfca03SJohn Marino 		 *		logged in (to default directory)
20496cdfca03SJohn Marino 		 * host:file			dir=NULL, urltype=CLASSIC_URL_T
20506cdfca03SJohn Marino 		 *		"RETR file"
20516cdfca03SJohn Marino 		 * host:dir/			dir="dir", urltype=CLASSIC_URL_T
20526cdfca03SJohn Marino 		 *		"CWD dir", logged in
20536cdfca03SJohn Marino 		 * ftp://host/			dir="", urltype=FTP_URL_T
20546cdfca03SJohn Marino 		 *		logged in (to default directory)
20556cdfca03SJohn Marino 		 * ftp://host/dir/		dir="dir", urltype=FTP_URL_T
20566cdfca03SJohn Marino 		 *		"CWD dir", logged in
20576cdfca03SJohn Marino 		 * ftp://host/file		dir=NULL, urltype=FTP_URL_T
20586cdfca03SJohn Marino 		 *		"RETR file"
20596cdfca03SJohn Marino 		 * ftp://host//file		dir="", urltype=FTP_URL_T
20606cdfca03SJohn Marino 		 *		"CWD ", "RETR file"
20616cdfca03SJohn Marino 		 * host:/file			dir="/", urltype=CLASSIC_URL_T
20626cdfca03SJohn Marino 		 *		"CWD /", "RETR file"
20636cdfca03SJohn Marino 		 * ftp://host///file		dir="/", urltype=FTP_URL_T
20646cdfca03SJohn Marino 		 *		"CWD ", "CWD ", "RETR file"
20656cdfca03SJohn Marino 		 * ftp://host/%2F/file		dir="%2F", urltype=FTP_URL_T
20666cdfca03SJohn Marino 		 *		"CWD /", "RETR file"
20676cdfca03SJohn Marino 		 * ftp://host/foo/file		dir="foo", urltype=FTP_URL_T
20686cdfca03SJohn Marino 		 *		"CWD foo", "RETR file"
20696cdfca03SJohn Marino 		 * ftp://host/foo/bar/file	dir="foo/bar"
20706cdfca03SJohn Marino 		 *		"CWD foo", "CWD bar", "RETR file"
20716cdfca03SJohn Marino 		 * ftp://host//foo/bar/file	dir="/foo/bar"
20726cdfca03SJohn Marino 		 *		"CWD ", "CWD foo", "CWD bar", "RETR file"
20736cdfca03SJohn Marino 		 * ftp://host/foo//bar/file	dir="foo//bar"
20746cdfca03SJohn Marino 		 *		"CWD foo", "CWD ", "CWD bar", "RETR file"
20756cdfca03SJohn Marino 		 * ftp://host/%2F/foo/bar/file	dir="%2F/foo/bar"
20766cdfca03SJohn Marino 		 *		"CWD /", "CWD foo", "CWD bar", "RETR file"
20776cdfca03SJohn Marino 		 * ftp://host/%2Ffoo/bar/file	dir="%2Ffoo/bar"
20786cdfca03SJohn Marino 		 *		"CWD /foo", "CWD bar", "RETR file"
20796cdfca03SJohn Marino 		 * ftp://host/%2Ffoo%2Fbar/file	dir="%2Ffoo%2Fbar"
20806cdfca03SJohn Marino 		 *		"CWD /foo/bar", "RETR file"
20816cdfca03SJohn Marino 		 * ftp://host/%2Ffoo%2Fbar%2Ffile	dir=NULL
20826cdfca03SJohn Marino 		 *		"RETR /foo/bar/file"
20836cdfca03SJohn Marino 		 *
20846cdfca03SJohn Marino 		 * Note that we don't need `dir' after this point.
20856cdfca03SJohn Marino 		 */
20866cdfca03SJohn Marino 		do {
2087*3a184c67SAntonio Huete Jimenez 			if (ui.utype == FTP_URL_T) {
20886cdfca03SJohn Marino 				nextpart = strchr(dir, '/');
20896cdfca03SJohn Marino 				if (nextpart) {
20906cdfca03SJohn Marino 					*nextpart = '\0';
20916cdfca03SJohn Marino 					nextpart++;
20926cdfca03SJohn Marino 				}
20936cdfca03SJohn Marino 				url_decode(dir);
20946cdfca03SJohn Marino 			} else
20956cdfca03SJohn Marino 				nextpart = NULL;
20966cdfca03SJohn Marino 			DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n",
20976cdfca03SJohn Marino 			    STRorNULL(dir), STRorNULL(nextpart));
2098*3a184c67SAntonio Huete Jimenez 			if (ui.utype == FTP_URL_T || *dir != '\0') {
20996cdfca03SJohn Marino 				(void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf));
21006cdfca03SJohn Marino 				xargv[0] = cmdbuf;
21016cdfca03SJohn Marino 				xargv[1] = dir;
21026cdfca03SJohn Marino 				xargv[2] = NULL;
21036cdfca03SJohn Marino 				dirchange = 0;
21046cdfca03SJohn Marino 				cd(2, xargv);
21056cdfca03SJohn Marino 				if (! dirchange) {
21066cdfca03SJohn Marino 					if (*dir == '\0' && code == 500)
21076cdfca03SJohn Marino 						fprintf(stderr,
21086cdfca03SJohn Marino "\n"
21096cdfca03SJohn Marino "ftp: The `CWD ' command (without a directory), which is required by\n"
21106cdfca03SJohn Marino "     RFC 3986 to support the empty directory in the URL pathname (`//'),\n"
21116cdfca03SJohn Marino "     conflicts with the server's conformance to RFC 959.\n"
21126cdfca03SJohn Marino "     Try the same URL without the `//' in the URL pathname.\n"
21136cdfca03SJohn Marino "\n");
21146cdfca03SJohn Marino 					goto cleanup_fetch_ftp;
21156cdfca03SJohn Marino 				}
21166cdfca03SJohn Marino 			}
21176cdfca03SJohn Marino 			dir = nextpart;
21186cdfca03SJohn Marino 		} while (dir != NULL);
21196cdfca03SJohn Marino 	}
21206cdfca03SJohn Marino 
21216cdfca03SJohn Marino 	if (EMPTYSTRING(file)) {
21226cdfca03SJohn Marino 		rval = -1;
21236cdfca03SJohn Marino 		goto cleanup_fetch_ftp;
21246cdfca03SJohn Marino 	}
21256cdfca03SJohn Marino 
21266cdfca03SJohn Marino 	if (dirhasglob) {
21276cdfca03SJohn Marino 		(void)strlcpy(rempath, dir,	sizeof(rempath));
21286cdfca03SJohn Marino 		(void)strlcat(rempath, "/",	sizeof(rempath));
21296cdfca03SJohn Marino 		(void)strlcat(rempath, file,	sizeof(rempath));
21306cdfca03SJohn Marino 		file = rempath;
21316cdfca03SJohn Marino 	}
21326cdfca03SJohn Marino 
21336cdfca03SJohn Marino 			/* Fetch the file(s). */
21346cdfca03SJohn Marino 	xargc = 2;
21356cdfca03SJohn Marino 	(void)strlcpy(cmdbuf, "get", sizeof(cmdbuf));
21366cdfca03SJohn Marino 	xargv[0] = cmdbuf;
21376cdfca03SJohn Marino 	xargv[1] = file;
21386cdfca03SJohn Marino 	xargv[2] = NULL;
21396cdfca03SJohn Marino 	if (dirhasglob || filehasglob) {
21406cdfca03SJohn Marino 		int ointeractive;
21416cdfca03SJohn Marino 
21426cdfca03SJohn Marino 		ointeractive = interactive;
21436cdfca03SJohn Marino 		interactive = 0;
21446cdfca03SJohn Marino 		if (restartautofetch)
21456cdfca03SJohn Marino 			(void)strlcpy(cmdbuf, "mreget", sizeof(cmdbuf));
21466cdfca03SJohn Marino 		else
21476cdfca03SJohn Marino 			(void)strlcpy(cmdbuf, "mget", sizeof(cmdbuf));
21486cdfca03SJohn Marino 		xargv[0] = cmdbuf;
21496cdfca03SJohn Marino 		mget(xargc, xargv);
21506cdfca03SJohn Marino 		interactive = ointeractive;
21516cdfca03SJohn Marino 	} else {
2152*3a184c67SAntonio Huete Jimenez 		char *destfile = outfile;
2153*3a184c67SAntonio Huete Jimenez 		if (destfile == NULL) {
21546cdfca03SJohn Marino 			cp = strrchr(file, '/');	/* find savefile */
21556cdfca03SJohn Marino 			if (cp != NULL)
2156*3a184c67SAntonio Huete Jimenez 				destfile = cp + 1;
21576cdfca03SJohn Marino 			else
2158*3a184c67SAntonio Huete Jimenez 				destfile = file;
21596cdfca03SJohn Marino 		}
2160*3a184c67SAntonio Huete Jimenez 		xargv[2] = (char *)destfile;
21616cdfca03SJohn Marino 		xargv[3] = NULL;
21626cdfca03SJohn Marino 		xargc++;
21636cdfca03SJohn Marino 		if (restartautofetch)
21646cdfca03SJohn Marino 			reget(xargc, xargv);
21656cdfca03SJohn Marino 		else
21666cdfca03SJohn Marino 			get(xargc, xargv);
21676cdfca03SJohn Marino 	}
21686cdfca03SJohn Marino 
21696cdfca03SJohn Marino 	if ((code / 100) == COMPLETE)
21706cdfca03SJohn Marino 		rval = 0;
21716cdfca03SJohn Marino 
21726cdfca03SJohn Marino  cleanup_fetch_ftp:
2173*3a184c67SAntonio Huete Jimenez 	freeurlinfo(&ui);
2174*3a184c67SAntonio Huete Jimenez 	freeauthinfo(&auth);
21756cdfca03SJohn Marino 	return (rval);
21766cdfca03SJohn Marino }
21776cdfca03SJohn Marino 
21786cdfca03SJohn Marino /*
21796cdfca03SJohn Marino  * Retrieve the given file to outfile.
21806cdfca03SJohn Marino  * Supports arguments of the form:
21816cdfca03SJohn Marino  *	"host:path", "ftp://host/path"	if $ftpproxy, call fetch_url() else
21826cdfca03SJohn Marino  *					call fetch_ftp()
21836cdfca03SJohn Marino  *	"http://host/path"		call fetch_url() to use HTTP
21846cdfca03SJohn Marino  *	"file:///path"			call fetch_url() to copy
21856cdfca03SJohn Marino  *	"about:..."			print a message
21866cdfca03SJohn Marino  *
21876cdfca03SJohn Marino  * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
21886cdfca03SJohn Marino  * is still open (e.g, ftp xfer with trailing /)
21896cdfca03SJohn Marino  */
21906cdfca03SJohn Marino static int
go_fetch(const char * url)21916cdfca03SJohn Marino go_fetch(const char *url)
21926cdfca03SJohn Marino {
21936cdfca03SJohn Marino 	char *proxyenv;
21946cdfca03SJohn Marino 	char *p;
21956cdfca03SJohn Marino 
21966cdfca03SJohn Marino #ifndef NO_ABOUT
21976cdfca03SJohn Marino 	/*
21986cdfca03SJohn Marino 	 * Check for about:*
21996cdfca03SJohn Marino 	 */
22006cdfca03SJohn Marino 	if (STRNEQUAL(url, ABOUT_URL)) {
22016cdfca03SJohn Marino 		url += sizeof(ABOUT_URL) -1;
22026cdfca03SJohn Marino 		if (strcasecmp(url, "ftp") == 0 ||
22036cdfca03SJohn Marino 		    strcasecmp(url, "tnftp") == 0) {
22046cdfca03SJohn Marino 			fputs(
22056cdfca03SJohn Marino "This version of ftp has been enhanced by Luke Mewburn <lukem@NetBSD.org>\n"
22066cdfca03SJohn Marino "for the NetBSD project.  Execute `man ftp' for more details.\n", ttyout);
22076cdfca03SJohn Marino 		} else if (strcasecmp(url, "lukem") == 0) {
22086cdfca03SJohn Marino 			fputs(
22096cdfca03SJohn Marino "Luke Mewburn is the author of most of the enhancements in this ftp client.\n"
22106cdfca03SJohn Marino "Please email feedback to <lukem@NetBSD.org>.\n", ttyout);
22116cdfca03SJohn Marino 		} else if (strcasecmp(url, "netbsd") == 0) {
22126cdfca03SJohn Marino 			fputs(
22136cdfca03SJohn Marino "NetBSD is a freely available and redistributable UNIX-like operating system.\n"
22146cdfca03SJohn Marino "For more information, see http://www.NetBSD.org/\n", ttyout);
22156cdfca03SJohn Marino 		} else if (strcasecmp(url, "version") == 0) {
22166cdfca03SJohn Marino 			fprintf(ttyout, "Version: %s %s%s\n",
22176cdfca03SJohn Marino 			    FTP_PRODUCT, FTP_VERSION,
22186cdfca03SJohn Marino #ifdef INET6
22196cdfca03SJohn Marino 			    ""
22206cdfca03SJohn Marino #else
22216cdfca03SJohn Marino 			    " (-IPv6)"
22226cdfca03SJohn Marino #endif
22236cdfca03SJohn Marino 			);
22246cdfca03SJohn Marino 		} else {
22256cdfca03SJohn Marino 			fprintf(ttyout, "`%s' is an interesting topic.\n", url);
22266cdfca03SJohn Marino 		}
22276cdfca03SJohn Marino 		fputs("\n", ttyout);
22286cdfca03SJohn Marino 		return (0);
22296cdfca03SJohn Marino 	}
22306cdfca03SJohn Marino #endif
22316cdfca03SJohn Marino 
22326cdfca03SJohn Marino 	/*
22336cdfca03SJohn Marino 	 * Check for file:// and http:// URLs.
22346cdfca03SJohn Marino 	 */
22356cdfca03SJohn Marino 	if (STRNEQUAL(url, HTTP_URL)
22366cdfca03SJohn Marino #ifdef WITH_SSL
22376cdfca03SJohn Marino 	    || STRNEQUAL(url, HTTPS_URL)
22386cdfca03SJohn Marino #endif
22396cdfca03SJohn Marino 	    || STRNEQUAL(url, FILE_URL))
22406cdfca03SJohn Marino 		return (fetch_url(url, NULL, NULL, NULL));
22416cdfca03SJohn Marino 
22426cdfca03SJohn Marino 	/*
22436cdfca03SJohn Marino 	 * If it contains "://" but does not begin with ftp://
22446cdfca03SJohn Marino 	 * or something that was already handled, then it's
22456cdfca03SJohn Marino 	 * unsupported.
22466cdfca03SJohn Marino 	 *
22476cdfca03SJohn Marino 	 * If it contains ":" but not "://" then we assume the
22486cdfca03SJohn Marino 	 * part before the colon is a host name, not an URL scheme,
22496cdfca03SJohn Marino 	 * so we don't try to match that here.
22506cdfca03SJohn Marino 	 */
22516cdfca03SJohn Marino 	if ((p = strstr(url, "://")) != NULL && ! STRNEQUAL(url, FTP_URL))
22526cdfca03SJohn Marino 		errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url);
22536cdfca03SJohn Marino 
22546cdfca03SJohn Marino 	/*
22556cdfca03SJohn Marino 	 * Try FTP URL-style and host:file arguments next.
22566cdfca03SJohn Marino 	 * If ftpproxy is set with an FTP URL, use fetch_url()
2257*3a184c67SAntonio Huete Jimenez 	 * Otherwise, use fetch_ftp().
22586cdfca03SJohn Marino 	 */
22596cdfca03SJohn Marino 	proxyenv = getoptionvalue("ftp_proxy");
22606cdfca03SJohn Marino 	if (!EMPTYSTRING(proxyenv) && STRNEQUAL(url, FTP_URL))
22616cdfca03SJohn Marino 		return (fetch_url(url, NULL, NULL, NULL));
22626cdfca03SJohn Marino 
22636cdfca03SJohn Marino 	return (fetch_ftp(url));
22646cdfca03SJohn Marino }
22656cdfca03SJohn Marino 
22666cdfca03SJohn Marino /*
22676cdfca03SJohn Marino  * Retrieve multiple files from the command line,
22686cdfca03SJohn Marino  * calling go_fetch() for each file.
22696cdfca03SJohn Marino  *
22706cdfca03SJohn Marino  * If an ftp path has a trailing "/", the path will be cd-ed into and
22716cdfca03SJohn Marino  * the connection remains open, and the function will return -1
22726cdfca03SJohn Marino  * (to indicate the connection is alive).
22736cdfca03SJohn Marino  * If an error occurs the return value will be the offset+1 in
22746cdfca03SJohn Marino  * argv[] of the file that caused a problem (i.e, argv[x]
22756cdfca03SJohn Marino  * returns x+1)
22766cdfca03SJohn Marino  * Otherwise, 0 is returned if all files retrieved successfully.
22776cdfca03SJohn Marino  */
22786cdfca03SJohn Marino int
auto_fetch(int argc,char * argv[])22796cdfca03SJohn Marino auto_fetch(int argc, char *argv[])
22806cdfca03SJohn Marino {
22816cdfca03SJohn Marino 	volatile int	argpos, rval;
22826cdfca03SJohn Marino 
22836cdfca03SJohn Marino 	argpos = rval = 0;
22846cdfca03SJohn Marino 
22856cdfca03SJohn Marino 	if (sigsetjmp(toplevel, 1)) {
22866cdfca03SJohn Marino 		if (connected)
22876cdfca03SJohn Marino 			disconnect(0, NULL);
22886cdfca03SJohn Marino 		if (rval > 0)
22896cdfca03SJohn Marino 			rval = argpos + 1;
22906cdfca03SJohn Marino 		return (rval);
22916cdfca03SJohn Marino 	}
22926cdfca03SJohn Marino 	(void)xsignal(SIGINT, intr);
22936cdfca03SJohn Marino 	(void)xsignal(SIGPIPE, lostpeer);
22946cdfca03SJohn Marino 
22956cdfca03SJohn Marino 	/*
22966cdfca03SJohn Marino 	 * Loop through as long as there's files to fetch.
22976cdfca03SJohn Marino 	 */
22986cdfca03SJohn Marino 	for (; (rval == 0) && (argpos < argc); argpos++) {
22996cdfca03SJohn Marino 		if (strchr(argv[argpos], ':') == NULL)
23006cdfca03SJohn Marino 			break;
23016cdfca03SJohn Marino 		redirect_loop = 0;
23026cdfca03SJohn Marino 		if (!anonftp)
23036cdfca03SJohn Marino 			anonftp = 2;	/* Handle "automatic" transfers. */
23046cdfca03SJohn Marino 		rval = go_fetch(argv[argpos]);
23056cdfca03SJohn Marino 		if (outfile != NULL && strcmp(outfile, "-") != 0
2306*3a184c67SAntonio Huete Jimenez 		    && outfile[0] != '|') {
2307*3a184c67SAntonio Huete Jimenez 			FREEPTR(outfile);
2308*3a184c67SAntonio Huete Jimenez 		}
23096cdfca03SJohn Marino 		if (rval > 0)
23106cdfca03SJohn Marino 			rval = argpos + 1;
23116cdfca03SJohn Marino 	}
23126cdfca03SJohn Marino 
23136cdfca03SJohn Marino 	if (connected && rval != -1)
23146cdfca03SJohn Marino 		disconnect(0, NULL);
23156cdfca03SJohn Marino 	return (rval);
23166cdfca03SJohn Marino }
23176cdfca03SJohn Marino 
23186cdfca03SJohn Marino 
23196cdfca03SJohn Marino /*
23206cdfca03SJohn Marino  * Upload multiple files from the command line.
23216cdfca03SJohn Marino  *
23226cdfca03SJohn Marino  * If an error occurs the return value will be the offset+1 in
23236cdfca03SJohn Marino  * argv[] of the file that caused a problem (i.e, argv[x]
23246cdfca03SJohn Marino  * returns x+1)
23256cdfca03SJohn Marino  * Otherwise, 0 is returned if all files uploaded successfully.
23266cdfca03SJohn Marino  */
23276cdfca03SJohn Marino int
auto_put(int argc,char ** argv,const char * uploadserver)23286cdfca03SJohn Marino auto_put(int argc, char **argv, const char *uploadserver)
23296cdfca03SJohn Marino {
23306cdfca03SJohn Marino 	char	*uargv[4], *path, *pathsep;
23316cdfca03SJohn Marino 	int	 uargc, rval, argpos;
23326cdfca03SJohn Marino 	size_t	 len;
23336cdfca03SJohn Marino 	char	 cmdbuf[MAX_C_NAME];
23346cdfca03SJohn Marino 
23356cdfca03SJohn Marino 	(void)strlcpy(cmdbuf, "mput", sizeof(cmdbuf));
23366cdfca03SJohn Marino 	uargv[0] = cmdbuf;
23376cdfca03SJohn Marino 	uargv[1] = argv[0];
23386cdfca03SJohn Marino 	uargc = 2;
23396cdfca03SJohn Marino 	uargv[2] = uargv[3] = NULL;
23406cdfca03SJohn Marino 	pathsep = NULL;
23416cdfca03SJohn Marino 	rval = 1;
23426cdfca03SJohn Marino 
23436cdfca03SJohn Marino 	DPRINTF("auto_put: target `%s'\n", uploadserver);
23446cdfca03SJohn Marino 
23456cdfca03SJohn Marino 	path = ftp_strdup(uploadserver);
23466cdfca03SJohn Marino 	len = strlen(path);
23476cdfca03SJohn Marino 	if (path[len - 1] != '/' && path[len - 1] != ':') {
23486cdfca03SJohn Marino 			/*
23496cdfca03SJohn Marino 			 * make sure we always pass a directory to auto_fetch
23506cdfca03SJohn Marino 			 */
23516cdfca03SJohn Marino 		if (argc > 1) {		/* more than one file to upload */
23526cdfca03SJohn Marino 			len = strlen(uploadserver) + 2;	/* path + "/" + "\0" */
23536cdfca03SJohn Marino 			free(path);
23546cdfca03SJohn Marino 			path = (char *)ftp_malloc(len);
23556cdfca03SJohn Marino 			(void)strlcpy(path, uploadserver, len);
23566cdfca03SJohn Marino 			(void)strlcat(path, "/", len);
23576cdfca03SJohn Marino 		} else {		/* single file to upload */
23586cdfca03SJohn Marino 			(void)strlcpy(cmdbuf, "put", sizeof(cmdbuf));
23596cdfca03SJohn Marino 			uargv[0] = cmdbuf;
23606cdfca03SJohn Marino 			pathsep = strrchr(path, '/');
23616cdfca03SJohn Marino 			if (pathsep == NULL) {
23626cdfca03SJohn Marino 				pathsep = strrchr(path, ':');
23636cdfca03SJohn Marino 				if (pathsep == NULL) {
23646cdfca03SJohn Marino 					warnx("Invalid URL `%s'", path);
23656cdfca03SJohn Marino 					goto cleanup_auto_put;
23666cdfca03SJohn Marino 				}
23676cdfca03SJohn Marino 				pathsep++;
23686cdfca03SJohn Marino 				uargv[2] = ftp_strdup(pathsep);
23696cdfca03SJohn Marino 				pathsep[0] = '/';
23706cdfca03SJohn Marino 			} else
23716cdfca03SJohn Marino 				uargv[2] = ftp_strdup(pathsep + 1);
23726cdfca03SJohn Marino 			pathsep[1] = '\0';
23736cdfca03SJohn Marino 			uargc++;
23746cdfca03SJohn Marino 		}
23756cdfca03SJohn Marino 	}
23766cdfca03SJohn Marino 	DPRINTF("auto_put: URL `%s' argv[2] `%s'\n",
23776cdfca03SJohn Marino 	    path, STRorNULL(uargv[2]));
23786cdfca03SJohn Marino 
23796cdfca03SJohn Marino 			/* connect and cwd */
23806cdfca03SJohn Marino 	rval = auto_fetch(1, &path);
23816cdfca03SJohn Marino 	if(rval >= 0)
23826cdfca03SJohn Marino 		goto cleanup_auto_put;
23836cdfca03SJohn Marino 
23846cdfca03SJohn Marino 	rval = 0;
23856cdfca03SJohn Marino 
23866cdfca03SJohn Marino 			/* target filename provided; upload 1 file */
23876cdfca03SJohn Marino 			/* XXX : is this the best way? */
23886cdfca03SJohn Marino 	if (uargc == 3) {
23896cdfca03SJohn Marino 		uargv[1] = argv[0];
23906cdfca03SJohn Marino 		put(uargc, uargv);
23916cdfca03SJohn Marino 		if ((code / 100) != COMPLETE)
23926cdfca03SJohn Marino 			rval = 1;
23936cdfca03SJohn Marino 	} else {	/* otherwise a target dir: upload all files to it */
23946cdfca03SJohn Marino 		for(argpos = 0; argv[argpos] != NULL; argpos++) {
23956cdfca03SJohn Marino 			uargv[1] = argv[argpos];
23966cdfca03SJohn Marino 			mput(uargc, uargv);
23976cdfca03SJohn Marino 			if ((code / 100) != COMPLETE) {
23986cdfca03SJohn Marino 				rval = argpos + 1;
23996cdfca03SJohn Marino 				break;
24006cdfca03SJohn Marino 			}
24016cdfca03SJohn Marino 		}
24026cdfca03SJohn Marino 	}
24036cdfca03SJohn Marino 
24046cdfca03SJohn Marino  cleanup_auto_put:
24056cdfca03SJohn Marino 	free(path);
24066cdfca03SJohn Marino 	FREEPTR(uargv[2]);
24076cdfca03SJohn Marino 	return (rval);
24086cdfca03SJohn Marino }
2409