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