diff options
-rw-r--r-- | net/tnftp/files/src/fetch.c | 1782 | ||||
-rw-r--r-- | net/tnftp/files/src/ftp.1 | 2334 | ||||
-rw-r--r-- | net/tnftp/files/src/main.c | 1030 | ||||
-rw-r--r-- | net/tnftp/files/src/progressbar.c | 464 |
4 files changed, 5610 insertions, 0 deletions
diff --git a/net/tnftp/files/src/fetch.c b/net/tnftp/files/src/fetch.c new file mode 100644 index 00000000000..845bd189631 --- /dev/null +++ b/net/tnftp/files/src/fetch.c @@ -0,0 +1,1782 @@ +/* $NetBSD: fetch.c,v 1.1.1.1 2003/07/31 06:18:43 lukem Exp $ */ + +/*- + * Copyright (c) 1997-2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Scott Aaron Bamford. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: fetch.c,v 1.1.1.1 2003/07/31 06:18:43 lukem Exp $"); +#endif /* not lint */ +#endif + +/* + * FTP User Program -- Command line file retrieval + */ + +#include "tnftp.h" + +#include "ftp_var.h" +#include "version.h" + +typedef enum { + UNKNOWN_URL_T=-1, + HTTP_URL_T, + FTP_URL_T, + FILE_URL_T, + CLASSIC_URL_T +} url_t; + +void aborthttp(int); +static int auth_url(const char *, char **, const char *, const char *); +static void base64_encode(const u_char *, size_t, u_char *); +static int go_fetch(const char *); +static int fetch_ftp(const char *); +static int fetch_url(const char *, const char *, char *, char *); +static int parse_url(const char *, const char *, url_t *, char **, + char **, char **, char **, in_port_t *, char **); +static void url_decode(char *); + +static int redirect_loop; + + +#define ABOUT_URL "about:" /* propaganda */ +#define FILE_URL "file://" /* file URL prefix */ +#define FTP_URL "ftp://" /* ftp URL prefix */ +#define HTTP_URL "http://" /* http URL prefix */ + + +/* + * Generate authorization response based on given authentication challenge. + * Returns -1 if an error occurred, otherwise 0. + * Sets response to a malloc(3)ed string; caller should free. + */ +static int +auth_url(const char *challenge, char **response, const char *guser, + const char *gpass) +{ + char *cp, *ep, *clear, *line, *realm, *scheme; + char user[BUFSIZ], *pass; + int rval; + size_t len, clen, rlen; + + *response = NULL; + clear = realm = scheme = NULL; + rval = -1; + line = xstrdup(challenge); + cp = line; + + if (debug) + fprintf(ttyout, "auth_url: challenge `%s'\n", challenge); + + scheme = strsep(&cp, " "); +#define SCHEME_BASIC "Basic" + if (strncasecmp(scheme, SCHEME_BASIC, sizeof(SCHEME_BASIC) - 1) != 0) { + warnx("Unsupported WWW Authentication challenge - `%s'", + challenge); + goto cleanup_auth_url; + } + cp += strspn(cp, " "); + +#define REALM "realm=\"" + if (strncasecmp(cp, REALM, sizeof(REALM) - 1) == 0) + cp += sizeof(REALM) - 1; + else { + warnx("Unsupported WWW Authentication challenge - `%s'", + challenge); + goto cleanup_auth_url; + } + if ((ep = strchr(cp, '\"')) != NULL) { + size_t len = ep - cp; + + realm = (char *)xmalloc(len + 1); + (void)strlcpy(realm, cp, len + 1); + } else { + warnx("Unsupported WWW Authentication challenge - `%s'", + challenge); + goto cleanup_auth_url; + } + + if (guser != NULL) + (void)strlcpy(user, guser, sizeof(user)); + else { + fprintf(ttyout, "Username for `%s': ", realm); + (void)fflush(ttyout); + if (fgets(user, sizeof(user) - 1, stdin) == NULL) { + clearerr(stdin); + goto cleanup_auth_url; + } + user[strlen(user) - 1] = '\0'; + } + if (gpass != NULL) + pass = (char *)gpass; + else + pass = getpass("Password: "); + + clen = strlen(user) + strlen(pass) + 2; /* user + ":" + pass + "\0" */ + clear = (char *)xmalloc(clen); + (void)strlcpy(clear, user, clen); + (void)strlcat(clear, ":", clen); + (void)strlcat(clear, pass, clen); + if (gpass == NULL) + memset(pass, 0, strlen(pass)); + + /* scheme + " " + enc + "\0" */ + rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1; + *response = (char *)xmalloc(rlen); + (void)strlcpy(*response, scheme, rlen); + len = strlcat(*response, " ", rlen); + base64_encode(clear, clen, (u_char *)*response + len); + memset(clear, 0, clen); + rval = 0; + + cleanup_auth_url: + FREEPTR(clear); + FREEPTR(line); + FREEPTR(realm); + return (rval); +} + +/* + * Encode len bytes starting at clear using base64 encoding into encoded, + * which should be at least ((len + 2) * 4 / 3 + 1) in size. + */ +static void +base64_encode(const u_char *clear, size_t len, u_char *encoded) +{ + static const u_char enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + u_char *cp; + int i; + + cp = encoded; + for (i = 0; i < len; i += 3) { + *(cp++) = enc[((clear[i + 0] >> 2))]; + *(cp++) = enc[((clear[i + 0] << 4) & 0x30) + | ((clear[i + 1] >> 4) & 0x0f)]; + *(cp++) = enc[((clear[i + 1] << 2) & 0x3c) + | ((clear[i + 2] >> 6) & 0x03)]; + *(cp++) = enc[((clear[i + 2] ) & 0x3f)]; + } + *cp = '\0'; + while (i-- > len) + *(--cp) = '='; +} + +/* + * Decode %xx escapes in given string, `in-place'. + */ +static void +url_decode(char *url) +{ + unsigned char *p, *q; + + if (EMPTYSTRING(url)) + return; + p = q = (unsigned char *)url; + +#define HEXTOINT(x) (x - (isdigit(x) ? '0' : (islower(x) ? 'a' : 'A') - 10)) + while (*p) { + if (p[0] == '%' + && p[1] && isxdigit((unsigned char)p[1]) + && p[2] && isxdigit((unsigned char)p[2])) { + *q++ = HEXTOINT(p[1]) * 16 + HEXTOINT(p[2]); + p+=3; + } else + *q++ = *p++; + } + *q = '\0'; +} + + +/* + * Parse URL of form: + * <type>://[<user>[:<password>@]]<host>[:<port>][/<path>] + * Returns -1 if a parse error occurred, otherwise 0. + * It's the caller's responsibility to url_decode() the returned + * user, pass and path. + * + * Sets type to url_t, each of the given char ** pointers to a + * malloc(3)ed strings of the relevant section, and port to + * the number given, or ftpport if ftp://, or httpport if http://. + * + * If <host> is surrounded by `[' and ']', it's parsed as an + * IPv6 address (as per RFC 2732). + * + * XXX: this is not totally RFC 1738 compliant; <path> will have the + * leading `/' unless it's an ftp:// URL, as this makes things easier + * for file:// and http:// URLs. ftp:// URLs have the `/' between the + * host and the URL-path removed, but any additional leading slashes + * in the URL-path are retained (because they imply that we should + * later do "CWD" with a null argument). + * + * Examples: + * input URL output path + * --------- ----------- + * "ftp://host" NULL + * "http://host/" NULL + * "file://host/dir/file" "dir/file" + * "ftp://host/" "" + * "ftp://host//" NULL + * "ftp://host//dir/file" "/dir/file" + */ +static int +parse_url(const char *url, const char *desc, url_t *type, + char **user, char **pass, char **host, char **port, + in_port_t *portnum, char **path) +{ + const char *origurl; + char *cp, *ep, *thost, *tport; + size_t len; + + if (url == NULL || desc == NULL || type == NULL || user == NULL + || pass == NULL || host == NULL || port == NULL || portnum == NULL + || path == NULL) + errx(1, "parse_url: invoked with NULL argument!"); + + origurl = url; + *type = UNKNOWN_URL_T; + *user = *pass = *host = *port = *path = NULL; + *portnum = 0; + tport = NULL; + + if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { + url += sizeof(HTTP_URL) - 1; + *type = HTTP_URL_T; + *portnum = HTTP_PORT; + tport = httpport; + } else if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) { + url += sizeof(FTP_URL) - 1; + *type = FTP_URL_T; + *portnum = FTP_PORT; + tport = ftpport; + } else if (strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) { + url += sizeof(FILE_URL) - 1; + *type = FILE_URL_T; + } else { + warnx("Invalid %s `%s'", desc, url); + cleanup_parse_url: + FREEPTR(*user); + FREEPTR(*pass); + FREEPTR(*host); + FREEPTR(*port); + FREEPTR(*path); + return (-1); + } + + if (*url == '\0') + return (0); + + /* find [user[:pass]@]host[:port] */ + ep = strchr(url, '/'); + if (ep == NULL) + thost = xstrdup(url); + else { + len = ep - url; + thost = (char *)xmalloc(len + 1); + (void)strlcpy(thost, url, len + 1); + if (*type == FTP_URL_T) /* skip first / for ftp URLs */ + ep++; + *path = xstrdup(ep); + } + + cp = strchr(thost, '@'); /* look for user[:pass]@ in URLs */ + if (cp != NULL) { + if (*type == FTP_URL_T) + anonftp = 0; /* disable anonftp */ + *user = thost; + *cp = '\0'; + thost = xstrdup(cp + 1); + cp = strchr(*user, ':'); + if (cp != NULL) { + *cp = '\0'; + *pass = xstrdup(cp + 1); + } + } + +#ifdef INET6 + /* + * Check if thost is an encoded IPv6 address, as per + * RFC 2732: + * `[' ipv6-address ']' + */ + if (*thost == '[') { + cp = thost + 1; + if ((ep = strchr(cp, ']')) == NULL || + (ep[1] != '\0' && ep[1] != ':')) { + warnx("Invalid address `%s' in %s `%s'", + thost, desc, origurl); + goto cleanup_parse_url; + } + len = ep - cp; /* change `[xyz]' -> `xyz' */ + memmove(thost, thost + 1, len); + thost[len] = '\0'; + if (! isipv6addr(thost)) { + warnx("Invalid IPv6 address `%s' in %s `%s'", + thost, desc, origurl); + goto cleanup_parse_url; + } + cp = ep + 1; + if (*cp == ':') + cp++; + else + cp = NULL; + } else +#endif /* INET6 */ + if ((cp = strchr(thost, ':')) != NULL) + *cp++ = '\0'; + *host = thost; + + /* look for [:port] */ + if (cp != NULL) { + long nport; + + nport = parseport(cp, -1); + if (nport == -1) { + warnx("Unknown port `%s' in %s `%s'", + cp, desc, origurl); + goto cleanup_parse_url; + } + *portnum = nport; + tport = cp; + } + + if (tport != NULL) + *port = xstrdup(tport); + if (*path == NULL) + *path = xstrdup("/"); + + if (debug) + fprintf(ttyout, + "parse_url: user `%s' pass `%s' host %s port %s(%d) " + "path `%s'\n", + *user ? *user : "<null>", *pass ? *pass : "<null>", + *host ? *host : "<null>", *port ? *port : "<null>", + *portnum ? *portnum : -1, *path ? *path : "<null>"); + + return (0); +} + +sigjmp_buf httpabort; + +/* + * Retrieve URL, via a proxy if necessary, using HTTP. + * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or + * http_proxy as appropriate. + * Supports HTTP redirects. + * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection + * is still open (e.g, ftp xfer with trailing /) + */ +static int +fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) +{ + struct addrinfo hints, *res, *res0 = NULL; + int error; + char hbuf[NI_MAXHOST]; + volatile sigfunc oldintr, oldintp; + volatile int s; + struct stat sb; + int ischunked, isproxy, rval, hcode; + size_t len; + static size_t bufsize; + static char *xferbuf; + char *cp, *ep, *buf, *savefile; + char *auth, *location, *message; + char *user, *pass, *host, *port, *path, *decodedpath; + char *puser, *ppass, *useragent; + off_t hashbytes, rangestart, rangeend, entitylen; + int (*closefunc)(FILE *); + FILE *fin, *fout; + time_t mtime; + url_t urltype; + in_port_t portnum; + + oldintr = oldintp = NULL; + closefunc = NULL; + fin = fout = NULL; + s = -1; + buf = savefile = NULL; + auth = location = message = NULL; + ischunked = isproxy = hcode = 0; + rval = 1; + user = pass = host = path = decodedpath = puser = ppass = NULL; + +#ifdef __GNUC__ /* shut up gcc warnings */ + (void)&closefunc; + (void)&fin; + (void)&fout; + (void)&buf; + (void)&savefile; + (void)&rval; + (void)&isproxy; + (void)&hcode; + (void)&ischunked; + (void)&message; + (void)&location; + (void)&auth; + (void)&decodedpath; +#endif + + if (parse_url(url, "URL", &urltype, &user, &pass, &host, &port, + &portnum, &path) == -1) + goto cleanup_fetch_url; + + if (urltype == FILE_URL_T && ! EMPTYSTRING(host) + && strcasecmp(host, "localhost") != 0) { + warnx("No support for non local file URL `%s'", url); + goto cleanup_fetch_url; + } + + if (EMPTYSTRING(path)) { + if (urltype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; + } + if (urltype != HTTP_URL_T || outfile == NULL) { + warnx("Invalid URL (no file after host) `%s'", url); + goto cleanup_fetch_url; + } + } + + decodedpath = xstrdup(path); + url_decode(decodedpath); + + if (outfile) + savefile = xstrdup(outfile); + else { + cp = strrchr(decodedpath, '/'); /* find savefile */ + if (cp != NULL) + savefile = xstrdup(cp + 1); + else + savefile = xstrdup(decodedpath); + } + if (EMPTYSTRING(savefile)) { + if (urltype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; + } + warnx("no file after directory (you must specify an " + "output file) `%s'", url); + goto cleanup_fetch_url; + } else { + if (debug) + fprintf(ttyout, "got savefile as `%s'\n", savefile); + } + + restart_point = 0; + filesize = -1; + rangestart = rangeend = entitylen = -1; + mtime = -1; + if (restartautofetch) { + if (strcmp(savefile, "-") != 0 && *savefile != '|' && + stat(savefile, &sb) == 0) + restart_point = sb.st_size; + } + if (urltype == FILE_URL_T) { /* file:// URLs */ + direction = "copied"; + fin = fopen(decodedpath, "r"); + if (fin == NULL) { + warn("Cannot open file `%s'", decodedpath); + goto cleanup_fetch_url; + } + if (fstat(fileno(fin), &sb) == 0) { + mtime = sb.st_mtime; + filesize = sb.st_size; + } + if (restart_point) { + if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) { + warn("Can't lseek to restart `%s'", + decodedpath); + goto cleanup_fetch_url; + } + } + if (verbose) { + fprintf(ttyout, "Copying %s", decodedpath); + if (restart_point) + fprintf(ttyout, " (restarting at " LLF ")", + (LLT)restart_point); + fputs("\n", ttyout); + } + } else { /* ftp:// or http:// URLs */ + char *leading; + int hasleading; + + if (proxyenv == NULL) { + if (urltype == HTTP_URL_T) + proxyenv = getoptionvalue("http_proxy"); + else if (urltype == FTP_URL_T) + proxyenv = getoptionvalue("ftp_proxy"); + } + direction = "retrieved"; + if (! EMPTYSTRING(proxyenv)) { /* use proxy */ + url_t purltype; + char *phost, *ppath; + char *pport, *no_proxy; + + isproxy = 1; + + /* check URL against list of no_proxied sites */ + no_proxy = getoptionvalue("no_proxy"); + if (! EMPTYSTRING(no_proxy)) { + char *np, *np_copy; + long np_port; + size_t hlen, plen; + + np_copy = xstrdup(no_proxy); + hlen = strlen(host); + while ((cp = strsep(&np_copy, " ,")) != NULL) { + if (*cp == '\0') + continue; + if ((np = strrchr(cp, ':')) != NULL) { + *np = '\0'; + np_port = + strtol(np + 1, &ep, 10); + if (*ep != '\0') + continue; + if (np_port != portnum) + continue; + } + plen = strlen(cp); + if (hlen < plen) + continue; + if (strncasecmp(host + hlen - plen, + cp, plen) == 0) { + isproxy = 0; + break; + } + } + FREEPTR(np_copy); + if (isproxy == 0 && urltype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; + } + } + + if (isproxy) { + if (parse_url(proxyenv, "proxy URL", &purltype, + &puser, &ppass, &phost, &pport, &portnum, + &ppath) == -1) + goto cleanup_fetch_url; + + if ((purltype != HTTP_URL_T + && purltype != FTP_URL_T) || + EMPTYSTRING(phost) || + (! EMPTYSTRING(ppath) + && strcmp(ppath, "/") != 0)) { + warnx("Malformed proxy URL `%s'", + proxyenv); + FREEPTR(phost); + FREEPTR(pport); + FREEPTR(ppath); + goto cleanup_fetch_url; + } + if (isipv6addr(host) && + strchr(host, '%') != NULL) { + warnx( +"Scoped address notation `%s' disallowed via web proxy", + host); + FREEPTR(phost); + FREEPTR(pport); + FREEPTR(ppath); + goto cleanup_fetch_url; + } + + FREEPTR(host); + host = phost; + FREEPTR(port); + port = pport; + FREEPTR(path); + path = xstrdup(url); + FREEPTR(ppath); + } + } /* ! EMPTYSTRING(proxyenv) */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(host, NULL, &hints, &res0); + if (error) { + warnx("%s", gai_strerror(error)); + goto cleanup_fetch_url; + } + if (res0->ai_canonname) + host = res0->ai_canonname; + + s = -1; + for (res = res0; res; res = res->ai_next) { + /* + * see comment in hookup() + */ + ai_unmapped(res); + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(hbuf, "invalid", sizeof(hbuf)); + + if (verbose && res != res0) + fprintf(ttyout, "Trying %s...\n", hbuf); + + ((struct sockaddr_in *)res->ai_addr)->sin_port = + htons(portnum); + s = socket(res->ai_family, SOCK_STREAM, + res->ai_protocol); + if (s < 0) { + warn("Can't create socket"); + continue; + } + + if (xconnect(s, res->ai_addr, res->ai_addrlen) < 0) { + warn("Connect to address `%s'", hbuf); + close(s); + s = -1; + continue; + } + + /* success */ + break; + } + freeaddrinfo(res0); + + if (s < 0) { + warn("Can't connect to %s", host); + goto cleanup_fetch_url; + } + + fin = fdopen(s, "r+"); + /* + * Construct and send the request. + */ + if (verbose) + fprintf(ttyout, "Requesting %s\n", url); + leading = " ("; + hasleading = 0; + if (isproxy) { + if (verbose) { + fprintf(ttyout, "%svia %s:%s", leading, + host, port); + leading = ", "; + hasleading++; + } + fprintf(fin, "GET %s HTTP/1.0\r\n", path); + if (flushcache) + fprintf(fin, "Pragma: no-cache\r\n"); + } else { + fprintf(fin, "GET %s HTTP/1.1\r\n", path); + if (strchr(host, ':')) { + char *h, *p; + + /* + * strip off IPv6 scope identifier, since it is + * local to the node + */ + h = xstrdup(host); + if (isipv6addr(h) && + (p = strchr(h, '%')) != NULL) { + *p = '\0'; + } + fprintf(fin, "Host: [%s]", h); + free(h); + } else + fprintf(fin, "Host: %s", host); + if (portnum != HTTP_PORT) + fprintf(fin, ":%u", portnum); + fprintf(fin, "\r\n"); + fprintf(fin, "Accept: */*\r\n"); + fprintf(fin, "Connection: close\r\n"); + if (restart_point) { + fputs(leading, ttyout); + fprintf(fin, "Range: bytes=" LLF "-\r\n", + (LLT)restart_point); + fprintf(ttyout, "restarting at " LLF, + (LLT)restart_point); + leading = ", "; + hasleading++; + } + if (flushcache) + fprintf(fin, "Cache-Control: no-cache\r\n"); + } + if ((useragent=getenv("FTPUSERAGENT")) != NULL) { + fprintf(fin, "User-Agent: %s\r\n", useragent); + } else { + fprintf(fin, "User-Agent: %s/%s\r\n", + FTP_PRODUCT, FTP_VERSION); + } + if (wwwauth) { + if (verbose) { + fprintf(ttyout, "%swith authorization", + leading); + leading = ", "; + hasleading++; + } + fprintf(fin, "Authorization: %s\r\n", wwwauth); + } + if (proxyauth) { + if (verbose) { + fprintf(ttyout, + "%swith proxy authorization", leading); + leading = ", "; + hasleading++; + } + fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth); + } + if (verbose && hasleading) + fputs(")\n", ttyout); + fprintf(fin, "\r\n"); + if (fflush(fin) == EOF) { + warn("Writing HTTP request"); + goto cleanup_fetch_url; + } + + /* Read the response */ + if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) { + warn("Receiving HTTP reply"); + goto cleanup_fetch_url; + } + while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) + buf[--len] = '\0'; + if (debug) + fprintf(ttyout, "received `%s'\n", buf); + + /* Determine HTTP response code */ + cp = strchr(buf, ' '); + if (cp == NULL) + goto improper; + else + cp++; + hcode = strtol(cp, &ep, 10); + if (*ep != '\0' && !isspace((unsigned char)*ep)) + goto improper; + message = xstrdup(cp); + + /* Read the rest of the header. */ + FREEPTR(buf); + while (1) { + if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) + == NULL) { + warn("Receiving HTTP reply"); + goto cleanup_fetch_url; + } + while (len > 0 && + (buf[len-1] == '\r' || buf[len-1] == '\n')) + buf[--len] = '\0'; + if (len == 0) + break; + if (debug) + fprintf(ttyout, "received `%s'\n", buf); + + /* Look for some headers */ + cp = buf; + +#define CONTENTLEN "Content-Length: " + if (strncasecmp(cp, CONTENTLEN, + sizeof(CONTENTLEN) - 1) == 0) { + cp += sizeof(CONTENTLEN) - 1; + filesize = STRTOLL(cp, &ep, 10); + if (filesize < 0 || *ep != '\0') + goto improper; + if (debug) + fprintf(ttyout, + "parsed len as: " LLF "\n", + (LLT)filesize); + +#define CONTENTRANGE "Content-Range: bytes " + } else if (strncasecmp(cp, CONTENTRANGE, + sizeof(CONTENTRANGE) - 1) == 0) { + cp += sizeof(CONTENTRANGE) - 1; + if (*cp == '*') { + ep = cp + 1; + } + else { + rangestart = STRTOLL(cp, &ep, 10); + if (rangestart < 0 || *ep != '-') + goto improper; + cp = ep + 1; + rangeend = STRTOLL(cp, &ep, 10); + if (rangeend < 0 || rangeend < rangestart) + goto improper; + } + if (*ep != '/') + goto improper; + cp = ep + 1; + if (*cp == '*') { + ep = cp + 1; + } + else { + entitylen = STRTOLL(cp, &ep, 10); + if (entitylen < 0) + goto improper; + } + if (*ep != '\0') + goto improper; + + if (debug) { + fprintf(ttyout, "parsed range as: "); + if (rangestart == -1) + fprintf(ttyout, "*"); + else + fprintf(ttyout, LLF "-" LLF, + (LLT)rangestart, + (LLT)rangeend); + fprintf(ttyout, "/" LLF "\n", (LLT)entitylen); + } + if (! restart_point) { + warnx( + "Received unexpected Content-Range header"); + goto cleanup_fetch_url; + } + +#define LASTMOD "Last-Modified: " + } else if (strncasecmp(cp, LASTMOD, + sizeof(LASTMOD) - 1) == 0) { + struct tm parsed; + char *t; + + cp += sizeof(LASTMOD) - 1; + /* RFC 1123 */ + if ((t = strptime(cp, + "%a, %d %b %Y %H:%M:%S GMT", + &parsed)) + /* RFC 850 */ + || (t = strptime(cp, + "%a, %d-%b-%y %H:%M:%S GMT", + &parsed)) + /* asctime */ + || (t = strptime(cp, + "%a, %b %d %H:%M:%S %Y", + &parsed))) { + parsed.tm_isdst = -1; + if (*t == '\0') + mtime = timegm(&parsed); + if (debug && mtime != -1) { + fprintf(ttyout, + "parsed date as: %s", + ctime(&mtime)); + } + } + +#define LOCATION "Location: " + } else if (strncasecmp(cp, LOCATION, + sizeof(LOCATION) - 1) == 0) { + cp += sizeof(LOCATION) - 1; + location = xstrdup(cp); + if (debug) + fprintf(ttyout, + "parsed location as: %s\n", cp); + +#define TRANSENC "Transfer-Encoding: " + } else if (strncasecmp(cp, TRANSENC, + sizeof(TRANSENC) - 1) == 0) { + cp += sizeof(TRANSENC) - 1; + if (strcasecmp(cp, "binary") == 0) { + warnx( + "Bogus transfer encoding - `%s' (fetching anyway)", + cp); + continue; + } + if (strcasecmp(cp, "chunked") != 0) { + warnx( + "Unsupported transfer encoding - `%s'", + cp); + goto cleanup_fetch_url; + } + ischunked++; + if (debug) + fprintf(ttyout, + "using chunked encoding\n"); + +#define PROXYAUTH "Proxy-Authenticate: " + } else if (strncasecmp(cp, PROXYAUTH, + sizeof(PROXYAUTH) - 1) == 0) { + cp += sizeof(PROXYAUTH) - 1; + FREEPTR(auth); + auth = xstrdup(cp); + if (debug) + fprintf(ttyout, + "parsed proxy-auth as: %s\n", cp); + +#define WWWAUTH "WWW-Authenticate: " + } else if (strncasecmp(cp, WWWAUTH, + sizeof(WWWAUTH) - 1) == 0) { + cp += sizeof(WWWAUTH) - 1; + FREEPTR(auth); + auth = xstrdup(cp); + if (debug) + fprintf(ttyout, + "parsed www-auth as: %s\n", cp); + + } + + } + /* finished parsing header */ + FREEPTR(buf); + + switch (hcode) { + case 200: + break; + case 206: + if (! restart_point) { + warnx("Not expecting partial content header"); + goto cleanup_fetch_url; + } + break; + case 300: + case 301: + case 302: + case 303: + case 305: + if (EMPTYSTRING(location)) { + warnx( + "No redirection Location provided by server"); + goto cleanup_fetch_url; + } + if (redirect_loop++ > 5) { + warnx("Too many redirections requested"); + goto cleanup_fetch_url; + } + if (hcode == 305) { + if (verbose) + fprintf(ttyout, "Redirected via %s\n", + location); + rval = fetch_url(url, location, + proxyauth, wwwauth); + } else { + if (verbose) + fprintf(ttyout, "Redirected to %s\n", + location); + rval = go_fetch(location); + } + goto cleanup_fetch_url; + case 401: + case 407: + { + char **authp; + char *auser, *apass; + + fprintf(ttyout, "%s\n", message); + if (EMPTYSTRING(auth)) { + warnx( + "No authentication challenge provided by server"); + goto cleanup_fetch_url; + } + if (hcode == 401) { + authp = &wwwauth; + auser = user; + apass = pass; + } else { + authp = &proxyauth; + auser = puser; + apass = ppass; + } + if (*authp != NULL) { + char reply[10]; + + fprintf(ttyout, + "Authorization failed. Retry (y/n)? "); + if (fgets(reply, sizeof(reply), stdin) + == NULL) { + clearerr(stdin); + goto cleanup_fetch_url; + } else { + if (tolower(reply[0]) != 'y') + goto cleanup_fetch_url; + } + auser = NULL; + apass = NULL; + } + if (auth_url(auth, authp, auser, apass) == 0) { + rval = fetch_url(url, proxyenv, + proxyauth, wwwauth); + memset(*authp, 0, strlen(*authp)); + FREEPTR(*authp); + } + goto cleanup_fetch_url; + } + default: + if (message) + warnx("Error retrieving file - `%s'", message); + else + warnx("Unknown error retrieving file"); + goto cleanup_fetch_url; + } + } /* end of ftp:// or http:// specific setup */ + + /* Open the output file. */ + if (strcmp(savefile, "-") == 0) { + fout = stdout; + } else if (*savefile == '|') { + oldintp = xsignal(SIGPIPE, SIG_IGN); + fout = popen(savefile + 1, "w"); + if (fout == NULL) { + warn("Can't run `%s'", savefile + 1); + goto cleanup_fetch_url; + } + closefunc = pclose; + } else { + if ((rangeend != -1 && rangeend <= restart_point) || + (rangestart == -1 && filesize != -1 && filesize <= restart_point)) { + /* already done */ + if (verbose) + fprintf(ttyout, "already done\n"); + rval = 0; + goto cleanup_fetch_url; + } + if (restart_point && rangestart != -1) { + if (entitylen != -1) + filesize = entitylen; + if (rangestart != restart_point) { + warnx( + "Size of `%s' differs from save file `%s'", + url, savefile); + goto cleanup_fetch_url; + } + fout = fopen(savefile, "a"); + } else + fout = fopen(savefile, "w"); + if (fout == NULL) { + warn("Can't open `%s'", savefile); + goto cleanup_fetch_url; + } + closefunc = fclose; + } + + /* Trap signals */ + if (sigsetjmp(httpabort, 1)) + goto cleanup_fetch_url; + (void)xsignal(SIGQUIT, psummary); + oldintr = xsignal(SIGINT, aborthttp); + + if (rcvbuf_size > bufsize) { + if (xferbuf) + (void)free(xferbuf); + bufsize = rcvbuf_size; + xferbuf = xmalloc(bufsize); + } + + bytes = 0; + hashbytes = mark; + progressmeter(-1); + + /* Finally, suck down the file. */ + do { + long chunksize; + + chunksize = 0; + /* read chunksize */ + if (ischunked) { + if (fgets(xferbuf, bufsize, fin) == NULL) { + warnx("Unexpected EOF reading chunksize"); + goto cleanup_fetch_url; + } + chunksize = strtol(xferbuf, &ep, 16); + + /* + * XXX: Work around bug in Apache 1.3.9 and + * 1.3.11, which incorrectly put trailing + * space after the chunksize. + */ + while (*ep == ' ') + ep++; + + if (strcmp(ep, "\r\n") != 0) { + warnx("Unexpected data following chunksize"); + goto cleanup_fetch_url; + } + if (debug) + fprintf(ttyout, "got chunksize of " LLF "\n", + (LLT)chunksize); + if (chunksize == 0) + break; + } + /* transfer file or chunk */ + while (1) { + struct timeval then, now, td; + off_t bufrem; + + if (rate_get) + (void)gettimeofday(&then, NULL); + bufrem = rate_get ? rate_get : bufsize; + if (ischunked) + bufrem = MIN(chunksize, bufrem); + while (bufrem > 0) { + len = fread(xferbuf, sizeof(char), + MIN(bufsize, bufrem), fin); + if (len <= 0) + goto chunkdone; + bytes += len; + bufrem -= len; + if (fwrite(xferbuf, sizeof(char), len, fout) + != len) { + warn("Writing `%s'", savefile); + goto cleanup_fetch_url; + } + if (hash && !progress) { + while (bytes >= hashbytes) { + (void)putc('#', ttyout); + hashbytes += mark; + } + (void)fflush(ttyout); + } + if (ischunked) { + chunksize -= len; + if (chunksize <= 0) + break; + } + } + if (rate_get) { + while (1) { + (void)gettimeofday(&now, NULL); + timersub(&now, &then, &td); + if (td.tv_sec > 0) + break; + usleep(1000000 - td.tv_usec); + } + } + if (ischunked && chunksize <= 0) + break; + } + /* read CRLF after chunk*/ + chunkdone: + if (ischunked) { + if (fgets(xferbuf, bufsize, fin) == NULL) + break; + if (strcmp(xferbuf, "\r\n") != 0) { + warnx("Unexpected data following chunk"); + goto cleanup_fetch_url; + } + } + } while (ischunked); + if (hash && !progress && bytes > 0) { + if (bytes < mark) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (ferror(fin)) { + warn("Reading file"); + goto cleanup_fetch_url; + } + progressmeter(1); + (void)fflush(fout); + if (closefunc == fclose && mtime != -1) { + struct timeval tval[2]; + + (void)gettimeofday(&tval[0], NULL); + tval[1].tv_sec = mtime; + tval[1].tv_usec = 0; + (*closefunc)(fout); + fout = NULL; + + if (utimes(savefile, tval) == -1) { + fprintf(ttyout, + "Can't change modification time to %s", + asctime(localtime(&mtime))); + } + } + if (bytes > 0) + ptransfer(0); + bytes = 0; + + rval = 0; + goto cleanup_fetch_url; + + improper: + warnx("Improper response from `%s'", host); + + cleanup_fetch_url: + if (oldintr) + (void)xsignal(SIGINT, oldintr); + if (oldintp) + (void)xsignal(SIGPIPE, oldintp); + if (fin != NULL) + fclose(fin); + else if (s != -1) + close(s); + if (closefunc != NULL && fout != NULL) + (*closefunc)(fout); + FREEPTR(savefile); + FREEPTR(user); + FREEPTR(pass); + FREEPTR(host); + FREEPTR(port); + FREEPTR(path); + FREEPTR(decodedpath); + FREEPTR(puser); + FREEPTR(ppass); + FREEPTR(buf); + FREEPTR(auth); + FREEPTR(location); + FREEPTR(message); + return (rval); +} + +/* + * Abort a HTTP retrieval + */ +void +aborthttp(int notused) +{ + char msgbuf[100]; + int len; + + alarmtimer(0); + len = strlcpy(msgbuf, "\nHTTP fetch aborted.\n", sizeof(msgbuf)); + write(fileno(ttyout), msgbuf, len); + siglongjmp(httpabort, 1); +} + +/* + * Retrieve ftp URL or classic ftp argument using FTP. + * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection + * is still open (e.g, ftp xfer with trailing /) + */ +static int +fetch_ftp(const char *url) +{ + char *cp, *xargv[5], rempath[MAXPATHLEN]; + char *host, *path, *dir, *file, *user, *pass; + char *port; + int dirhasglob, filehasglob, oautologin, rval, type, xargc; + in_port_t portnum; + url_t urltype; + + host = path = dir = file = user = pass = NULL; + port = NULL; + rval = 1; + type = TYPE_I; + + if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) { + if ((parse_url(url, "URL", &urltype, &user, &pass, + &host, &port, &portnum, &path) == -1) || + (user != NULL && *user == '\0') || + (pass != NULL && *pass == '\0') || + EMPTYSTRING(host)) { + warnx("Invalid URL `%s'", url); + goto cleanup_fetch_ftp; + } + url_decode(user); + url_decode(pass); + /* + * Note: Don't url_decode(path) here. We need to keep the + * distinction between "/" and "%2F" until later. + */ + + /* check for trailing ';type=[aid]' */ + if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) { + if (strcasecmp(cp, ";type=a") == 0) + type = TYPE_A; + else if (strcasecmp(cp, ";type=i") == 0) + type = TYPE_I; + else if (strcasecmp(cp, ";type=d") == 0) { + warnx( + "Directory listing via a URL is not supported"); + goto cleanup_fetch_ftp; + } else { + warnx("Invalid suffix `%s' in URL `%s'", cp, + url); + goto cleanup_fetch_ftp; + } + *cp = 0; + } + } else { /* classic style `[user@]host:[file]' */ + urltype = CLASSIC_URL_T; + host = xstrdup(url); + cp = strchr(host, '@'); + if (cp != NULL) { + *cp = '\0'; + user = host; + anonftp = 0; /* disable anonftp */ + host = xstrdup(cp + 1); + } + cp = strchr(host, ':'); + if (cp != NULL) { + *cp = '\0'; + path = xstrdup(cp + 1); + } + } + if (EMPTYSTRING(host)) + goto cleanup_fetch_ftp; + + /* Extract the file and (if present) directory name. */ + dir = path; + if (! EMPTYSTRING(dir)) { + /* + * If we are dealing with classic `[user@]host:[path]' syntax, + * then a path of the form `/file' (resulting from input of the + * form `host:/file') means that we should do "CWD /" before + * retrieving the file. So we set dir="/" and file="file". + * + * But if we are dealing with URLs like `ftp://host/path' then + * a path of the form `/file' (resulting from a URL of the form + * `ftp://host//file') means that we should do `CWD ' (with an + * empty argument) before retrieving the file. So we set + * dir="" and file="file". + * + * If the path does not contain / at all, we set dir=NULL. + * (We get a path without any slashes if we are dealing with + * classic `[user@]host:[file]' or URL `ftp://host/file'.) + * + * In all other cases, we set dir to a string that does not + * include the final '/' that separates the dir part from the + * file part of the path. (This will be the empty string if + * and only if we are dealing with a path of the form `/file' + * resulting from an URL of the form `ftp://host//file'.) + */ + cp = strrchr(dir, '/'); + if (cp == dir && urltype == CLASSIC_URL_T) { + file = cp + 1; + dir = "/"; + } else if (cp != NULL) { + *cp++ = '\0'; + file = cp; + } else { + file = dir; + dir = NULL; + } + } else + dir = NULL; + if (urltype == FTP_URL_T && file != NULL) { + url_decode(file); + /* but still don't url_decode(dir) */ + } + if (debug) + fprintf(ttyout, + "fetch_ftp: user `%s' pass `%s' host %s port %s " + "path `%s' dir `%s' file `%s'\n", + user ? user : "<null>", pass ? pass : "<null>", + host ? host : "<null>", port ? port : "<null>", + path ? path : "<null>", + dir ? dir : "<null>", file ? file : "<null>"); + + dirhasglob = filehasglob = 0; + if (doglob && urltype == CLASSIC_URL_T) { + if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL) + dirhasglob = 1; + if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL) + filehasglob = 1; + } + + /* Set up the connection */ + if (connected) + disconnect(0, NULL); + xargv[0] = (char *)getprogname(); /* XXX discards const */ + xargv[1] = host; + xargv[2] = NULL; + xargc = 2; + if (port) { + xargv[2] = port; + xargv[3] = NULL; + xargc = 3; + } + oautologin = autologin; + /* don't autologin in setpeer(), use ftp_login() below */ + autologin = 0; + setpeer(xargc, xargv); + autologin = oautologin; + if ((connected == 0) || + (connected == 1 && !ftp_login(host, user, pass))) { + warnx("Can't connect or login to host `%s'", host); + goto cleanup_fetch_ftp; + } + + switch (type) { + case TYPE_A: + setascii(1, xargv); + break; + case TYPE_I: + setbinary(1, xargv); + break; + default: + errx(1, "fetch_ftp: unknown transfer type %d", type); + } + + /* + * Change directories, if necessary. + * + * Note: don't use EMPTYSTRING(dir) below, because + * dir=="" means something different from dir==NULL. + */ + if (dir != NULL && !dirhasglob) { + char *nextpart; + + /* + * If we are dealing with a classic `[user@]host:[path]' + * (urltype is CLASSIC_URL_T) then we have a raw directory + * name (not encoded in any way) and we can change + * directories in one step. + * + * If we are dealing with an `ftp://host/path' URL + * (urltype is FTP_URL_T), then RFC 1738 says we need to + * send a separate CWD command for each unescaped "/" + * in the path, and we have to interpret %hex escaping + * *after* we find the slashes. It's possible to get + * empty components here, (from multiple adjacent + * slashes in the path) and RFC 1738 says that we should + * still do `CWD ' (with a null argument) in such cases. + * + * Many ftp servers don't support `CWD ', so if there's an + * error performing that command, bail out with a descriptive + * message. + * + * Examples: + * + * host: dir="", urltype=CLASSIC_URL_T + * logged in (to default directory) + * host:file dir=NULL, urltype=CLASSIC_URL_T + * "RETR file" + * host:dir/ dir="dir", urltype=CLASSIC_URL_T + * "CWD dir", logged in + * ftp://host/ dir="", urltype=FTP_URL_T + * logged in (to default directory) + * ftp://host/dir/ dir="dir", urltype=FTP_URL_T + * "CWD dir", logged in + * ftp://host/file dir=NULL, urltype=FTP_URL_T + * "RETR file" + * ftp://host//file dir="", urltype=FTP_URL_T + * "CWD ", "RETR file" + * host:/file dir="/", urltype=CLASSIC_URL_T + * "CWD /", "RETR file" + * ftp://host///file dir="/", urltype=FTP_URL_T + * "CWD ", "CWD ", "RETR file" + * ftp://host/%2F/file dir="%2F", urltype=FTP_URL_T + * "CWD /", "RETR file" + * ftp://host/foo/file dir="foo", urltype=FTP_URL_T + * "CWD foo", "RETR file" + * ftp://host/foo/bar/file dir="foo/bar" + * "CWD foo", "CWD bar", "RETR file" + * ftp://host//foo/bar/file dir="/foo/bar" + * "CWD ", "CWD foo", "CWD bar", "RETR file" + * ftp://host/foo//bar/file dir="foo//bar" + * "CWD foo", "CWD ", "CWD bar", "RETR file" + * ftp://host/%2F/foo/bar/file dir="%2F/foo/bar" + * "CWD /", "CWD foo", "CWD bar", "RETR file" + * ftp://host/%2Ffoo/bar/file dir="%2Ffoo/bar" + * "CWD /foo", "CWD bar", "RETR file" + * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar" + * "CWD /foo/bar", "RETR file" + * ftp://host/%2Ffoo%2Fbar%2Ffile dir=NULL + * "RETR /foo/bar/file" + * + * Note that we don't need `dir' after this point. + */ + do { + if (urltype == FTP_URL_T) { + nextpart = strchr(dir, '/'); + if (nextpart) { + *nextpart = '\0'; + nextpart++; + } + url_decode(dir); + } else + nextpart = NULL; + if (debug) + fprintf(ttyout, "dir `%s', nextpart `%s'\n", + dir ? dir : "<null>", + nextpart ? nextpart : "<null>"); + if (urltype == FTP_URL_T || *dir != '\0') { + xargv[0] = "cd"; + xargv[1] = dir; + xargv[2] = NULL; + dirchange = 0; + cd(2, xargv); + if (! dirchange) { + if (*dir == '\0' && code == 500) + fprintf(stderr, +"\n" +"ftp: The `CWD ' command (without a directory), which is required by\n" +" RFC 1738 to support the empty directory in the URL pathname (`//'),\n" +" conflicts with the server's conformance to RFC 959.\n" +" Try the same URL without the `//' in the URL pathname.\n" +"\n"); + goto cleanup_fetch_ftp; + } + } + dir = nextpart; + } while (dir != NULL); + } + + if (EMPTYSTRING(file)) { + rval = -1; + goto cleanup_fetch_ftp; + } + + if (dirhasglob) { + (void)strlcpy(rempath, dir, sizeof(rempath)); + (void)strlcat(rempath, "/", sizeof(rempath)); + (void)strlcat(rempath, file, sizeof(rempath)); + file = rempath; + } + + /* Fetch the file(s). */ + xargc = 2; + xargv[0] = "get"; + xargv[1] = file; + xargv[2] = NULL; + if (dirhasglob || filehasglob) { + int ointeractive; + + ointeractive = interactive; + interactive = 0; + xargv[0] = "mget"; + mget(xargc, xargv); + interactive = ointeractive; + } else { + if (outfile == NULL) { + cp = strrchr(file, '/'); /* find savefile */ + if (cp != NULL) + outfile = cp + 1; + else + outfile = file; + } + xargv[2] = (char *)outfile; + xargv[3] = NULL; + xargc++; + if (restartautofetch) + reget(xargc, xargv); + else + get(xargc, xargv); + } + + if ((code / 100) == COMPLETE) + rval = 0; + + cleanup_fetch_ftp: + FREEPTR(host); + FREEPTR(path); + FREEPTR(user); + FREEPTR(pass); + return (rval); +} + +/* + * Retrieve the given file to outfile. + * Supports arguments of the form: + * "host:path", "ftp://host/path" if $ftpproxy, call fetch_url() else + * call fetch_ftp() + * "http://host/path" call fetch_url() to use HTTP + * "file:///path" call fetch_url() to copy + * "about:..." print a message + * + * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection + * is still open (e.g, ftp xfer with trailing /) + */ +static int +go_fetch(const char *url) +{ + char *proxy; + + /* + * Check for about:* + */ + if (strncasecmp(url, ABOUT_URL, sizeof(ABOUT_URL) - 1) == 0) { + url += sizeof(ABOUT_URL) -1; + if (strcasecmp(url, "ftp") == 0 || + strcasecmp(url, "tnftp") == 0) { + fputs( +"This version of ftp has been enhanced by Luke Mewburn <lukem@NetBSD.org>\n" +"for the NetBSD project. Execute `man ftp' for more details.\n", ttyout); + } else if (strcasecmp(url, "lukem") == 0) { + fputs( +"Luke Mewburn is the author of most of the enhancements in this ftp client.\n" +"Please email feedback to <lukem@NetBSD.org>.\n", ttyout); + } else if (strcasecmp(url, "netbsd") == 0) { + fputs( +"NetBSD is a freely available and redistributable UNIX-like operating system.\n" +"For more information, see http://www.NetBSD.org/\n", ttyout); + } else if (strcasecmp(url, "version") == 0) { + fprintf(ttyout, "Version: %s %s%s\n", + FTP_PRODUCT, FTP_VERSION, +#ifdef INET6 + "" +#else + " (-IPv6)" +#endif + ); + } else { + fprintf(ttyout, "`%s' is an interesting topic.\n", url); + } + fputs("\n", ttyout); + return (0); + } + + /* + * Check for file:// and http:// URLs. + */ + if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || + strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) + return (fetch_url(url, NULL, NULL, NULL)); + + /* + * Try FTP URL-style and host:file arguments next. + * If ftpproxy is set with an FTP URL, use fetch_url() + * Othewise, use fetch_ftp(). + */ + proxy = getoptionvalue("ftp_proxy"); + if (!EMPTYSTRING(proxy) && + strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) + return (fetch_url(url, NULL, NULL, NULL)); + + return (fetch_ftp(url)); +} + +/* + * Retrieve multiple files from the command line, + * calling go_fetch() for each file. + * + * If an ftp path has a trailing "/", the path will be cd-ed into and + * the connection remains open, and the function will return -1 + * (to indicate the connection is alive). + * If an error occurs the return value will be the offset+1 in + * argv[] of the file that caused a problem (i.e, argv[x] + * returns x+1) + * Otherwise, 0 is returned if all files retrieved successfully. + */ +int +auto_fetch(int argc, char *argv[]) +{ + volatile int argpos; + int rval; + + argpos = 0; + + if (sigsetjmp(toplevel, 1)) { + if (connected) + disconnect(0, NULL); + return (argpos + 1); + } + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); + + /* + * Loop through as long as there's files to fetch. + */ + for (rval = 0; (rval == 0) && (argpos < argc); argpos++) { + if (strchr(argv[argpos], ':') == NULL) + break; + redirect_loop = 0; + if (!anonftp) + anonftp = 2; /* Handle "automatic" transfers. */ + rval = go_fetch(argv[argpos]); + if (outfile != NULL && strcmp(outfile, "-") != 0 + && outfile[0] != '|') + outfile = NULL; + if (rval > 0) + rval = argpos + 1; + } + + if (connected && rval != -1) + disconnect(0, NULL); + return (rval); +} + + +int +auto_put(int argc, char **argv, const char *uploadserver) +{ + char *uargv[4], *path, *pathsep; + int uargc, rval, len; + + uargc = 0; + uargv[uargc++] = "mput"; + uargv[uargc++] = argv[0]; + uargv[2] = uargv[3] = NULL; + pathsep = NULL; + rval = 1; + + if (debug) + fprintf(ttyout, "auto_put: target `%s'\n", uploadserver); + + path = xstrdup(uploadserver); + len = strlen(path); + if (path[len - 1] != '/' && path[len - 1] != ':') { + /* + * make sure we always pass a directory to auto_fetch + */ + if (argc > 1) { /* more than one file to upload */ + int len; + + len = strlen(uploadserver) + 2; /* path + "/" + "\0" */ + free(path); + path = (char *)xmalloc(len); + (void)strlcpy(path, uploadserver, len); + (void)strlcat(path, "/", len); + } else { /* single file to upload */ + uargv[0] = "put"; + pathsep = strrchr(path, '/'); + if (pathsep == NULL) { + pathsep = strrchr(path, ':'); + if (pathsep == NULL) { + warnx("Invalid URL `%s'", path); + goto cleanup_auto_put; + } + pathsep++; + uargv[2] = xstrdup(pathsep); + pathsep[0] = '/'; + } else + uargv[2] = xstrdup(pathsep + 1); + pathsep[1] = '\0'; + uargc++; + } + } + if (debug) + fprintf(ttyout, "auto_put: URL `%s' argv[2] `%s'\n", + path, uargv[2] ? uargv[2] : "<null>"); + + /* connect and cwd */ + rval = auto_fetch(1, &path); + free(path); + if(rval >= 0) + goto cleanup_auto_put; + + /* XXX : is this the best way? */ + if (uargc == 3) { + uargv[1] = argv[0]; + put(uargc, uargv); + goto cleanup_auto_put; + } + + for(; argv[0] != NULL; argv++) { + uargv[1] = argv[0]; + mput(uargc, uargv); + } + rval = 0; + + cleanup_auto_put: + FREEPTR(uargv[2]); + return (rval); +} diff --git a/net/tnftp/files/src/ftp.1 b/net/tnftp/files/src/ftp.1 new file mode 100644 index 00000000000..11bed2d9eb2 --- /dev/null +++ b/net/tnftp/files/src/ftp.1 @@ -0,0 +1,2334 @@ +.\" $NetBSD: ftp.1,v 1.1.1.1 2003/07/31 06:18:44 lukem Exp $ +.\" +.\" Copyright (c) 1996-2003 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.\" Copyright (c) 1985, 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 +.\" +.Dd July 31, 2003 +.Dt FTP 1 +.Os +.Sh NAME +.Nm ftp +.Nd +Internet file transfer program +.Sh SYNOPSIS +.Nm +.Op Fl 46AadefginpRtvV +.Bk -words +.Op Fl N Ar netrc +.Ek +.Bk -words +.Op Fl o Ar output +.Ek +.Bk -words +.Op Fl P Ar port +.Ek +.Bk -words +.Op Fl q Ar quittime +.Ek +.Bk -words +.Op Fl r Ar retry +.Ek +.Bk -words +.\" [-T dir,max[,inc]] +.Oo +.Fl T Xo +.Sm off +.Ar dir , +.Ar max +.Op , Ar inc +.Sm on +.Xc +.Oc +.Ek +.Bk -words +.\" [[user@]host [port]] +.Oo +.Oo Ar user Ns Li \&@ Oc Ns Ar host +.Op Ar port +.Oc +.Ek +.Bk -words +.\" [[user@]host:[path][/]] +.Sm off +.Oo +.Op Ar user Li \&@ +.Ar host Li \&: +.Op Ar path +.Op Li / +.Oc +.Sm on +.Ek +.Bk -words +.\" [file:///path] +.Sm off +.Oo +.Li file:/// Ar path +.Oc +.Sm on +.Ek +.Bk -words +.\" [ftp://[user[:password]@]host[:port]/path[/]] +.Sm off +.Oo +.Li ftp:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Op Li / +.Op Li ;type= Ar X +.Oc +.Sm on +.Ek +.Bk -words +.\" [http://[user[:password]@]host[:port]/path] +.Sm off +.Oo +.Li http:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Oc +.Sm on +.Ek +.Op Ar \&.\&.\&. +.Nm +.Bk -words +.Fl u Ar URL Ar file +.Ek +.Op Ar \&.\&.\&. +.Sh DESCRIPTION +.Nm +is the user interface to the Internet standard File Transfer Protocol. +The program allows a user to transfer files to and from a +remote network site. +.Pp +The last five arguments will fetch a file using the +.Tn FTP +or +.Tn HTTP +protocols, or by direct copying, into the current directory. +This is ideal for scripts. +Refer to +.Sx AUTO-FETCHING FILES +below for more information. +.Pp +Options may be specified at the command line, or to the +command interpreter. +.Bl -tag -width "port " +.It Fl 4 +Forces +.Nm +to only use IPv4 addresses. +.It Fl 6 +Forces +.Nm +to only use IPv6 addresses. +.It Fl A +Force active mode ftp. +By default, +.Nm +will try to use passive mode ftp and fall back to active mode +if passive is not supported by the server. +This option causes +.Nm +to always use an active connection. +It is only useful for connecting to very old servers that do not +implement passive mode properly. +.It Fl a +Causes +.Nm +to bypass normal login procedure, and use an anonymous login instead. +.It Fl d +Enables debugging. +.It Fl e +Disables command line editing. +This is useful for Emacs ange-ftp mode. +.It Fl f +Forces a cache reload for transfers that go through the +.Tn FTP +or +.Tn HTTP +proxies. +.It Fl g +Disables file name globbing. +.It Fl i +Turns off interactive prompting during +multiple file transfers. +.It Fl n +Restrains +.Nm +from attempting +.Dq auto-login +upon initial connection. +If auto-login is enabled, +.Nm +will check the +.Pa .netrc +(see below) file in the user's home directory for an entry describing +an account on the remote machine. +If no entry exists, +.Nm +will prompt for the remote machine login name (default is the user +identity on the local machine), and, if necessary, prompt for a password +and an account with which to login. +.It Fl N Ar netrc +Use +.Ar netrc +instead of +.Pa ~/.netrc . +Refer to +.Sx THE .netrc FILE +for more information. +.It Fl o Ar output +When auto-fetching files, save the contents in +.Ar output . +.Ar output +is parsed according to the +.Sx FILE NAMING CONVENTIONS +below. +If +.Ar output +is not +.Sq - +or doesn't start with +.Sq \&| , +then only the first file specified will be retrieved into +.Ar output ; +all other files will be retrieved into the basename of their +remote name. +.It Fl p +Enable passive mode operation for use behind connection filtering firewalls. +This option has been deprecated as +.Nm +now tries to use passive mode by default, falling back to active mode +if the server does not support passive connections. +.It Fl P Ar port +Sets the port number to +.Ar port . +.It Fl r Ar wait +Retry the connection attempt if it failed, pausing for +.Ar wait +seconds. +.It Fl q Ar quittime +Quit if the connection has stalled for +.Ar quittime +seconds. +.It Fl R +Restart all non-proxied auto-fetches. +.It Fl t +Enables packet tracing. +.It Xo +.Fl T +.Sm off +.Ar direction , +.Ar maximum +.Op , Ar increment +.Sm on +.Xc +Set the maximum transfer rate for +.Ar direction +to +.Ar maximum +bytes/second, +and if specified, the increment to +.Ar increment +bytes/second. +Refer to +.Ic rate +for more information. +.It Fl u Ar URL file Op \&.\&.\&. +Upload files on the command line to +.Ar URL +where +.Ar URL +is one of the ftp URL types as supported by auto-fetch +(with an optional target filename for single file uploads), and +.Ar file +is one or more local files to be uploaded. +.It Fl v +Enable +.Ic verbose +and +.Ic progress . +This is the default if output is to a terminal (and in the case of +.Ic progress , +.Nm +is the foreground process). +Forces +.Nm +to show all responses from the remote server, as well +as report on data transfer statistics. +.It Fl V +Disable +.Ic verbose +and +.Ic progress , +overriding the default of enabled when output is to a terminal. +.El +.Pp +The client host with which +.Nm +is to communicate may be specified on the command line. +If this is done, +.Nm +will immediately attempt to establish a connection to an +.Tn FTP +server on that host; otherwise, +.Nm +will enter its command interpreter and await instructions +from the user. +When +.Nm +is awaiting commands from the user the prompt +.Ql ftp\*[Gt] +is provided to the user. +The following commands are recognized +by +.Nm ftp : +.Bl -tag -width Fl +.It Ic \&! Op Ar command Op Ar args +Invoke an interactive shell on the local machine. +If there are arguments, the first is taken to be a command to execute +directly, with the rest of the arguments as its arguments. +.It Ic \&$ Ar macro-name Op Ar args +Execute the macro +.Ar macro-name +that was defined with the +.Ic macdef +command. +Arguments are passed to the macro unglobbed. +.It Ic account Op Ar passwd +Supply a supplemental password required by a remote system for access +to resources once a login has been successfully completed. +If no argument is included, the user will be prompted for an account +password in a non-echoing input mode. +.It Ic append Ar local-file Op Ar remote-file +Append a local file to a file on the remote machine. +If +.Ar remote-file +is left unspecified, the local file name is used in naming the +remote file after being altered by any +.Ic ntrans +or +.Ic nmap +setting. +File transfer uses the current settings for +.Ic type , +.Ic format , +.Ic mode , +and +.Ic structure . +.It Ic ascii +Set the file transfer +.Ic type +to network +.Tn ASCII . +This is the default type. +.It Ic bell +Arrange that a bell be sounded after each file transfer +command is completed. +.It Ic binary +Set the file transfer +.Ic type +to support binary image transfer. +.It Ic bye +Terminate the +.Tn FTP +session with the remote server +and exit +.Nm ftp . +An end of file will also terminate the session and exit. +.It Ic case +Toggle remote computer file name case mapping during +.Ic get , +.Ic mget +and +.Ic mput +commands. +When +.Ic case +is on (default is off), remote computer file names with all letters in +upper case are written in the local directory with the letters mapped +to lower case. +.It Ic \&cd Ar remote-directory +Change the working directory on the remote machine +to +.Ar remote-directory . +.It Ic cdup +Change the remote machine working directory to the parent of the +current remote machine working directory. +.It Ic chmod Ar mode remote-file +Change the permission modes of the file +.Ar remote-file +on the remote +system to +.Ar mode . +.It Ic close +Terminate the +.Tn FTP +session with the remote server, and +return to the command interpreter. +Any defined macros are erased. +.It Ic \&cr +Toggle carriage return stripping during +ascii type file retrieval. +Records are denoted by a carriage return/linefeed sequence +during ascii type file transfer. +When +.Ic \&cr +is on (the default), carriage returns are stripped from this +sequence to conform with the +.Ux +single linefeed record +delimiter. +Records on +.Pf non\- Ns Ux +remote systems may contain single linefeeds; +when an ascii type transfer is made, these linefeeds may be +distinguished from a record delimiter only when +.Ic \&cr +is off. +.It Ic debug Op Ar debug-value +Toggle debugging mode. +If an optional +.Ar debug-value +is specified it is used to set the debugging level. +When debugging is on, +.Nm +prints each command sent to the remote machine, preceded +by the string +.Ql \-\-\*[Gt] +.It Ic delete Ar remote-file +Delete the file +.Ar remote-file +on the remote machine. +.It Ic dir Op Ar remote-path Op Ar local-file +Print a listing of the contents of a +directory on the remote machine. +The listing includes any system-dependent information that the server +chooses to include; for example, most +.Ux +systems will produce +output from the command +.Ql ls \-l . +If +.Ar remote-path +is left unspecified, the current working directory is used. +If interactive prompting is on, +.Nm +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic dir +output. +If no local file is specified, or if +.Ar local-file +is +.Sq Fl , +the output is sent to the terminal. +.It Ic disconnect +A synonym for +.Ic close . +.It Ic edit +Toggle command line editing, and context sensitive command and file +completion. +This is automatically enabled if input is from a terminal, and +disabled otherwise. +.It Ic epsv4 +Toggle the use of the extended +.Dv EPSV +and +.Dv EPRT +commands on IPv4 connections; first try +.Dv EPSV / +.Dv EPRT , +and then +.Dv PASV / +.Dv PORT . +This is enabled by default. +If an extended command fails then this option will be temporarily +disabled for the duration of the current connection, or until +.Ic epsv4 +is executed again. +.It Ic exit +A synonym for +.Ic bye . +.It Ic features +Display what features the remote server supports (using the +.Dv FEAT +command). +.It Ic fget Ar localfile +Retrieve the files listed in +.Ar localfile , +which has one line per filename. +.It Ic form Ar format +Set the file transfer +.Ic form +to +.Ar format . +The default (and only supported) +format is +.Dq non-print . +.It Ic ftp Ar host Op Ar port +A synonym for +.Ic open . +.It Ic gate Op Ar host Op Ar port +Toggle gate-ftp mode, which used to connect through the +TIS FWTK and Gauntlet ftp proxies. +This will not be permitted if the gate-ftp server hasn't been set +(either explicitly by the user, or from the +.Ev FTPSERVER +environment variable). +If +.Ar host +is given, +then gate-ftp mode will be enabled, and the gate-ftp server will be set to +.Ar host . +If +.Ar port +is also given, that will be used as the port to connect to on the +gate-ftp server. +.It Ic get Ar remote-file Op Ar local-file +Retrieve the +.Ar remote-file +and store it on the local machine. +If the local +file name is not specified, it is given the same +name it has on the remote machine, subject to +alteration by the current +.Ic case , +.Ic ntrans , +and +.Ic nmap +settings. +The current settings for +.Ic type , +.Ic form , +.Ic mode , +and +.Ic structure +are used while transferring the file. +.It Ic glob +Toggle filename expansion for +.Ic mdelete , +.Ic mget , +.Ic mput , +and +.Ic mreget . +If globbing is turned off with +.Ic glob , +the file name arguments +are taken literally and not expanded. +Globbing for +.Ic mput +is done as in +.Xr csh 1 . +For +.Ic mdelete , +.Ic mget , +and +.Ic mreget , +each remote file name is expanded +separately on the remote machine and the lists are not merged. +Expansion of a directory name is likely to be +different from expansion of the name of an ordinary file: +the exact result depends on the foreign operating system and ftp server, +and can be previewed by doing +.Ql mls remote-files \- +Note: +.Ic mget , +.Ic mput +and +.Ic mreget +are not meant to transfer +entire directory subtrees of files. +That can be done by +transferring a +.Xr tar 1 +archive of the subtree (in binary mode). +.It Ic hash Op Ar size +Toggle hash-sign (``#'') printing for each data block +transferred. +The size of a data block defaults to 1024 bytes. +This can be changed by specifying +.Ar size +in bytes. +Enabling +.Ic hash +disables +.Ic progress . +.It Ic help Op Ar command +Print an informative message about the meaning of +.Ar command . +If no argument is given, +.Nm +prints a list of the known commands. +.It Ic idle Op Ar seconds +Set the inactivity timer on the remote server to +.Ar seconds +seconds. +If +.Ar seconds +is omitted, the current inactivity timer is printed. +.It Ic image +A synonym for +.Ic binary . +.It Ic lcd Op Ar directory +Change the working directory on the local machine. +If +no +.Ar directory +is specified, the user's home directory is used. +.It Ic less Ar file +A synonym for +.Ic page . +.It Ic lpage Ar local-file +Display +.Ar local-file +with the program specified by the +.Ic "set pager" +option. +.It Ic lpwd +Print the working directory on the local machine. +.It Ic \&ls Op Ar remote-path Op Ar local-file +A synonym for +.Ic dir . +.It Ic macdef Ar macro-name +Define a macro. +Subsequent lines are stored as the macro +.Ar macro-name ; +a null line (consecutive newline characters +in a file or +carriage returns from the terminal) terminates macro input mode. +There is a limit of 16 macros and 4096 total characters in all +defined macros. +Macros remain defined until a +.Ic close +command is executed. +The macro processor interprets `$' and `\e' as special characters. +A `$' followed by a number (or numbers) is replaced by the +corresponding argument on the macro invocation command line. +A `$' followed by an `i' signals that macro processor that the +executing macro is to be looped. +On the first pass `$i' is +replaced by the first argument on the macro invocation command line, +on the second pass it is replaced by the second argument, and so on. +A `\e' followed by any character is replaced by that character. +Use the `\e' to prevent special treatment of the `$'. +.It Ic mdelete Op Ar remote-files +Delete the +.Ar remote-files +on the remote machine. +.It Ic mdir Ar remote-files local-file +Like +.Ic dir , +except multiple remote files may be specified. +If interactive prompting is on, +.Nm +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic mdir +output. +.It Ic mget Ar remote-files +Expand the +.Ar remote-files +on the remote machine +and do a +.Ic get +for each file name thus produced. +See +.Ic glob +for details on the filename expansion. +Resulting file names will then be processed according to +.Ic case , +.Ic ntrans , +and +.Ic nmap +settings. +Files are transferred into the local working directory, +which can be changed with +.Ql lcd directory ; +new local directories can be created with +.Ql "\&! mkdir directory" . +.It Ic mkdir Ar directory-name +Make a directory on the remote machine. +.It Ic mls Ar remote-files local-file +Like +.Ic ls , +except multiple remote files may be specified, +and the +.Ar local-file +must be specified. +If interactive prompting is on, +.Nm +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic mls +output. +.It Ic mlsd Op Ar remote-path +Display the contents of +.Ar remote-path +(which should default to the current directory if not given) +in a machine-parsable form, using +.Dv MLSD . +The format of display can be changed with +.Sq "remopts mlst ..." . +.It Ic mlst Op Ar remote-path +Display the details about +.Ar remote-path +(which should default to the current directory if not given) +in a machine-parsable form, using +.Dv MLST . +The format of display can be changed with +.Sq "remopts mlst ..." . +.It Ic mode Ar mode-name +Set the file transfer +.Ic mode +to +.Ar mode-name . +The default (and only supported) +mode is +.Dq stream . +.It Ic modtime Ar remote-file +Show the last modification time of the file on the remote machine. +.It Ic more Ar file +A synonym for +.Ic page . +.It Ic mput Ar local-files +Expand wild cards in the list of local files given as arguments +and do a +.Ic put +for each file in the resulting list. +See +.Ic glob +for details of filename expansion. +Resulting file names will then be processed according to +.Ic ntrans +and +.Ic nmap +settings. +.It Ic mreget Ar remote-files +As per +.Ic mget , +but performs a +.Ic reget +instead of +.Ic get . +.It Ic msend Ar local-files +A synonym for +.Ic mput . +.It Ic newer Ar remote-file Op Ar local-file +Get the file only if the modification time of the remote file is more +recent that the file on the current system. +If the file does not +exist on the current system, the remote file is considered +.Ic newer . +Otherwise, this command is identical to +.Ar get . +.It Ic nlist Op Ar remote-path Op Ar local-file +A synonym for +.Ic ls . +.It Ic nmap Op Ar inpattern outpattern +Set or unset the filename mapping mechanism. +If no arguments are specified, the filename mapping mechanism is unset. +If arguments are specified, remote filenames are mapped during +.Ic mput +commands and +.Ic put +commands issued without a specified remote target filename. +If arguments are specified, local filenames are mapped during +.Ic mget +commands and +.Ic get +commands issued without a specified local target filename. +This command is useful when connecting to a +.No non\- Ns Ux +remote computer +with different file naming conventions or practices. +The mapping follows the pattern set by +.Ar inpattern +and +.Ar outpattern . +.Op Ar Inpattern +is a template for incoming filenames (which may have already been +processed according to the +.Ic ntrans +and +.Ic case +settings). +Variable templating is accomplished by including the +sequences `$1', `$2', ..., `$9' in +.Ar inpattern . +Use `\\' to prevent this special treatment of the `$' character. +All other characters are treated literally, and are used to determine the +.Ic nmap +.Op Ar inpattern +variable values. +For example, given +.Ar inpattern +$1.$2 and the remote file name "mydata.data", $1 would have the value +"mydata", and $2 would have the value "data". +The +.Ar outpattern +determines the resulting mapped filename. +The sequences `$1', `$2', ...., `$9' are replaced by any value resulting +from the +.Ar inpattern +template. +The sequence `$0' is replace by the original filename. +Additionally, the sequence +.Ql Op Ar seq1 , Ar seq2 +is replaced by +.Op Ar seq1 +if +.Ar seq1 +is not a null string; otherwise it is replaced by +.Ar seq2 . +For example, the command +.Pp +.Bd -literal -offset indent -compact +nmap $1.$2.$3 [$1,$2].[$2,file] +.Ed +.Pp +would yield +the output filename "myfile.data" for input filenames "myfile.data" and +"myfile.data.old", "myfile.file" for the input filename "myfile", and +"myfile.myfile" for the input filename ".myfile". +Spaces may be included in +.Ar outpattern , +as in the example: `nmap $1 sed "s/ *$//" \*[Gt] $1' . +Use the `\e' character to prevent special treatment +of the `$','[',']', and `,' characters. +.It Ic ntrans Op Ar inchars Op Ar outchars +Set or unset the filename character translation mechanism. +If no arguments are specified, the filename character +translation mechanism is unset. +If arguments are specified, characters in +remote filenames are translated during +.Ic mput +commands and +.Ic put +commands issued without a specified remote target filename. +If arguments are specified, characters in +local filenames are translated during +.Ic mget +commands and +.Ic get +commands issued without a specified local target filename. +This command is useful when connecting to a +.No non\- Ns Ux +remote computer +with different file naming conventions or practices. +Characters in a filename matching a character in +.Ar inchars +are replaced with the corresponding character in +.Ar outchars . +If the character's position in +.Ar inchars +is longer than the length of +.Ar outchars , +the character is deleted from the file name. +.It Ic open Ar host Op Ar port +Establish a connection to the specified +.Ar host +.Tn FTP +server. +An optional port number may be supplied, +in which case, +.Nm +will attempt to contact an +.Tn FTP +server at that port. +If the +.Ic "set auto-login" +option is on (default), +.Nm +will also attempt to automatically log the user in to +the +.Tn FTP +server (see below). +.It Ic page Ar file +Retrieve +.Ic file +and display with the program specified by the +.Ic "set pager" +option. +.It Ic passive Op Cm auto +Toggle passive mode (if no arguments are given). +If +.Cm auto +is given, act as if +.Ev FTPMODE +is set to +.Sq auto . +If passive mode is turned on (default), +.Nm +will send a +.Dv PASV +command for all data connections instead of a +.Dv PORT +command. +The +.Dv PASV +command requests that the remote server open a port for the data connection +and return the address of that port. +The remote server listens on that port and the client connects to it. +When using the more traditional +.Dv PORT +command, the client listens on a port and sends that address to the remote +server, who connects back to it. +Passive mode is useful when using +.Nm +through a gateway router or host that controls the directionality of +traffic. +(Note that though +.Tn FTP +servers are required to support the +.Dv PASV +command by RFC 1123, some do not.) +.It Ic pdir Op Ar remote-path +Perform +.Ic dir +.Op Ar remote-path , +and display the result with the program specified by the +.Ic "set pager" +option. +.It Ic pls Op Ar remote-path +Perform +.Ic ls +.Op Ar remote-path , +and display the result with the program specified by the +.Ic "set pager" +option. +.It Ic pmlsd Op Ar remote-path +Perform +.Ic mlsd +.Op Ar remote-path , +and display the result with the program specified by the +.Ic "set pager" +option. +.It Ic preserve +Toggle preservation of modification times on retrieved files. +.It Ic progress +Toggle display of transfer progress bar. +The progress bar will be disabled for a transfer that has +.Ar local-file +as +.Sq Fl +or a command that starts with +.Sq \&| . +Refer to +.Sx FILE NAMING CONVENTIONS +for more information. +Enabling +.Ic progress +disables +.Ic hash . +.It Ic prompt +Toggle interactive prompting. +Interactive prompting +occurs during multiple file transfers to allow the +user to selectively retrieve or store files. +If prompting is turned off (default is on), any +.Ic mget +or +.Ic mput +will transfer all files, and any +.Ic mdelete +will delete all files. +.Pp +When prompting is on, the following commands are available at a prompt: +.Bl -tag -width 2n -offset indent +.It Cm a +Answer +.Sq yes +to the current file, and automatically answer +.Sq yes +to any remaining files for the current command. +.It Cm n +Answer +.Sq no , +and do not transfer the file. +.It Cm p +Answer +.Sq yes +to the current file, and turn off prompt mode +(as is +.Dq prompt off +had been given). +.It Cm q +Terminate the current operation. +.It Cm y +Answer +.Sq yes , +and transfer the file. +.It Cm \&? +Display a help message. +.El +.Pp +Any other response will answer +.Sq yes +to the current file. +.It Ic proxy Ar ftp-command +Execute an ftp command on a secondary control connection. +This command allows simultaneous connection to two remote +.Tn FTP +servers for transferring files between the two servers. +The first +.Ic proxy +command should be an +.Ic open , +to establish the secondary control connection. +Enter the command "proxy ?" to see other +.Tn FTP +commands executable on the secondary connection. +The following commands behave differently when prefaced by +.Ic proxy : +.Ic open +will not define new macros during the auto-login process, +.Ic close +will not erase existing macro definitions, +.Ic get +and +.Ic mget +transfer files from the host on the primary control connection +to the host on the secondary control connection, and +.Ic put , +.Ic mput , +and +.Ic append +transfer files from the host on the secondary control connection +to the host on the primary control connection. +Third party file transfers depend upon support of the +.Tn FTP +protocol +.Dv PASV +command by the server on the secondary control connection. +.It Ic put Ar local-file Op Ar remote-file +Store a local file on the remote machine. +If +.Ar remote-file +is left unspecified, the local file name is used +after processing according to any +.Ic ntrans +or +.Ic nmap +settings +in naming the remote file. +File transfer uses the +current settings for +.Ic type , +.Ic format , +.Ic mode , +and +.Ic structure . +.It Ic pwd +Print the name of the current working directory on the remote +machine. +.It Ic quit +A synonym for +.Ic bye . +.It Ic quote Ar arg1 arg2 ... +The arguments specified are sent, verbatim, to the remote +.Tn FTP +server. +.It Xo +.Ic rate Ar direction +.Op Ar maximum Op Ar increment +.Xc +Throttle the maximum transfer rate to +.Ar maximum +bytes/second. +If +.Ar maximum +is 0, disable the throttle. +.Pp +.Ar direction +may be one of: +.Bl -tag -width "all" -offset indent -compact +.It Cm all +Both directions. +.It Cm get +Incoming transfers. +.It Cm put +Outgoing transfers. +.El +.Pp +.Ar maximum +can by modified on the fly by +.Ar increment +bytes (default: 1024) each time a given signal is received: +.B +.Bl -tag -width "SIGUSR1" -offset indent +.It Dv SIGUSR1 +Increment +.Ar maximum +by +.Ar increment +bytes. +.It Dv SIGUSR2 +Decrement +.Ar maximum +by +.Ar increment +bytes. +The result must be a positive number. +.El +.Pp +If +.Ar maximum +is not supplied, the current throttle rates are displayed. +.Pp +Note: +.Ic rate +is not yet implemented for ascii mode transfers. +.It Ic rcvbuf Ar size +Set the size of the socket receive buffer to +.Ar size . +.It Ic recv Ar remote-file Op Ar local-file +A synonym for +.Ic get . +.It Ic reget Ar remote-file Op Ar local-file +.Ic reget +acts like +.Ic get , +except that if +.Ar local-file +exists and is +smaller than +.Ar remote-file , +.Ar local-file +is presumed to be +a partially transferred copy of +.Ar remote-file +and the transfer +is continued from the apparent point of failure. +This command +is useful when transferring very large files over networks that +are prone to dropping connections. +.It Ic remopts Ar command Op Ar command-options +Set options on the remote +.Tn FTP +server for +.Ar command +to +.Ar command-options +(whose absence is handled on a command-specific basis). +Remote +.Tn FTP +commands known to support options include: +.Sq MLST +(used for +.Dv MLSD +and +.Dv MLST ) . +.It Ic rename Op Ar from Op Ar to +Rename the file +.Ar from +on the remote machine, to the file +.Ar to . +.It Ic reset +Clear reply queue. +This command re-synchronizes command/reply sequencing with the remote +.Tn FTP +server. +Resynchronization may be necessary following a violation of the +.Tn FTP +protocol by the remote server. +.It Ic restart Ar marker +Restart the immediately following +.Ic get +or +.Ic put +at the +indicated +.Ar marker . +On +.Ux +systems, marker is usually a byte +offset into the file. +.It Ic rhelp Op Ar command-name +Request help from the remote +.Tn FTP +server. +If a +.Ar command-name +is specified it is supplied to the server as well. +.It Ic rmdir Ar directory-name +Delete a directory on the remote machine. +.It Ic rstatus Op Ar remote-file +With no arguments, show status of remote machine. +If +.Ar remote-file +is specified, show status of +.Ar remote-file +on remote machine. +.It Ic runique +Toggle storing of files on the local system with unique filenames. +If a file already exists with a name equal to the target +local filename for a +.Ic get +or +.Ic mget +command, a ".1" is appended to the name. +If the resulting name matches another existing file, +a ".2" is appended to the original name. +If this process continues up to ".99", an error +message is printed, and the transfer does not take place. +The generated unique filename will be reported. +Note that +.Ic runique +will not affect local files generated from a shell command +(see below). +The default value is off. +.It Ic send Ar local-file Op Ar remote-file +A synonym for +.Ic put . +.It Ic sendport +Toggle the use of +.Dv PORT +commands. +By default, +.Nm +will attempt to use a +.Dv PORT +command when establishing +a connection for each data transfer. +The use of +.Dv PORT +commands can prevent delays +when performing multiple file transfers. +If the +.Dv PORT +command fails, +.Nm +will use the default data port. +When the use of +.Dv PORT +commands is disabled, no attempt will be made to use +.Dv PORT +commands for each data transfer. +This is useful +for certain +.Tn FTP +implementations which do ignore +.Dv PORT +commands but, incorrectly, indicate they've been accepted. +.It Ic set Op Ar option Ar value +Set +.Ar option +to +.Ar value . +If +.Ar option +and +.Ar value +are not given, display all of the options and their values. +The currently supported options are: +.Bl -tag -width "http_proxy" -offset indent +.It Cm anonpass +Defaults to +.Ev $FTPANONPASS +.It Cm ftp_proxy +Defaults to +.Ev $ftp_proxy . +.It Cm http_proxy +Defaults to +.Ev $http_proxy . +.It Cm no_proxy +Defaults to +.Ev $no_proxy . +.It Cm pager +Defaults to +.Ev $PAGER . +.It Cm prompt +Defaults to +.Ev $FTPPROMPT . +.It Cm rprompt +Defaults to +.Ev $FTPRPROMPT . +.El +.It Ic site Ar arg1 arg2 ... +The arguments specified are sent, verbatim, to the remote +.Tn FTP +server as a +.Dv SITE +command. +.It Ic size Ar remote-file +Return size of +.Ar remote-file +on remote machine. +.It Ic sndbuf Ar size +Set the size of the socket send buffer to +.Ar size . +.It Ic status +Show the current status of +.Nm ftp . +.It Ic struct Ar struct-name +Set the file transfer +.Ar structure +to +.Ar struct-name . +The default (and only supported) +structure is +.Dq file . +.It Ic sunique +Toggle storing of files on remote machine under unique file names. +The remote +.Tn FTP +server must support +.Tn FTP +protocol +.Dv STOU +command for +successful completion. +The remote server will report unique name. +Default value is off. +.It Ic system +Show the type of operating system running on the remote machine. +.It Ic tenex +Set the file transfer type to that needed to +talk to +.Tn TENEX +machines. +.It Ic throttle +A synonym for +.Ic rate . +.It Ic trace +Toggle packet tracing. +.It Ic type Op Ar type-name +Set the file transfer +.Ic type +to +.Ar type-name . +If no type is specified, the current type +is printed. +The default type is network +.Tn ASCII . +.It Ic umask Op Ar newmask +Set the default umask on the remote server to +.Ar newmask . +If +.Ar newmask +is omitted, the current umask is printed. +.It Ic unset Ar option +Unset +.Ar option . +Refer to +.Ic set +for more information. +.It Ic usage Ar command +Print the usage message for +.Ar command . +.It Xo +.Ic user Ar user-name +.Op Ar password Op Ar account +.Xc +Identify yourself to the remote +.Tn FTP +server. +If the +.Ar password +is not specified and the server requires it, +.Nm +will prompt the user for it (after disabling local echo). +If an +.Ar account +field is not specified, and the +.Tn FTP +server +requires it, the user will be prompted for it. +If an +.Ar account +field is specified, an account command will +be relayed to the remote server after the login sequence +is completed if the remote server did not require it +for logging in. +Unless +.Nm +is invoked with +.Dq auto-login +disabled, this process is done automatically on initial connection to the +.Tn FTP +server. +.It Ic verbose +Toggle verbose mode. +In verbose mode, all responses from +the +.Tn FTP +server are displayed to the user. +In addition, +if verbose is on, when a file transfer completes, statistics +regarding the efficiency of the transfer are reported. +By default, +verbose is on. +.It Ic xferbuf Ar size +Set the size of the socket send and receive buffers to +.Ar size . +.It Ic \&? Op Ar command +A synonym for +.Ic help . +.El +.Pp +Command arguments which have embedded spaces may be quoted with +quote `"' marks. +.Pp +Commands which toggle settings can take an explicit +.Ic on +or +.Ic off +argument to force the setting appropriately. +.Pp +Commands which take a byte count as an argument +(e.g., +.Ic hash , +.Ic rate , +and +.Ic xferbuf ) +support an optional suffix on the argument which changes the +interpretation of the argument. +Supported suffixes are: +.Bl -tag -width 3n -offset indent -compact +.It Li b +Causes no modification. +(Optional) +.It Li k +Kilo; multiply the argument by 1024 +.It Li m +Mega; multiply the argument by 1048576 +.It Li g +Giga; multiply the argument by 1073741824 +.El +.Pp +If +.Nm +receives a +.Dv SIGINFO +(see the +.Dq status +argument of +.Xr stty 1 ) +or +.Dv SIGQUIT +signal whilst a transfer is in progress, the current transfer rate +statistics will be written to the standard error output, in the +same format as the standard completion message. +.Sh AUTO-FETCHING FILES +In addition to standard commands, this version of +.Nm +supports an auto-fetch feature. +To enable auto-fetch, simply pass the list of hostnames/files +on the command line. +.Pp +The following formats are valid syntax for an auto-fetch element: +.Bl -tag -width "FOO " +.\" [user@]host:[path][/] +.It Xo +.Sm off +.Op Ar user Li \&@ +.Ar host Li \&: +.Op Ar path +.Op Li / +.Sm on +.Xc +.Dq Classic +.Tn FTP +format. +.Pp +If +.Ar path +contains a glob character and globbing is enabled, +(see +.Ic glob ) , +then the equivalent of +.Ql mget path +is performed. +.Pp +If the directory component of +.Ar path +contains no globbing characters, +it is stored locally with the name basename (see +.Xr basename 1 ) +of +.Ic path , +in the current directory. +Otherwise, the full remote name is used as the local name, +relative to the local root directory. +.\" ftp://[user[:password]@]host[:port]/path[/][;type=X] +.It Xo +.Sm off +.Li ftp:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Op Li / +.Op Li ;type= Ar X +.Sm on +.Xc +An +.Tn FTP +URL, retrieved using the +.Tn FTP +protocol if +.Ic "set ftp_proxy" +isn't defined. +Otherwise, transfer the URL using +.Tn HTTP +via the proxy defined in +.Ic "set ftp_proxy" . +If +.Ic "set ftp_proxy" +isn't defined and +.Ar user +is given, login as +.Ar user . +In this case, use +.Ar password +if supplied, otherwise prompt the user for one. +.Pp +If a suffix of +.Sq ;type=A +or +.Sq ;type=I +is supplied, then the transfer type will take place as +ascii or binary (respectively). +The default transfer type is binary. +.Pp +In order to be compliant with +.Cm RFC 1738 , +.Nm +interprets the +.Ar path +part of an +.Dq ftp:// +auto-fetch URL as follows: +.Bl -bullet +.It +The +.Sq Li / +immediately after the +.Ar host Ns Oo Li \&: Ns Ar port Oc +is interpreted as a separator before the +.Ar path , +and not as part of the +.Ar path +itself. +.It +The +.Ar path +is interpreted as a +.So Li / Sc Ns -separated +list of name components. +For all but the last such component, +.Nm +performs the equivalent of a +.Ic cd +command. +For the last path component, +.Nm +performs the equivalent of a +.Ic get +command. +.It +Empty name components, +which result from +.Sq Li // +within the +.Ar path , +or from an extra +.Sq Li / +at the beginning of the +.Ar path , +will cause the equivalent of a +.Ic cd +command without a directory name. +This is unlikely to be useful. +.It +Any +.Sq Li \&% Ns Ar XX +codes within the path components are decoded, with +.Ar XX +representing a character code in hexadecimal. +This decoding takes place after the +.Ar path +has been split into components, +but before each component is used in the equivalent of a +.Ic cd +or +.Ic get +command. +Some often-used codes are +.Sq Li \&%2F +(which represents +.Sq Li / ) +and +.Sq Li \&%7E +(which represents +.Sq Li ~ ) . +.El +.Pp +The above interpretation has the following consequences: +.Bl -bullet +.It +The path is interpreted relative to the +default login directory of the specified user or of the +.Sq anonymous +user. +If the +.Pa / +directory is required, use a leading path of +.Dq %2F . +If a user's home directory is required (and the remote server supports +the syntax), use a leading path of +.Dq %7Euser/ . +For example, to retrieve +.Pa /etc/motd +from +.Sq localhost +as the user +.Sq myname +with the password +.Sq mypass , +use +.Dq ftp://myname:mypass@localhost/%2fetc/motd +.It +The exact +.Ic cd +and +.Ic get +commands can be controlled by careful choice of +where to use +.Sq / +and where to use +.Sq %2F +(or +.Sq %2f ) . +For example, the following URLs correspond to the +equivalents of the indicated commands: +.Bl -tag -width "ftp://host/%2Fdir1%2Fdir2%2Ffile" +.It ftp://host/dir1/dir2/file +.Dq "cd dir1" , +.Dq "cd dir2" , +.Dq "get file" . +.It ftp://host/%2Fdir1/dir2/file +.Dq "cd /dir1" , +.Dq "cd dir2" , +.Dq "get file" . +.It ftp://host/dir1%2Fdir2/file +.Dq "cd dir1/dir2" , +.Dq "get file" . +.It ftp://host/%2Fdir1%2Fdir2/file +.Dq "cd /dir1/dir2" , +.Dq "get file" . +.It ftp://host/dir1%2Fdir2%2Ffile +.Dq "get dir1/dir2/file" . +.It ftp://host/%2Fdir1%2Fdir2%2Ffile +.Dq "get /dir1/dir2/file" . +.El +.It +You must have appropriate access permission for each of the +intermediate directories that is used in the equivalent of a +.Ic cd +command. +.El +.\" http://[user[:password]@]host[:port]/path +.It Xo +.Sm off +.Li http:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Sm on +.Xc +An +.Tn HTTP +URL, retrieved using the +.Tn HTTP +protocol. +If +.Ic "set http_proxy" +is defined, it is used as a URL to an +.Tn HTTP +proxy server. +If +.Tn HTTP +authorization is required to retrieve +.Ar path , +and +.Sq user +(and optionally +.Sq password ) +is in the URL, use them for the first attempt to authenticate. +.\" file:///path +.It Xo +.Sm off +.Li file:/// Ar path +.Sm on +.Xc +A local URL, copied from +.Pa / Ns Ar path +on the local host. +.El +.Pp +Unless noted otherwise above, and +.Fl o Ar output +is not given, the file is stored in the current directory as the +.Xr basename 1 +of +.Ar path . +.Pp +If a classic format or an +.Tn FTP +URL format has a trailing +.Sq / +or an empty +.Ar path +component, then +.Nm +will connect to the site and +.Ic cd +to the directory given as the path, and leave the user in interactive +mode ready for further input. +This will not work if +.Ic "set ftp_proxy" +is being used. +.Pp +Direct +.Tn HTTP +transfers use HTTP 1.1. +Proxied +.Tn FTP +and +.Tn HTTP +transfers use HTTP 1.0. +.Pp +If +.Fl R +is given, all auto-fetches that don't go via the +.Tn FTP +or +.Tn HTTP +proxies will be restarted. +For +.Tn FTP , +this is implemented by using +.Nm reget +instead of +.Nm get . +For +.Tn HTTP , +this is implemented by using the +.Sq "Range: bytes=" +.Tn "HTTP/1.1" +directive. +.Pp +If WWW or proxy WWW authentication is required, you will be prompted +to enter a username and password to authenticate with. +.Pp +When specifying IPv6 numeric addresses in a URL, you need to +surround the address in square brackets. +E.g.: +.Dq ftp://[::1]:21/ . +This is because colons are used in IPv6 numeric address as well as +being the separator for the port number. +.Sh ABORTING A FILE TRANSFER +To abort a file transfer, use the terminal interrupt key +(usually Ctrl-C). +Sending transfers will be immediately halted. +Receiving transfers will be halted by sending an +.Tn FTP +protocol +.Dv ABOR +command to the remote server, and discarding any further data received. +The speed at which this is accomplished depends upon the remote +server's support for +.Dv ABOR +processing. +If the remote server does not support the +.Dv ABOR +command, the prompt will not appear until the remote server has completed +sending the requested file. +.Pp +If the terminal interrupt key sequence is used whilst +.Nm +is awaiting a reply from the remote server for the ABOR processing, +then the connection will be closed. +This is different from the traditional behaviour (which ignores the +terminal interrupt during this phase), but is considered more useful. +.Sh FILE NAMING CONVENTIONS +Files specified as arguments to +.Nm +commands are processed according to the following rules. +.Bl -enum +.It +If the file name +.Sq Fl +is specified, the +.Ar stdin +(for reading) or +.Ar stdout +(for writing) is used. +.It +If the first character of the file name is +.Sq \&| , +the +remainder of the argument is interpreted as a shell command. +.Nm +then forks a shell, using +.Xr popen 3 +with the argument supplied, and reads (writes) from the stdout +(stdin). +If the shell command includes spaces, the argument +must be quoted; e.g. +.Dq Qq Li \&| ls\ \-lt . +A particularly +useful example of this mechanism is: +.Dq Li dir \&"\&" \&|more . +.It +Failing the above checks, if ``globbing'' is enabled, +local file names are expanded +according to the rules used in the +.Xr csh 1 ; +c.f. the +.Ic glob +command. +If the +.Nm +command expects a single local file (e.g. +.Ic put ) , +only the first filename generated by the "globbing" operation is used. +.It +For +.Ic mget +commands and +.Ic get +commands with unspecified local file names, the local filename is +the remote filename, which may be altered by a +.Ic case , +.Ic ntrans , +or +.Ic nmap +setting. +The resulting filename may then be altered if +.Ic runique +is on. +.It +For +.Ic mput +commands and +.Ic put +commands with unspecified remote file names, the remote filename is +the local filename, which may be altered by a +.Ic ntrans +or +.Ic nmap +setting. +The resulting filename may then be altered by the remote server if +.Ic sunique +is on. +.El +.Sh FILE TRANSFER PARAMETERS +The +.Tn FTP +specification specifies many parameters which may affect a file transfer. +The +.Ic type +may be one of +.Dq ascii , +.Dq image +(binary), +.Dq ebcdic , +and +.Dq local byte size +(for +.Tn PDP Ns -10's +and +.Tn PDP Ns -20's +mostly). +.Nm +supports the ascii and image types of file transfer, +plus local byte size 8 for +.Ic tenex +mode transfers. +.Pp +.Nm +supports only the default values for the remaining +file transfer parameters: +.Ic mode , +.Ic form , +and +.Ic struct . +.Sh THE .netrc FILE +The +.Pa .netrc +file contains login and initialization information +used by the auto-login process. +It resides in the user's home directory, +unless overridden with the +.Fl N Ar netrc +option, or specified in the +.Ev NETRC +environment variable. +The following tokens are recognized; they may be separated by spaces, +tabs, or new-lines: +.Bl -tag -width password +.It Ic machine Ar name +Identify a remote machine +.Ar name . +The auto-login process searches the +.Pa .netrc +file for a +.Ic machine +token that matches the remote machine specified on the +.Nm +command line or as an +.Ic open +command argument. +Once a match is made, the subsequent +.Pa .netrc +tokens are processed, +stopping when the end of file is reached or another +.Ic machine +or a +.Ic default +token is encountered. +.It Ic default +This is the same as +.Ic machine +.Ar name +except that +.Ic default +matches any name. +There can be only one +.Ic default +token, and it must be after all +.Ic machine +tokens. +This is normally used as: +.Pp +.Dl default login anonymous password user@site +.Pp +thereby giving the user an automatic anonymous +.Tn FTP +login to +machines not specified in +.Pa .netrc . +This can be overridden +by using the +.Fl n +flag to disable auto-login. +.It Ic login Ar name +Identify a user on the remote machine. +If this token is present, the auto-login process will initiate +a login using the specified +.Ar name . +.It Ic password Ar string +Supply a password. +If this token is present, the auto-login process will supply the +specified string if the remote server requires a password as part +of the login process. +Note that if this token is present in the +.Pa .netrc +file for any user other +than +.Ar anonymous , +.Nm +will abort the auto-login process if the +.Pa .netrc +is readable by +anyone besides the user. +.It Ic account Ar string +Supply an additional account password. +If this token is present, the auto-login process will supply the +specified string if the remote server requires an additional +account password, or the auto-login process will initiate an +.Dv ACCT +command if it does not. +.It Ic macdef Ar name +Define a macro. +This token functions like the +.Nm +.Ic macdef +command functions. +A macro is defined with the specified name; its contents begin with the +next +.Pa .netrc +line and continue until a blank line (consecutive new-line +characters) is encountered. +If a macro named +.Ic init +is defined, it is automatically executed as the last step in the +auto-login process. +For example, +.Bd -literal -offset indent +default +macdef init +epsv4 off +.Ed +.Pp +followed by a blank line. +.El +.Sh COMMAND LINE EDITING +.Nm +supports interactive command line editing, via the +.Xr editline 3 +library. +It is enabled with the +.Ic edit +command, and is enabled by default if input is from a tty. +Previous lines can be recalled and edited with the arrow keys, +and other GNU Emacs-style editing keys may be used as well. +.Pp +The +.Xr editline 3 +library is configured with a +.Pa .editrc +file - refer to +.Xr editrc 5 +for more information. +.Pp +An extra key binding is available to +.Nm +to provide context sensitive command and filename completion +(including remote file completion). +To use this, bind a key to the +.Xr editline 3 +command +.Ic ftp-complete . +By default, this is bound to the TAB key. +.Sh COMMAND LINE PROMPT +By default, +.Nm +displays a command line prompt of +.Dq "ftp\*[Gt] " +to the user. +This can be changed with the +.Ic "set prompt" +command. +.Pp +A prompt can be displayed on the right side of the screen (after the +command input) with the +.Ic "set rprompt" +command. +.Pp +The following formatting sequences are replaced by the given +information: +.Bl -tag -width "%% " -offset indent +.It Li \&%/ +The current remote working directory. +.\" %c[[0]n], %.[[0]n] +.It Xo +.Sm off +.Li \&%c +.Op Oo Li 0 Oc Ar n Ns , +.Li \&%. +.Op Oo Li 0 Oc Ar n +.Sm on +.Xc +The trailing component of the current remote working directory, or +.Em n +trailing components if a digit +.Em n +is given. +If +.Em n +begins with +.Sq 0 , +the number of skipped components precede the trailing component(s) in +the format +.\" ``/<number>trailing'' +.Do +.Sm off +.Li / Li \*[Lt] Va number Li \*[Gt] +.Va trailing +.Sm on +.Dc +(for +.Sq \&%c ) +or +.\" ``...trailing'' +.Dq Li \&... Ns Va trailing +(for +.Sq \&%. ) . +.It Li \&%M +The remote host name. +.It Li \&%m +The remote host name, up to the first +.Sq \&. . +.It Li \&%n +The remote user name. +.It Li \&%% +A single +.Sq % . +.El +.Sh ENVIRONMENT +.Nm +uses the following environment variables. +.Bl -tag -width "FTPSERVERPORT" +.It Ev FTPANONPASS +Password to send in an anonymous +.Tn FTP +transfer. +Defaults to +.Dq Li `whoami`@ . +.It Ev FTPMODE +Overrides the default operation mode. +Support values are: +.Bl -tag -width "passive" +.It Cm active +active mode +.Tn FTP +only +.It Cm auto +automatic determination of passive or active (this is the default) +.It Cm gate +gate-ftp mode +.It Cm passive +passive mode +.Tn FTP +only +.El +.It Ev FTPPROMPT +Command-line prompt to use. +Defaults to +.Dq "ftp\*[Gt] " . +Refer to +.Sx COMMAND LINE PROMPT +for more information. +.It Ev FTPRPROMPT +Command-line right side prompt to use. +Defaults to +.Dq "" . +Refer to +.Sx COMMAND LINE PROMPT +for more information. +.It Ev FTPSERVER +Host to use as gate-ftp server when +.Ic gate +is enabled. +.It Ev FTPSERVERPORT +Port to use when connecting to gate-ftp server when +.Ic gate +is enabled. +Default is port returned by a +.Fn getservbyname +lookup of +.Dq ftpgate/tcp . +.It Ev FTPUSERAGENT +The value to send for the +.Tn HTTP +User-Agent +header. +.It Ev HOME +For default location of a +.Pa .netrc +file, if one exists. +.It Ev NETRC +An alternate location of the +.Pa .netrc +file. +.It Ev PAGER +Used by various commands to display files. +Defaults to +.Xr more 1 +if empty or not set. +.It Ev SHELL +For default shell. +.It Ev ftp_proxy +URL of +.Tn FTP +proxy to use when making +.Tn FTP +URL requests +(if not defined, use the standard +.Tn FTP +protocol). +.Pp +.Em NOTE : +this is not used for interactive sessions, only for command-line +fetches. +.It Ev http_proxy +URL of +.Tn HTTP +proxy to use when making +.Tn HTTP +URL requests. +If proxy authentication is required and there is a username and +password in this URL, they will automatically be used in the first +attempt to authenticate to the proxy. +.Pp +Note that the use of a username and password in +.Ev ftp_proxy +and +.Ev http_proxy +may be incompatible with other programs that use it +(such as +.Xr lynx 1 ) . +.Pp +.Em NOTE : +this is not used for interactive sessions, only for command-line +fetches. +.It Ev no_proxy +A space or comma separated list of hosts (or domains) for which +proxying is not to be used. +Each entry may have an optional trailing ":port", which restricts +the matching to connections to that port. +.El +.Sh EXTENDED PASSIVE MODE AND FIREWALLS +Some firewall configurations do not allow +.Nm +to use extended passive mode. +If you find that even a simple +.Ic ls +appears to hang after printing a message such as this: +.Pp +.Dl 229 Entering Extended Passive Mode (|||58551|) +.Pp +then you will need to disable extended passive mode with +.Ic epsv4 off . +See the above section +.Sx The .netrc File +for an example of how to make this automatic. +.Sh SEE ALSO +.Xr getservbyname 3 , +.Xr editrc 5 , +.Xr services 5 , +.Xr ftpd 8 +.Sh STANDARDS +.Nm +attempts to be compliant with +.Cm RFC 959 , +.Cm RFC 1123 , +.Cm RFC 1738 , +.Cm RFC 2068 , +.Cm RFC 2389 , +.Cm RFC 2428 , +.Cm RFC 2732 , +and +.Cm draft-ietf-ftpext-mlst-11 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Pp +Various features such as command line editing, context sensitive +command and file completion, dynamic progress bar, automatic +fetching of files and URLs, modification time preservation, +transfer rate throttling, configurable command line prompt, +and other enhancements over the standard +.Bx +.Nm +were implemented in +.Nx 1.3 +and later releases +by +.An Luke Mewburn +.Aq lukem@NetBSD.org . +.Pp +IPv6 support was added by the WIDE/KAME project +(but may not be present in all non-NetBSD versions of this program, depending +if the operating system supports IPv6 in a similar manner to KAME). +.Sh BUGS +Correct execution of many commands depends upon proper behavior +by the remote server. +.Pp +An error in the treatment of carriage returns +in the +.Bx 4.2 +ascii-mode transfer code +has been corrected. +This correction may result in incorrect transfers of binary files +to and from +.Bx 4.2 +servers using the ascii type. +Avoid this problem by using the binary image type. +.Pp +.Nm +assumes that all IPv4 mapped addresses +.Po +IPv6 addresses with a form like +.Li ::ffff:10.1.1.1 +.Pc +indicate IPv4 destinations which can be handled by +.Dv AF_INET +sockets. +However, in certain IPv6 network configurations, this assumption is not true. +In such an environment, IPv4 mapped addresses must be passed to +.Dv AF_INET6 +sockets directly. +For example, if your site uses a SIIT translator for IPv6-to-IPv4 translation, +.Nm +is unable to support your configuration. diff --git a/net/tnftp/files/src/main.c b/net/tnftp/files/src/main.c new file mode 100644 index 00000000000..5823130dc4b --- /dev/null +++ b/net/tnftp/files/src/main.c @@ -0,0 +1,1030 @@ +/* $NetBSD: main.c,v 1.1.1.1 2003/07/31 06:18:44 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; +#else +__RCSID("$NetBSD: main.c,v 1.1.1.1 2003/07/31 06:18:44 lukem Exp $"); +#endif +#endif /* not lint */ +#endif + +/* + * FTP User Program -- Command Interface. + */ + +#include "tnftp.h" + +#define GLOBAL /* force GLOBAL decls in ftp_var.h to be declared */ +#include "ftp_var.h" + +#define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */ +#define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */ +#define NO_PROXY "no_proxy" /* env var with list of non-proxied + * hosts, comma or space separated */ + +static void setupoption(char *, char *, char *); +int main(int, char *[]); + +int +main(int argc, char *argv[]) +{ + int ch, rval; + struct passwd *pw; + char *cp, *ep, *anonuser, *anonpass, *upload_path; + int dumbterm, s, len, isupload; + +#if 0 /* XXX */ + setlocale(LC_ALL, ""); +#endif + setprogname(argv[0]); + + ftpport = "ftp"; + httpport = "http"; + gateport = NULL; + cp = getenv("FTPSERVERPORT"); + if (cp != NULL) + gateport = cp; + else + gateport = "ftpgate"; + doglob = 1; + interactive = 1; + autologin = 1; + passivemode = 1; + activefallback = 1; + preserve = 1; + verbose = 0; + progress = 0; + gatemode = 0; + data = -1; + outfile = NULL; + restartautofetch = 0; +#ifndef NO_EDITCOMPLETE + editing = 0; + el = NULL; + hist = NULL; +#endif + bytes = 0; + mark = HASHBYTES; + rate_get = 0; + rate_get_incr = DEFAULTINCR; + rate_put = 0; + rate_put_incr = DEFAULTINCR; +#ifdef INET6 + epsv4 = 1; +#else + epsv4 = 0; +#endif + epsv4bad = 0; + upload_path = NULL; + isupload = 0; + reply_callback = NULL; + family = AF_UNSPEC; + + netrc[0] = '\0'; + cp = getenv("NETRC"); + if (cp != NULL && strlcpy(netrc, cp, sizeof(netrc)) >= sizeof(netrc)) + errx(1, "$NETRC `%s': %s", cp, strerror(ENAMETOOLONG)); + + /* + * Get the default socket buffer sizes if we don't already have them. + * It doesn't matter which socket we do this to, because on the first + * call no socket buffer sizes will have been modified, so we are + * guaranteed to get the system defaults. + */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(1, "can't create socket"); + len = sizeof(rcvbuf_size); + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf_size, &len) + < 0) + err(1, "unable to get default rcvbuf size"); + len = sizeof(sndbuf_size); + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf_size, &len) + < 0) + err(1, "unable to get default sndbuf size"); + (void)close(s); + /* sanity check returned buffer sizes */ + if (rcvbuf_size <= 0) + rcvbuf_size = 8192; + if (sndbuf_size <= 0) + sndbuf_size = 8192; + + marg_sl = xsl_init(); + if ((tmpdir = getenv("TMPDIR")) == NULL) + tmpdir = _PATH_TMP; + + /* Set default operation mode based on FTPMODE environment variable */ + if ((cp = getenv("FTPMODE")) != NULL) { + if (strcasecmp(cp, "passive") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcasecmp(cp, "active") == 0) { + passivemode = 0; + activefallback = 0; + } else if (strcasecmp(cp, "gate") == 0) { + gatemode = 1; + } else if (strcasecmp(cp, "auto") == 0) { + passivemode = 1; + activefallback = 1; + } else + warnx("unknown $FTPMODE '%s'; using defaults", cp); + } + + if (strcmp(getprogname(), "pftp") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcmp(getprogname(), "gate-ftp") == 0) + gatemode = 1; + + gateserver = getenv("FTPSERVER"); + if (gateserver == NULL || *gateserver == '\0') + gateserver = GATE_SERVER; + if (gatemode) { + if (*gateserver == '\0') { + warnx( +"Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); + gatemode = 0; + } + } + + cp = getenv("TERM"); + if (cp == NULL || strcmp(cp, "dumb") == 0) + dumbterm = 1; + else + dumbterm = 0; + fromatty = isatty(fileno(stdin)); + ttyout = stdout; + if (isatty(fileno(ttyout))) { + verbose = 1; /* verbose if to a tty */ + if (! dumbterm) { +#ifndef NO_EDITCOMPLETE + if (fromatty) /* editing mode on if tty is usable */ + editing = 1; +#endif +#ifndef NO_PROGRESS + if (foregroundproc()) + progress = 1; /* progress bar on if fg */ +#endif + } + } + + while ((ch = getopt(argc, argv, "46AadefginN:o:pP:q:r:RtT:u:vV")) != -1) { + switch (ch) { + case '4': + family = AF_INET; + break; + + case '6': +#ifdef INET6 + family = AF_INET6; +#else + warnx("INET6 support is not available; ignoring -6"); +#endif + break; + + case 'A': + activefallback = 0; + passivemode = 0; + break; + + case 'a': + anonftp = 1; + break; + + case 'd': + options |= SO_DEBUG; + debug++; + break; + + case 'e': +#ifndef NO_EDITCOMPLETE + editing = 0; +#endif + break; + + case 'f': + flushcache = 1; + break; + + case 'g': + doglob = 0; + break; + + case 'i': + interactive = 0; + break; + + case 'n': + autologin = 0; + break; + + case 'N': + if (strlcpy(netrc, optarg, sizeof(netrc)) + >= sizeof(netrc)) + errx(1, "%s: %s", optarg, + strerror(ENAMETOOLONG)); + break; + + case 'o': + outfile = optarg; + if (strcmp(outfile, "-") == 0) + ttyout = stderr; + break; + + case 'p': + passivemode = 1; + activefallback = 0; + break; + + case 'P': + ftpport = optarg; + break; + + case 'q': + quit_time = strtol(optarg, &ep, 10); + if (quit_time < 1 || *ep != '\0') + errx(1, "bad quit value: %s", optarg); + break; + + case 'r': + retry_connect = strtol(optarg, &ep, 10); + if (retry_connect < 1 || *ep != '\0') + errx(1, "bad retry value: %s", optarg); + break; + + case 'R': + restartautofetch = 1; + break; + + case 't': + trace = 1; + break; + + case 'T': + { + int targc; + char *targv[6], *oac; + + /* look for `dir,max[,incr]' */ + targc = 0; + targv[targc++] = "-T"; + oac = xstrdup(optarg); + + while ((cp = strsep(&oac, ",")) != NULL) { + if (*cp == '\0') { + warnx("bad throttle value: %s", optarg); + usage(); + /* NOTREACHED */ + } + targv[targc++] = cp; + if (targc >= 5) + break; + } + if (parserate(targc, targv, 1) == -1) + usage(); + free(oac); + break; + } + + case 'u': + { + isupload = 1; + interactive = 0; + upload_path = xstrdup(optarg); + + break; + } + + case 'v': + progress = verbose = 1; + break; + + case 'V': + progress = verbose = 0; + break; + + default: + usage(); + } + } + /* set line buffering on ttyout */ + setvbuf(ttyout, NULL, _IOLBF, 0); + argc -= optind; + argv += optind; + + cpend = 0; /* no pending replies */ + proxy = 0; /* proxy not active */ + crflag = 1; /* strip c.r. on ascii gets */ + sendport = -1; /* not using ports */ + + /* + * Cache the user name and home directory. + */ + localhome = NULL; + localname = NULL; + anonuser = "anonymous"; + cp = getenv("HOME"); + if (! EMPTYSTRING(cp)) + localhome = xstrdup(cp); + pw = NULL; + cp = getlogin(); + if (cp != NULL) + pw = getpwnam(cp); + if (pw == NULL) + pw = getpwuid(getuid()); + if (pw != NULL) { + if (localhome == NULL && !EMPTYSTRING(pw->pw_dir)) + localhome = xstrdup(pw->pw_dir); + localname = xstrdup(pw->pw_name); + anonuser = localname; + } + if (netrc[0] == '\0' && localhome != NULL) { + if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) || + strlcat(netrc, "/.netrc", sizeof(netrc)) >= sizeof(netrc)) { + warnx("%s/.netrc: %s", localhome, + strerror(ENAMETOOLONG)); + netrc[0] = '\0'; + } + } + if (localhome == NULL) + localhome = xstrdup("/"); + + /* + * Every anonymous FTP server I've encountered will accept the + * string "username@", and will append the hostname itself. We + * do this by default since many servers are picky about not + * having a FQDN in the anonymous password. + * - thorpej@NetBSD.org + */ + len = strlen(anonuser) + 2; + anonpass = xmalloc(len); + (void)strlcpy(anonpass, anonuser, len); + (void)strlcat(anonpass, "@", len); + + /* + * set all the defaults for options defined in + * struct option optiontab[] declared in cmdtab.c + */ + setupoption("anonpass", getenv("FTPANONPASS"), anonpass); + setupoption("ftp_proxy", getenv(FTP_PROXY), ""); + setupoption("http_proxy", getenv(HTTP_PROXY), ""); + setupoption("no_proxy", getenv(NO_PROXY), ""); + setupoption("pager", getenv("PAGER"), DEFAULTPAGER); + setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT); + setupoption("rprompt", getenv("FTPRPROMPT"), DEFAULTRPROMPT); + + free(anonpass); + + setttywidth(0); +#ifdef SIGINFO + (void)xsignal(SIGINFO, psummary); +#endif + (void)xsignal(SIGQUIT, psummary); + (void)xsignal(SIGUSR1, crankrate); + (void)xsignal(SIGUSR2, crankrate); + (void)xsignal(SIGWINCH, setttywidth); + +#ifdef __GNUC__ /* to shut up gcc warnings */ + (void)&argc; + (void)&argv; +#endif + + if (argc > 0) { + if (isupload) { + rval = auto_put(argc, argv, upload_path); + exit(rval); + } else if (strchr(argv[0], ':') != NULL + && ! isipv6addr(argv[0])) { + rval = auto_fetch(argc, argv); + if (rval >= 0) /* -1 == connected and cd-ed */ + exit(rval); + } else { + char *xargv[4], *user, *host; + + if (sigsetjmp(toplevel, 1)) + exit(0); + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); + user = NULL; + host = argv[0]; + cp = strchr(host, '@'); + if (cp) { + *cp = '\0'; + user = host; + host = cp + 1; + } + /* XXX discards const */ + xargv[0] = (char *)getprogname(); + xargv[1] = host; + xargv[2] = argv[1]; + xargv[3] = NULL; + do { + int oautologin; + + oautologin = autologin; + if (user != NULL) { + anonftp = 0; + autologin = 0; + } + setpeer(argc+1, xargv); + autologin = oautologin; + if (connected == 1 && user != NULL) + (void)ftp_login(host, user, NULL); + if (!retry_connect) + break; + if (!connected) { + macnum = 0; + fprintf(ttyout, + "Retrying in %d seconds...\n", + retry_connect); + sleep(retry_connect); + } + } while (!connected); + retry_connect = 0; /* connected, stop hiding msgs */ + } + } + if (isupload) + usage(); + +#ifndef NO_EDITCOMPLETE + controlediting(); +#endif /* !NO_EDITCOMPLETE */ + + (void)sigsetjmp(toplevel, 1); + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); + for (;;) + cmdscanner(); +} + +/* + * Generate a prompt + */ +char * +prompt(void) +{ + static char **prompt; + static char buf[MAXPATHLEN]; + + if (prompt == NULL) { + struct option *o; + + o = getoption("prompt"); + if (o == NULL) + errx(1, "no such option `prompt'"); + prompt = &(o->value); + } + formatbuf(buf, sizeof(buf), *prompt ? *prompt : DEFAULTPROMPT); + return (buf); +} + +/* + * Generate an rprompt + */ +char * +rprompt(void) +{ + static char **rprompt; + static char buf[MAXPATHLEN]; + + if (rprompt == NULL) { + struct option *o; + + o = getoption("rprompt"); + if (o == NULL) + errx(1, "no such option `rprompt'"); + rprompt = &(o->value); + } + formatbuf(buf, sizeof(buf), *rprompt ? *rprompt : DEFAULTRPROMPT); + return (buf); +} + +/* + * Command parser. + */ +void +cmdscanner(void) +{ + struct cmd *c; + char *p; + int num; + + for (;;) { +#ifndef NO_EDITCOMPLETE + if (!editing) { +#endif /* !NO_EDITCOMPLETE */ + if (fromatty) { + fputs(prompt(), ttyout); + p = rprompt(); + if (*p) + fprintf(ttyout, "%s ", p); + (void)fflush(ttyout); + } + if (fgets(line, sizeof(line), stdin) == NULL) { + if (fromatty) + putc('\n', ttyout); + quit(0, NULL); + } + num = strlen(line); + if (num == 0) + break; + if (line[--num] == '\n') { + if (num == 0) + break; + line[num] = '\0'; + } else if (num == sizeof(line) - 2) { + fputs("sorry, input line too long.\n", ttyout); + while ((num = getchar()) != '\n' && num != EOF) + /* void */; + break; + } /* else it was a line without a newline */ +#ifndef NO_EDITCOMPLETE + } else { + const char *buf; + HistEvent ev; + cursor_pos = NULL; + + if ((buf = el_gets(el, &num)) == NULL || num == 0) { + if (fromatty) + putc('\n', ttyout); + quit(0, NULL); + } + if (buf[--num] == '\n') { + if (num == 0) + break; + } else if (num >= sizeof(line)) { + fputs("sorry, input line too long.\n", ttyout); + break; + } + memcpy(line, buf, num); + line[num] = '\0'; + history(hist, &ev, H_ENTER, buf); + } +#endif /* !NO_EDITCOMPLETE */ + + makeargv(); + if (margc == 0) + continue; + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + fputs("?Ambiguous command.\n", ttyout); + continue; + } + if (c == NULL) { +#if !defined(NO_EDITCOMPLETE) + /* + * attempt to el_parse() unknown commands. + * any command containing a ':' would be parsed + * as "[prog:]cmd ...", and will result in a + * false positive if prog != "ftp", so treat + * such commands as invalid. + */ + if (strchr(margv[0], ':') != NULL || + el_parse(el, margc, (const char **)margv) != 0) +#endif /* !NO_EDITCOMPLETE */ + fputs("?Invalid command.\n", ttyout); + continue; + } + if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + continue; + } + confirmrest = 0; + margv[0] = c->c_name; + (*c->c_handler)(margc, margv); + if (bell && c->c_bell) + (void)putc('\007', ttyout); + if (c->c_handler != help) + break; + } + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); +} + +struct cmd * +getcmd(const char *name) +{ + const char *p, *q; + struct cmd *c, *found; + int nmatches, longest; + + if (name == NULL) + return (0); + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->c_name) != NULL; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return (c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return ((struct cmd *)-1); + return (found); +} + +/* + * Slice a string up into argc/argv. + */ + +int slrflag; + +void +makeargv(void) +{ + char *argp; + + stringbase = line; /* scan from first of buffer */ + argbase = argbuf; /* store from first of buffer */ + slrflag = 0; + marg_sl->sl_cur = 0; /* reset to start of marg_sl */ + for (margc = 0; ; margc++) { + argp = slurpstring(); + xsl_add(marg_sl, argp); + if (argp == NULL) + break; + } +#ifndef NO_EDITCOMPLETE + if (cursor_pos == line) { + cursor_argc = 0; + cursor_argo = 0; + } else if (cursor_pos != NULL) { + cursor_argc = margc; + cursor_argo = strlen(margv[margc-1]); + } +#endif /* !NO_EDITCOMPLETE */ +} + +#ifdef NO_EDITCOMPLETE +#define INC_CHKCURSOR(x) (x)++ +#else /* !NO_EDITCOMPLETE */ +#define INC_CHKCURSOR(x) { (x)++ ; \ + if (x == cursor_pos) { \ + cursor_argc = margc; \ + cursor_argo = ap-argbase; \ + cursor_pos = NULL; \ + } } + +#endif /* !NO_EDITCOMPLETE */ + +/* + * Parse string into argbuf; + * implemented with FSM to + * handle quoting and strings + */ +char * +slurpstring(void) +{ + int got_one = 0; + char *sb = stringbase; + char *ap = argbase; + char *tmp = argbase; /* will return this if token found */ + + if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ + switch (slrflag) { /* and $ as token for macro invoke */ + case 0: + slrflag++; + INC_CHKCURSOR(stringbase); + return ((*sb == '!') ? "!" : "$"); + /* NOTREACHED */ + case 1: + slrflag++; + altarg = stringbase; + break; + default: + break; + } + } + +S0: + switch (*sb) { + + case '\0': + goto OUT; + + case ' ': + case '\t': + INC_CHKCURSOR(sb); + goto S0; + + default: + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = sb; + break; + default: + break; + } + goto S1; + } + +S1: + switch (*sb) { + + case ' ': + case '\t': + case '\0': + goto OUT; /* end of token */ + + case '\\': + INC_CHKCURSOR(sb); + goto S2; /* slurp next character */ + + case '"': + INC_CHKCURSOR(sb); + goto S3; /* slurp quoted string */ + + default: + *ap = *sb; /* add character to token */ + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S1; + } + +S2: + switch (*sb) { + + case '\0': + goto OUT; + + default: + *ap = *sb; + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S1; + } + +S3: + switch (*sb) { + + case '\0': + goto OUT; + + case '"': + INC_CHKCURSOR(sb); + goto S1; + + default: + *ap = *sb; + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S3; + } + +OUT: + if (got_one) + *ap++ = '\0'; + argbase = ap; /* update storage pointer */ + stringbase = sb; /* update scan pointer */ + if (got_one) { + return (tmp); + } + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = NULL; + break; + default: + break; + } + return (NULL); +} + +/* + * Help/usage command. + * Call each command handler with argc == 0 and argv[0] == name. + */ +void +help(int argc, char *argv[]) +{ + struct cmd *c; + char *nargv[1], *p, *cmd; + int isusage; + + cmd = argv[0]; + isusage = (strcmp(cmd, "usage") == 0); + if (argc == 0 || (isusage && argc == 1)) { + fprintf(ttyout, "usage: %s [command [...]]\n", cmd); + return; + } + if (argc == 1) { + StringList *buf; + + buf = xsl_init(); + fprintf(ttyout, + "%sommands may be abbreviated. Commands are:\n\n", + proxy ? "Proxy c" : "C"); + for (c = cmdtab; (p = c->c_name) != NULL; c++) + if (!proxy || c->c_proxy) + xsl_add(buf, p); + list_vertical(buf); + sl_free(buf, 0); + return; + } + +#define HELPINDENT ((int) sizeof("disconnect")) + + while (--argc > 0) { + char *arg; + + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + fprintf(ttyout, "?Ambiguous %s command `%s'\n", + cmd, arg); + else if (c == NULL) + fprintf(ttyout, "?Invalid %s command `%s'\n", + cmd, arg); + else { + if (isusage) { + nargv[0] = c->c_name; + (*c->c_handler)(0, nargv); + } else + fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, + c->c_name, c->c_help); + } + } +} + +struct option * +getoption(const char *name) +{ + const char *p; + struct option *c; + + if (name == NULL) + return (NULL); + for (c = optiontab; (p = c->name) != NULL; c++) { + if (strcasecmp(p, name) == 0) + return (c); + } + return (NULL); +} + +char * +getoptionvalue(const char *name) +{ + struct option *c; + + if (name == NULL) + errx(1, "getoptionvalue() invoked with NULL name"); + c = getoption(name); + if (c != NULL) + return (c->value); + errx(1, "getoptionvalue() invoked with unknown option `%s'", name); + /* NOTREACHED */ +} + +static void +setupoption(char *name, char *value, char *defaultvalue) +{ + char *nargv[3]; + int overbose; + + nargv[0] = "setupoption()"; + nargv[1] = name; + nargv[2] = (value ? value : defaultvalue); + overbose = verbose; + verbose = 0; + setoption(3, nargv); + verbose = overbose; +} + +void +usage(void) +{ + const char *progname = getprogname(); + + (void)fprintf(stderr, +"usage: %s [-46AadefginpRtvV] [-N netrc] [-o outfile] [-P port] [-r retry]\n" +" [-T dir,max[,inc][[user@]host [port]]] [host:path[/]]\n" +" [file:///file] [ftp://[user[:pass]@]host[:port]/path[/]]\n" +" [http://[user[:pass]@]host[:port]/path] [...]\n" +" %s -u URL file [...]\n", progname, progname); + exit(1); +} diff --git a/net/tnftp/files/src/progressbar.c b/net/tnftp/files/src/progressbar.c new file mode 100644 index 00000000000..980ab6f6514 --- /dev/null +++ b/net/tnftp/files/src/progressbar.c @@ -0,0 +1,464 @@ +/* $NetBSD: progressbar.c,v 1.1.1.1 2003/07/31 06:18:44 lukem Exp $ */ + +/*- + * Copyright (c) 1997-2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: progressbar.c,v 1.1.1.1 2003/07/31 06:18:44 lukem Exp $"); +#endif /* not lint */ +#endif + +/* + * FTP User Program -- Misc support routines + */ + +#include "tnftp.h" + +#include "progressbar.h" + +#if !defined(NO_PROGRESS) +/* + * return non-zero if we're the current foreground process + */ +int +foregroundproc(void) +{ + static pid_t pgrp = -1; + + if (pgrp == -1) +#if GETPGRP_VOID + pgrp = getpgrp(); +#else /* ! GETPGRP_VOID */ + pgrp = getpgrp(0); +#endif /* ! GETPGRP_VOID */ + + return (tcgetpgrp(fileno(ttyout)) == pgrp); +} +#endif /* !defined(NO_PROGRESS) */ + + +#ifndef NO_PROGRESS +static void updateprogressmeter(int); + +/* + * SIGALRM handler to update the progress meter + */ +static void +updateprogressmeter(int dummy) +{ + int oerrno = errno; + + progressmeter(0); + errno = oerrno; +} +#endif /* NO_PROGRESS */ + + +/* + * List of order of magnitude prefixes. + * The last is `P', as 2^64 = 16384 Petabytes + */ +static const char prefixes[] = " KMGTP"; + +/* + * Display a transfer progress bar if progress is non-zero. + * SIGALRM is hijacked for use by this function. + * - Before the transfer, set filesize to size of file (or -1 if unknown), + * and call with flag = -1. This starts the once per second timer, + * and a call to updateprogressmeter() upon SIGALRM. + * - During the transfer, updateprogressmeter will call progressmeter + * with flag = 0 + * - After the transfer, call with flag = 1 + */ +static struct timeval start; +static struct timeval lastupdate; + +#define BUFLEFT (sizeof(buf) - len) + +void +progressmeter(int flag) +{ + static off_t lastsize; + off_t cursize; + struct timeval now, wait; +#ifndef NO_PROGRESS + struct timeval td; + off_t abbrevsize, bytespersec; + double elapsed; + int ratio, barlength, i, len, remaining; + + /* + * Work variables for progress bar. + * + * XXX: if the format of the progress bar changes + * (especially the number of characters in the + * `static' portion of it), be sure to update + * these appropriately. + */ + char buf[256]; /* workspace for progress bar */ +#define BAROVERHEAD 43 /* non `*' portion of progress bar */ + /* + * stars should contain at least + * sizeof(buf) - BAROVERHEAD entries + */ + static const char stars[] = +"*****************************************************************************" +"*****************************************************************************" +"*****************************************************************************"; + +#endif + + if (flag == -1) { + (void)gettimeofday(&start, NULL); + lastupdate = start; + lastsize = restart_point; + } + + (void)gettimeofday(&now, NULL); + cursize = bytes + restart_point; + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + wait.tv_sec = 0; + } else { +#ifndef STANDALONE_PROGRESS + if (quit_time > 0 && wait.tv_sec > quit_time) { + len = snprintf(buf, sizeof(buf), "\r\n%s: " + "transfer aborted because stalled for %lu sec.\r\n", + getprogname(), (unsigned long)wait.tv_sec); + (void)write(fileno(ttyout), buf, len); + (void)xsignal(SIGALRM, SIG_DFL); + alarmtimer(0); + siglongjmp(toplevel, 1); + } +#endif /* !STANDALONE_PROGRESS */ + } + /* + * Always set the handler even if we are not the foreground process. + */ +#ifdef STANDALONE_PROGRESS + if (progress) { +#else + if (quit_time > 0 || progress) { +#endif /* !STANDALONE_PROGRESS */ + if (flag == -1) { + (void)xsignal_restart(SIGALRM, updateprogressmeter, 1); + alarmtimer(1); /* set alarm timer for 1 Hz */ + } else if (flag == 1) { + (void)xsignal(SIGALRM, SIG_DFL); + alarmtimer(0); + } + } +#ifndef NO_PROGRESS + if (!progress) + return; + len = 0; + + /* + * print progress bar only if we are foreground process. + */ + if (! foregroundproc()) + return; + + len += snprintf(buf + len, BUFLEFT, "\r"); + if (filesize > 0) { + ratio = (int)((double)cursize * 100.0 / (double)filesize); + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio); + + /* + * calculate the length of the `*' bar, ensuring that + * the number of stars won't exceed the buffer size + */ + barlength = MIN(sizeof(buf) - 1, ttywidth) - BAROVERHEAD; + if (barlength > 0) { + i = barlength * ratio / 100; + len += snprintf(buf + len, BUFLEFT, + "|%.*s%*s|", i, stars, barlength - i, ""); + } + } + + abbrevsize = cursize; + for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++) + abbrevsize >>= 10; + len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %c%c ", + (LLT)abbrevsize, + prefixes[i], + i == 0 ? ' ' : 'B'); + + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + bytespersec = 0; + if (bytes > 0) { + bytespersec = bytes; + if (elapsed > 0.0) + bytespersec /= elapsed; + } + for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++) + bytespersec >>= 10; + len += snprintf(buf + len, BUFLEFT, + " " LLFP("3") ".%02d %cB/s ", + (LLT)(bytespersec / 1024), + (int)((bytespersec % 1024) * 100 / 1024), + prefixes[i]); + + if (filesize > 0) { + if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { + len += snprintf(buf + len, BUFLEFT, " --:-- ETA"); + } else if (wait.tv_sec >= STALLTIME) { + len += snprintf(buf + len, BUFLEFT, " - stalled -"); + } else { + remaining = (int) + ((filesize - restart_point) / (bytes / elapsed) - + elapsed); + if (remaining >= 100 * SECSPERHOUR) + len += snprintf(buf + len, BUFLEFT, + " --:-- ETA"); + else { + i = remaining / SECSPERHOUR; + if (i) + len += snprintf(buf + len, BUFLEFT, + "%2d:", i); + else + len += snprintf(buf + len, BUFLEFT, + " "); + i = remaining % SECSPERHOUR; + len += snprintf(buf + len, BUFLEFT, + "%02d:%02d ETA", i / 60, i % 60); + } + } + } + if (flag == 1) + len += snprintf(buf + len, BUFLEFT, "\n"); + (void)write(fileno(ttyout), buf, len); + +#endif /* !NO_PROGRESS */ +} + +#ifndef STANDALONE_PROGRESS +/* + * Display transfer statistics. + * Requires start to be initialised by progressmeter(-1), + * direction to be defined by xfer routines, and filesize and bytes + * to be updated by xfer routines + * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr + * instead of ttyout. + */ +void +ptransfer(int siginfo) +{ + struct timeval now, td, wait; + double elapsed; + off_t bytespersec; + int remaining, hh, i, len; + + char buf[256]; /* Work variable for transfer status. */ + + if (!verbose && !progress && !siginfo) + return; + + (void)gettimeofday(&now, NULL); + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + bytespersec = 0; + if (bytes > 0) { + bytespersec = bytes; + if (elapsed > 0.0) + bytespersec /= elapsed; + } + len = 0; + len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ", + (LLT)bytes, bytes == 1 ? "" : "s", direction); + remaining = (int)elapsed; + if (remaining > SECSPERDAY) { + int days; + + days = remaining / SECSPERDAY; + remaining %= SECSPERDAY; + len += snprintf(buf + len, BUFLEFT, + "%d day%s ", days, days == 1 ? "" : "s"); + } + hh = remaining / SECSPERHOUR; + remaining %= SECSPERHOUR; + if (hh) + len += snprintf(buf + len, BUFLEFT, "%2d:", hh); + len += snprintf(buf + len, BUFLEFT, + "%02d:%02d ", remaining / 60, remaining % 60); + + for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++) + bytespersec >>= 10; + len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %cB/s)", + (LLT)(bytespersec / 1024), + (int)((bytespersec % 1024) * 100 / 1024), + prefixes[i]); + + if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 + && bytes + restart_point <= filesize) { + remaining = (int)((filesize - restart_point) / + (bytes / elapsed) - elapsed); + hh = remaining / SECSPERHOUR; + remaining %= SECSPERHOUR; + len += snprintf(buf + len, BUFLEFT, " ETA: "); + if (hh) + len += snprintf(buf + len, BUFLEFT, "%2d:", hh); + len += snprintf(buf + len, BUFLEFT, "%02d:%02d", + remaining / 60, remaining % 60); + timersub(&now, &lastupdate, &wait); + if (wait.tv_sec >= STALLTIME) + len += snprintf(buf + len, BUFLEFT, " (stalled)"); + } + len += snprintf(buf + len, BUFLEFT, "\n"); + (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len); +} + +/* + * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress + */ +void +psummary(int notused) +{ + int oerrno = errno; + + if (bytes > 0) { + if (fromatty) + write(fileno(ttyout), "\n", 1); + ptransfer(1); + } + errno = oerrno; +} +#endif /* !STANDALONE_PROGRESS */ + + +/* + * Set the SIGALRM interval timer for wait seconds, 0 to disable. + */ +void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + + +/* + * Install a POSIX signal handler, allowing the invoker to set whether + * the signal should be restartable or not + */ +sigfunc +xsignal_restart(int sig, sigfunc func, int restartable) +{ +#ifdef ultrix /* XXX: this is lame - how do we test sigvec vs. sigaction? */ + struct sigvec vec, ovec; + + vec.sv_handler = func; + sigemptyset(&vec.sv_mask); + vec.sv_flags = 0; + if (sigvec(sig, &vec, &ovec) < 0) + return (SIG_ERR); + return (ovec.sv_handler); +#else /* ! ultrix */ + struct sigaction act, oact; + act.sa_handler = func; + + sigemptyset(&act.sa_mask); +#if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */ + act.sa_flags = restartable ? SA_RESTART : 0; +#elif defined(SA_INTERRUPT) /* SunOS 4.x */ + act.sa_flags = restartable ? 0 : SA_INTERRUPT; +#else +#error "system must have SA_RESTART or SA_INTERRUPT" +#endif + if (sigaction(sig, &act, &oact) < 0) + return (SIG_ERR); + return (oact.sa_handler); +#endif /* ! ultrix */ +} + +/* + * Install a signal handler with the `restartable' flag set dependent upon + * which signal is being set. (This is a wrapper to xsignal_restart()) + */ +sigfunc +xsignal(int sig, sigfunc func) +{ + int restartable; + + /* + * Some signals print output or change the state of the process. + * There should be restartable, so that reads and writes are + * not affected. Some signals should cause program flow to change; + * these signals should not be restartable, so that the system call + * will return with EINTR, and the program will go do something + * different. If the signal handler calls longjmp() or siglongjmp(), + * it doesn't matter if it's restartable. + */ + + switch(sig) { +#ifdef SIGINFO + case SIGINFO: +#endif + case SIGQUIT: + case SIGUSR1: + case SIGUSR2: + case SIGWINCH: + restartable = 1; + break; + + case SIGALRM: + case SIGINT: + case SIGPIPE: + restartable = 0; + break; + + default: + /* + * This is unpleasant, but I don't know what would be better. + * Right now, this "can't happen" + */ + errx(1, "xsignal_restart called with signal %d", sig); + } + + return(xsignal_restart(sig, func, restartable)); +} |