diff options
author | gavin <gavin@FreeBSD.org> | 2011-06-19 16:54:06 +0000 |
---|---|---|
committer | gavin <gavin@FreeBSD.org> | 2011-06-19 16:54:06 +0000 |
commit | cd23bc1631ac018f140ef5d8cfa8fd8afa0d28e1 (patch) | |
tree | d536f826304d5d1048670212a5e33e4818109b14 /src | |
parent | 7e8a9605b1b776a2e6c8f20d00d300a4c160d2fc (diff) | |
download | FreeBSD-src-cd23bc1631ac018f140ef5d8cfa8fd8afa0d28e1.zip FreeBSD-src-cd23bc1631ac018f140ef5d8cfa8fd8afa0d28e1.tar.gz |
Bootstrap lukemftp vendor area
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 28 | ||||
-rw-r--r-- | src/Makefile.in | 43 | ||||
-rw-r--r-- | src/cmds.c | 2753 | ||||
-rw-r--r-- | src/cmdtab.c | 309 | ||||
-rw-r--r-- | src/complete.c | 435 | ||||
-rw-r--r-- | src/domacro.c | 143 | ||||
-rw-r--r-- | src/extern.h | 257 | ||||
-rw-r--r-- | src/fetch.c | 1834 | ||||
-rw-r--r-- | src/ftp.1 | 2357 | ||||
-rw-r--r-- | src/ftp.c | 2171 | ||||
-rw-r--r-- | src/ftp.cat1 | 1055 | ||||
-rw-r--r-- | src/ftp_var.h | 337 | ||||
-rw-r--r-- | src/main.c | 1051 | ||||
-rw-r--r-- | src/progressbar.c | 464 | ||||
-rw-r--r-- | src/progressbar.h | 99 | ||||
-rw-r--r-- | src/ruserpass.c | 293 | ||||
-rw-r--r-- | src/util.c | 1400 | ||||
-rw-r--r-- | src/version.h | 44 |
18 files changed, 15073 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..79cc5a6 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,28 @@ +# $NetBSD: Makefile,v 1.30 2005/02/11 15:13:28 jmc Exp $ +# from: @(#)Makefile 8.2 (Berkeley) 4/3/94 + +.include <bsd.own.mk> + +PROG= ftp +SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c \ + progressbar.c ruserpass.c util.c + +# Uncomment the following to provide defaults for gate-ftp operation +# +#CPPFLAGS+=-DGATE_SERVER=\"ftp-gw.host\" # -DGATE_PORT=21 + +.if defined(SMALLPROG) +CPPFLAGS+=-DNO_EDITCOMPLETE -DNO_ABOUT -DNO_AUTH -DNO_HELP -DNO_STATUS +.else +LDADD+= -ledit -ltermcap +DPADD+= ${LIBEDIT} ${LIBTERMCAP} +.endif + +.if (!defined(SMALLPROG) || defined(SMALLPROG_INET6)) && (${USE_INET6} != "no") +CPPFLAGS+= -DINET6 +.endif + +cmds.o fetch.o: version.h +main.o: ftp_var.h + +.include <bsd.prog.mk> diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..bb60f10 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,43 @@ +# +# $Id: Makefile.in,v 1.8 2000/08/08 07:04:27 lukem Exp $ +# + +srcdir = @srcdir@ +VPATH = @srcdir@ +SHELL = /bin/sh + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +mandir = @mandir@ +transform = @program_transform_name@ + +mandircat1 = ${mandir}/cat1 + +CC = @CC@ +CFLAGS = -I${srcdir} -I${srcdir}/.. -I. -I.. @INCLUDES@ @CFLAGS@ +LIBS = @LIBS@ +LDFLAGS = @LDFLAGS@ + +INSTALL = @INSTALL@ + +PROG = ftp +OBJS = cmds.o cmdtab.o complete.o domacro.o fetch.o ftp.o main.o \ + ruserpass.o util.o + +all: ${PROG} + +install: all + -mkdir -p ${bindir} + ${INSTALL} -m 555 ${PROG} ${bindir}/`echo ${PROG}|sed '$(transform)'` + -mkdir -p ${mandircat1} + ${INSTALL} -m 444 ${srcdir}/${PROG}.cat1 ${mandircat1}/`echo ${PROG}|sed '$(transform)'`.1 + +${PROG}: ${OBJS} @LIBDEPENDS@ + ${CC} ${CFLAGS} ${LDFLAGS} -o ${PROG} ${OBJS} ${LIBS} + +clean: + rm -f core ${PROG} ${OBJS} + +distclean: clean + rm -f Makefile diff --git a/src/cmds.c b/src/cmds.c new file mode 100644 index 0000000..69dbd18 --- /dev/null +++ b/src/cmds.c @@ -0,0 +1,2753 @@ +/* $NetBSD: cmds.c,v 1.112 2005/04/11 01:49:31 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2005 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 Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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. 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; +#else +__RCSID("$NetBSD: cmds.c,v 1.112 2005/04/11 01:49:31 lukem Exp $"); +#endif +#endif /* not lint */ + +/* + * FTP User Program -- Command Routines. + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <arpa/ftp.h> + +#include <ctype.h> +#include <err.h> +#include <glob.h> +#include <limits.h> +#include <netdb.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <libutil.h> + +#include "ftp_var.h" +#include "version.h" + +struct types { + char *t_name; + char *t_mode; + int t_type; + char *t_arg; +} types[] = { + { "ascii", "A", TYPE_A, 0 }, + { "binary", "I", TYPE_I, 0 }, + { "image", "I", TYPE_I, 0 }, + { "ebcdic", "E", TYPE_E, 0 }, + { "tenex", "L", TYPE_L, bytename }, + { NULL } +}; + +sigjmp_buf jabort; +const char *mname; + +static int confirm(const char *, const char *); + +static const char *doprocess(char *, size_t, const char *, int, int, int); +static const char *domap(char *, size_t, const char *); +static const char *docase(char *, size_t, const char *); +static const char *dotrans(char *, size_t, const char *); + +static int +confirm(const char *cmd, const char *file) +{ + char line[BUFSIZ]; + + if (!interactive || confirmrest) + return (1); + while (1) { + fprintf(ttyout, "%s %s [anpqy?]? ", cmd, file); + (void)fflush(ttyout); + if (fgets(line, sizeof(line), stdin) == NULL) { + mflag = 0; + fprintf(ttyout, "\nEOF received; %s aborted\n", mname); + clearerr(stdin); + return (0); + } + switch (tolower((unsigned char)*line)) { + case 'a': + confirmrest = 1; + fprintf(ttyout, + "Prompting off for duration of %s.\n", cmd); + break; + case 'p': + interactive = 0; + fputs("Interactive mode: off.\n", ttyout); + break; + case 'q': + mflag = 0; + fprintf(ttyout, "%s aborted.\n", mname); + /* FALLTHROUGH */ + case 'n': + return (0); + case '?': + fprintf(ttyout, + " confirmation options:\n" + "\ta answer `yes' for the duration of %s\n" + "\tn answer `no' for this file\n" + "\tp turn off `prompt' mode\n" + "\tq stop the current %s\n" + "\ty answer `yes' for this file\n" + "\t? this help list\n", + cmd, cmd); + continue; /* back to while(1) */ + } + return (1); + } + /* NOTREACHED */ +} + +/* + * Set transfer type. + */ +void +settype(int argc, char *argv[]) +{ + struct types *p; + int comret; + + if (argc == 0 || argc > 2) { + char *sep; + + fprintf(ttyout, "usage: %s [", argv[0]); + sep = " "; + for (p = types; p->t_name; p++) { + fprintf(ttyout, "%s%s", sep, p->t_name); + sep = " | "; + } + fputs(" ]\n", ttyout); + code = -1; + return; + } + if (argc < 2) { + fprintf(ttyout, "Using %s mode to transfer files.\n", typename); + code = 0; + return; + } + for (p = types; p->t_name; p++) + if (strcmp(argv[1], p->t_name) == 0) + break; + if (p->t_name == 0) { + fprintf(ttyout, "%s: unknown mode.\n", argv[1]); + code = -1; + return; + } + if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) + comret = command("TYPE %s %s", p->t_mode, p->t_arg); + else + comret = command("TYPE %s", p->t_mode); + if (comret == COMPLETE) { + (void)strlcpy(typename, p->t_name, sizeof(typename)); + curtype = type = p->t_type; + } +} + +/* + * Internal form of settype; changes current type in use with server + * without changing our notion of the type for data transfers. + * Used to change to and from ascii for listings. + */ +void +changetype(int newtype, int show) +{ + struct types *p; + int comret, oldverbose = verbose; + + if (newtype == 0) + newtype = TYPE_I; + if (newtype == curtype) + return; + if (debug == 0 && show == 0) + verbose = 0; + for (p = types; p->t_name; p++) + if (newtype == p->t_type) + break; + if (p->t_name == 0) { + warnx("internal error: unknown type %d.", newtype); + return; + } + if (newtype == TYPE_L && bytename[0] != '\0') + comret = command("TYPE %s %s", p->t_mode, bytename); + else + comret = command("TYPE %s", p->t_mode); + if (comret == COMPLETE) + curtype = newtype; + verbose = oldverbose; +} + +char *stype[] = { + "type", + "", + 0 +}; + +/* + * Set binary transfer type. + */ +/*VARARGS*/ +void +setbinary(int argc, char *argv[]) +{ + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + stype[1] = "binary"; + settype(2, stype); +} + +/* + * Set ascii transfer type. + */ +/*VARARGS*/ +void +setascii(int argc, char *argv[]) +{ + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + stype[1] = "ascii"; + settype(2, stype); +} + +/* + * Set tenex transfer type. + */ +/*VARARGS*/ +void +settenex(int argc, char *argv[]) +{ + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + stype[1] = "tenex"; + settype(2, stype); +} + +/* + * Set file transfer mode. + */ +/*ARGSUSED*/ +void +setftmode(int argc, char *argv[]) +{ + + if (argc != 2) { + fprintf(ttyout, "usage: %s mode-name\n", argv[0]); + code = -1; + return; + } + fprintf(ttyout, "We only support %s mode, sorry.\n", modename); + code = -1; +} + +/* + * Set file transfer format. + */ +/*ARGSUSED*/ +void +setform(int argc, char *argv[]) +{ + + if (argc != 2) { + fprintf(ttyout, "usage: %s format\n", argv[0]); + code = -1; + return; + } + fprintf(ttyout, "We only support %s format, sorry.\n", formname); + code = -1; +} + +/* + * Set file transfer structure. + */ +/*ARGSUSED*/ +void +setstruct(int argc, char *argv[]) +{ + + if (argc != 2) { + fprintf(ttyout, "usage: %s struct-mode\n", argv[0]); + code = -1; + return; + } + fprintf(ttyout, "We only support %s structure, sorry.\n", structname); + code = -1; +} + +/* + * Send a single file. + */ +void +put(int argc, char *argv[]) +{ + char buf[MAXPATHLEN]; + char *cmd; + int loc = 0; + char *locfile; + const char *remfile; + + if (argc == 2) { + argc++; + argv[2] = argv[1]; + loc++; + } + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { + usage: + fprintf(ttyout, "usage: %s local-file [remote-file]\n", + argv[0]); + code = -1; + return; + } + if ((locfile = globulize(argv[1])) == NULL) { + code = -1; + return; + } + remfile = argv[2]; + if (loc) /* If argv[2] is a copy of the old argv[1], update it */ + remfile = locfile; + cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR"); + remfile = doprocess(buf, sizeof(buf), remfile, + 0, loc && ntflag, loc && mapflag); + sendrequest(cmd, locfile, remfile, + locfile != argv[1] || remfile != argv[2]); + free(locfile); +} + +static const char * +doprocess(char *dst, size_t dlen, const char *src, + int casef, int transf, int mapf) +{ + if (casef) + src = docase(dst, dlen, src); + if (transf) + src = dotrans(dst, dlen, src); + if (mapf) + src = domap(dst, dlen, src); + return src; +} + +/* + * Send multiple files. + */ +void +mput(int argc, char *argv[]) +{ + int i; + sigfunc oldintr; + int ointer; + const char *tp; + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) { + fprintf(ttyout, "usage: %s local-files\n", argv[0]); + code = -1; + return; + } + mname = argv[0]; + mflag = 1; + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(); + if (proxy) { + char *cp; + + while ((cp = remglob(argv, 0, NULL)) != NULL) { + if (*cp == '\0' || !connected) { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + char buf[MAXPATHLEN]; + tp = doprocess(buf, sizeof(buf), cp, + mcase, ntflag, mapflag); + sendrequest((sunique) ? "STOU" : "STOR", + cp, tp, cp != tp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", "mput")) { + mflag++; + } + interactive = ointer; + } + } + } + goto cleanupmput; + } + for (i = 1; i < argc && connected; i++) { + char **cpp; + glob_t gl; + int flags; + + if (!doglob) { + if (mflag && confirm(argv[0], argv[i])) { + char buf[MAXPATHLEN]; + tp = doprocess(buf, sizeof(buf), argv[i], + 0, ntflag, mapflag); + sendrequest((sunique) ? "STOU" : "STOR", + argv[i], tp, tp != argv[i] || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", "mput")) { + mflag++; + } + interactive = ointer; + } + } + continue; + } + + memset(&gl, 0, sizeof(gl)); + flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; + if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { + warnx("%s: not found", argv[i]); + globfree(&gl); + continue; + } + for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected; + cpp++) { + if (mflag && confirm(argv[0], *cpp)) { + char buf[MAXPATHLEN]; + tp = *cpp; + tp = doprocess(buf, sizeof(buf), *cpp, + 0, ntflag, mapflag); + sendrequest((sunique) ? "STOU" : "STOR", + *cpp, tp, *cpp != tp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", "mput")) { + mflag++; + } + interactive = ointer; + } + } + } + globfree(&gl); + } + cleanupmput: + (void)xsignal(SIGINT, oldintr); + mflag = 0; +} + +void +reget(int argc, char *argv[]) +{ + + (void)getit(argc, argv, 1, "r+"); +} + +void +get(int argc, char *argv[]) +{ + + (void)getit(argc, argv, 0, restart_point ? "r+" : "w" ); +} + +/* + * Receive one file. + * If restartit is 1, restart the xfer always. + * If restartit is -1, restart the xfer only if the remote file is newer. + */ +int +getit(int argc, char *argv[], int restartit, const char *mode) +{ + int loc, rval; + char *remfile, *olocfile; + const char *locfile; + char buf[MAXPATHLEN]; + + loc = rval = 0; + if (argc == 2) { + argc++; + argv[2] = argv[1]; + loc++; + } + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { + usage: + fprintf(ttyout, "usage: %s remote-file [local-file]\n", + argv[0]); + code = -1; + return (0); + } + remfile = argv[1]; + if ((olocfile = globulize(argv[2])) == NULL) { + code = -1; + return (0); + } + locfile = doprocess(buf, sizeof(buf), olocfile, + loc && mcase, loc && ntflag, loc && mapflag); + if (restartit) { + struct stat stbuf; + int ret; + + if (! features[FEAT_REST_STREAM]) { + fprintf(ttyout, + "Restart is not supported by the remote server.\n"); + return (0); + } + ret = stat(locfile, &stbuf); + if (restartit == 1) { + if (ret < 0) { + warn("local: %s", locfile); + goto freegetit; + } + restart_point = stbuf.st_size; + } else { + if (ret == 0) { + time_t mtime; + + mtime = remotemodtime(argv[1], 0); + if (mtime == -1) + goto freegetit; + if (stbuf.st_mtime >= mtime) { + rval = 1; + goto freegetit; + } + } + } + } + + recvrequest("RETR", locfile, remfile, mode, + remfile != argv[1] || locfile != argv[2], loc); + restart_point = 0; + freegetit: + (void)free(olocfile); + return (rval); +} + +/* ARGSUSED */ +void +mintr(int signo) +{ + + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + siglongjmp(jabort, 1); +} + +void +mabort(void) +{ + int ointer, oconf; + + if (mflag && fromatty) { + ointer = interactive; + oconf = confirmrest; + interactive = 1; + confirmrest = 0; + if (confirm("Continue with", mname)) { + interactive = ointer; + confirmrest = oconf; + return; + } + interactive = ointer; + confirmrest = oconf; + } + mflag = 0; +} + +/* + * Get multiple files. + */ +void +mget(int argc, char *argv[]) +{ + sigfunc oldintr; + int ointer; + char *cp; + const char *tp; + int restartit; + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "remote-files"))) { + fprintf(ttyout, "usage: %s remote-files\n", argv[0]); + code = -1; + return; + } + mname = argv[0]; + mflag = 1; + restart_point = 0; + restartit = 0; + if (strcmp(argv[0], "mreget") == 0) { + if (! features[FEAT_REST_STREAM]) { + fprintf(ttyout, + "Restart is not supported by the remote server.\n"); + return; + } + restartit = 1; + } + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(); + while ((cp = remglob(argv, proxy, NULL)) != NULL) { + char buf[MAXPATHLEN]; + if (*cp == '\0' || !connected) { + mflag = 0; + continue; + } + if (! mflag) + continue; + if (! fileindir(cp, localcwd)) { + fprintf(ttyout, "Skipping non-relative filename `%s'\n", + cp); + continue; + } + if (!confirm(argv[0], cp)) + continue; + tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag); + if (restartit) { + struct stat stbuf; + + if (stat(tp, &stbuf) == 0) + restart_point = stbuf.st_size; + else + warn("stat %s", tp); + } + recvrequest("RETR", tp, cp, restart_point ? "r+" : "w", + tp != cp || !interactive, 1); + restart_point = 0; + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", "mget")) + mflag++; + interactive = ointer; + } + } + (void)xsignal(SIGINT, oldintr); + mflag = 0; +} + +/* + * Read list of filenames from a local file and get those + */ +void +fget(int argc, char *argv[]) +{ + char *buf, *mode; + FILE *fp; + + if (argc != 2) { + fprintf(ttyout, "usage: %s localfile\n", argv[0]); + code = -1; + return; + } + + fp = fopen(argv[1], "r"); + if (fp == NULL) { + fprintf(ttyout, "Cannot open source file %s\n", argv[1]); + code = -1; + return; + } + + argv[0] = "get"; + mode = restart_point ? "r+" : "w"; + + for (; + (buf = fparseln(fp, NULL, NULL, "\0\0\0", 0)) != NULL; + free(buf)) { + if (buf[0] == '\0') + continue; + argv[1] = buf; + (void)getit(argc, argv, 0, mode); + } + fclose(fp); +} + +char * +onoff(int bool) +{ + + return (bool ? "on" : "off"); +} + +/* + * Show status. + */ +/*ARGSUSED*/ +void +status(int argc, char *argv[]) +{ + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } +#ifndef NO_STATUS + if (connected) + fprintf(ttyout, "Connected %sto %s.\n", + connected == -1 ? "and logged in" : "", hostname); + else + fputs("Not connected.\n", ttyout); + if (!proxy) { + pswitch(1); + if (connected) { + fprintf(ttyout, "Connected for proxy commands to %s.\n", + hostname); + } + else { + fputs("No proxy connection.\n", ttyout); + } + pswitch(0); + } + fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), + *gateserver ? gateserver : "(none)", gateport); + fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", + onoff(passivemode), onoff(activefallback)); + fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", + modename, typename, formname, structname); + fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", + onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob)); + fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", + onoff(sunique), onoff(runique)); + fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); + fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), + onoff(crflag)); + if (ntflag) { + fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); + } + else { + fputs("Ntrans: off.\n", ttyout); + } + if (mapflag) { + fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); + } + else { + fputs("Nmap: off.\n", ttyout); + } + fprintf(ttyout, + "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", + onoff(hash), mark, onoff(progress)); + fprintf(ttyout, + "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_get), rate_get, rate_get_incr); + fprintf(ttyout, + "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_put), rate_put, rate_put_incr); + fprintf(ttyout, + "Socket buffer sizes: send %d, receive %d.\n", + sndbuf_size, rcvbuf_size); + fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport)); + fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), + epsv4bad ? " (disabled for this connection)" : ""); + fprintf(ttyout, "Command line editing: %s.\n", +#ifdef NO_EDITCOMPLETE + "support not compiled in" +#else /* !def NO_EDITCOMPLETE */ + onoff(editing) +#endif /* !def NO_EDITCOMPLETE */ + ); + if (macnum > 0) { + int i; + + fputs("Macros:\n", ttyout); + for (i=0; i<macnum; i++) { + fprintf(ttyout, "\t%s\n", macros[i].mac_name); + } + } +#endif /* !def NO_STATUS */ + fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION); + code = 0; +} + +/* + * Toggle a variable + */ +int +togglevar(int argc, char *argv[], int *var, const char *mesg) +{ + if (argc == 1) { + *var = !*var; + } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { + *var = 1; + } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { + *var = 0; + } else { + fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]); + return (-1); + } + if (mesg) + fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); + return (*var); +} + +/* + * Set beep on cmd completed mode. + */ +/*VARARGS*/ +void +setbell(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &bell, "Bell mode"); +} + +/* + * Set command line editing + */ +/*VARARGS*/ +void +setedit(int argc, char *argv[]) +{ + +#ifdef NO_EDITCOMPLETE + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + if (verbose) + fputs("Editing support not compiled in; ignoring command.\n", + ttyout); +#else /* !def NO_EDITCOMPLETE */ + code = togglevar(argc, argv, &editing, "Editing mode"); + controlediting(); +#endif /* !def NO_EDITCOMPLETE */ +} + +/* + * Turn on packet tracing. + */ +/*VARARGS*/ +void +settrace(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &trace, "Packet tracing"); +} + +/* + * Toggle hash mark printing during transfers, or set hash mark bytecount. + */ +/*VARARGS*/ +void +sethash(int argc, char *argv[]) +{ + if (argc == 1) + hash = !hash; + else if (argc != 2) { + fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n", + argv[0]); + code = -1; + return; + } else if (strcasecmp(argv[1], "on") == 0) + hash = 1; + else if (strcasecmp(argv[1], "off") == 0) + hash = 0; + else { + int nmark; + + nmark = strsuftoi(argv[1]); + if (nmark < 1) { + fprintf(ttyout, "mark: bad bytecount value `%s'.\n", + argv[1]); + code = -1; + return; + } + mark = nmark; + hash = 1; + } + fprintf(ttyout, "Hash mark printing %s", onoff(hash)); + if (hash) + fprintf(ttyout, " (%d bytes/hash mark)", mark); + fputs(".\n", ttyout); + if (hash) + progress = 0; + code = hash; +} + +/* + * Turn on printing of server echo's. + */ +/*VARARGS*/ +void +setverbose(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &verbose, "Verbose mode"); +} + +/* + * Toggle PORT/LPRT cmd use before each data connection. + */ +/*VARARGS*/ +void +setport(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); +} + +/* + * Toggle transfer progress bar. + */ +/*VARARGS*/ +void +setprogress(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &progress, "Progress bar"); + if (progress) + hash = 0; +} + +/* + * Turn on interactive prompting during mget, mput, and mdelete. + */ +/*VARARGS*/ +void +setprompt(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &interactive, "Interactive mode"); +} + +/* + * Toggle gate-ftp mode, or set gate-ftp server + */ +/*VARARGS*/ +void +setgate(int argc, char *argv[]) +{ + static char gsbuf[MAXHOSTNAMELEN]; + + if (argc == 0 || argc > 3) { + fprintf(ttyout, + "usage: %s [ on | off | gateserver [port] ]\n", argv[0]); + code = -1; + return; + } else if (argc < 2) { + gatemode = !gatemode; + } else { + if (argc == 2 && strcasecmp(argv[1], "on") == 0) + gatemode = 1; + else if (argc == 2 && strcasecmp(argv[1], "off") == 0) + gatemode = 0; + else { + if (argc == 3) + gateport = xstrdup(argv[2]); + (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf)); + gateserver = gsbuf; + gatemode = 1; + } + } + if (gatemode && (gateserver == NULL || *gateserver == '\0')) { + fprintf(ttyout, + "Disabling gate-ftp mode - no gate-ftp server defined.\n"); + gatemode = 0; + } else { + fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", + onoff(gatemode), *gateserver ? gateserver : "(none)", + gateport); + } + code = gatemode; +} + +/* + * Toggle metacharacter interpretation on local file names. + */ +/*VARARGS*/ +void +setglob(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &doglob, "Globbing"); +} + +/* + * Toggle preserving modification times on retrieved files. + */ +/*VARARGS*/ +void +setpreserve(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &preserve, "Preserve modification times"); +} + +/* + * Set debugging mode on/off and/or set level of debugging. + */ +/*VARARGS*/ +void +setdebug(int argc, char *argv[]) +{ + if (argc == 0 || argc > 2) { + fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n", + argv[0]); + code = -1; + return; + } else if (argc == 2) { + if (strcasecmp(argv[1], "on") == 0) + debug = 1; + else if (strcasecmp(argv[1], "off") == 0) + debug = 0; + else { + int val; + + val = strsuftoi(argv[1]); + if (val < 0) { + fprintf(ttyout, "%s: bad debugging value.\n", + argv[1]); + code = -1; + return; + } + debug = val; + } + } else + debug = !debug; + if (debug) + options |= SO_DEBUG; + else + options &= ~SO_DEBUG; + fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug); + code = debug > 0; +} + +/* + * Set current working directory on remote machine. + */ +void +cd(int argc, char *argv[]) +{ + int r; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-directory"))) { + fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); + code = -1; + return; + } + r = command("CWD %s", argv[1]); + if (r == ERROR && code == 500) { + if (verbose) + fputs("CWD command not recognized, trying XCWD.\n", + ttyout); + r = command("XCWD %s", argv[1]); + } + if (r == COMPLETE) { + dirchange = 1; + updateremotecwd(); + } +} + +/* + * Set current working directory on local machine. + */ +void +lcd(int argc, char *argv[]) +{ + char *locdir; + + code = -1; + if (argc == 1) { + argc++; + argv[1] = localhome; + } + if (argc != 2) { + fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]); + return; + } + if ((locdir = globulize(argv[1])) == NULL) + return; + if (chdir(locdir) == -1) + warn("lcd %s", locdir); + else { + updatelocalcwd(); + if (localcwd[0]) { + fprintf(ttyout, "Local directory now: %s\n", localcwd); + code = 0; + } else { + fprintf(ttyout, "Unable to determine local directory\n"); + } + } + (void)free(locdir); +} + +/* + * Delete a single file. + */ +void +delete(int argc, char *argv[]) +{ + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + fprintf(ttyout, "usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + if (command("DELE %s", argv[1]) == COMPLETE) + dirchange = 1; +} + +/* + * Delete multiple files. + */ +void +mdelete(int argc, char *argv[]) +{ + sigfunc oldintr; + int ointer; + char *cp; + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "remote-files"))) { + fprintf(ttyout, "usage: %s [remote-files]\n", argv[0]); + code = -1; + return; + } + mname = argv[0]; + mflag = 1; + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(); + while ((cp = remglob(argv, 0, NULL)) != NULL) { + if (*cp == '\0') { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + if (command("DELE %s", cp) == COMPLETE) + dirchange = 1; + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", "mdelete")) { + mflag++; + } + interactive = ointer; + } + } + } + (void)xsignal(SIGINT, oldintr); + mflag = 0; +} + +/* + * Rename a remote file. + */ +void +renamefile(int argc, char *argv[]) +{ + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { + usage: + fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]); + code = -1; + return; + } + if (command("RNFR %s", argv[1]) == CONTINUE && + command("RNTO %s", argv[2]) == COMPLETE) + dirchange = 1; +} + +/* + * Get a directory listing of remote files. + * Supports being invoked as: + * cmd runs + * --- ---- + * dir, ls LIST + * mlsd MLSD + * nlist NLST + * pdir, pls LIST |$PAGER + * mmlsd MLSD |$PAGER + */ +void +ls(int argc, char *argv[]) +{ + const char *cmd; + char *remdir, *locfile; + int freelocfile, pagecmd, mlsdcmd; + + remdir = NULL; + locfile = "-"; + freelocfile = pagecmd = mlsdcmd = 0; + /* + * the only commands that start with `p' are + * the `pager' versions. + */ + if (argv[0][0] == 'p') + pagecmd = 1; + if (strcmp(argv[0] + pagecmd , "mlsd") == 0) { + if (! features[FEAT_MLST]) { + fprintf(ttyout, + "MLSD is not supported by the remote server.\n"); + return; + } + mlsdcmd = 1; + } + if (argc == 0) + goto usage; + + if (mlsdcmd) + cmd = "MLSD"; + else if (strcmp(argv[0] + pagecmd, "nlist") == 0) + cmd = "NLST"; + else + cmd = "LIST"; + + if (argc > 1) + remdir = argv[1]; + if (argc > 2) + locfile = argv[2]; + if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) { + usage: + if (pagecmd || mlsdcmd) + fprintf(ttyout, + "usage: %s [remote-path]\n", argv[0]); + else + fprintf(ttyout, + "usage: %s [remote-path [local-file]]\n", + argv[0]); + code = -1; + goto freels; + } + + if (pagecmd) { + char *p; + int len; + + p = getoptionvalue("pager"); + if (EMPTYSTRING(p)) + p = DEFAULTPAGER; + len = strlen(p) + 2; + locfile = xmalloc(len); + locfile[0] = '|'; + (void)strlcpy(locfile + 1, p, len - 1); + freelocfile = 1; + } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') { + mname = argv[0]; + if ((locfile = globulize(locfile)) == NULL || + !confirm("output to local-file:", locfile)) { + code = -1; + goto freels; + } + freelocfile = 1; + } + recvrequest(cmd, locfile, remdir, "w", 0, 0); + freels: + if (freelocfile && locfile) + (void)free(locfile); +} + +/* + * Get a directory listing of multiple remote files. + */ +void +mls(int argc, char *argv[]) +{ + sigfunc oldintr; + int ointer, i; + int dolist; + char *mode, *dest, *odest; + + if (argc == 0) + goto usage; + if (argc < 2 && !another(&argc, &argv, "remote-files")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "local-file")) { + usage: + fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]); + code = -1; + return; + } + odest = dest = argv[argc - 1]; + argv[argc - 1] = NULL; + mname = argv[0]; + if (strcmp(dest, "-") && *dest != '|') + if (((dest = globulize(dest)) == NULL) || + !confirm("output to local-file:", dest)) { + code = -1; + return; + } + dolist = strcmp(argv[0], "mls"); + mflag = 1; + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(); + for (i = 1; mflag && i < argc-1 && connected; i++) { + mode = (i == 1) ? "w" : "a"; + recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode, + 0, 0); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", argv[0])) { + mflag++; + } + interactive = ointer; + } + } + (void)xsignal(SIGINT, oldintr); + mflag = 0; + if (dest != odest) /* free up after globulize() */ + free(dest); +} + +/* + * Do a shell escape + */ +/*ARGSUSED*/ +void +shell(int argc, char *argv[]) +{ + pid_t pid; + sigfunc oldintr; + char shellnam[MAXPATHLEN], *shell, *namep; + int wait_status; + + if (argc == 0) { + fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]); + code = -1; + return; + } + oldintr = xsignal(SIGINT, SIG_IGN); + if ((pid = fork()) == 0) { + for (pid = 3; pid < 20; pid++) + (void)close(pid); + (void)xsignal(SIGINT, SIG_DFL); + shell = getenv("SHELL"); + if (shell == NULL) + shell = _PATH_BSHELL; + namep = strrchr(shell, '/'); + if (namep == NULL) + namep = shell; + else + namep++; + (void)strlcpy(shellnam, namep, sizeof(shellnam)); + if (debug) { + fputs(shell, ttyout); + putc('\n', ttyout); + } + if (argc > 1) { + execl(shell, shellnam, "-c", altarg, (char *)0); + } + else { + execl(shell, shellnam, (char *)0); + } + warn("%s", shell); + code = -1; + exit(1); + } + if (pid > 0) + while (wait(&wait_status) != pid) + ; + (void)xsignal(SIGINT, oldintr); + if (pid == -1) { + warn("Try again later"); + code = -1; + } else + code = 0; +} + +/* + * Send new user information (re-login) + */ +void +user(int argc, char *argv[]) +{ + char acct[80]; + int n, aflag = 0; + + if (argc == 0) + goto usage; + if (argc < 2) + (void)another(&argc, &argv, "username"); + if (argc < 2 || argc > 4) { + usage: + fprintf(ttyout, "usage: %s username [password [account]]\n", + argv[0]); + code = -1; + return; + } + n = command("USER %s", argv[1]); + if (n == CONTINUE) { + if (argc < 3) { + argv[2] = getpass("Password: "); + argc++; + } + n = command("PASS %s", argv[2]); + } + if (n == CONTINUE) { + if (argc < 4) { + (void)fputs("Account: ", ttyout); + (void)fflush(ttyout); + if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) { + fprintf(ttyout, + "\nEOF received; login aborted.\n"); + clearerr(stdin); + code = -1; + return; + } + acct[strlen(acct) - 1] = '\0'; + argv[3] = acct; argc++; + } + n = command("ACCT %s", argv[3]); + aflag++; + } + if (n != COMPLETE) { + fputs("Login failed.\n", ttyout); + return; + } + if (!aflag && argc == 4) { + (void)command("ACCT %s", argv[3]); + } + connected = -1; + getremoteinfo(); +} + +/* + * Print working directory on remote machine. + */ +/*VARARGS*/ +void +pwd(int argc, char *argv[]) +{ + + code = -1; + if (argc != 1) { + fprintf(ttyout, "usage: %s\n", argv[0]); + return; + } + if (! remotecwd[0]) + updateremotecwd(); + if (! remotecwd[0]) + fprintf(ttyout, "Unable to determine remote directory\n"); + else { + fprintf(ttyout, "Remote directory: %s\n", remotecwd); + code = 0; + } +} + +/* + * Print working directory on local machine. + */ +void +lpwd(int argc, char *argv[]) +{ + + code = -1; + if (argc != 1) { + fprintf(ttyout, "usage: %s\n", argv[0]); + return; + } + if (! localcwd[0]) + updatelocalcwd(); + if (! localcwd[0]) + fprintf(ttyout, "Unable to determine local directory\n"); + else { + fprintf(ttyout, "Local directory: %s\n", localcwd); + code = 0; + } +} + +/* + * Make a directory. + */ +void +makedir(int argc, char *argv[]) +{ + int r; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "directory-name"))) { + fprintf(ttyout, "usage: %s directory-name\n", argv[0]); + code = -1; + return; + } + r = command("MKD %s", argv[1]); + if (r == ERROR && code == 500) { + if (verbose) + fputs("MKD command not recognized, trying XMKD.\n", + ttyout); + r = command("XMKD %s", argv[1]); + } + if (r == COMPLETE) + dirchange = 1; +} + +/* + * Remove a directory. + */ +void +removedir(int argc, char *argv[]) +{ + int r; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "directory-name"))) { + fprintf(ttyout, "usage: %s directory-name\n", argv[0]); + code = -1; + return; + } + r = command("RMD %s", argv[1]); + if (r == ERROR && code == 500) { + if (verbose) + fputs("RMD command not recognized, trying XRMD.\n", + ttyout); + r = command("XRMD %s", argv[1]); + } + if (r == COMPLETE) + dirchange = 1; +} + +/* + * Send a line, verbatim, to the remote machine. + */ +void +quote(int argc, char *argv[]) +{ + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "command line to send"))) { + fprintf(ttyout, "usage: %s line-to-send\n", argv[0]); + code = -1; + return; + } + quote1("", argc, argv); +} + +/* + * Send a SITE command to the remote machine. The line + * is sent verbatim to the remote machine, except that the + * word "SITE" is added at the front. + */ +void +site(int argc, char *argv[]) +{ + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){ + fprintf(ttyout, "usage: %s line-to-send\n", argv[0]); + code = -1; + return; + } + quote1("SITE ", argc, argv); +} + +/* + * Turn argv[1..argc) into a space-separated string, then prepend initial text. + * Send the result as a one-line command and get response. + */ +void +quote1(const char *initial, int argc, char *argv[]) +{ + int i; + char buf[BUFSIZ]; /* must be >= sizeof(line) */ + + (void)strlcpy(buf, initial, sizeof(buf)); + for (i = 1; i < argc; i++) { + (void)strlcat(buf, argv[i], sizeof(buf)); + if (i < (argc - 1)) + (void)strlcat(buf, " ", sizeof(buf)); + } + if (command("%s", buf) == PRELIM) { + while (getreply(0) == PRELIM) + continue; + } + dirchange = 1; +} + +void +do_chmod(int argc, char *argv[]) +{ + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { + usage: + fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]); + code = -1; + return; + } + (void)command("SITE CHMOD %s %s", argv[1], argv[2]); +} + +#define COMMAND_1ARG(argc, argv, cmd) \ + if (argc == 1) \ + command(cmd); \ + else \ + command(cmd " %s", argv[1]) + +void +do_umask(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + fprintf(ttyout, "usage: %s [umask]\n", argv[0]); + code = -1; + return; + } + verbose = 1; + COMMAND_1ARG(argc, argv, "SITE UMASK"); + verbose = oldverbose; +} + +void +idlecmd(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc < 1 || argc > 2) { + fprintf(ttyout, "usage: %s [seconds]\n", argv[0]); + code = -1; + return; + } + verbose = 1; + COMMAND_1ARG(argc, argv, "SITE IDLE"); + verbose = oldverbose; +} + +/* + * Ask the other side for help. + */ +void +rmthelp(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + verbose = 1; + COMMAND_1ARG(argc, argv, "HELP"); + verbose = oldverbose; +} + +/* + * Terminate session and exit. + * May be called with 0, NULL. + */ +/*VARARGS*/ +void +quit(int argc, char *argv[]) +{ + + /* this may be called with argc == 0, argv == NULL */ + if (argc == 0 && argv != NULL) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + if (connected) + disconnect(0, NULL); + pswitch(1); + if (connected) + disconnect(0, NULL); + exit(0); +} + +/* + * Terminate session, but don't exit. + * May be called with 0, NULL. + */ +void +disconnect(int argc, char *argv[]) +{ + + /* this may be called with argc == 0, argv == NULL */ + if (argc == 0 && argv != NULL) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + if (!connected) + return; + (void)command("QUIT"); + cleanuppeer(); +} + +void +account(int argc, char *argv[]) +{ + char *ap; + + if (argc == 0 || argc > 2) { + fprintf(ttyout, "usage: %s [password]\n", argv[0]); + code = -1; + return; + } + else if (argc == 2) + ap = argv[1]; + else + ap = getpass("Account:"); + (void)command("ACCT %s", ap); +} + +sigjmp_buf abortprox; + +void +proxabort(int notused) +{ + + sigint_raised = 1; + alarmtimer(0); + if (!proxy) { + pswitch(1); + } + if (connected) { + proxflag = 1; + } + else { + proxflag = 0; + } + pswitch(0); + siglongjmp(abortprox, 1); +} + +void +doproxy(int argc, char *argv[]) +{ + struct cmd *c; + int cmdpos; + sigfunc oldintr; + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { + fprintf(ttyout, "usage: %s command\n", argv[0]); + code = -1; + return; + } + c = getcmd(argv[1]); + if (c == (struct cmd *) -1) { + fputs("?Ambiguous command.\n", ttyout); + code = -1; + return; + } + if (c == 0) { + fputs("?Invalid command.\n", ttyout); + code = -1; + return; + } + if (!c->c_proxy) { + fputs("?Invalid proxy command.\n", ttyout); + code = -1; + return; + } + if (sigsetjmp(abortprox, 1)) { + code = -1; + return; + } + oldintr = xsignal(SIGINT, proxabort); + pswitch(1); + if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + pswitch(0); + (void)xsignal(SIGINT, oldintr); + code = -1; + return; + } + cmdpos = strcspn(line, " \t"); + if (cmdpos > 0) /* remove leading "proxy " from input buffer */ + memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); + argv[1] = c->c_name; + (*c->c_handler)(argc-1, argv+1); + if (connected) { + proxflag = 1; + } + else { + proxflag = 0; + } + pswitch(0); + (void)xsignal(SIGINT, oldintr); +} + +void +setcase(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &mcase, "Case mapping"); +} + +/* + * convert the given name to lower case if it's all upper case, into + * a static buffer which is returned to the caller + */ +static const char * +docase(char *dst, size_t dlen, const char *src) +{ + size_t i; + int dochange = 1; + + for (i = 0; src[i] != '\0' && i < dlen - 1; i++) { + dst[i] = src[i]; + if (islower((unsigned char)dst[i])) + dochange = 0; + } + dst[i] = '\0'; + + if (dochange) { + for (i = 0; dst[i] != '\0'; i++) + if (isupper((unsigned char)dst[i])) + dst[i] = tolower((unsigned char)dst[i]); + } + return dst; +} + +void +setcr(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); +} + +void +setntrans(int argc, char *argv[]) +{ + + if (argc == 0 || argc > 3) { + fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]); + code = -1; + return; + } + if (argc == 1) { + ntflag = 0; + fputs("Ntrans off.\n", ttyout); + code = ntflag; + return; + } + ntflag++; + code = ntflag; + (void)strlcpy(ntin, argv[1], sizeof(ntin)); + if (argc == 2) { + ntout[0] = '\0'; + return; + } + (void)strlcpy(ntout, argv[2], sizeof(ntout)); +} + +static const char * +dotrans(char *dst, size_t dlen, const char *src) +{ + const char *cp1; + char *cp2 = dst; + size_t i, ostop; + + for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) + continue; + for (cp1 = src; *cp1; cp1++) { + int found = 0; + for (i = 0; *(ntin + i) && i < 16; i++) { + if (*cp1 == *(ntin + i)) { + found++; + if (i < ostop) { + *cp2++ = *(ntout + i); + if (cp2 - dst >= dlen - 1) + goto out; + } + break; + } + } + if (!found) { + *cp2++ = *cp1; + } + } +out: + *cp2 = '\0'; + return dst; +} + +void +setnmap(int argc, char *argv[]) +{ + char *cp; + + if (argc == 1) { + mapflag = 0; + fputs("Nmap off.\n", ttyout); + code = mapflag; + return; + } + if (argc == 0 || + (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { + fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]); + code = -1; + return; + } + mapflag = 1; + code = 1; + cp = strchr(altarg, ' '); + if (proxy) { + while(*++cp == ' ') + continue; + altarg = cp; + cp = strchr(altarg, ' '); + } + *cp = '\0'; + (void)strlcpy(mapin, altarg, MAXPATHLEN); + while (*++cp == ' ') + continue; + (void)strlcpy(mapout, cp, MAXPATHLEN); +} + +static const char * +domap(char *dst, size_t dlen, const char *src) +{ + const char *cp1 = src; + char *cp2 = mapin; + const char *tp[9], *te[9]; + int i, toks[9], toknum = 0, match = 1; + + for (i=0; i < 9; ++i) { + toks[i] = 0; + } + while (match && *cp1 && *cp2) { + switch (*cp2) { + case '\\': + if (*++cp2 != *cp1) { + match = 0; + } + break; + case '$': + if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { + if (*cp1 != *(++cp2+1)) { + toks[toknum = *cp2 - '1']++; + tp[toknum] = cp1; + while (*++cp1 && *(cp2+1) + != *cp1); + te[toknum] = cp1; + } + cp2++; + break; + } + /* FALLTHROUGH */ + default: + if (*cp2 != *cp1) { + match = 0; + } + break; + } + if (match && *cp1) { + cp1++; + } + if (match && *cp2) { + cp2++; + } + } + if (!match && *cp1) /* last token mismatch */ + { + toks[toknum] = 0; + } + cp2 = dst; + *cp2 = '\0'; + cp1 = mapout; + while (*cp1) { + match = 0; + switch (*cp1) { + case '\\': + if (*(cp1 + 1)) { + *cp2++ = *++cp1; + } + break; + case '[': +LOOP: + if (*++cp1 == '$' && + isdigit((unsigned char)*(cp1+1))) { + if (*++cp1 == '0') { + const char *cp3 = src; + + while (*cp3) { + *cp2++ = *cp3++; + } + match = 1; + } + else if (toks[toknum = *cp1 - '1']) { + const char *cp3 = tp[toknum]; + + while (cp3 != te[toknum]) { + *cp2++ = *cp3++; + } + match = 1; + } + } + else { + while (*cp1 && *cp1 != ',' && + *cp1 != ']') { + if (*cp1 == '\\') { + cp1++; + } + else if (*cp1 == '$' && + isdigit((unsigned char)*(cp1+1))) { + if (*++cp1 == '0') { + const char *cp3 = src; + + while (*cp3) { + *cp2++ = *cp3++; + } + } + else if (toks[toknum = + *cp1 - '1']) { + const char *cp3=tp[toknum]; + + while (cp3 != + te[toknum]) { + *cp2++ = *cp3++; + } + } + } + else if (*cp1) { + *cp2++ = *cp1++; + } + } + if (!*cp1) { + fputs( + "nmap: unbalanced brackets.\n", + ttyout); + return (src); + } + match = 1; + cp1--; + } + if (match) { + while (*++cp1 && *cp1 != ']') { + if (*cp1 == '\\' && *(cp1 + 1)) { + cp1++; + } + } + if (!*cp1) { + fputs( + "nmap: unbalanced brackets.\n", + ttyout); + return (src); + } + break; + } + switch (*++cp1) { + case ',': + goto LOOP; + case ']': + break; + default: + cp1--; + goto LOOP; + } + break; + case '$': + if (isdigit((unsigned char)*(cp1 + 1))) { + if (*++cp1 == '0') { + const char *cp3 = src; + + while (*cp3) { + *cp2++ = *cp3++; + } + } + else if (toks[toknum = *cp1 - '1']) { + const char *cp3 = tp[toknum]; + + while (cp3 != te[toknum]) { + *cp2++ = *cp3++; + } + } + break; + } + /* intentional drop through */ + default: + *cp2++ = *cp1; + break; + } + cp1++; + } + *cp2 = '\0'; + return *dst ? dst : src; +} + +void +setpassive(int argc, char *argv[]) +{ + + if (argc == 1) { + passivemode = !passivemode; + activefallback = passivemode; + } else if (argc != 2) { + passiveusage: + fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]); + code = -1; + return; + } else if (strcasecmp(argv[1], "on") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcasecmp(argv[1], "off") == 0) { + passivemode = 0; + activefallback = 0; + } else if (strcasecmp(argv[1], "auto") == 0) { + passivemode = 1; + activefallback = 1; + } else + goto passiveusage; + fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", + onoff(passivemode), onoff(activefallback)); + code = passivemode; +} + +void +setepsv4(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &epsv4, + verbose ? "EPSV/EPRT on IPv4" : NULL); + epsv4bad = 0; +} + +void +setsunique(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &sunique, "Store unique"); +} + +void +setrunique(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &runique, "Receive unique"); +} + +int +parserate(int argc, char *argv[], int cmdlineopt) +{ + int dir, max, incr, showonly; + sigfunc oldusr1, oldusr2; + + if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { + usage: + if (cmdlineopt) + fprintf(ttyout, + "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", + argv[0]); + else + fprintf(ttyout, + "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", + argv[0]); + return -1; + } + dir = max = incr = showonly = 0; +#define RATE_GET 1 +#define RATE_PUT 2 +#define RATE_ALL (RATE_GET | RATE_PUT) + + if (strcasecmp(argv[1], "all") == 0) + dir = RATE_ALL; + else if (strcasecmp(argv[1], "get") == 0) + dir = RATE_GET; + else if (strcasecmp(argv[1], "put") == 0) + dir = RATE_PUT; + else + goto usage; + + if (argc >= 3) { + if ((max = strsuftoi(argv[2])) < 0) + goto usage; + } else + showonly = 1; + + if (argc == 4) { + if ((incr = strsuftoi(argv[3])) <= 0) + goto usage; + } else + incr = DEFAULTINCR; + + oldusr1 = xsignal(SIGUSR1, SIG_IGN); + oldusr2 = xsignal(SIGUSR2, SIG_IGN); + if (dir & RATE_GET) { + if (!showonly) { + rate_get = max; + rate_get_incr = incr; + } + if (!cmdlineopt || verbose) + fprintf(ttyout, + "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_get), rate_get, rate_get_incr); + } + if (dir & RATE_PUT) { + if (!showonly) { + rate_put = max; + rate_put_incr = incr; + } + if (!cmdlineopt || verbose) + fprintf(ttyout, + "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_put), rate_put, rate_put_incr); + } + (void)xsignal(SIGUSR1, oldusr1); + (void)xsignal(SIGUSR2, oldusr2); + return 0; +} + +void +setrate(int argc, char *argv[]) +{ + + code = parserate(argc, argv, 0); +} + +/* change directory to parent directory */ +void +cdup(int argc, char *argv[]) +{ + int r; + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + r = command("CDUP"); + if (r == ERROR && code == 500) { + if (verbose) + fputs("CDUP command not recognized, trying XCUP.\n", + ttyout); + r = command("XCUP"); + } + if (r == COMPLETE) { + dirchange = 1; + updateremotecwd(); + } +} + +/* + * Restart transfer at specific point + */ +void +restart(int argc, char *argv[]) +{ + + if (argc == 0 || argc > 2) { + fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_REST_STREAM]) { + fprintf(ttyout, + "Restart is not supported by the remote server.\n"); + return; + } + if (argc == 2) { + off_t rp; + char *ep; + + rp = STRTOLL(argv[1], &ep, 10); + if (rp < 0 || *ep != '\0') + fprintf(ttyout, "restart: Invalid offset `%s'\n", + argv[1]); + else + restart_point = rp; + } + if (restart_point == 0) + fputs("No restart point defined.\n", ttyout); + else + fprintf(ttyout, + "Restarting at " LLF " for next get, put or append\n", + (LLT)restart_point); +} + +/* + * Show remote system type + */ +void +syst(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + (void)command("SYST"); + verbose = oldverbose; +} + +void +macdef(int argc, char *argv[]) +{ + char *tmp; + int c; + + if (argc == 0) + goto usage; + if (macnum == 16) { + fputs("Limit of 16 macros have already been defined.\n", + ttyout); + code = -1; + return; + } + if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { + usage: + fprintf(ttyout, "usage: %s macro_name\n", argv[0]); + code = -1; + return; + } + if (interactive) + fputs( + "Enter macro line by line, terminating it with a null line.\n", + ttyout); + (void)strlcpy(macros[macnum].mac_name, argv[1], + sizeof(macros[macnum].mac_name)); + if (macnum == 0) + macros[macnum].mac_start = macbuf; + else + macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; + tmp = macros[macnum].mac_start; + while (tmp != macbuf+4096) { + if ((c = getchar()) == EOF) { + fputs("macdef: end of file encountered.\n", ttyout); + code = -1; + return; + } + if ((*tmp = c) == '\n') { + if (tmp == macros[macnum].mac_start) { + macros[macnum++].mac_end = tmp; + code = 0; + return; + } + if (*(tmp-1) == '\0') { + macros[macnum++].mac_end = tmp - 1; + code = 0; + return; + } + *tmp = '\0'; + } + tmp++; + } + while (1) { + while ((c = getchar()) != '\n' && c != EOF) + /* LOOP */; + if (c == EOF || getchar() == '\n') { + fputs("Macro not defined - 4K buffer exceeded.\n", + ttyout); + code = -1; + return; + } + } +} + +/* + * Get size of file on remote machine + */ +void +sizecmd(int argc, char *argv[]) +{ + off_t size; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + fprintf(ttyout, "usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + size = remotesize(argv[1], 1); + if (size != -1) + fprintf(ttyout, + "%s\t" LLF "\n", argv[1], (LLT)size); + code = (size > 0); +} + +/* + * Get last modification time of file on remote machine + */ +void +modtime(int argc, char *argv[]) +{ + time_t mtime; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + fprintf(ttyout, "usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + mtime = remotemodtime(argv[1], 1); + if (mtime != -1) + fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); + code = (mtime > 0); +} + +/* + * Show status on remote machine + */ +void +rmtstatus(int argc, char *argv[]) +{ + + if (argc == 0) { + fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]); + code = -1; + return; + } + COMMAND_1ARG(argc, argv, "STAT"); +} + +/* + * Get file if modtime is more recent than current file + */ +void +newer(int argc, char *argv[]) +{ + + if (getit(argc, argv, -1, "w")) + fprintf(ttyout, + "Local file \"%s\" is newer than remote file \"%s\".\n", + argv[2], argv[1]); +} + +/* + * Display one local file through $PAGER. + */ +void +lpage(int argc, char *argv[]) +{ + int len; + char *p, *pager, *locfile; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "local-file"))) { + fprintf(ttyout, "usage: %s local-file\n", argv[0]); + code = -1; + return; + } + if ((locfile = globulize(argv[1])) == NULL) { + code = -1; + return; + } + p = getoptionvalue("pager"); + if (EMPTYSTRING(p)) + p = DEFAULTPAGER; + len = strlen(p) + strlen(locfile) + 2; + pager = xmalloc(len); + (void)strlcpy(pager, p, len); + (void)strlcat(pager, " ", len); + (void)strlcat(pager, locfile, len); + system(pager); + code = 0; + (void)free(pager); + (void)free(locfile); +} + +/* + * Display one remote file through $PAGER. + */ +void +page(int argc, char *argv[]) +{ + int ohash, orestart_point, overbose, len; + char *p, *pager; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + fprintf(ttyout, "usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + p = getoptionvalue("pager"); + if (EMPTYSTRING(p)) + p = DEFAULTPAGER; + len = strlen(p) + 2; + pager = xmalloc(len); + pager[0] = '|'; + (void)strlcpy(pager + 1, p, len - 1); + + ohash = hash; + orestart_point = restart_point; + overbose = verbose; + hash = restart_point = verbose = 0; + recvrequest("RETR", pager, argv[1], "r+", 1, 0); + hash = ohash; + restart_point = orestart_point; + verbose = overbose; + (void)free(pager); +} + +/* + * Set the socket send or receive buffer size. + */ +void +setxferbuf(int argc, char *argv[]) +{ + int size, dir; + + if (argc != 2) { + usage: + fprintf(ttyout, "usage: %s size\n", argv[0]); + code = -1; + return; + } + if (strcasecmp(argv[0], "sndbuf") == 0) + dir = RATE_PUT; + else if (strcasecmp(argv[0], "rcvbuf") == 0) + dir = RATE_GET; + else if (strcasecmp(argv[0], "xferbuf") == 0) + dir = RATE_ALL; + else + goto usage; + + if ((size = strsuftoi(argv[1])) == -1) + goto usage; + + if (size == 0) { + fprintf(ttyout, "%s: size must be positive.\n", argv[0]); + goto usage; + } + + if (dir & RATE_PUT) + sndbuf_size = size; + if (dir & RATE_GET) + rcvbuf_size = size; + fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n", + sndbuf_size, rcvbuf_size); + code = 0; +} + +/* + * Set or display options (defaults are provided by various env vars) + */ +void +setoption(int argc, char *argv[]) +{ + struct option *o; + + code = -1; + if (argc == 0 || (argc != 1 && argc != 3)) { + fprintf(ttyout, "usage: %s [option value]\n", argv[0]); + return; + } + +#define OPTIONINDENT ((int) sizeof("http_proxy")) + if (argc == 1) { + for (o = optiontab; o->name != NULL; o++) { + fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, + o->name, o->value ? o->value : ""); + } + } else { + o = getoption(argv[1]); + if (o == NULL) { + fprintf(ttyout, "No such option `%s'.\n", argv[1]); + return; + } + FREEPTR(o->value); + o->value = xstrdup(argv[2]); + if (verbose) + fprintf(ttyout, "Setting `%s' to `%s'.\n", + o->name, o->value); + } + code = 0; +} + +/* + * Unset an option + */ +void +unsetoption(int argc, char *argv[]) +{ + struct option *o; + + code = -1; + if (argc == 0 || argc != 2) { + fprintf(ttyout, "usage: %s option\n", argv[0]); + return; + } + + o = getoption(argv[1]); + if (o == NULL) { + fprintf(ttyout, "No such option `%s'.\n", argv[1]); + return; + } + FREEPTR(o->value); + fprintf(ttyout, "Unsetting `%s'.\n", o->name); + code = 0; +} + +/* + * Display features supported by the remote host. + */ +void +feat(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_FEAT]) { + fprintf(ttyout, + "FEAT is not supported by the remote server.\n"); + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + (void)command("FEAT"); + verbose = oldverbose; +} + +void +mlst(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc < 1 || argc > 2) { + fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_MLST]) { + fprintf(ttyout, + "MLST is not supported by the remote server.\n"); + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + COMMAND_1ARG(argc, argv, "MLST"); + verbose = oldverbose; +} + +void +opts(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc < 2 || argc > 3) { + fprintf(ttyout, "usage: %s command [options]\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_FEAT]) { + fprintf(ttyout, + "OPTS is not supported by the remote server.\n"); + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + if (argc == 2) + command("OPTS %s", argv[1]); + else + command("OPTS %s %s", argv[1], argv[2]); + verbose = oldverbose; +} diff --git a/src/cmdtab.c b/src/cmdtab.c new file mode 100644 index 0000000..04efade --- /dev/null +++ b/src/cmdtab.c @@ -0,0 +1,309 @@ +/* $NetBSD: cmdtab.c,v 1.44 2005/04/11 01:49:31 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2000 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. 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cmdtab.c 8.4 (Berkeley) 10/9/94"; +#else +__RCSID("$NetBSD: cmdtab.c,v 1.44 2005/04/11 01:49:31 lukem Exp $"); +#endif +#endif /* not lint */ + +#include <stdio.h> +#include "ftp_var.h" + +/* + * User FTP -- Command Tables. + */ + +#define HSTR static const char + +#ifndef NO_HELP +HSTR accounthelp[] = "send account command to remote server"; +HSTR appendhelp[] = "append to a file"; +HSTR asciihelp[] = "set ascii transfer type"; +HSTR beephelp[] = "beep when command completed"; +HSTR binaryhelp[] = "set binary transfer type"; +HSTR casehelp[] = "toggle mget upper/lower case id mapping"; +HSTR cdhelp[] = "change remote working directory"; +HSTR cduphelp[] = "change remote working directory to parent directory"; +HSTR chmodhelp[] = "change file permissions of remote file"; +HSTR connecthelp[] = "connect to remote ftp server"; +HSTR crhelp[] = "toggle carriage return stripping on ascii gets"; +HSTR debughelp[] = "toggle/set debugging mode"; +HSTR deletehelp[] = "delete remote file"; +HSTR disconhelp[] = "terminate ftp session"; +HSTR domachelp[] = "execute macro"; +HSTR edithelp[] = "toggle command line editing"; +HSTR epsv4help[] = "toggle use of EPSV/EPRT on IPv4 ftp"; +HSTR feathelp[] = "show FEATures supported by remote system"; +HSTR formhelp[] = "set file transfer format"; +HSTR gatehelp[] = "toggle gate-ftp; specify host[:port] to change proxy"; +HSTR globhelp[] = "toggle metacharacter expansion of local file names"; +HSTR hashhelp[] = "toggle printing `#' marks; specify number to set size"; +HSTR helphelp[] = "print local help information"; +HSTR idlehelp[] = "get (set) idle timer on remote side"; +HSTR lcdhelp[] = "change local working directory"; +HSTR lpagehelp[] = "view a local file through your pager"; +HSTR lpwdhelp[] = "print local working directory"; +HSTR lshelp[] = "list contents of remote path"; +HSTR macdefhelp[] = "define a macro"; +HSTR mdeletehelp[] = "delete multiple files"; +HSTR mgethelp[] = "get multiple files"; +HSTR mregethelp[] = "get multiple files restarting at end of local file"; +HSTR fgethelp[] = "get files using a localfile as a source of names"; +HSTR mkdirhelp[] = "make directory on the remote machine"; +HSTR mlshelp[] = "list contents of multiple remote directories"; +HSTR mlsdhelp[] = "list contents of remote directory in a machine " + "parsable form"; +HSTR mlsthelp[] = "list remote path in a machine parsable form"; +HSTR modehelp[] = "set file transfer mode"; +HSTR modtimehelp[] = "show last modification time of remote file"; +HSTR mputhelp[] = "send multiple files"; +HSTR newerhelp[] = "get file if remote file is newer than local file "; +HSTR nmaphelp[] = "set templates for default file name mapping"; +HSTR ntranshelp[] = "set translation table for default file name mapping"; +HSTR optshelp[] = "show or set options for remote commands"; +HSTR pagehelp[] = "view a remote file through your pager"; +HSTR passivehelp[] = "toggle use of passive transfer mode"; +HSTR plshelp[] = "list contents of remote path through your pager"; +HSTR pmlsdhelp[] = "list contents of remote directory in a machine " + "parsable form through your pager"; +HSTR porthelp[] = "toggle use of PORT/LPRT cmd for each data connection"; +HSTR preservehelp[] ="toggle preservation of modification time of " + "retrieved files"; +HSTR progresshelp[] ="toggle transfer progress meter"; +HSTR prompthelp[] = "force interactive prompting on multiple commands"; +HSTR proxyhelp[] = "issue command on alternate connection"; +HSTR pwdhelp[] = "print working directory on remote machine"; +HSTR quithelp[] = "terminate ftp session and exit"; +HSTR quotehelp[] = "send arbitrary ftp command"; +HSTR ratehelp[] = "set transfer rate limit (in bytes/second)"; +HSTR receivehelp[] = "receive file"; +HSTR regethelp[] = "get file restarting at end of local file"; +HSTR remotehelp[] = "get help from remote server"; +HSTR renamehelp[] = "rename file"; +HSTR resethelp[] = "clear queued command replies"; +HSTR restarthelp[]= "restart file transfer at bytecount"; +HSTR rmdirhelp[] = "remove directory on the remote machine"; +HSTR rmtstatushelp[]="show status of remote machine"; +HSTR runiquehelp[] = "toggle store unique for local files"; +HSTR sendhelp[] = "send one file"; +HSTR sethelp[] = "set or display options"; +HSTR shellhelp[] = "escape to the shell"; +HSTR sitehelp[] = "send site specific command to remote server\n" + "\t\tTry \"rhelp site\" or \"site help\" " + "for more information"; +HSTR sizecmdhelp[] = "show size of remote file"; +HSTR statushelp[] = "show current status"; +HSTR structhelp[] = "set file transfer structure"; +HSTR suniquehelp[] = "toggle store unique on remote machine"; +HSTR systemhelp[] = "show remote system type"; +HSTR tenexhelp[] = "set tenex file transfer type"; +HSTR tracehelp[] = "toggle packet tracing"; +HSTR typehelp[] = "set file transfer type"; +HSTR umaskhelp[] = "get (set) umask on remote side"; +HSTR unsethelp[] = "unset an option"; +HSTR usagehelp[] = "show command usage"; +HSTR userhelp[] = "send new user information"; +HSTR verbosehelp[] = "toggle verbose mode"; +HSTR xferbufhelp[] = "set socket send/receive buffer size"; +#endif + +HSTR empty[] = ""; + +#ifdef NO_HELP +#define H(x) empty +#else +#define H(x) x +#endif + +#ifdef NO_EDITCOMPLETE +#define CMPL(x) +#define CMPL0 +#else /* !NO_EDITCOMPLETE */ +#define CMPL(x) #x, +#define CMPL0 empty, +#endif /* !NO_EDITCOMPLETE */ + +struct cmd cmdtab[] = { + { "!", H(shellhelp), 0, 0, 0, CMPL0 shell }, + { "$", H(domachelp), 1, 0, 0, CMPL0 domacro }, + { "account", H(accounthelp), 0, 1, 1, CMPL0 account}, + { "append", H(appendhelp), 1, 1, 1, CMPL(lr) put }, + { "ascii", H(asciihelp), 0, 1, 1, CMPL0 setascii }, + { "bell", H(beephelp), 0, 0, 0, CMPL0 setbell }, + { "binary", H(binaryhelp), 0, 1, 1, CMPL0 setbinary }, + { "bye", H(quithelp), 0, 0, 0, CMPL0 quit }, + { "case", H(casehelp), 0, 0, 1, CMPL0 setcase }, + { "cd", H(cdhelp), 0, 1, 1, CMPL(r) cd }, + { "cdup", H(cduphelp), 0, 1, 1, CMPL0 cdup }, + { "chmod", H(chmodhelp), 0, 1, 1, CMPL(nr) do_chmod }, + { "close", H(disconhelp), 0, 1, 1, CMPL0 disconnect }, + { "cr", H(crhelp), 0, 0, 0, CMPL0 setcr }, + { "debug", H(debughelp), 0, 0, 0, CMPL0 setdebug }, + { "delete", H(deletehelp), 0, 1, 1, CMPL(r) delete }, + { "dir", H(lshelp), 1, 1, 1, CMPL(rl) ls }, + { "disconnect", H(disconhelp), 0, 1, 1, CMPL0 disconnect }, + { "edit", H(edithelp), 0, 0, 0, CMPL0 setedit }, + { "epsv4", H(epsv4help), 0, 0, 0, CMPL0 setepsv4 }, + { "exit", H(quithelp), 0, 0, 0, CMPL0 quit }, + { "features", H(feathelp), 0, 1, 1, CMPL0 feat }, + { "fget", H(fgethelp), 1, 1, 1, CMPL(l) fget }, + { "form", H(formhelp), 0, 1, 1, CMPL0 setform }, + { "ftp", H(connecthelp), 0, 0, 1, CMPL0 setpeer }, + { "gate", H(gatehelp), 0, 0, 0, CMPL0 setgate }, + { "get", H(receivehelp), 1, 1, 1, CMPL(rl) get }, + { "glob", H(globhelp), 0, 0, 0, CMPL0 setglob }, + { "hash", H(hashhelp), 0, 0, 0, CMPL0 sethash }, + { "help", H(helphelp), 0, 0, 1, CMPL(C) help }, + { "idle", H(idlehelp), 0, 1, 1, CMPL0 idlecmd }, + { "image", H(binaryhelp), 0, 1, 1, CMPL0 setbinary }, + { "lcd", H(lcdhelp), 0, 0, 0, CMPL(l) lcd }, + { "less", H(pagehelp), 1, 1, 1, CMPL(r) page }, + { "lpage", H(lpagehelp), 0, 0, 0, CMPL(l) lpage }, + { "lpwd", H(lpwdhelp), 0, 0, 0, CMPL0 lpwd }, + { "ls", H(lshelp), 1, 1, 1, CMPL(rl) ls }, + { "macdef", H(macdefhelp), 0, 0, 0, CMPL0 macdef }, + { "mdelete", H(mdeletehelp), 1, 1, 1, CMPL(R) mdelete }, + { "mdir", H(mlshelp), 1, 1, 1, CMPL(R) mls }, + { "mget", H(mgethelp), 1, 1, 1, CMPL(R) mget }, + { "mkdir", H(mkdirhelp), 0, 1, 1, CMPL(r) makedir }, + { "mls", H(mlshelp), 1, 1, 1, CMPL(R) mls }, + { "mlsd", H(mlsdhelp), 1, 1, 1, CMPL(r) ls }, + { "mlst", H(mlsthelp), 1, 1, 1, CMPL(r) mlst }, + { "mode", H(modehelp), 0, 1, 1, CMPL0 setftmode }, + { "modtime", H(modtimehelp), 0, 1, 1, CMPL(r) modtime }, + { "more", H(pagehelp), 1, 1, 1, CMPL(r) page }, + { "mput", H(mputhelp), 1, 1, 1, CMPL(L) mput }, + { "mreget", H(mregethelp), 1, 1, 1, CMPL(R) mget }, + { "msend", H(mputhelp), 1, 1, 1, CMPL(L) mput }, + { "newer", H(newerhelp), 1, 1, 1, CMPL(r) newer }, + { "nlist", H(lshelp), 1, 1, 1, CMPL(rl) ls }, + { "nmap", H(nmaphelp), 0, 0, 1, CMPL0 setnmap }, + { "ntrans", H(ntranshelp), 0, 0, 1, CMPL0 setntrans }, + { "open", H(connecthelp), 0, 0, 1, CMPL0 setpeer }, + { "page", H(pagehelp), 1, 1, 1, CMPL(r) page }, + { "passive", H(passivehelp), 0, 0, 0, CMPL0 setpassive }, + { "pdir", H(plshelp), 1, 1, 1, CMPL(r) ls }, + { "pls", H(plshelp), 1, 1, 1, CMPL(r) ls }, + { "pmlsd", H(pmlsdhelp), 1, 1, 1, CMPL(r) ls }, + { "preserve", H(preservehelp),0, 0, 0, CMPL0 setpreserve }, + { "progress", H(progresshelp),0, 0, 0, CMPL0 setprogress }, + { "prompt", H(prompthelp), 0, 0, 0, CMPL0 setprompt }, + { "proxy", H(proxyhelp), 0, 0, 1, CMPL(c) doproxy }, + { "put", H(sendhelp), 1, 1, 1, CMPL(lr) put }, + { "pwd", H(pwdhelp), 0, 1, 1, CMPL0 pwd }, + { "quit", H(quithelp), 0, 0, 0, CMPL0 quit }, + { "quote", H(quotehelp), 1, 1, 1, CMPL0 quote }, + { "rate", H(ratehelp), 0, 0, 0, CMPL0 setrate }, + { "rcvbuf", H(xferbufhelp), 0, 0, 0, CMPL0 setxferbuf }, + { "recv", H(receivehelp), 1, 1, 1, CMPL(rl) get }, + { "reget", H(regethelp), 1, 1, 1, CMPL(rl) reget }, + { "remopts", H(optshelp), 0, 1, 1, CMPL0 opts }, + { "rename", H(renamehelp), 0, 1, 1, CMPL(rr) renamefile }, + { "reset", H(resethelp), 0, 1, 1, CMPL0 reset }, + { "restart", H(restarthelp), 1, 1, 1, CMPL0 restart }, + { "rhelp", H(remotehelp), 0, 1, 1, CMPL0 rmthelp }, + { "rmdir", H(rmdirhelp), 0, 1, 1, CMPL(r) removedir }, + { "rstatus", H(rmtstatushelp),0, 1, 1, CMPL(r) rmtstatus }, + { "runique", H(runiquehelp), 0, 0, 1, CMPL0 setrunique }, + { "send", H(sendhelp), 1, 1, 1, CMPL(lr) put }, + { "sendport", H(porthelp), 0, 0, 0, CMPL0 setport }, + { "set", H(sethelp), 0, 0, 0, CMPL(o) setoption }, + { "site", H(sitehelp), 0, 1, 1, CMPL0 site }, + { "size", H(sizecmdhelp), 1, 1, 1, CMPL(r) sizecmd }, + { "sndbuf", H(xferbufhelp), 0, 0, 0, CMPL0 setxferbuf }, + { "status", H(statushelp), 0, 0, 1, CMPL0 status }, + { "struct", H(structhelp), 0, 1, 1, CMPL0 setstruct }, + { "sunique", H(suniquehelp), 0, 0, 1, CMPL0 setsunique }, + { "system", H(systemhelp), 0, 1, 1, CMPL0 syst }, + { "tenex", H(tenexhelp), 0, 1, 1, CMPL0 settenex }, + { "throttle", H(ratehelp), 0, 0, 0, CMPL0 setrate }, + { "trace", H(tracehelp), 0, 0, 0, CMPL0 settrace }, + { "type", H(typehelp), 0, 1, 1, CMPL0 settype }, + { "umask", H(umaskhelp), 0, 1, 1, CMPL0 do_umask }, + { "unset", H(unsethelp), 0, 0, 0, CMPL(o) unsetoption }, + { "usage", H(usagehelp), 0, 0, 1, CMPL(C) help }, + { "user", H(userhelp), 0, 1, 1, CMPL0 user }, + { "verbose", H(verbosehelp), 0, 0, 0, CMPL0 setverbose }, + { "xferbuf", H(xferbufhelp), 0, 0, 0, CMPL0 setxferbuf }, + { "?", H(helphelp), 0, 0, 1, CMPL(C) help }, + { 0 }, +}; + +struct option optiontab[] = { + { "anonpass", NULL }, + { "ftp_proxy", NULL }, + { "http_proxy", NULL }, + { "no_proxy", NULL }, + { "pager", NULL }, + { "prompt", NULL }, + { "rprompt", NULL }, + { 0 }, +}; diff --git a/src/complete.c b/src/complete.c new file mode 100644 index 0000000..8831bc1 --- /dev/null +++ b/src/complete.c @@ -0,0 +1,435 @@ +/* $NetBSD: complete.c,v 1.38 2000/05/01 10:35:17 lukem Exp $ */ + +/*- + * Copyright (c) 1997-2000 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: complete.c,v 1.38 2000/05/01 10:35:17 lukem Exp $"); +#endif /* not lint */ + +/* + * FTP user program - command and file completion routines + */ + +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ftp_var.h" + +#ifndef NO_EDITCOMPLETE + +static int comparstr (const void *, const void *); +static unsigned char complete_ambiguous (char *, int, StringList *); +static unsigned char complete_command (char *, int); +static unsigned char complete_local (char *, int); +static unsigned char complete_option (char *, int); +static unsigned char complete_remote (char *, int); + +static int +comparstr(const void *a, const void *b) +{ + return (strcmp(*(const char **)a, *(const char **)b)); +} + +/* + * Determine if complete is ambiguous. If unique, insert. + * If no choices, error. If unambiguous prefix, insert that. + * Otherwise, list choices. words is assumed to be filtered + * to only contain possible choices. + * Args: + * word word which started the match + * list list by default + * words stringlist containing possible matches + * Returns a result as per el_set(EL_ADDFN, ...) + */ +static unsigned char +complete_ambiguous(char *word, int list, StringList *words) +{ + char insertstr[MAXPATHLEN]; + char *lastmatch, *p; + int i, j; + size_t matchlen, wordlen; + + wordlen = strlen(word); + if (words->sl_cur == 0) + return (CC_ERROR); /* no choices available */ + + if (words->sl_cur == 1) { /* only once choice available */ + p = words->sl_str[0] + wordlen; + if (*p == '\0') /* at end of word? */ + return (CC_REFRESH); + ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); + if (el_insertstr(el, insertstr) == -1) + return (CC_ERROR); + else + return (CC_REFRESH); + } + + if (!list) { + matchlen = 0; + lastmatch = words->sl_str[0]; + matchlen = strlen(lastmatch); + for (i = 1 ; i < words->sl_cur ; i++) { + for (j = wordlen ; j < strlen(words->sl_str[i]); j++) + if (lastmatch[j] != words->sl_str[i][j]) + break; + if (j < matchlen) + matchlen = j; + } + if (matchlen > wordlen) { + ftpvis(insertstr, sizeof(insertstr), + lastmatch + wordlen, matchlen - wordlen); + if (el_insertstr(el, insertstr) == -1) + return (CC_ERROR); + else + return (CC_REFRESH_BEEP); + } + } + + putc('\n', ttyout); + qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); + list_vertical(words); + return (CC_REDISPLAY); +} + +/* + * Complete a command + */ +static unsigned char +complete_command(char *word, int list) +{ + struct cmd *c; + StringList *words; + size_t wordlen; + unsigned char rv; + + words = xsl_init(); + wordlen = strlen(word); + + for (c = cmdtab; c->c_name != NULL; c++) { + if (wordlen > strlen(c->c_name)) + continue; + if (strncmp(word, c->c_name, wordlen) == 0) + xsl_add(words, c->c_name); + } + + rv = complete_ambiguous(word, list, words); + if (rv == CC_REFRESH) { + if (el_insertstr(el, " ") == -1) + rv = CC_ERROR; + } + sl_free(words, 0); + return (rv); +} + +/* + * Complete a local file + */ +static unsigned char +complete_local(char *word, int list) +{ + StringList *words; + char dir[MAXPATHLEN]; + char *file; + DIR *dd; + struct dirent *dp; + unsigned char rv; + size_t len; + + if ((file = strrchr(word, '/')) == NULL) { + dir[0] = '.'; + dir[1] = '\0'; + file = word; + } else { + if (file == word) { + dir[0] = '/'; + dir[1] = '\0'; + } else + (void)strlcpy(dir, word, file - word + 1); + file++; + } + if (dir[0] == '~') { + char *p; + + if ((p = globulize(dir)) == NULL) + return (CC_ERROR); + (void)strlcpy(dir, p, sizeof(dir)); + free(p); + } + + if ((dd = opendir(dir)) == NULL) + return (CC_ERROR); + + words = xsl_init(); + len = strlen(file); + + for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + +#if defined(DIRENT_MISSING_D_NAMLEN) + if (len > strlen(dp->d_name)) + continue; +#else + if (len > dp->d_namlen) + continue; +#endif + if (strncmp(file, dp->d_name, len) == 0) { + char *tcp; + + tcp = xstrdup(dp->d_name); + xsl_add(words, tcp); + } + } + closedir(dd); + + rv = complete_ambiguous(file, list, words); + if (rv == CC_REFRESH) { + struct stat sb; + char path[MAXPATHLEN]; + + (void)strlcpy(path, dir, sizeof(path)); + (void)strlcat(path, "/", sizeof(path)); + (void)strlcat(path, words->sl_str[0], sizeof(path)); + + if (stat(path, &sb) >= 0) { + char suffix[2] = " "; + + if (S_ISDIR(sb.st_mode)) + suffix[0] = '/'; + if (el_insertstr(el, suffix) == -1) + rv = CC_ERROR; + } + } + sl_free(words, 1); + return (rv); +} +/* + * Complete an option + */ +static unsigned char +complete_option(char *word, int list) +{ + struct option *o; + StringList *words; + size_t wordlen; + unsigned char rv; + + words = xsl_init(); + wordlen = strlen(word); + + for (o = optiontab; o->name != NULL; o++) { + if (wordlen > strlen(o->name)) + continue; + if (strncmp(word, o->name, wordlen) == 0) + xsl_add(words, o->name); + } + + rv = complete_ambiguous(word, list, words); + if (rv == CC_REFRESH) { + if (el_insertstr(el, " ") == -1) + rv = CC_ERROR; + } + sl_free(words, 0); + return (rv); +} + +/* + * Complete a remote file + */ +static unsigned char +complete_remote(char *word, int list) +{ + static StringList *dirlist; + static char lastdir[MAXPATHLEN]; + StringList *words; + char dir[MAXPATHLEN]; + char *file, *cp; + int i; + unsigned char rv; + + char *dummyargv[] = { "complete", NULL, NULL }; + dummyargv[1] = dir; + + if ((file = strrchr(word, '/')) == NULL) { + dir[0] = '\0'; + file = word; + } else { + cp = file; + while (*cp == '/' && cp > word) + cp--; + (void)strlcpy(dir, word, cp - word + 2); + file++; + } + + if (dirchange || dirlist == NULL || + strcmp(dir, lastdir) != 0) { /* dir not cached */ + char *emesg; + + if (dirlist != NULL) + sl_free(dirlist, 1); + dirlist = xsl_init(); + + mflag = 1; + emesg = NULL; + while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { + char *tcp; + + if (!mflag) + continue; + if (*cp == '\0') { + mflag = 0; + continue; + } + tcp = strrchr(cp, '/'); + if (tcp) + tcp++; + else + tcp = cp; + tcp = xstrdup(tcp); + xsl_add(dirlist, tcp); + } + if (emesg != NULL) { + fprintf(ttyout, "\n%s\n", emesg); + return (CC_REDISPLAY); + } + (void)strlcpy(lastdir, dir, sizeof(lastdir)); + dirchange = 0; + } + + words = xsl_init(); + for (i = 0; i < dirlist->sl_cur; i++) { + cp = dirlist->sl_str[i]; + if (strlen(file) > strlen(cp)) + continue; + if (strncmp(file, cp, strlen(file)) == 0) + xsl_add(words, cp); + } + rv = complete_ambiguous(file, list, words); + sl_free(words, 0); + return (rv); +} + +/* + * Generic complete routine + */ +unsigned char +complete(EditLine *el, int ch) +{ + static char word[FTPBUFLEN]; + static int lastc_argc, lastc_argo; + + struct cmd *c; + const LineInfo *lf; + int celems, dolist, cmpltype; + size_t len; + + lf = el_line(el); + len = lf->lastchar - lf->buffer; + if (len >= sizeof(line)) + return (CC_ERROR); + (void)strlcpy(line, lf->buffer, len + 1); + cursor_pos = line + (lf->cursor - lf->buffer); + lastc_argc = cursor_argc; /* remember last cursor pos */ + lastc_argo = cursor_argo; + makeargv(); /* build argc/argv of current line */ + + if (cursor_argo >= sizeof(word)) + return (CC_ERROR); + + dolist = 0; + /* if cursor and word is same, list alternatives */ + if (lastc_argc == cursor_argc && lastc_argo == cursor_argo + && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", + cursor_argo) == 0) + dolist = 1; + else if (cursor_argc < margc) + (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); + word[cursor_argo] = '\0'; + + if (cursor_argc == 0) + return (complete_command(word, dolist)); + + c = getcmd(margv[0]); + if (c == (struct cmd *)-1 || c == 0) + return (CC_ERROR); + celems = strlen(c->c_complete); + + /* check for 'continuation' completes (which are uppercase) */ + if ((cursor_argc > celems) && (celems > 0) + && isupper((unsigned char) c->c_complete[celems-1])) + cursor_argc = celems; + + if (cursor_argc > celems) + return (CC_ERROR); + + cmpltype = c->c_complete[cursor_argc - 1]; + switch (cmpltype) { + case 'c': /* command complete */ + case 'C': + return (complete_command(word, dolist)); + case 'l': /* local complete */ + case 'L': + return (complete_local(word, dolist)); + case 'n': /* no complete */ + case 'N': /* no complete */ + return (CC_ERROR); + case 'o': /* local complete */ + case 'O': + return (complete_option(word, dolist)); + case 'r': /* remote complete */ + case 'R': + if (connected != -1) { + fputs("\nMust be logged in to complete.\n", + ttyout); + return (CC_REDISPLAY); + } + return (complete_remote(word, dolist)); + default: + errx(1, "unknown complete type `%c'", cmpltype); + return (CC_ERROR); + } + /* NOTREACHED */ +} + +#endif /* !NO_EDITCOMPLETE */ diff --git a/src/domacro.c b/src/domacro.c new file mode 100644 index 0000000..2e10e1c --- /dev/null +++ b/src/domacro.c @@ -0,0 +1,143 @@ +/* $NetBSD: domacro.c,v 1.20 2003/08/07 11:13:53 agc Exp $ */ + +/* + * Copyright (c) 1985, 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. 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: domacro.c,v 1.20 2003/08/07 11:13:53 agc Exp $"); +#endif +#endif /* not lint */ + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include "ftp_var.h" + +void +domacro(int argc, char *argv[]) +{ + int i, j, count = 2, loopflg = 0; + char *cp1, *cp2, line2[FTPBUFLEN]; + struct cmd *c; + + if ((argc == 0 && argv != NULL) || + (argc < 2 && !another(&argc, &argv, "macro name"))) { + fprintf(ttyout, "usage: %s macro_name [args]\n", argv[0]); + code = -1; + return; + } + for (i = 0; i < macnum; ++i) { + if (!strncmp(argv[1], macros[i].mac_name, 9)) + break; + } + if (i == macnum) { + fprintf(ttyout, "'%s' macro not found.\n", argv[1]); + code = -1; + return; + } + (void)strlcpy(line2, line, sizeof(line2)); + TOP: + cp1 = macros[i].mac_start; + while (cp1 != macros[i].mac_end) { + while (isspace((unsigned char)*cp1)) + cp1++; + cp2 = line; + while (*cp1 != '\0') { + switch(*cp1) { + case '\\': + *cp2++ = *++cp1; + break; + case '$': + if (isdigit((unsigned char)*(cp1+1))) { + j = 0; + while (isdigit((unsigned char)*++cp1)) + j = 10*j + *cp1 - '0'; + cp1--; + if (argc - 2 >= j) { + (void)strlcpy(cp2, argv[j+1], + sizeof(line) - (cp2 - line)); + cp2 += strlen(argv[j+1]); + } + break; + } + if (*(cp1+1) == 'i') { + loopflg = 1; + cp1++; + if (count < argc) { + (void)strlcpy(cp2, argv[count], + sizeof(line) - (cp2 - line)); + cp2 += strlen(argv[count]); + } + break; + } + /* intentional drop through */ + default: + *cp2++ = *cp1; + break; + } + if (*cp1 != '\0') + cp1++; + } + *cp2 = '\0'; + makeargv(); + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + fputs("?Ambiguous command.\n", ttyout); + code = -1; + } else if (c == 0) { + fputs("?Invalid command.\n", ttyout); + code = -1; + } else if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + code = -1; + } else { + if (verbose) { + fputs(line, ttyout); + putc('\n', ttyout); + } + margv[0] = c->c_name; + (*c->c_handler)(margc, margv); + if (bell && c->c_bell) + (void)putc('\007', ttyout); + (void)strlcpy(line, line2, sizeof(line)); + makeargv(); + argc = margc; + argv = margv; + } + if (cp1 != macros[i].mac_end) + cp1++; + } + if (loopflg && ++count < argc) + goto TOP; +} diff --git a/src/extern.h b/src/extern.h new file mode 100644 index 0000000..f2b802a --- /dev/null +++ b/src/extern.h @@ -0,0 +1,257 @@ +/* $NetBSD: extern.h,v 1.67 2005/05/14 15:26:43 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2005 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) 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. 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. + * + * @(#)extern.h 8.3 (Berkeley) 10/9/94 + */ + +/* + * 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. + */ + +struct sockaddr; +struct tm; +struct addrinfo; + +void abort_remote(FILE *); +void abort_squared(int); +void abortpt(int); +void abortxfer(int); +void account(int, char **); +void ai_unmapped(struct addrinfo *); +int another(int *, char ***, const char *); +int auto_fetch(int, char **); +int auto_put(int, char **, const char *); +void blkfree(char **); +void cd(int, char **); +void cdup(int, char **); +void changetype(int, int); +void cleanuppeer(void); +void cmdabort(int); +void cmdtimeout(int); +void cmdscanner(void); +int command(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +#ifndef NO_EDITCOMPLETE +unsigned char complete(EditLine *, int); +void controlediting(void); +#endif /* !NO_EDITCOMPLETE */ +void crankrate(int); +FILE *dataconn(const char *); +void delete(int, char **); +void disconnect(int, char **); +void do_chmod(int, char **); +void do_umask(int, char **); +void domacro(int, char **); +void doproxy(int, char **); +void feat(int, char **); +void fget(int, char **); +int fileindir(const char *, const char *); +int foregroundproc(void); +void formatbuf(char *, size_t, const char *); +void ftpvis(char *, size_t, const char *, size_t); +int ftp_login(const char *, const char *, const char *); +void get(int, char **); +struct cmd *getcmd(const char *); +int getit(int, char **, int, const char *); +struct option *getoption(const char *); +char *getoptionvalue(const char *); +void getremoteinfo(void); +int getreply(int); +char *globulize(const char *); +char *gunique(const char *); +void help(int, char **); +char *hookup(char *, char *); +void idlecmd(int, char **); +int initconn(void); +void intr(int); +int isipv6addr(const char *); +void list_vertical(StringList *); +void lcd(int, char **); +void lostpeer(int); +void lpage(int, char **); +void lpwd(int, char **); +void ls(int, char **); +void mabort(void); +void macdef(int, char **); +void makeargv(void); +void makedir(int, char **); +void mdelete(int, char **); +void mget(int, char **); +void mintr(int); +void mls(int, char **); +void mlst(int, char **); +void modtime(int, char **); +void mput(int, char **); +char *onoff(int); +void opts(int, char **); +void newer(int, char **); +void page(int, char **); +int parseport(const char *, int); +int parserate(int, char **, int); +char *prompt(void); +void proxabort(int); +void proxtrans(const char *, const char *, const char *); +void psabort(int); +void pswitch(int); +void put(int, char **); +void pwd(int, char **); +void quit(int, char **); +void quote(int, char **); +void quote1(const char *, int, char **); +void recvrequest(const char *, const char *, const char *, + const char *, int, int); +void reget(int, char **); +char *remglob(char **, int, char **); +time_t remotemodtime(const char *, int); +off_t remotesize(const char *, int); +void removedir(int, char **); +void renamefile(int, char **); +void reset(int, char **); +void restart(int, char **); +void rmthelp(int, char **); +void rmtstatus(int, char **); +char *rprompt(void); +int ruserpass(const char *, const char **, const char **, + const char **); +void sendrequest(const char *, const char *, const char *, int); +void setascii(int, char **); +void setbell(int, char **); +void setbinary(int, char **); +void setcase(int, char **); +void setcr(int, char **); +void setdebug(int, char **); +void setedit(int, char **); +void setepsv4(int, char **); +void setform(int, char **); +void setftmode(int, char **); +void setgate(int, char **); +void setglob(int, char **); +void sethash(int, char **); +void setnmap(int, char **); +void setntrans(int, char **); +void setoption(int, char **); +void setpassive(int, char **); +void setpeer(int, char **); +void setport(int, char **); +void setpreserve(int, char **); +void setprogress(int, char **); +void setprompt(int, char **); +void setrate(int, char **); +void setrunique(int, char **); +void setstruct(int, char **); +void setsunique(int, char **); +void settenex(int, char **); +void settrace(int, char **); +void setttywidth(int); +void settype(int, char **); +void setupsockbufsize(int); +void setverbose(int, char **); +void setxferbuf(int, char **); +void shell(int, char **); +void site(int, char **); +void sizecmd(int, char **); +char *slurpstring(void); +void status(int, char **); +int strsuftoi(const char *); +void syst(int, char **); +int togglevar(int, char **, int *, const char *); +void unsetoption(int, char **); +void updatelocalcwd(void); +void updateremotecwd(void); +void usage(void); +void user(int, char **); +int xconnect(int, const struct sockaddr *, socklen_t); +int xlisten(int, int); +int xpoll(struct pollfd *, int, int); +void *xmalloc(size_t); +StringList *xsl_init(void); +void xsl_add(StringList *, char *); +char *xstrdup(const char *); diff --git a/src/fetch.c b/src/fetch.c new file mode 100644 index 0000000..e2acb01 --- /dev/null +++ b/src/fetch.c @@ -0,0 +1,1834 @@ +/* $NetBSD: fetch.c,v 1.158 2005/05/14 15:26:43 lukem Exp $ */ + +/*- + * Copyright (c) 1997-2004 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: fetch.c,v 1.158 2005/05/14 15:26:43 lukem Exp $"); +#endif /* not lint */ + +/* + * FTP User Program -- Command line file retrieval + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <netinet/in.h> + +#include <arpa/ftp.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <libutil.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); +#ifndef NO_AUTH +static int auth_url(const char *, char **, const char *, const char *); +static void base64_encode(const unsigned char *, size_t, unsigned char *); +#endif +static int go_fetch(const char *); +static int fetch_ftp(const char *); +static int fetch_url(const char *, const char *, char *, char *); +static const char *match_token(const char **, const char *); +static int parse_url(const char *, const char *, url_t *, char **, + char **, char **, char **, in_port_t *, char **); +static void url_decode(char *); + +static int redirect_loop; + + +#define STRNEQUAL(a,b) (strncasecmp((a), (b), sizeof((b))-1) == 0) +#define ISLWS(x) ((x)=='\r' || (x)=='\n' || (x)==' ' || (x)=='\t') +#define SKIPLWS(x) do { while (ISLWS((*x))) x++; } while (0) + + +#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 */ + + +/* + * Determine if token is the next word in buf (case insensitive). + * If so, advance buf past the token and any trailing LWS, and + * return a pointer to the token (in buf). Otherwise, return NULL. + * token may be preceeded by LWS. + * token must be followed by LWS or NUL. (I.e, don't partial match). + */ +static const char * +match_token(const char **buf, const char *token) +{ + const char *p, *orig; + size_t tlen; + + tlen = strlen(token); + p = *buf; + SKIPLWS(p); + orig = p; + if (strncasecmp(p, token, tlen) != 0) + return NULL; + p += tlen; + if (*p != '\0' && !ISLWS(*p)) + return NULL; + SKIPLWS(p); + orig = *buf; + *buf = p; + return orig; +} + +#ifndef NO_AUTH +/* + * 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) +{ + const char *cp, *scheme; + char *ep, *clear, *realm; + char user[BUFSIZ], *pass; + int rval; + size_t len, clen, rlen; + + *response = NULL; + clear = realm = NULL; + rval = -1; + cp = challenge; + scheme = "Basic"; /* only support Basic authentication */ + + if (debug) + fprintf(ttyout, "auth_url: challenge `%s'\n", challenge); + + if (! match_token(&cp, scheme)) { + warnx("Unsupported authentication challenge - `%s'", + challenge); + goto cleanup_auth_url; + } + +#define REALM "realm=\"" + if (STRNEQUAL(cp, REALM)) + cp += sizeof(REALM) - 1; + else { + warnx("Unsupported authentication challenge - `%s'", + challenge); + goto cleanup_auth_url; + } +/* XXX: need to improve quoted-string parsing to support \ quoting, etc. */ + if ((ep = strchr(cp, '\"')) != NULL) { + size_t len = ep - cp; + + realm = (char *)xmalloc(len + 1); + (void)strlcpy(realm, cp, len + 1); + } else { + warnx("Unsupported authentication challenge - `%s'", + challenge); + goto cleanup_auth_url; + } + + fprintf(ttyout, "Username for `%s': ", realm); + if (guser != NULL) { + (void)strlcpy(user, guser, sizeof(user)); + fprintf(ttyout, "%s\n", user); + } else { + (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); + /* use `clen - 1' to not encode the trailing NUL */ + base64_encode((unsigned char *)clear, clen - 1, + (unsigned char *)*response + len); + memset(clear, 0, clen); + rval = 0; + + cleanup_auth_url: + FREEPTR(clear); + 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 unsigned char *clear, size_t len, unsigned char *encoded) +{ + static const unsigned char enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned 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) = '='; +} +#endif + +/* + * 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 (STRNEQUAL(url, HTTP_URL)) { + url += sizeof(HTTP_URL) - 1; + *type = HTTP_URL_T; + *portnum = HTTP_PORT; + tport = httpport; + } else if (STRNEQUAL(url, FTP_URL)) { + url += sizeof(FTP_URL) - 1; + *type = FTP_URL_T; + *portnum = FTP_PORT; + tport = ftpport; + } else if (STRNEQUAL(url, FILE_URL)) { + 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); + } + url_decode(*user); + if (*pass) + url_decode(*pass); + } + +#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; + const char *cp, *token; + char *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, "savefile `%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; + } + + 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 && (ISLWS(buf[len-1]))) + 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. */ + while (1) { + FREEPTR(buf); + if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) + == NULL) { + warn("Receiving HTTP reply"); + goto cleanup_fetch_url; + } + while (len > 0 && (ISLWS(buf[len-1]))) + buf[--len] = '\0'; + if (len == 0) + break; + if (debug) + fprintf(ttyout, "received `%s'\n", buf); + + /* + * Look for some headers + */ + + cp = buf; + + if (match_token(&cp, "Content-Length:")) { + filesize = STRTOLL(cp, &ep, 10); + if (filesize < 0 || *ep != '\0') + goto improper; + if (debug) + fprintf(ttyout, + "parsed len as: " LLF "\n", + (LLT)filesize); + + } else if (match_token(&cp, "Content-Range:")) { + if (! match_token(&cp, "bytes")) + goto improper; + + if (*cp == '*') + cp++; + else { + rangestart = STRTOLL(cp, &ep, 10); + if (rangestart < 0 || *ep != '-') + goto improper; + cp = ep + 1; + rangeend = STRTOLL(cp, &ep, 10); + if (rangeend < 0 || rangeend < rangestart) + goto improper; + cp = ep; + } + if (*cp != '/') + goto improper; + cp++; + if (*cp == '*') + cp++; + else { + entitylen = STRTOLL(cp, &ep, 10); + if (entitylen < 0) + goto improper; + cp = ep; + } + if (*cp != '\0') + goto improper; + + 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; + } + + } else if (match_token(&cp, "Last-Modified:")) { + struct tm parsed; + char *t; + + /* 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)); + } + } + + } else if (match_token(&cp, "Location:")) { + location = xstrdup(cp); + if (debug) + fprintf(ttyout, + "parsed location as `%s'\n", cp); + + } else if (match_token(&cp, "Transfer-Encoding:")) { + if (match_token(&cp, "binary")) { + warnx( + "Bogus transfer encoding - `binary' (fetching anyway)"); + continue; + } + if (! (token = match_token(&cp, "chunked"))) { + warnx( + "Unsupported transfer encoding - `%s'", + token); + goto cleanup_fetch_url; + } + ischunked++; + if (debug) + fprintf(ttyout, + "using chunked encoding\n"); + + } else if (match_token(&cp, "Proxy-Authenticate:") + || match_token(&cp, "WWW-Authenticate:")) { + if (! (token = match_token(&cp, "Basic"))) { + if (debug) + fprintf(ttyout, + "skipping unknown auth scheme `%s'\n", + token); + continue; + } + FREEPTR(auth); + auth = xstrdup(token); + if (debug) + fprintf(ttyout, + "parsed 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; +#ifndef NO_AUTH + case 401: + case 407: + { + char **authp; + char *auser, *apass; + + if (hcode == 401) { + authp = &wwwauth; + auser = user; + apass = pass; + } else { + authp = &proxyauth; + auser = puser; + apass = ppass; + } + if (verbose || *authp == NULL || + auser == NULL || apass == NULL) + fprintf(ttyout, "%s\n", message); + if (EMPTYSTRING(auth)) { + warnx( + "No authentication challenge provided by server"); + goto cleanup_fetch_url; + } + if (*authp != NULL) { + char reply[10]; + + fprintf(ttyout, + "Authorization failed. Retry (y/n)? "); + if (fgets(reply, sizeof(reply), stdin) + == NULL) { + clearerr(stdin); + goto cleanup_fetch_url; + } + if (tolower((unsigned char)reply[0]) != 'y') + goto cleanup_fetch_url; + auser = NULL; + apass = NULL; + } + if (auth_url(auth, authp, auser, apass) == 0) { + rval = fetch_url(url, proxyenv, + proxyauth, wwwauth); + memset(*authp, 0, strlen(*authp)); + FREEPTR(*authp); + } + goto cleanup_fetch_url; + } +#endif + default: + if (message) + warnx("Error retrieving file - `%s'", message); + else + warnx("Unknown error retrieving file"); + goto cleanup_fetch_url; + } + } /* 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); + if (res0) + freeaddrinfo(res0); + 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; + + sigint_raised = 1; + 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 (STRNEQUAL(url, FTP_URL)) { + if ((parse_url(url, "URL", &urltype, &user, &pass, + &host, &port, &portnum, &path) == -1) || + (user != NULL && *user == '\0') || + EMPTYSTRING(host)) { + warnx("Invalid URL `%s'", url); + goto cleanup_fetch_ftp; + } + /* + * 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; + if (restartautofetch) + xargv[0] = "mreget"; + else + 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; + +#ifndef NO_ABOUT + /* + * Check for about:* + */ + if (STRNEQUAL(url, ABOUT_URL)) { + 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); + } +#endif + + /* + * Check for file:// and http:// URLs. + */ + if (STRNEQUAL(url, HTTP_URL) || STRNEQUAL(url, FILE_URL)) + 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) && STRNEQUAL(url, FTP_URL)) + 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); + if (rval > 0) + rval = argpos + 1; + return (rval); + } + (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/src/ftp.1 b/src/ftp.1 new file mode 100644 index 0000000..917980f --- /dev/null +++ b/src/ftp.1 @@ -0,0 +1,2357 @@ +.\" $NetBSD: ftp.1,v 1.109 2005/02/20 20:54:01 wiz Exp $ +.\" +.\" Copyright (c) 1996-2004 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. 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 January 15, 2005 +.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 for non auto-fetch transfers. +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. +To override the auto-login for auto-fetch transfers, specify the +username (and optionally, password) as appropriate. +.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 +.Li 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 be 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 +.Li 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 +(per +.Li RFC 1738 ) +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 . +Note that if a +.Tn HTTP +redirect is received, the fetch is retried using the new target URL +supplied by the server, with a corresponding new +.Ar path . +Using an explicit +.Fl o Ar output +is recommended, to avoid writing to unexpected file names. +.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 +See +.Ev http_proxy +for further notes about proxy use. +.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 +If +.Dq unsafe +URL characters are required in the username or password +(for example +.Sq @ +or +.Sq / ) , +encode them with +.Li RFC 1738 +.Sq Li \&% Ns Ar XX +encoding. +.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 +.Li RFC 959 , +.Li RFC 1123 , +.Li RFC 1738 , +.Li RFC 2068 , +.Li RFC 2389 , +.Li RFC 2428 , +.Li 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/src/ftp.c b/src/ftp.c new file mode 100644 index 0000000..dae6131 --- /dev/null +++ b/src/ftp.c @@ -0,0 +1,2171 @@ +/* $NetBSD: ftp.c,v 1.132 2005/05/14 15:26:43 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2005 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. 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; +#else +__RCSID("$NetBSD: ftp.c,v 1.132 2005/05/14 15:26:43 lukem Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <arpa/ftp.h> +#include <arpa/telnet.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <stdarg.h> + +#include "ftp_var.h" + +volatile sig_atomic_t abrtflag; +volatile sig_atomic_t timeoutflag; + +sigjmp_buf ptabort; +int ptabflg; +int ptflag = 0; +char pasv[BUFSIZ]; /* passive port for proxy data connection */ + +static int empty(FILE *, FILE *, int); + +struct sockinet { + union sockunion { + struct sockaddr_in su_sin; +#ifdef INET6 + struct sockaddr_in6 su_sin6; +#endif + } si_su; +#if !HAVE_SOCKADDR_SA_LEN + int si_len; +#endif +}; + +#if !HAVE_SOCKADDR_SA_LEN +# define su_len si_len +#else +# define su_len si_su.su_sin.sin_len +#endif +#define su_family si_su.su_sin.sin_family +#define su_port si_su.su_sin.sin_port + +struct sockinet myctladdr, hisctladdr, data_addr; + +char * +hookup(char *host, char *port) +{ + int s = -1, error, portnum; + struct addrinfo hints, *res, *res0; + char hbuf[MAXHOSTNAMELEN]; + static char hostnamebuf[MAXHOSTNAMELEN]; + char *cause = "unknown"; + socklen_t len; + int on = 1; + + memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); + memset((char *)&myctladdr, 0, sizeof (myctladdr)); + memset(&hints, 0, sizeof(hints)); + portnum = parseport(port, FTP_PORT); + hints.ai_flags = AI_CANONNAME; + 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)); + code = -1; + return (0); + } + + if (res0->ai_canonname) + (void)strlcpy(hostnamebuf, res0->ai_canonname, + sizeof(hostnamebuf)); + else + (void)strlcpy(hostnamebuf, host, sizeof(hostnamebuf)); + hostname = hostnamebuf; + + for (res = res0; res; res = res->ai_next) { + /* + * make sure that ai_addr is NOT an IPv4 mapped address. + * IPv4 mapped address complicates too many things in FTP + * protocol handling, as FTP protocol is defined differently + * between IPv4 and IPv6. + * + * This may not be the best way to handle this situation, + * since the semantics of IPv4 mapped address is defined in + * the kernel. There are configurations where we should use + * IPv4 mapped address as native IPv6 address, not as + * "an IPv6 address that embeds IPv4 address" (namely, SIIT). + * + * More complete solution would be to have an additional + * getsockopt to grab "real" peername/sockname. "real" + * peername/sockname will be AF_INET if IPv4 mapped address + * is used to embed IPv4 address, and will be AF_INET6 if + * we use it as native. What a mess! + */ + ai_unmapped(res); +#if 0 /*old behavior*/ + if (res != res0) /* not on the first address */ +#else + if (res0->ai_next) /* if we have multiple possibilities */ +#endif + { + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) + strlcpy(hbuf, "?", sizeof(hbuf)); + 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) { + cause = "socket"; + continue; + } + error = xconnect(s, res->ai_addr, res->ai_addrlen); + if (error) { + /* this "if" clause is to prevent print warning twice */ + if (res->ai_next) { + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST)) + strlcpy(hbuf, "?", sizeof(hbuf)); + warn("connect to address %s", hbuf); + } + cause = "connect"; + close(s); + s = -1; + continue; + } + + /* finally we got one */ + break; + } + if (s < 0) { + warn("%s", cause); + code = -1; + freeaddrinfo(res0); + return 0; + } + memcpy(&hisctladdr.si_su, res->ai_addr, res->ai_addrlen); + hisctladdr.su_len = res->ai_addrlen; + freeaddrinfo(res0); + res0 = res = NULL; + + len = hisctladdr.su_len; + if (getsockname(s, (struct sockaddr *)&myctladdr.si_su, &len) == -1) { + warn("getsockname"); + code = -1; + goto bad; + } + myctladdr.su_len = len; + +#ifdef IPTOS_LOWDELAY + if (hisctladdr.su_family == AF_INET) { + int tos = IPTOS_LOWDELAY; + if (setsockopt(s, IPPROTO_IP, IP_TOS, + (void *)&tos, sizeof(tos)) == -1) { + if (debug) + warn("setsockopt %s (ignored)", + "IPTOS_LOWDELAY"); + } + } +#endif + cin = fdopen(s, "r"); + cout = fdopen(s, "w"); + if (cin == NULL || cout == NULL) { + warnx("fdopen failed."); + if (cin) + (void)fclose(cin); + if (cout) + (void)fclose(cout); + code = -1; + goto bad; + } + if (verbose) + fprintf(ttyout, "Connected to %s.\n", hostname); + if (getreply(0) > 2) { /* read startup message from server */ + if (cin) + (void)fclose(cin); + if (cout) + (void)fclose(cout); + code = -1; + goto bad; + } + + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, + (void *)&on, sizeof(on)) == -1) { + if (debug) + warn("setsockopt %s (ignored)", "SO_OOBINLINE"); + } + + return (hostname); + bad: + (void)close(s); + return (NULL); +} + +void +cmdabort(int notused) +{ + int oerrno = errno; + + sigint_raised = 1; + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + abrtflag++; + if (ptflag) + siglongjmp(ptabort, 1); + errno = oerrno; +} + +void +cmdtimeout(int notused) +{ + int oerrno = errno; + + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + timeoutflag++; + if (ptflag) + siglongjmp(ptabort, 1); + errno = oerrno; +} + +/*VARARGS*/ +int +command(const char *fmt, ...) +{ + va_list ap; + int r; + sigfunc oldsigint; + + if (debug) { + fputs("---> ", ttyout); + va_start(ap, fmt); + if (strncmp("PASS ", fmt, 5) == 0) + fputs("PASS XXXX", ttyout); + else if (strncmp("ACCT ", fmt, 5) == 0) + fputs("ACCT XXXX", ttyout); + else + vfprintf(ttyout, fmt, ap); + va_end(ap); + putc('\n', ttyout); + } + if (cout == NULL) { + warnx("No control connection for command."); + code = -1; + return (0); + } + + abrtflag = 0; + + oldsigint = xsignal(SIGINT, cmdabort); + + va_start(ap, fmt); + vfprintf(cout, fmt, ap); + va_end(ap); + fputs("\r\n", cout); + (void)fflush(cout); + cpend = 1; + r = getreply(!strcmp(fmt, "QUIT")); + if (abrtflag && oldsigint != SIG_IGN) + (*oldsigint)(SIGINT); + (void)xsignal(SIGINT, oldsigint); + return (r); +} + +int +getreply(int expecteof) +{ + char current_line[BUFSIZ]; /* last line of previous reply */ + int c, n, line; + int dig; + int originalcode = 0, continuation = 0; + sigfunc oldsigint, oldsigalrm; + int pflag = 0; + char *cp, *pt = pasv; + + abrtflag = 0; + timeoutflag = 0; + + oldsigint = xsignal(SIGINT, cmdabort); + oldsigalrm = xsignal(SIGALRM, cmdtimeout); + + for (line = 0 ;; line++) { + dig = n = code = 0; + cp = current_line; + while (alarmtimer(60),((c = getc(cin)) != '\n')) { + if (c == IAC) { /* handle telnet commands */ + switch (c = getc(cin)) { + case WILL: + case WONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, DONT, c); + (void)fflush(cout); + break; + case DO: + case DONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, WONT, c); + (void)fflush(cout); + break; + default: + break; + } + continue; + } + dig++; + if (c == EOF) { + /* + * these will get trashed by pswitch() + * in lostpeer() + */ + int reply_timeoutflag = timeoutflag; + int reply_abrtflag = abrtflag; + + alarmtimer(0); + if (expecteof && feof(cin)) { + (void)xsignal(SIGINT, oldsigint); + (void)xsignal(SIGALRM, oldsigalrm); + code = 221; + return (0); + } + cpend = 0; + lostpeer(0); + if (verbose) { + if (reply_timeoutflag) + fputs( + "421 Service not available, remote server timed out. Connection closed\n", + ttyout); + else if (reply_abrtflag) + fputs( + "421 Service not available, user interrupt. Connection closed.\n", + ttyout); + else + fputs( + "421 Service not available, remote server has closed connection.\n", + ttyout); + (void)fflush(ttyout); + } + code = 421; + (void)xsignal(SIGINT, oldsigint); + (void)xsignal(SIGALRM, oldsigalrm); + return (4); + } + if (c != '\r' && (verbose > 0 || + ((verbose > -1 && n == '5' && dig > 4) && + (((!n && c < '5') || (n && n < '5')) + || !retry_connect)))) { + if (proxflag && + (dig == 1 || (dig == 5 && verbose == 0))) + fprintf(ttyout, "%s:", hostname); + (void)putc(c, ttyout); + } + if (dig < 4 && isdigit(c)) + code = code * 10 + (c - '0'); + if (!pflag && (code == 227 || code == 228)) + pflag = 1; + else if (!pflag && code == 229) + pflag = 100; + if (dig > 4 && pflag == 1 && isdigit(c)) + pflag = 2; + if (pflag == 2) { + if (c != '\r' && c != ')') { + if (pt < &pasv[sizeof(pasv) - 1]) + *pt++ = c; + } else { + *pt = '\0'; + pflag = 3; + } + } + if (pflag == 100 && c == '(') + pflag = 2; + if (dig == 4 && c == '-') { + if (continuation) + code = 0; + continuation++; + } + if (n == 0) + n = c; + if (cp < ¤t_line[sizeof(current_line) - 1]) + *cp++ = c; + } + if (verbose > 0 || ((verbose > -1 && n == '5') && + (n < '5' || !retry_connect))) { + (void)putc(c, ttyout); + (void)fflush (ttyout); + } + if (cp[-1] == '\r') + cp[-1] = '\0'; + *cp = '\0'; + if (line == 0) + (void)strlcpy(reply_string, current_line, + sizeof(reply_string)); + if (line > 0 && code == 0 && reply_callback != NULL) + (*reply_callback)(current_line); + if (continuation && code != originalcode) { + if (originalcode == 0) + originalcode = code; + continue; + } + if (n != '1') + cpend = 0; + alarmtimer(0); + (void)xsignal(SIGINT, oldsigint); + (void)xsignal(SIGALRM, oldsigalrm); + if (code == 421 || originalcode == 421) + lostpeer(0); + if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN) + (*oldsigint)(SIGINT); + if (timeoutflag && oldsigalrm != cmdtimeout && + oldsigalrm != SIG_IGN) + (*oldsigalrm)(SIGINT); + return (n - '0'); + } +} + +static int +empty(FILE *cin, FILE *din, int sec) +{ + int nr, nfd; + struct pollfd pfd[2]; + + nfd = 0; + if (cin) { + pfd[nfd].fd = fileno(cin); + pfd[nfd++].events = POLLIN; + } + + if (din) { + pfd[nfd].fd = fileno(din); + pfd[nfd++].events = POLLIN; + } + + if ((nr = xpoll(pfd, nfd, sec * 1000)) <= 0) + return nr; + + nr = 0; + nfd = 0; + if (cin) + nr |= (pfd[nfd++].revents & POLLIN) ? 1 : 0; + if (din) + nr |= (pfd[nfd++].revents & POLLIN) ? 2 : 0; + return nr; +} + +sigjmp_buf xferabort; + +void +abortxfer(int notused) +{ + char msgbuf[100]; + size_t len; + + sigint_raised = 1; + alarmtimer(0); + mflag = 0; + abrtflag = 0; + switch (direction[0]) { + case 'r': + strlcpy(msgbuf, "\nreceive", sizeof(msgbuf)); + break; + case 's': + strlcpy(msgbuf, "\nsend", sizeof(msgbuf)); + break; + default: + errx(1, "abortxfer called with unknown direction `%s'", + direction); + } + len = strlcat(msgbuf, " aborted. Waiting for remote to finish abort.\n", + sizeof(msgbuf)); + write(fileno(ttyout), msgbuf, len); + siglongjmp(xferabort, 1); +} + +void +sendrequest(const char *cmd, const char *local, const char *remote, + int printnames) +{ + struct stat st; + int c, d; + FILE *fin, *dout; + int (*closefunc)(FILE *); + sigfunc oldintr, oldintp; + volatile off_t hashbytes; + char *lmode, *bufp; + static size_t bufsize; + static char *buf; + int oprogress; + +#ifdef __GNUC__ /* to shut up gcc warnings */ + (void)&fin; + (void)&dout; + (void)&closefunc; + (void)&oldintr; + (void)&oldintp; + (void)&lmode; +#endif + + hashbytes = mark; + direction = "sent"; + dout = NULL; + bytes = 0; + filesize = -1; + oprogress = progress; + if (verbose && printnames) { + if (local && *local != '-') + fprintf(ttyout, "local: %s ", local); + if (remote) + fprintf(ttyout, "remote: %s\n", remote); + } + if (proxy) { + proxtrans(cmd, local, remote); + return; + } + if (curtype != type) + changetype(type, 0); + closefunc = NULL; + oldintr = NULL; + oldintp = NULL; + lmode = "w"; + if (sigsetjmp(xferabort, 1)) { + while (cpend) + (void)getreply(0); + code = -1; + goto cleanupsend; + } + (void)xsignal(SIGQUIT, psummary); + oldintr = xsignal(SIGINT, abortxfer); + if (strcmp(local, "-") == 0) { + fin = stdin; + progress = 0; + } else if (*local == '|') { + oldintp = xsignal(SIGPIPE, SIG_IGN); + fin = popen(local + 1, "r"); + if (fin == NULL) { + warn("%s", local + 1); + code = -1; + goto cleanupsend; + } + progress = 0; + closefunc = pclose; + } else { + fin = fopen(local, "r"); + if (fin == NULL) { + warn("local: %s", local); + code = -1; + goto cleanupsend; + } + closefunc = fclose; + if (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) { + fprintf(ttyout, "%s: not a plain file.\n", local); + code = -1; + goto cleanupsend; + } + filesize = st.st_size; + } + if (initconn()) { + code = -1; + goto cleanupsend; + } + if (sigsetjmp(xferabort, 1)) + goto abort; + + if (restart_point && + (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) { + int rc; + + rc = -1; + switch (curtype) { + case TYPE_A: + rc = fseeko(fin, restart_point, SEEK_SET); + break; + case TYPE_I: + case TYPE_L: + rc = lseek(fileno(fin), restart_point, SEEK_SET); + break; + } + if (rc < 0) { + warn("local: %s", local); + goto cleanupsend; + } + if (command("REST " LLF, (LLT)restart_point) != CONTINUE) + goto cleanupsend; + lmode = "r+"; + } + if (remote) { + if (command("%s %s", cmd, remote) != PRELIM) + goto cleanupsend; + } else { + if (command("%s", cmd) != PRELIM) + goto cleanupsend; + } + dirchange = 1; + dout = dataconn(lmode); + if (dout == NULL) + goto abort; + + if (sndbuf_size > bufsize) { + if (buf) + (void)free(buf); + bufsize = sndbuf_size; + buf = xmalloc(bufsize); + } + + progressmeter(-1); + oldintp = xsignal(SIGPIPE, SIG_IGN); + + switch (curtype) { + + case TYPE_I: + case TYPE_L: + if (rate_put) { /* rate limited */ + while (1) { + struct timeval then, now, td; + off_t bufrem; + + (void)gettimeofday(&then, NULL); + errno = c = d = 0; + bufrem = rate_put; + while (bufrem > 0) { + if ((c = read(fileno(fin), buf, + MIN(bufsize, bufrem))) <= 0) + goto senddone; + bytes += c; + bufrem -= c; + for (bufp = buf; c > 0; + c -= d, bufp += d) + if ((d = write(fileno(dout), + bufp, c)) <= 0) + break; + if (d < 0) + goto senddone; + if (hash && + (!progress || filesize < 0) ) { + while (bytes >= hashbytes) { + (void)putc('#', ttyout); + hashbytes += mark; + } + (void)fflush(ttyout); + } + } + while (1) { + (void)gettimeofday(&now, NULL); + timersub(&now, &then, &td); + if (td.tv_sec > 0) + break; + usleep(1000000 - td.tv_usec); + } + } + } else { /* simpler/faster; no rate limit */ + while (1) { + errno = c = d = 0; + if ((c = read(fileno(fin), buf, bufsize)) <= 0) + goto senddone; + bytes += c; + for (bufp = buf; c > 0; c -= d, bufp += d) + if ((d = write(fileno(dout), bufp, c)) + <= 0) + break; + if (d < 0) + goto senddone; + if (hash && (!progress || filesize < 0) ) { + while (bytes >= hashbytes) { + (void)putc('#', ttyout); + hashbytes += mark; + } + (void)fflush(ttyout); + } + } + } + senddone: + if (hash && (!progress || filesize < 0) && bytes > 0) { + if (bytes < mark) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (c < 0) + warn("local: %s", local); + if (d < 0) { + if (errno != EPIPE) + warn("netout"); + bytes = -1; + } + break; + + case TYPE_A: + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + while (hash && (!progress || filesize < 0) && + (bytes >= hashbytes)) { + (void)putc('#', ttyout); + (void)fflush(ttyout); + hashbytes += mark; + } + if (ferror(dout)) + break; + (void)putc('\r', dout); + bytes++; + } + (void)putc(c, dout); + bytes++; +#if 0 /* this violates RFC */ + if (c == '\r') { + (void)putc('\0', dout); + bytes++; + } +#endif + } + if (hash && (!progress || filesize < 0)) { + if (bytes < hashbytes) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (ferror(fin)) + warn("local: %s", local); + if (ferror(dout)) { + if (errno != EPIPE) + warn("netout"); + bytes = -1; + } + break; + } + + progressmeter(1); + if (closefunc != NULL) { + (*closefunc)(fin); + fin = NULL; + } + (void)fclose(dout); + dout = NULL; + (void)getreply(0); + if (bytes > 0) + ptransfer(0); + goto cleanupsend; + + abort: + (void)xsignal(SIGINT, oldintr); + oldintr = NULL; + if (!cpend) { + code = -1; + goto cleanupsend; + } + if (data >= 0) { + (void)close(data); + data = -1; + } + if (dout) { + (void)fclose(dout); + dout = NULL; + } + (void)getreply(0); + code = -1; + if (bytes > 0) + ptransfer(0); + + cleanupsend: + if (oldintr) + (void)xsignal(SIGINT, oldintr); + if (oldintp) + (void)xsignal(SIGPIPE, oldintp); + if (data >= 0) { + (void)close(data); + data = -1; + } + if (closefunc != NULL && fin != NULL) + (*closefunc)(fin); + if (dout) + (void)fclose(dout); + progress = oprogress; + restart_point = 0; + bytes = 0; +} + +void +recvrequest(const char *cmd, const char *local, const char *remote, + const char *lmode, int printnames, int ignorespecial) +{ + FILE *fout, *din; + int (*closefunc)(FILE *); + sigfunc oldintr, oldintp; + int c, d; + volatile int is_retr, tcrflag, bare_lfs; + static size_t bufsize; + static char *buf; + volatile off_t hashbytes; + struct stat st; + time_t mtime; + struct timeval tval[2]; + int oprogress; + int opreserve; + +#ifdef __GNUC__ /* to shut up gcc warnings */ + (void)&local; + (void)&fout; + (void)&din; + (void)&closefunc; + (void)&oldintr; + (void)&oldintp; +#endif + + fout = NULL; + din = NULL; + hashbytes = mark; + direction = "received"; + bytes = 0; + bare_lfs = 0; + filesize = -1; + oprogress = progress; + opreserve = preserve; + is_retr = (strcmp(cmd, "RETR") == 0); + if (is_retr && verbose && printnames) { + if (local && (ignorespecial || *local != '-')) + fprintf(ttyout, "local: %s ", local); + if (remote) + fprintf(ttyout, "remote: %s\n", remote); + } + if (proxy && is_retr) { + proxtrans(cmd, local, remote); + return; + } + closefunc = NULL; + oldintr = NULL; + oldintp = NULL; + tcrflag = !crflag && is_retr; + if (sigsetjmp(xferabort, 1)) { + while (cpend) + (void)getreply(0); + code = -1; + goto cleanuprecv; + } + (void)xsignal(SIGQUIT, psummary); + oldintr = xsignal(SIGINT, abortxfer); + if (ignorespecial || (strcmp(local, "-") && *local != '|')) { + if (access(local, W_OK) < 0) { + char *dir = strrchr(local, '/'); + + if (errno != ENOENT && errno != EACCES) { + warn("local: %s", local); + code = -1; + goto cleanuprecv; + } + if (dir != NULL) + *dir = 0; + d = access(dir == local ? "/" : + dir ? local : ".", W_OK); + if (dir != NULL) + *dir = '/'; + if (d < 0) { + warn("local: %s", local); + code = -1; + goto cleanuprecv; + } + if (!runique && errno == EACCES && + chmod(local, (S_IRUSR|S_IWUSR)) < 0) { + warn("local: %s", local); + code = -1; + goto cleanuprecv; + } + if (runique && errno == EACCES && + (local = gunique(local)) == NULL) { + code = -1; + goto cleanuprecv; + } + } + else if (runique && (local = gunique(local)) == NULL) { + code = -1; + goto cleanuprecv; + } + } + if (!is_retr) { + if (curtype != TYPE_A) + changetype(TYPE_A, 0); + } else { + if (curtype != type) + changetype(type, 0); + filesize = remotesize(remote, 0); + if (code == 421 || code == -1) + goto cleanuprecv; + } + if (initconn()) { + code = -1; + goto cleanuprecv; + } + if (sigsetjmp(xferabort, 1)) + goto abort; + if (is_retr && restart_point && + command("REST " LLF, (LLT) restart_point) != CONTINUE) + goto cleanuprecv; + if (! EMPTYSTRING(remote)) { + if (command("%s %s", cmd, remote) != PRELIM) + goto cleanuprecv; + } else { + if (command("%s", cmd) != PRELIM) + goto cleanuprecv; + } + din = dataconn("r"); + if (din == NULL) + goto abort; + if (!ignorespecial && strcmp(local, "-") == 0) { + fout = stdout; + progress = 0; + preserve = 0; + } else if (!ignorespecial && *local == '|') { + oldintp = xsignal(SIGPIPE, SIG_IGN); + fout = popen(local + 1, "w"); + if (fout == NULL) { + warn("%s", local+1); + goto abort; + } + progress = 0; + preserve = 0; + closefunc = pclose; + } else { + fout = fopen(local, lmode); + if (fout == NULL) { + warn("local: %s", local); + goto abort; + } + closefunc = fclose; + } + + if (fstat(fileno(fout), &st) != -1 && !S_ISREG(st.st_mode)) { + progress = 0; + preserve = 0; + } + if (rcvbuf_size > bufsize) { + if (buf) + (void)free(buf); + bufsize = rcvbuf_size; + buf = xmalloc(bufsize); + } + + progressmeter(-1); + + switch (curtype) { + + case TYPE_I: + case TYPE_L: + if (is_retr && restart_point && + lseek(fileno(fout), restart_point, SEEK_SET) < 0) { + warn("local: %s", local); + goto cleanuprecv; + } + if (rate_get) { /* rate limiting */ + while (1) { + struct timeval then, now, td; + off_t bufrem; + + (void)gettimeofday(&then, NULL); + errno = c = d = 0; + for (bufrem = rate_get; bufrem > 0; ) { + if ((c = read(fileno(din), buf, + MIN(bufsize, bufrem))) <= 0) + goto recvdone; + bytes += c; + bufrem -=c; + if ((d = write(fileno(fout), buf, c)) + != c) + goto recvdone; + if (hash && + (!progress || filesize < 0)) { + while (bytes >= hashbytes) { + (void)putc('#', ttyout); + hashbytes += mark; + } + (void)fflush(ttyout); + } + } + /* sleep until time is up */ + while (1) { + (void)gettimeofday(&now, NULL); + timersub(&now, &then, &td); + if (td.tv_sec > 0) + break; + usleep(1000000 - td.tv_usec); + } + } + } else { /* faster code (no limiting) */ + while (1) { + errno = c = d = 0; + if ((c = read(fileno(din), buf, bufsize)) <= 0) + goto recvdone; + bytes += c; + if ((d = write(fileno(fout), buf, c)) != c) + goto recvdone; + if (hash && (!progress || filesize < 0)) { + while (bytes >= hashbytes) { + (void)putc('#', ttyout); + hashbytes += mark; + } + (void)fflush(ttyout); + } + } + } + recvdone: + if (hash && (!progress || filesize < 0) && bytes > 0) { + if (bytes < mark) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (c < 0) { + if (errno != EPIPE) + warn("netin"); + bytes = -1; + } + if (d < c) { + if (d < 0) + warn("local: %s", local); + else + warnx("%s: short write", local); + } + break; + + case TYPE_A: + if (is_retr && restart_point) { + int ch; + off_t i; + + if (fseeko(fout, (off_t)0, SEEK_SET) < 0) + goto done; + for (i = 0; i++ < restart_point;) { + if ((ch = getc(fout)) == EOF) + goto done; + if (ch == '\n') + i++; + } + if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { + done: + warn("local: %s", local); + goto cleanuprecv; + } + } + while ((c = getc(din)) != EOF) { + if (c == '\n') + bare_lfs++; + while (c == '\r') { + while (hash && (!progress || filesize < 0) && + (bytes >= hashbytes)) { + (void)putc('#', ttyout); + (void)fflush(ttyout); + hashbytes += mark; + } + bytes++; + if ((c = getc(din)) != '\n' || tcrflag) { + if (ferror(fout)) + goto break2; + (void)putc('\r', fout); + if (c == '\0') { + bytes++; + goto contin2; + } + if (c == EOF) + goto contin2; + } + } + (void)putc(c, fout); + bytes++; + contin2: ; + } + break2: + if (hash && (!progress || filesize < 0)) { + if (bytes < hashbytes) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (ferror(din)) { + if (errno != EPIPE) + warn("netin"); + bytes = -1; + } + if (ferror(fout)) + warn("local: %s", local); + break; + } + + progressmeter(1); + if (closefunc != NULL) { + (*closefunc)(fout); + fout = NULL; + } + (void)fclose(din); + din = NULL; + (void)getreply(0); + if (bare_lfs) { + fprintf(ttyout, + "WARNING! %d bare linefeeds received in ASCII mode.\n", + bare_lfs); + fputs("File may not have transferred correctly.\n", ttyout); + } + if (bytes >= 0 && is_retr) { + if (bytes > 0) + ptransfer(0); + if (preserve && (closefunc == fclose)) { + mtime = remotemodtime(remote, 0); + if (mtime != -1) { + (void)gettimeofday(&tval[0], NULL); + tval[1].tv_sec = mtime; + tval[1].tv_usec = 0; + if (utimes(local, tval) == -1) { + fprintf(ttyout, + "Can't change modification time on %s to %s", + local, asctime(localtime(&mtime))); + } + } + } + } + goto cleanuprecv; + + abort: + /* + * abort using RFC 959 recommended IP,SYNC sequence + */ + if (! sigsetjmp(xferabort, 1)) { + /* this is the first call */ + (void)xsignal(SIGINT, abort_squared); + if (!cpend) { + code = -1; + goto cleanuprecv; + } + abort_remote(din); + } + code = -1; + if (bytes > 0) + ptransfer(0); + + cleanuprecv: + if (oldintr) + (void)xsignal(SIGINT, oldintr); + if (oldintp) + (void)xsignal(SIGPIPE, oldintp); + if (data >= 0) { + (void)close(data); + data = -1; + } + if (closefunc != NULL && fout != NULL) + (*closefunc)(fout); + if (din) + (void)fclose(din); + progress = oprogress; + preserve = opreserve; + bytes = 0; +} + +/* + * Need to start a listen on the data channel before we send the command, + * otherwise the server's connect may fail. + */ +int +initconn(void) +{ + char *p, *a; + int result, tmpno = 0; + int on = 1; + int error; + u_int addr[16], port[2]; + u_int af, hal, pal; + socklen_t len; + char *pasvcmd = NULL; + +#ifdef INET6 + if (myctladdr.su_family == AF_INET6 && debug && + (IN6_IS_ADDR_LINKLOCAL(&myctladdr.si_su.su_sin6.sin6_addr) || + IN6_IS_ADDR_SITELOCAL(&myctladdr.si_su.su_sin6.sin6_addr))) { + warnx("use of scoped address can be troublesome"); + } +#endif + reinit: + if (passivemode) { + data_addr = myctladdr; + data = socket(data_addr.su_family, SOCK_STREAM, 0); + if (data < 0) { + warn("socket"); + return (1); + } + if ((options & SO_DEBUG) && + setsockopt(data, SOL_SOCKET, SO_DEBUG, + (void *)&on, sizeof(on)) == -1) { + if (debug) + warn("setsockopt %s (ignored)", "SO_DEBUG"); + } + result = COMPLETE + 1; + switch (data_addr.su_family) { + case AF_INET: + if (epsv4 && !epsv4bad) { + pasvcmd = "EPSV"; + result = command("EPSV"); + if (!connected) + return (1); + /* + * this code is to be friendly with broken + * BSDI ftpd + */ + if (code / 10 == 22 && code != 229) { + fputs( +"wrong server: return code must be 229\n", + ttyout); + result = COMPLETE + 1; + } + if (result != COMPLETE) { + epsv4bad = 1; + if (debug) + fputs( + "disabling epsv4 for this connection\n", + ttyout); + } + } + if (result != COMPLETE) { + pasvcmd = "PASV"; + result = command("PASV"); + if (!connected) + return (1); + } + break; +#ifdef INET6 + case AF_INET6: + pasvcmd = "EPSV"; + result = command("EPSV"); + if (!connected) + return (1); + /* this code is to be friendly with broken BSDI ftpd */ + if (code / 10 == 22 && code != 229) { + fputs( +"wrong server: return code must be 229\n", + ttyout); + result = COMPLETE + 1; + } + if (result != COMPLETE) { + pasvcmd = "LPSV"; + result = command("LPSV"); + } + if (!connected) + return (1); + break; +#endif + default: + result = COMPLETE + 1; + break; + } + if (result != COMPLETE) { + if (activefallback) { + (void)close(data); + data = -1; + passivemode = 0; +#if 0 + activefallback = 0; +#endif + goto reinit; + } + fputs("Passive mode refused.\n", ttyout); + goto bad; + } + +#define pack2(var, off) \ + (((var[(off) + 0] & 0xff) << 8) | ((var[(off) + 1] & 0xff) << 0)) +#define pack4(var, off) \ + (((var[(off) + 0] & 0xff) << 24) | ((var[(off) + 1] & 0xff) << 16) | \ + ((var[(off) + 2] & 0xff) << 8) | ((var[(off) + 3] & 0xff) << 0)) +#define UC(b) (((int)b)&0xff) + + /* + * What we've got at this point is a string of comma separated + * one-byte unsigned integer values, separated by commas. + */ + if (strcmp(pasvcmd, "PASV") == 0) { + if (data_addr.su_family != AF_INET) { + fputs( + "Passive mode AF mismatch. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + if (code / 10 == 22 && code != 227) { + fputs("wrong server: return code must be 227\n", + ttyout); + error = 1; + goto bad; + } + error = sscanf(pasv, "%u,%u,%u,%u,%u,%u", + &addr[0], &addr[1], &addr[2], &addr[3], + &port[0], &port[1]); + if (error != 6) { + fputs( +"Passive mode address scan failure. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + error = 0; + memset(&data_addr, 0, sizeof(data_addr)); + data_addr.su_family = AF_INET; + data_addr.su_len = sizeof(struct sockaddr_in); + data_addr.si_su.su_sin.sin_addr.s_addr = + htonl(pack4(addr, 0)); + data_addr.su_port = htons(pack2(port, 0)); + } else if (strcmp(pasvcmd, "LPSV") == 0) { + if (code / 10 == 22 && code != 228) { + fputs("wrong server: return code must be 228\n", + ttyout); + error = 1; + goto bad; + } + switch (data_addr.su_family) { + case AF_INET: + error = sscanf(pasv, +"%u,%u,%u,%u,%u,%u,%u,%u,%u", + &af, &hal, + &addr[0], &addr[1], &addr[2], &addr[3], + &pal, &port[0], &port[1]); + if (error != 9) { + fputs( +"Passive mode address scan failure. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + if (af != 4 || hal != 4 || pal != 2) { + fputs( +"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + + error = 0; + memset(&data_addr, 0, sizeof(data_addr)); + data_addr.su_family = AF_INET; + data_addr.su_len = sizeof(struct sockaddr_in); + data_addr.si_su.su_sin.sin_addr.s_addr = + htonl(pack4(addr, 0)); + data_addr.su_port = htons(pack2(port, 0)); + break; +#ifdef INET6 + case AF_INET6: + error = sscanf(pasv, +"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", + &af, &hal, + &addr[0], &addr[1], &addr[2], &addr[3], + &addr[4], &addr[5], &addr[6], &addr[7], + &addr[8], &addr[9], &addr[10], + &addr[11], &addr[12], &addr[13], + &addr[14], &addr[15], + &pal, &port[0], &port[1]); + if (error != 21) { + fputs( +"Passive mode address scan failure. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + if (af != 6 || hal != 16 || pal != 2) { + fputs( +"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + + error = 0; + memset(&data_addr, 0, sizeof(data_addr)); + data_addr.su_family = AF_INET6; + data_addr.su_len = sizeof(struct sockaddr_in6); + { + int i; + for (i = 0; i < sizeof(struct in6_addr); i++) { + data_addr.si_su.su_sin6.sin6_addr.s6_addr[i] = + UC(addr[i]); + } + } + data_addr.su_port = htons(pack2(port, 0)); + break; +#endif + default: + error = 1; + } + } else if (strcmp(pasvcmd, "EPSV") == 0) { + char delim[4]; + + port[0] = 0; + if (code / 10 == 22 && code != 229) { + fputs("wrong server: return code must be 229\n", + ttyout); + error = 1; + goto bad; + } + if (sscanf(pasv, "%c%c%c%d%c", &delim[0], + &delim[1], &delim[2], &port[1], + &delim[3]) != 5) { + fputs("parse error!\n", ttyout); + error = 1; + goto bad; + } + if (delim[0] != delim[1] || delim[0] != delim[2] + || delim[0] != delim[3]) { + fputs("parse error!\n", ttyout); + error = 1; + goto bad; + } + data_addr = hisctladdr; + data_addr.su_port = htons(port[1]); + } else + goto bad; + + while (xconnect(data, (struct sockaddr *)&data_addr.si_su, + data_addr.su_len) < 0) { + if (activefallback) { + (void)close(data); + data = -1; + passivemode = 0; +#if 0 + activefallback = 0; +#endif + goto reinit; + } + warn("connect for data channel"); + goto bad; + } +#ifdef IPTOS_THROUGHPUT + if (data_addr.su_family == AF_INET) { + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, + (void *)&on, sizeof(on)) == -1) { + if (debug) + warn("setsockopt %s (ignored)", + "IPTOS_THROUGHPUT"); + } + } +#endif + return (0); + } + + noport: + data_addr = myctladdr; + if (sendport) + data_addr.su_port = 0; /* let system pick one */ + if (data != -1) + (void)close(data); + data = socket(data_addr.su_family, SOCK_STREAM, 0); + if (data < 0) { + warn("socket"); + if (tmpno) + sendport = 1; + return (1); + } + if (!sendport) + if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, + (void *)&on, sizeof(on)) == -1) { + warn("setsockopt %s", "SO_REUSEADDR"); + goto bad; + } + if (bind(data, (struct sockaddr *)&data_addr.si_su, + data_addr.su_len) < 0) { + warn("bind"); + goto bad; + } + if ((options & SO_DEBUG) && + setsockopt(data, SOL_SOCKET, SO_DEBUG, + (void *)&on, sizeof(on)) == -1) { + if (debug) + warn("setsockopt %s (ignored)", "SO_DEBUG"); + } + len = sizeof(data_addr.si_su); + memset((char *)&data_addr, 0, sizeof (data_addr)); + if (getsockname(data, (struct sockaddr *)&data_addr.si_su, &len) == -1) { + warn("getsockname"); + goto bad; + } + data_addr.su_len = len; + if (xlisten(data, 1) < 0) + warn("listen"); + + if (sendport) { + char hname[NI_MAXHOST], sname[NI_MAXSERV]; + int af; + struct sockinet tmp; + + switch (data_addr.su_family) { + case AF_INET: + if (!epsv4 || epsv4bad) { + result = COMPLETE + 1; + break; + } + /* FALLTHROUGH */ +#ifdef INET6 + case AF_INET6: +#endif + af = (data_addr.su_family == AF_INET) ? 1 : 2; + tmp = data_addr; +#ifdef INET6 + if (tmp.su_family == AF_INET6) + tmp.si_su.su_sin6.sin6_scope_id = 0; +#endif + if (getnameinfo((struct sockaddr *)&tmp.si_su, + tmp.su_len, hname, sizeof(hname), sname, + sizeof(sname), NI_NUMERICHOST | NI_NUMERICSERV)) { + result = ERROR; + } else { + result = command("EPRT |%d|%s|%s|", af, hname, + sname); + if (!connected) + return (1); + if (result != COMPLETE) { + epsv4bad = 1; + if (debug) + fputs( + "disabling epsv4 for this connection\n", + ttyout); + } + } + break; + default: + result = COMPLETE + 1; + break; + } + if (result == COMPLETE) + goto skip_port; + + switch (data_addr.su_family) { + case AF_INET: + a = (char *)&data_addr.si_su.su_sin.sin_addr; + p = (char *)&data_addr.su_port; + result = command("PORT %d,%d,%d,%d,%d,%d", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + break; +#ifdef INET6 + case AF_INET6: + a = (char *)&data_addr.si_su.su_sin6.sin6_addr; + p = (char *)&data_addr.su_port; + result = command( + "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + 6, 16, + UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), + UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]), + UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]), + UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]), + 2, UC(p[0]), UC(p[1])); + break; +#endif + default: + result = COMPLETE + 1; /* xxx */ + } + if (!connected) + return (1); + skip_port: + + if (result == ERROR && sendport == -1) { + sendport = 0; + tmpno = 1; + goto noport; + } + return (result != COMPLETE); + } + if (tmpno) + sendport = 1; +#ifdef IPTOS_THROUGHPUT + if (data_addr.su_family == AF_INET) { + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, + (void *)&on, sizeof(on)) == -1) + if (debug) + warn("setsockopt %s (ignored)", + "IPTOS_THROUGHPUT"); + } +#endif + return (0); + bad: + (void)close(data); + data = -1; + if (tmpno) + sendport = 1; + return (1); +} + +FILE * +dataconn(const char *lmode) +{ + struct sockinet from; + int s, flags, rv, timeout; + struct timeval endtime, now, td; + struct pollfd pfd[1]; + socklen_t fromlen; + + if (passivemode) /* passive data connection */ + return (fdopen(data, lmode)); + + /* active mode data connection */ + + if ((flags = fcntl(data, F_GETFL, 0)) == -1) + goto dataconn_failed; /* get current socket flags */ + if (fcntl(data, F_SETFL, flags | O_NONBLOCK) == -1) + goto dataconn_failed; /* set non-blocking connect */ + + /* NOTE: we now must restore socket flags on successful exit */ + + /* limit time waiting on listening socket */ + pfd[0].fd = data; + pfd[0].events = POLLIN; + (void)gettimeofday(&endtime, NULL); /* determine end time */ + endtime.tv_sec += (quit_time > 0) ? quit_time: 60; + /* without -q, default to 60s */ + do { + (void)gettimeofday(&now, NULL); + timersub(&endtime, &now, &td); + timeout = td.tv_sec * 1000 + td.tv_usec/1000; + if (timeout < 0) + timeout = 0; + rv = xpoll(pfd, 1, timeout); + } while (rv == -1 && errno == EINTR); /* loop until poll ! EINTR */ + if (rv == -1) { + warn("poll waiting before accept"); + goto dataconn_failed; + } + if (rv == 0) { + warn("poll timeout waiting before accept"); + goto dataconn_failed; + } + + /* (non-blocking) accept the connection */ + fromlen = myctladdr.su_len; + do { + s = accept(data, (struct sockaddr *) &from.si_su, &fromlen); + } while (s == -1 && errno == EINTR); /* loop until accept ! EINTR */ + if (s == -1) { + warn("accept"); + goto dataconn_failed; + } + + (void)close(data); + data = s; + if (fcntl(data, F_SETFL, flags) == -1) /* restore socket flags */ + goto dataconn_failed; + +#ifdef IPTOS_THROUGHPUT + if (from.su_family == AF_INET) { + int tos = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, + (void *)&tos, sizeof(tos)) == -1) { + if (debug) + warn("setsockopt %s (ignored)", + "IPTOS_THROUGHPUT"); + } + } +#endif + return (fdopen(data, lmode)); + + dataconn_failed: + (void)close(data); + data = -1; + return (NULL); +} + +void +psabort(int notused) +{ + int oerrno = errno; + + sigint_raised = 1; + alarmtimer(0); + abrtflag++; + errno = oerrno; +} + +void +pswitch(int flag) +{ + sigfunc oldintr; + static struct comvars { + int connect; + char name[MAXHOSTNAMELEN]; + struct sockinet mctl; + struct sockinet hctl; + FILE *in; + FILE *out; + int tpe; + int curtpe; + int cpnd; + int sunqe; + int runqe; + int mcse; + int ntflg; + char nti[17]; + char nto[17]; + int mapflg; + char mi[MAXPATHLEN]; + char mo[MAXPATHLEN]; + } proxstruct, tmpstruct; + struct comvars *ip, *op; + + abrtflag = 0; + oldintr = xsignal(SIGINT, psabort); + if (flag) { + if (proxy) + return; + ip = &tmpstruct; + op = &proxstruct; + proxy++; + } else { + if (!proxy) + return; + ip = &proxstruct; + op = &tmpstruct; + proxy = 0; + } + ip->connect = connected; + connected = op->connect; + if (hostname) + (void)strlcpy(ip->name, hostname, sizeof(ip->name)); + else + ip->name[0] = '\0'; + hostname = op->name; + ip->hctl = hisctladdr; + hisctladdr = op->hctl; + ip->mctl = myctladdr; + myctladdr = op->mctl; + ip->in = cin; + cin = op->in; + ip->out = cout; + cout = op->out; + ip->tpe = type; + type = op->tpe; + ip->curtpe = curtype; + curtype = op->curtpe; + ip->cpnd = cpend; + cpend = op->cpnd; + ip->sunqe = sunique; + sunique = op->sunqe; + ip->runqe = runique; + runique = op->runqe; + ip->mcse = mcase; + mcase = op->mcse; + ip->ntflg = ntflag; + ntflag = op->ntflg; + (void)strlcpy(ip->nti, ntin, sizeof(ip->nti)); + (void)strlcpy(ntin, op->nti, sizeof(ntin)); + (void)strlcpy(ip->nto, ntout, sizeof(ip->nto)); + (void)strlcpy(ntout, op->nto, sizeof(ntout)); + ip->mapflg = mapflag; + mapflag = op->mapflg; + (void)strlcpy(ip->mi, mapin, sizeof(ip->mi)); + (void)strlcpy(mapin, op->mi, sizeof(mapin)); + (void)strlcpy(ip->mo, mapout, sizeof(ip->mo)); + (void)strlcpy(mapout, op->mo, sizeof(mapout)); + (void)xsignal(SIGINT, oldintr); + if (abrtflag) { + abrtflag = 0; + (*oldintr)(SIGINT); + } +} + +void +abortpt(int notused) +{ + + sigint_raised = 1; + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + ptabflg++; + mflag = 0; + abrtflag = 0; + siglongjmp(ptabort, 1); +} + +void +proxtrans(const char *cmd, const char *local, const char *remote) +{ + sigfunc oldintr; + int prox_type, nfnd; + volatile int secndflag; + char *cmd2; + +#ifdef __GNUC__ /* to shut up gcc warnings */ + (void)&oldintr; + (void)&cmd2; +#endif + + oldintr = NULL; + secndflag = 0; + if (strcmp(cmd, "RETR")) + cmd2 = "RETR"; + else + cmd2 = runique ? "STOU" : "STOR"; + if ((prox_type = type) == 0) { + if (unix_server && unix_proxy) + prox_type = TYPE_I; + else + prox_type = TYPE_A; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PASV") != COMPLETE) { + fputs("proxy server does not support third party transfers.\n", + ttyout); + return; + } + pswitch(0); + if (!connected) { + fputs("No primary connection.\n", ttyout); + pswitch(1); + code = -1; + return; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PORT %s", pasv) != COMPLETE) { + pswitch(1); + return; + } + if (sigsetjmp(ptabort, 1)) + goto abort; + oldintr = xsignal(SIGINT, abortpt); + if ((restart_point && + (command("REST " LLF, (LLT) restart_point) != CONTINUE)) + || (command("%s %s", cmd, remote) != PRELIM)) { + (void)xsignal(SIGINT, oldintr); + pswitch(1); + return; + } + sleep(2); + pswitch(1); + secndflag++; + if ((restart_point && + (command("REST " LLF, (LLT) restart_point) != CONTINUE)) + || (command("%s %s", cmd2, local) != PRELIM)) + goto abort; + ptflag++; + (void)getreply(0); + pswitch(0); + (void)getreply(0); + (void)xsignal(SIGINT, oldintr); + pswitch(1); + ptflag = 0; + fprintf(ttyout, "local: %s remote: %s\n", local, remote); + return; + abort: + if (sigsetjmp(xferabort, 1)) { + (void)xsignal(SIGINT, oldintr); + return; + } + (void)xsignal(SIGINT, abort_squared); + ptflag = 0; + if (strcmp(cmd, "RETR") && !proxy) + pswitch(1); + else if (!strcmp(cmd, "RETR") && proxy) + pswitch(0); + if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote(NULL); + } + pswitch(1); + if (ptabflg) + code = -1; + (void)xsignal(SIGINT, oldintr); + return; + } + if (cpend) + abort_remote(NULL); + pswitch(!proxy); + if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote(NULL); + pswitch(1); + if (ptabflg) + code = -1; + (void)xsignal(SIGINT, oldintr); + return; + } + } + if (cpend) + abort_remote(NULL); + pswitch(!proxy); + if (cpend) { + if ((nfnd = empty(cin, NULL, 10)) <= 0) { + if (nfnd < 0) + warn("abort"); + if (ptabflg) + code = -1; + lostpeer(0); + } + (void)getreply(0); + (void)getreply(0); + } + if (proxy) + pswitch(0); + pswitch(1); + if (ptabflg) + code = -1; + (void)xsignal(SIGINT, oldintr); +} + +void +reset(int argc, char *argv[]) +{ + int nfnd = 1; + + if (argc == 0 && argv != NULL) { + fprintf(ttyout, "usage: %s\n", argv[0]); + code = -1; + return; + } + while (nfnd > 0) { + if ((nfnd = empty(cin, NULL, 0)) < 0) { + warn("reset"); + code = -1; + lostpeer(0); + } else if (nfnd) + (void)getreply(0); + } +} + +char * +gunique(const char *local) +{ + static char new[MAXPATHLEN]; + char *cp = strrchr(local, '/'); + int d, count=0, len; + char ext = '1'; + + if (cp) + *cp = '\0'; + d = access(cp == local ? "/" : cp ? local : ".", W_OK); + if (cp) + *cp = '/'; + if (d < 0) { + warn("local: %s", local); + return (NULL); + } + len = strlcpy(new, local, sizeof(new)); + cp = &new[len]; + *cp++ = '.'; + while (!d) { + if (++count == 100) { + fputs("runique: can't find unique file name.\n", + ttyout); + return (NULL); + } + *cp++ = ext; + *cp = '\0'; + if (ext == '9') + ext = '0'; + else + ext++; + if ((d = access(new, F_OK)) < 0) + break; + if (ext != '0') + cp--; + else if (*(cp - 2) == '.') + *(cp - 1) = '1'; + else { + *(cp - 2) = *(cp - 2) + 1; + cp--; + } + } + return (new); +} + +/* + * abort_squared -- + * aborts abort_remote(). lostpeer() is called because if the user is + * too impatient to wait or there's another problem then ftp really + * needs to get back to a known state. + */ +void +abort_squared(int dummy) +{ + char msgbuf[100]; + size_t len; + + sigint_raised = 1; + alarmtimer(0); + len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n", + sizeof(msgbuf)); + write(fileno(ttyout), msgbuf, len); + lostpeer(0); + siglongjmp(xferabort, 1); +} + +void +abort_remote(FILE *din) +{ + char buf[BUFSIZ]; + int nfnd; + + if (cout == NULL) { + warnx("Lost control connection for abort."); + if (ptabflg) + code = -1; + lostpeer(0); + return; + } + /* + * send IAC in urgent mode instead of DM because 4.3BSD places oob mark + * after urgent byte rather than before as is protocol now + */ + buf[0] = IAC; + buf[1] = IP; + buf[2] = IAC; + if (send(fileno(cout), buf, 3, MSG_OOB) != 3) + warn("abort"); + fprintf(cout, "%cABOR\r\n", DM); + (void)fflush(cout); + if ((nfnd = empty(cin, din, 10)) <= 0) { + if (nfnd < 0) + warn("abort"); + if (ptabflg) + code = -1; + lostpeer(0); + } + if (din && (nfnd & 2)) { + while (read(fileno(din), buf, BUFSIZ) > 0) + continue; + } + if (getreply(0) == ERROR && code == 552) { + /* 552 needed for nic style abort */ + (void)getreply(0); + } + (void)getreply(0); +} + +void +ai_unmapped(struct addrinfo *ai) +{ +#ifdef INET6 + struct sockaddr_in6 *sin6; + struct sockaddr_in sin; + socklen_t len; + + if (ai->ai_family != AF_INET6) + return; + if (ai->ai_addrlen != sizeof(struct sockaddr_in6) || + sizeof(sin) > ai->ai_addrlen) + return; + sin6 = (struct sockaddr_in6 *)ai->ai_addr; + if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + return; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], + sizeof(sin.sin_addr)); + sin.sin_port = sin6->sin6_port; + + ai->ai_family = AF_INET; +#if HAVE_SOCKADDR_SA_LEN + sin.sin_len = len; +#endif + memcpy(ai->ai_addr, &sin, len); + ai->ai_addrlen = len; +#endif +} diff --git a/src/ftp.cat1 b/src/ftp.cat1 new file mode 100644 index 0000000..091a119 --- /dev/null +++ b/src/ftp.cat1 @@ -0,0 +1,1055 @@ +FTP(1) NetBSD Reference Manual FTP(1) + +NNAAMMEE + ffttpp - Internet file transfer program + +SSYYNNOOPPSSIISS + ffttpp [--4466AAaaddeeffggiinnppRRttvvVV] [--NN _n_e_t_r_c] [--oo _o_u_t_p_u_t] [--PP _p_o_r_t] [--rr _r_e_t_r_y] [--TT + _d_i_r,_m_a_x[,_i_n_c]] [[_u_s_e_r@]_h_o_s_t [_p_o_r_t]] [_u_s_e_r@]_h_o_s_t:[_p_a_t_h][/] + [file:///_p_a_t_h] [ftp://[_u_s_e_r[:_p_a_s_s_w_o_r_d]@]_h_o_s_t[:_p_o_r_t]/_p_a_t_h[/]] + [http://[_u_s_e_r[:_p_a_s_s_w_o_r_d]@]_h_o_s_t[:_p_o_r_t]/_p_a_t_h] [_._._.] + ffttpp --uu _u_r_l file [_._._.] + +DDEESSCCRRIIPPTTIIOONN + ffttpp is the user interface to the Internet standard File Transfer Proto- + col. The program allows a user to transfer files to and from a remote + network site. + + The last five arguments will fetch a file using the FTP or HTTP proto- + cols, or by direct copying, into the current directory. This is ideal + for scripts. Refer to _A_U_T_O_-_F_E_T_C_H_I_N_G _F_I_L_E_S below for more information. + + Options may be specified at the command line, or to the command inter- + preter. + + --44 Forces ffttpp to only use IPv4 addresses. + + --66 Forces ffttpp to only use IPv6 addresses. + + --AA Force active mode ftp. By default, ffttpp will try to use passive + mode ftp and fall back to active mode if passive is not support- + ed by the server. This option causes ffttpp to always use an ac- + tive connection. It is only useful for connecting to very old + servers that do not implement passive mode properly. + + --aa Causes ffttpp to bypass normal login procedure, and use an anony- + mous login instead. + + --dd Enables debugging. + + --ee Disables command line editing. This is useful for Emacs ange- + ftp mode. + + --ff Forces a cache reload for transfers that go through the FTP or + HTTP proxies. + + --gg Disables file name globbing. + + --ii Turns off interactive prompting during multiple file transfers. + + --nn Restrains ffttpp from attempting ``auto-login'' upon initial con- + nection. If auto-login is enabled, ffttpp will check the _._n_e_t_r_c + (see below) file in the user's home directory for an entry de- + scribing an account on the remote machine. If no entry exists, + ffttpp 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. + + --NN _n_e_t_r_c + Use _n_e_t_r_c instead of _~_/_._n_e_t_r_c. Refer to _T_H_E _._n_e_t_r_c _F_I_L_E for + more information. + + --oo _o_u_t_p_u_t + When auto-fetching files, save the contents in _o_u_t_p_u_t. _o_u_t_p_u_t + is parsed according to the _F_I_L_E _N_A_M_I_N_G _C_O_N_V_E_N_T_I_O_N_S below. If + _o_u_t_p_u_t is not `-' or doesn't start with `|', then only the first + file specified will be retrieved into _o_u_t_p_u_t; all other files + will be retrieved into the basename of their remote name. + + --pp Enable passive mode operation for use behind connection filter- + ing firewalls. This option has been deprecated as ffttpp now tries + to use passive mode by default, falling back to active mode if + the server does not support passive connections. + + --PP _p_o_r_t Sets the port number to _p_o_r_t. + + --rr _w_a_i_t Retry the connection attempt if it failed, pausing for _w_a_i_t sec- + onds. + + --RR Restart all non-proxied auto-fetches. + + --tt Enables packet tracing. + + --TT _d_i_r_e_c_t_i_o_n,_m_a_x_i_m_u_m[,_i_n_c_r_e_m_e_n_t] + Set the maximum transfer rate for _d_i_r_e_c_t_i_o_n to _m_a_x_i_m_u_m + bytes/second, and if specified, the increment to _i_n_c_r_e_m_e_n_t + bytes/second. Refer to rraattee for more information. + + --uu _u_r_l _f_i_l_e [...] + Upload files on the command line to _u_r_l where _u_r_l is one of the + ftp URL types as supported by auto-fetch (with an optional tar- + get filename for single file uploads), and _f_i_l_e is one or more + local files to be uploaded. + + --vv Enable vveerrbboossee and pprrooggrreessss. This is the default if output is + to a terminal (and in the case of pprrooggrreessss, ffttpp is the fore- + ground process). Forces ffttpp to show all responses from the re- + mote server, as well as report on data transfer statistics. + + --VV Disable vveerrbboossee and pprrooggrreessss, overriding the default of enabled + when output is to a terminal. + + The client host with which ffttpp is to communicate may be specified on the + command line. If this is done, ffttpp will immediately attempt to establish + a connection to an FTP server on that host; otherwise, ffttpp will enter its + command interpreter and await instructions from the user. When ffttpp is + awaiting commands from the user the prompt `ftp>' is provided to the us- + er. The following commands are recognized by ffttpp: + + !! [_c_o_m_m_a_n_d [_a_r_g_s]] + 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. + + $$ _m_a_c_r_o_-_n_a_m_e [_a_r_g_s] + Execute the macro _m_a_c_r_o_-_n_a_m_e that was defined with the mmaaccddeeff + command. Arguments are passed to the macro unglobbed. + + aaccccoouunntt [_p_a_s_s_w_d] + 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. + + aappppeenndd _l_o_c_a_l_-_f_i_l_e [_r_e_m_o_t_e_-_f_i_l_e] + Append a local file to a file on the remote machine. If + _r_e_m_o_t_e_-_f_i_l_e is left unspecified, the local file name is used + in naming the remote file after being altered by any nnttrraannss + or nnmmaapp setting. File transfer uses the current settings for + ttyyppee, ffoorrmmaatt, mmooddee, and ssttrruuccttuurree. + + aasscciiii Set the file transfer ttyyppee to network ASCII. This is the de- + fault type. + + bbeellll Arrange that a bell be sounded after each file transfer com- + mand is completed. + + bbiinnaarryy Set the file transfer ttyyppee to support binary image transfer. + + bbyyee Terminate the FTP session with the remote server and exit + ffttpp. An end of file will also terminate the session and ex- + it. + + ccaassee Toggle remote computer file name case mapping during ggeett, + mmggeett and mmppuutt commands. When ccaassee 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. + + ccdd _r_e_m_o_t_e_-_d_i_r_e_c_t_o_r_y + Change the working directory on the remote machine to _r_e_m_o_t_e_- + _d_i_r_e_c_t_o_r_y. + + ccdduupp Change the remote machine working directory to the parent of + the current remote machine working directory. + + cchhmmoodd _m_o_d_e _r_e_m_o_t_e_-_f_i_l_e + Change the permission modes of the file _r_e_m_o_t_e_-_f_i_l_e on the + remote system to _m_o_d_e. + + cclloossee Terminate the FTP session with the remote server, and return + to the command interpreter. Any defined macros are erased. + + ccrr Toggle carriage return stripping during ascii type file re- + trieval. Records are denoted by a carriage return/linefeed + sequence during ascii type file transfer. When ccrr is on (the + default), carriage returns are stripped from this sequence to + conform with the UNIX single linefeed record delimiter. + Records on non-UNIX remote systems may contain single line- + feeds; when an ascii type transfer is made, these linefeeds + may be distinguished from a record delimiter only when ccrr is + off. + + ddeebbuugg [_d_e_b_u_g_-_v_a_l_u_e] + Toggle debugging mode. If an optional _d_e_b_u_g_-_v_a_l_u_e is speci- + fied it is used to set the debugging level. When debugging + is on, ffttpp prints each command sent to the remote machine, + preceded by the string `-->' + + ddeelleettee _r_e_m_o_t_e_-_f_i_l_e + Delete the file _r_e_m_o_t_e_-_f_i_l_e on the remote machine. + + ddiirr [_r_e_m_o_t_e_-_p_a_t_h [_l_o_c_a_l_-_f_i_l_e]] + Print a listing of the contents of a directory on the remote + machine. The listing includes any system-dependent informa- + tion that the server chooses to include; for example, most + UNIX systems will produce output from the command `ls -l'. + If _r_e_m_o_t_e_-_p_a_t_h is left unspecified, the current working di- + rectory is used. If interactive prompting is on, ffttpp will + prompt the user to verify that the last argument is indeed + the target local file for receiving ddiirr output. If no local + file is specified, or if _l_o_c_a_l_-_f_i_l_e is `--', the output is + sent to the terminal. + + ddiissccoonnnneecctt A synonym for cclloossee. + + eeddiitt Toggle command line editing, and context sensitive command + and file completion. This is automatically enabled if input + is from a terminal, and disabled otherwise. + + eeppssvv44 Toggle the use of the extended EPSV and EPRT commands on IPv4 + connections; first try EPSV / EPRT, and then PASV / PORT. + This is enabled by default. If an extended command fails + then this option will be temporarily disabled for the dura- + tion of the current connection, or until eeppssvv44 is executed + again. + + eexxiitt A synonym for bbyyee. + + ffeeaattuurreess Display what features the remote server supports (using the + FEAT command). + + ffggeett _l_o_c_a_l_f_i_l_e + Retrieve the files listed in _l_o_c_a_l_f_i_l_e, which has one line + per filename. + + ffoorrmm _f_o_r_m_a_t + Set the file transfer ffoorrmm to _f_o_r_m_a_t. The default (and only + supported) format is ``non-print''. + + ffttpp _h_o_s_t [_p_o_r_t] + A synonym for ooppeenn. + + ggaattee [_h_o_s_t [_p_o_r_t]] + 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 FTPSERVER environment variable). If _h_o_s_t + is given, then gate-ftp mode will be enabled, and the gate- + ftp server will be set to _h_o_s_t. If _p_o_r_t is also given, that + will be used as the port to connect to on the gate-ftp serv- + er. + + ggeett _r_e_m_o_t_e_-_f_i_l_e [_l_o_c_a_l_-_f_i_l_e] + Retrieve the _r_e_m_o_t_e_-_f_i_l_e 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 ccaassee, nnttrraannss, and nnmmaapp settings. The current + settings for ttyyppee, ffoorrmm, mmooddee, and ssttrruuccttuurree are used while + transferring the file. + + gglloobb Toggle filename expansion for mmddeelleettee, mmggeett, mmppuutt, and + mmrreeggeett. If globbing is turned off with gglloobb, the file name + arguments are taken literally and not expanded. Globbing for + mmppuutt is done as in csh(1). For mmddeelleettee, mmggeett, and mmrreeggeett, + each remote file name is expanded separately on the remote + machine and the lists are not merged. Expansion of a direc- + tory 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 `mls remote-files -' Note: mmggeett, mmppuutt and mmrreeggeett are + not meant to transfer entire directory subtrees of files. + That can be done by transferring a tar(1) archive of the sub- + tree (in binary mode). + + hhaasshh [_s_i_z_e] + Toggle hash-sign (``#'') printing for each data block trans- + ferred. The size of a data block defaults to 1024 bytes. + This can be changed by specifying _s_i_z_e in bytes. Enabling + hhaasshh disables pprrooggrreessss. + + hheellpp [_c_o_m_m_a_n_d] + Print an informative message about the meaning of _c_o_m_m_a_n_d. + If no argument is given, ffttpp prints a list of the known com- + mands. + + iiddllee [_s_e_c_o_n_d_s] + Set the inactivity timer on the remote server to _s_e_c_o_n_d_s sec- + onds. If _s_e_c_o_n_d_s is omitted, the current inactivity timer is + printed. + + iimmaaggee A synonym for bbiinnaarryy. + + llccdd [_d_i_r_e_c_t_o_r_y] + Change the working directory on the local machine. If no + _d_i_r_e_c_t_o_r_y is specified, the user's home directory is used. + + lleessss _f_i_l_e A synonym for ppaaggee. + + llppaaggee _l_o_c_a_l_-_f_i_l_e + Display _l_o_c_a_l_-_f_i_l_e with the program specified by the sseett + ppaaggeerr option. + + llppwwdd Print the working directory on the local machine. + + llss [_r_e_m_o_t_e_-_p_a_t_h [_l_o_c_a_l_-_f_i_l_e]] + A synonym for ddiirr. + + mmaaccddeeff _m_a_c_r_o_-_n_a_m_e + Define a macro. Subsequent lines are stored as the macro + _m_a_c_r_o_-_n_a_m_e; 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 un- + til a cclloossee command is executed. The macro processor inter- + prets `$' and `\' 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 `\' followed by any character is replaced by that charac- + ter. Use the `\' to prevent special treatment of the `$'. + + mmddeelleettee [_r_e_m_o_t_e_-_f_i_l_e_s] + Delete the _r_e_m_o_t_e_-_f_i_l_e_s on the remote machine. + + mmddiirr _r_e_m_o_t_e_-_f_i_l_e_s _l_o_c_a_l_-_f_i_l_e + Like ddiirr, except multiple remote files may be specified. If + interactive prompting is on, ffttpp will prompt the user to ver- + ify that the last argument is indeed the target local file + for receiving mmddiirr output. + + mmggeett _r_e_m_o_t_e_-_f_i_l_e_s + Expand the _r_e_m_o_t_e_-_f_i_l_e_s on the remote machine and do a ggeett + for each file name thus produced. See gglloobb for details on + the filename expansion. Resulting file names will then be + processed according to ccaassee, nnttrraannss, and nnmmaapp settings. + Files are transferred into the local working directory, which + can be changed with `lcd directory'; new local directories + can be created with `! mkdir directory'. + + mmkkddiirr _d_i_r_e_c_t_o_r_y_-_n_a_m_e + Make a directory on the remote machine. + + mmllss _r_e_m_o_t_e_-_f_i_l_e_s _l_o_c_a_l_-_f_i_l_e + Like llss, except multiple remote files may be specified, and + the _l_o_c_a_l_-_f_i_l_e must be specified. If interactive prompting + is on, ffttpp will prompt the user to verify that the last argu- + ment is indeed the target local file for receiving mmllss out- + put. + + mmllssdd [_r_e_m_o_t_e_-_p_a_t_h] + Display the contents of _r_e_m_o_t_e_-_p_a_t_h (which should default to + the current directory if not given) in a machine-parsable + form, using MLSD. The format of display can be changed with + `remopts mlst ...'. + + mmllsstt [_r_e_m_o_t_e_-_p_a_t_h] + Display the details about _r_e_m_o_t_e_-_p_a_t_h (which should default + to the current directory if not given) in a machine-parsable + form, using MLST. The format of display can be changed with + `remopts mlst ...'. + + mmooddee _m_o_d_e_-_n_a_m_e + Set the file transfer mmooddee to _m_o_d_e_-_n_a_m_e. The default (and + only supported) mode is ``stream''. + + mmooddttiimmee _r_e_m_o_t_e_-_f_i_l_e + Show the last modification time of the file on the remote ma- + chine. + + mmoorree _f_i_l_e A synonym for ppaaggee. + + mmppuutt _l_o_c_a_l_-_f_i_l_e_s + Expand wild cards in the list of local files given as argu- + ments and do a ppuutt for each file in the resulting list. See + gglloobb for details of filename expansion. Resulting file names + will then be processed according to nnttrraannss and nnmmaapp settings. + + mmrreeggeett _r_e_m_o_t_e_-_f_i_l_e_s + As per mmggeett, but performs a rreeggeett instead of ggeett. + + mmsseenndd _l_o_c_a_l_-_f_i_l_e_s + A synonym for mmppuutt. + + nneewweerr _r_e_m_o_t_e_-_f_i_l_e [_l_o_c_a_l_-_f_i_l_e] + 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 nneewweerr. Otherwise, this command is identical to + _g_e_t. + + nnlliisstt [_r_e_m_o_t_e_-_p_a_t_h [_l_o_c_a_l_-_f_i_l_e]] + A synonym for llss. + + nnmmaapp [_i_n_p_a_t_t_e_r_n _o_u_t_p_a_t_t_e_r_n] + 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 + mmppuutt commands and ppuutt commands issued without a specified re- + mote target filename. If arguments are specified, local + filenames are mapped during mmggeett commands and ggeett commands + issued without a specified local target filename. This com- + mand is useful when connecting to a non-UNIX remote computer + with different file naming conventions or practices. The + mapping follows the pattern set by _i_n_p_a_t_t_e_r_n and _o_u_t_p_a_t_t_e_r_n. + [_I_n_p_a_t_t_e_r_n] is a template for incoming filenames (which may + have already been processed according to the nnttrraannss and ccaassee + settings). Variable templating is accomplished by including + the sequences `$1', `$2', ..., `$9' in _i_n_p_a_t_t_e_r_n. Use `\' to + prevent this special treatment of the `$' character. All + other characters are treated literally, and are used to de- + termine the nnmmaapp [_i_n_p_a_t_t_e_r_n] variable values. For example, + given _i_n_p_a_t_t_e_r_n $1.$2 and the remote file name "mydata.data", + $1 would have the value "mydata", and $2 would have the value + "data". The _o_u_t_p_a_t_t_e_r_n determines the resulting mapped file- + name. The sequences `$1', `$2', ...., `$9' are replaced by + any value resulting from the _i_n_p_a_t_t_e_r_n template. The se- + quence `$0' is replace by the original filename. Additional- + ly, the sequence `[_s_e_q_1, _s_e_q_2]' is replaced by [_s_e_q_1] if _s_e_q_1 + is not a null string; otherwise it is replaced by _s_e_q_2. For + example, the command + + nmap $1.$2.$3 [$1,$2].[$2,file] + + would yield the output filename "myfile.data" for input file- + names "myfile.data" and "myfile.data.old", "myfile.file" for + the input filename "myfile", and "myfile.myfile" for the in- + put filename ".myfile". Spaces may be included in + _o_u_t_p_a_t_t_e_r_n, as in the example: `nmap $1 sed "s/ *$//" > $1' + . Use the `\' character to prevent special treatment of the + `$','[',']', and `,' characters. + + nnttrraannss [_i_n_c_h_a_r_s [_o_u_t_c_h_a_r_s]] + Set or unset the filename character translation mechanism. + If no arguments are specified, the filename character trans- + lation mechanism is unset. If arguments are specified, char- + acters in remote filenames are translated during mmppuutt com- + mands and ppuutt commands issued without a specified remote tar- + get filename. If arguments are specified, characters in lo- + cal filenames are translated during mmggeett commands and ggeett + commands issued without a specified local target filename. + This command is useful when connecting to a non-UNIX remote + computer with different file naming conventions or practices. + Characters in a filename matching a character in _i_n_c_h_a_r_s are + replaced with the corresponding character in _o_u_t_c_h_a_r_s. If + the character's position in _i_n_c_h_a_r_s is longer than the length + of _o_u_t_c_h_a_r_s, the character is deleted from the file name. + + ooppeenn _h_o_s_t [_p_o_r_t] + Establish a connection to the specified _h_o_s_t FTP server. An + optional port number may be supplied, in which case, ffttpp will + attempt to contact an FTP server at that port. If the aauuttoo-- + llooggiinn option is on (default), ffttpp will also attempt to auto- + matically log the user in to the FTP server (see below). + + ppaaggee _f_i_l_e Retrieve ffiillee and display with the program specified by the + sseett ppaaggeerr option. + + ppaassssiivvee [aauuttoo] + Toggle passive mode (if no arguments are given). If aauuttoo is + given, act as if FTPMODE is set to `auto'. If passive mode + is turned on (default), ffttpp will send a PASV command for all + data connections instead of a PORT command. The PASV command + requests that the remote server open a port for the data con- + nection 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 PORT command, the client lis- + tens on a port and sends that address to the remote server, + who connects back to it. Passive mode is useful when using + ffttpp through a gateway router or host that controls the direc- + tionality of traffic. (Note that though FTP servers are re- + quired to support the PASV command by RFC 1123, some do not.) + + ppddiirr [_r_e_m_o_t_e_-_p_a_t_h] + Perform ddiirr [_r_e_m_o_t_e_-_p_a_t_h], and display the result with the + program specified by the sseett ppaaggeerr option. + + ppllss [_r_e_m_o_t_e_-_p_a_t_h] + Perform llss [_r_e_m_o_t_e_-_p_a_t_h], and display the result with the + program specified by the sseett ppaaggeerr option. + + ppmmllssdd [_r_e_m_o_t_e_-_p_a_t_h] + Perform mmllssdd [_r_e_m_o_t_e_-_p_a_t_h], and display the result with the + program specified by the sseett ppaaggeerr option. + + pprreesseerrvvee Toggle preservation of modification times on retrieved files. + + pprrooggrreessss Toggle display of transfer progress bar. The progress bar + will be disabled for a transfer that has _l_o_c_a_l_-_f_i_l_e as `--' or + a command that starts with `|'. Refer to _F_I_L_E _N_A_M_I_N_G + _C_O_N_V_E_N_T_I_O_N_S for more information. Enabling pprrooggrreessss disables + hhaasshh. + + pprroommpptt Toggle interactive prompting. Interactive prompting occurs + during multiple file transfers to allow the user to selec- + tively retrieve or store files. If prompting is turned off + (default is on), any mmggeett or mmppuutt will transfer all files, + and any mmddeelleettee will delete all files. + + When prompting is on, the following commands are available at + a prompt: + + aa Answer `yes' to the current file, and automatically + answer `yes' to any remaining files for the current + command. + + nn Answer `no', and do not transfer the file. + + pp Answer `yes' to the current file, and turn off + prompt mode (as is ``prompt off'' had been given). + + qq Terminate the current operation. + + yy Answer `yes', and transfer the file. + + ?? Display a help message. + + Any other reponse will answer `yes' to the current file. + + pprrooxxyy _f_t_p_-_c_o_m_m_a_n_d + Execute an ftp command on a secondary control connection. + This command allows simultaneous connection to two remote FTP + servers for transferring files between the two servers. The + first pprrooxxyy command should be an ooppeenn, to establish the sec- + ondary control connection. Enter the command "proxy ?" to + see other FTP commands executable on the secondary connec- + tion. The following commands behave differently when pref- + aced by pprrooxxyy: ooppeenn will not define new macros during the au- + to-login process, cclloossee will not erase existing macro defini- + tions, ggeett and mmggeett transfer files from the host on the pri- + mary control connection to the host on the secondary control + connection, and ppuutt, mmppuutt, and aappppeenndd transfer files from the + host on the secondary control connection to the host on the + primary control connection. Third party file transfers de- + pend upon support of the FTP protocol PASV command by the + server on the secondary control connection. + + ppuutt _l_o_c_a_l_-_f_i_l_e [_r_e_m_o_t_e_-_f_i_l_e] + Store a local file on the remote machine. If _r_e_m_o_t_e_-_f_i_l_e is + left unspecified, the local file name is used after process- + ing according to any nnttrraannss or nnmmaapp settings in naming the + remote file. File transfer uses the current settings for + ttyyppee, ffoorrmmaatt, mmooddee, and ssttrruuccttuurree. + + ppwwdd Print the name of the current working directory on the remote + machine. + + qquuiitt A synonym for bbyyee. + + qquuoottee _a_r_g_1 _a_r_g_2 _._._. + The arguments specified are sent, verbatim, to the remote FTP + server. + + rraattee _d_i_r_e_c_t_i_o_n [_m_a_x_i_m_u_m [_i_n_c_r_e_m_e_n_t]] + Throttle the maximum transfer rate to _m_a_x_i_m_u_m bytes/second. + If _m_a_x_i_m_u_m is 0, disable the throttle. + + _d_i_r_e_c_t_i_o_n may be one of: + aallll Both directions. + ggeett Incoming transfers. + ppuutt Outgoing transfers. + + _m_a_x_i_m_u_m can by modified on the fly by _i_n_c_r_e_m_e_n_t bytes (de- + fault: 1024) each time a given signal is received: + + SIGUSR1 Increment _m_a_x_i_m_u_m by _i_n_c_r_e_m_e_n_t bytes. + + SIGUSR2 Decrement _m_a_x_i_m_u_m by _i_n_c_r_e_m_e_n_t bytes. The re- + sult must be a positive number. + + If _m_a_x_i_m_u_m is not supplied, the current throttle rates are + displayed. + + Note: rraattee is not yet implemented for ascii mode transfers. + + rrccvvbbuuff _s_i_z_e + Set the size of the socket receive buffer to _s_i_z_e. + + rreeccvv _r_e_m_o_t_e_-_f_i_l_e [_l_o_c_a_l_-_f_i_l_e] + A synonym for ggeett. + + rreeggeett _r_e_m_o_t_e_-_f_i_l_e [_l_o_c_a_l_-_f_i_l_e] + rreeggeett acts like ggeett, except that if _l_o_c_a_l_-_f_i_l_e exists and is + smaller than _r_e_m_o_t_e_-_f_i_l_e, _l_o_c_a_l_-_f_i_l_e is presumed to be a par- + tially transferred copy of _r_e_m_o_t_e_-_f_i_l_e 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. + + rreemmooppttss _c_o_m_m_a_n_d [_c_o_m_m_a_n_d_-_o_p_t_i_o_n_s] + Set options on the remote FTP server for _c_o_m_m_a_n_d to _c_o_m_m_a_n_d_- + _o_p_t_i_o_n_s (whose absence is handled on a command-specific ba- + sis). Remote FTP commands known to support options include: + `MLST' (used for MLSD and MLST). + + rreennaammee [_f_r_o_m [_t_o]] + Rename the file _f_r_o_m on the remote machine, to the file _t_o. + + rreesseett Clear reply queue. This command re-synchronizes command/re- + ply sequencing with the remote FTP server. Resynchronization + may be necessary following a violation of the FTP protocol by + the remote server. + + rreessttaarrtt _m_a_r_k_e_r + Restart the immediately following ggeett or ppuutt at the indicated + _m_a_r_k_e_r. On UNIX systems, marker is usually a byte offset in- + to the file. + + rrhheellpp [_c_o_m_m_a_n_d_-_n_a_m_e] + Request help from the remote FTP server. If a _c_o_m_m_a_n_d_-_n_a_m_e + is specified it is supplied to the server as well. + + rrmmddiirr _d_i_r_e_c_t_o_r_y_-_n_a_m_e + Delete a directory on the remote machine. + + rrssttaattuuss [_r_e_m_o_t_e_-_f_i_l_e] + With no arguments, show status of remote machine. If _r_e_m_o_t_e_- + _f_i_l_e is specified, show status of _r_e_m_o_t_e_-_f_i_l_e on remote ma- + chine. + + rruunniiqquuee Toggle storing of files on the local system with unique file- + names. If a file already exists with a name equal to the + target local filename for a ggeett or mmggeett 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 rruunniiqquuee will not + affect local files generated from a shell command (see be- + low). The default value is off. + + sseenndd _l_o_c_a_l_-_f_i_l_e [_r_e_m_o_t_e_-_f_i_l_e] + A synonym for ppuutt. + + sseennddppoorrtt Toggle the use of PORT commands. By default, ffttpp will at- + tempt to use a PORT command when establishing a connection + for each data transfer. The use of PORT commands can prevent + delays when performing multiple file transfers. If the PORT + command fails, ffttpp will use the default data port. When the + use of PORT commands is disabled, no attempt will be made to + use PORT commands for each data transfer. This is useful for + certain FTP implementations which do ignore PORT commands + but, incorrectly, indicate they've been accepted. + + sseett [_o_p_t_i_o_n _v_a_l_u_e] + Set _o_p_t_i_o_n to _v_a_l_u_e. If _o_p_t_i_o_n and _v_a_l_u_e are not given, dis- + play all of the options and their values. The currently sup- + ported options are: + + anonpass Defaults to $FTPANONPASS + + ftp_proxy Defaults to $ftp_proxy. + + http_proxy Defaults to $http_proxy. + + no_proxy Defaults to $no_proxy. + + pager Defaults to $PAGER. + + prompt Defaults to $FTPPROMPT. + + rprompt Defaults to $FTPRPROMPT. + + ssiittee _a_r_g_1 _a_r_g_2 _._._. + The arguments specified are sent, verbatim, to the remote FTP + server as a SITE command. + + ssiizzee _r_e_m_o_t_e_-_f_i_l_e + Return size of _r_e_m_o_t_e_-_f_i_l_e on remote machine. + + ssnnddbbuuff _s_i_z_e + Set the size of the socket send buffer to _s_i_z_e. + + ssttaattuuss Show the current status of ffttpp. + + ssttrruucctt _s_t_r_u_c_t_-_n_a_m_e + Set the file transfer _s_t_r_u_c_t_u_r_e to _s_t_r_u_c_t_-_n_a_m_e. The default + (and only supported) structure is ``file''. + + ssuunniiqquuee Toggle storing of files on remote machine under unique file + names. The remote FTP server must support FTP protocol STOU + command for successful completion. The remote server will + report unique name. Default value is off. + + ssyysstteemm Show the type of operating system running on the remote ma- + chine. + + tteenneexx Set the file transfer type to that needed to talk to TENEX + machines. + + tthhrroottttllee A synonym for rraattee. + + ttrraaccee Toggle packet tracing. + + ttyyppee [_t_y_p_e_-_n_a_m_e] + Set the file transfer ttyyppee to _t_y_p_e_-_n_a_m_e. If no type is spec- + ified, the current type is printed. The default type is net- + work ASCII. + + uummaasskk [_n_e_w_m_a_s_k] + Set the default umask on the remote server to _n_e_w_m_a_s_k. If + _n_e_w_m_a_s_k is omitted, the current umask is printed. + + uunnsseett _o_p_t_i_o_n + Unset _o_p_t_i_o_n. Refer to sseett for more information. + + uussaaggee _c_o_m_m_a_n_d + Print the usage message for _c_o_m_m_a_n_d. + + uusseerr _u_s_e_r_-_n_a_m_e [_p_a_s_s_w_o_r_d [_a_c_c_o_u_n_t]] + Identify yourself to the remote FTP server. If the _p_a_s_s_w_o_r_d + is not specified and the server requires it, ffttpp will prompt + the user for it (after disabling local echo). If an _a_c_c_o_u_n_t + field is not specified, and the FTP server requires it, the + user will be prompted for it. If an _a_c_c_o_u_n_t field is speci- + fied, 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 ffttpp is invoked + with ``auto-login'' disabled, this process is done automati- + cally on initial connection to the FTP server. + + vveerrbboossee Toggle verbose mode. In verbose mode, all responses from the + FTP server are displayed to the user. In addition, if ver- + bose is on, when a file transfer completes, statistics re- + garding the efficiency of the transfer are reported. By de- + fault, verbose is on. + + xxffeerrbbuuff _s_i_z_e + Set the size of the socket send and receive buffers to _s_i_z_e. + + ?? [_c_o_m_m_a_n_d] + A synonym for hheellpp. + + Command arguments which have embedded spaces may be quoted with quote `"' + marks. + + Commands which toggle settings can take an explicit oonn or ooffff argument to + force the setting appropriately. + + Commands which take a byte count as an argument (e.g., hhaasshh, rraattee, and + xxffeerrbbuuff) support an optional suffix on the argument which changes the in- + terpretation of the argument. Supported suffixes are: + b Causes no modification. (Optional) + k Kilo; multiply the argument by 1024 + m Mega; multiply the argument by 1048576 + g Giga; multiply the argument by 1073741824 + + If ffttpp receives a SIGINFO (see the ``status'' argument of stty(1)) or + 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. + +AAUUTTOO--FFEETTCCHHIINNGG FFIILLEESS + In addition to standard commands, this version of ffttpp supports an auto- + fetch feature. To enable auto-fetch, simply pass the list of host- + names/files on the command line. + + The following formats are valid syntax for an auto-fetch element: + + [user@]host:[path][/] + ``Classic'' FTP format. + + If _p_a_t_h contains a glob character and globbing is enabled, (see + gglloobb), then the equivalent of `mget path' is performed. + + If the directory component of _p_a_t_h contains no globbing characters, + it is stored locally with the name basename (see basename(1)) of + ppaatthh, 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] + An FTP URL, retrieved using the FTP protocol if sseett ffttpp__pprrooxxyy isn't + defined. Otherwise, transfer the URL using HTTP via the proxy de- + fined in sseett ffttpp__pprrooxxyy. If sseett ffttpp__pprrooxxyy isn't defined and _u_s_e_r is + given, login as _u_s_e_r. In this case, use _p_a_s_s_w_o_r_d if supplied, oth- + erwise prompt the user for one. + + In order to be compliant with RRFFCC 11773388, ffttpp strips the leading `/' + from _p_a_t_h, resulting in a transfer relative from the default login + directory of the user. If the _/ directory is required, use a lead- + ing path of ``%2F''. If a user's home directory is required (and + the remote server supports the syntax), use a leading path of + ``%7Euser/''. For example, to retrieve _/_e_t_c_/_m_o_t_d from `localhost' + as the user `myname' with the password `mypass', use + ``ftp://myname:mypass@localhost/%2fetc/motd'' + + If a suffix of `;type=A' or `;type=I' is supplied, then the trans- + fer type will take place as ascii or binary (respectively). The + default transfer type is binary. + + http://[user[:password]@]host[:port]/path + An HTTP URL, retrieved using the HTTP protocol. If sseett hhttttpp__pprrooxxyy + is defined, it is used as a URL to an HTTP proxy server. If HTTP + authorisation is required to retrieve _p_a_t_h, and `user' (and option- + ally `password') is in the URL, use them for the first attempt to + authenticate. + + file:///path + A local URL, copied from _/_p_a_t_h. + + Unless noted otherwise above, and --oo _o_u_t_p_u_t is not given, the file is + stored in the current directory as the basename(1) of _p_a_t_h. + + If a classic format or an FTP URL format has a trailing `/' or an empty + _p_a_t_h component, then ffttpp will connect to the site and ccdd to the directory + given as the path, and leave the user in interactive mode ready for fur- + ther input. This will not work if sseett ffttpp__pprrooxxyy is being used. + + Direct HTTP transfers use HTTP 1.1. Proxied FTP and HTTP transfers use + HTTP 1.0. + + If --RR is given, all auto-fetches that don't go via the FTP or HTTP prox- + ies will be restarted. For FTP, this is implemented by using rreeggeett in- + stead of ggeett. For HTTP, this is implemented by using the `Range: bytes=' + HTTP/1.1 directive. + + If WWW or proxy WWW authentication is required, you will be prompted to + enter a username and password to authenticate with. + + When specifying IPv6 numeric addresses in a URL, you need to surround the + address in square brackets. E.g.: ``ftp://[::1]:21/''. This is because + colons are used in IPv6 numeric address as well as being the separator + for the port number. + +AABBOORRTTIINNGG AA FFIILLEE TTRRAANNSSFFEERR + 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 FTP protocol 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 ABOR + processing. If the remote server does not support the ABOR command, the + prompt will not appear until the remote server has completed sending the + requested file. + + If the terminal interrupt key sequence is used whilst ffttpp 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. + +FFIILLEE NNAAMMIINNGG CCOONNVVEENNTTIIOONNSS + Files specified as arguments to ffttpp commands are processed according to + the following rules. + + 1. If the file name `--' is specified, the _s_t_d_i_n (for reading) or _s_t_d_o_u_t + (for writing) is used. + + 2. If the first character of the file name is `|', the remainder of the + argument is interpreted as a shell command. ffttpp then forks a shell, + using popen(3) with the argument supplied, and reads (writes) from + the stdout (stdin). If the shell command includes spaces, the argu- + ment must be quoted; e.g. ``"| ls -lt"''. A particularly useful + example of this mechanism is: ``dir "" |more''. + + 3. Failing the above checks, if ``globbing'' is enabled, local file + names are expanded according to the rules used in the csh(1); c.f. + the gglloobb command. If the ffttpp command expects a single local file + (e.g. ppuutt), only the first filename generated by the "globbing" op- + eration is used. + + 4. For mmggeett commands and ggeett commands with unspecified local file + names, the local filename is the remote filename, which may be al- + tered by a ccaassee, nnttrraannss, or nnmmaapp setting. The resulting filename + may then be altered if rruunniiqquuee is on. + + 5. For mmppuutt commands and ppuutt commands with unspecified remote file + names, the remote filename is the local filename, which may be al- + tered by a nnttrraannss or nnmmaapp setting. The resulting filename may then + be altered by the remote server if ssuunniiqquuee is on. + +FFIILLEE TTRRAANNSSFFEERR PPAARRAAMMEETTEERRSS + The FTP specification specifies many parameters which may affect a file + transfer. The ttyyppee may be one of ``ascii'', ``image'' (binary), + ``ebcdic'', and ``local byte size'' (for PDP-10's and PDP-20's mostly). + ffttpp supports the ascii and image types of file transfer, plus local byte + size 8 for tteenneexx mode transfers. + + ffttpp supports only the default values for the remaining file transfer pa- + rameters: mmooddee, ffoorrmm, and ssttrruucctt. + +TTHHEE ..nneettrrcc FFIILLEE + The _._n_e_t_r_c file contains login and initialization information used by the + auto-login process. It resides in the user's home directory, unless + overridden with the --NN _n_e_t_r_c option, or specified in the NETRC environ- + ment variable. The following tokens are recognized; they may be separat- + ed by spaces, tabs, or new-lines: + + mmaacchhiinnee _n_a_m_e + Identify a remote machine _n_a_m_e. The auto-login process search- + es the _._n_e_t_r_c file for a mmaacchhiinnee token that matches the remote + machine specified on the ffttpp command line or as an ooppeenn command + argument. Once a match is made, the subsequent _._n_e_t_r_c tokens + are processed, stopping when the end of file is reached or an- + other mmaacchhiinnee or a ddeeffaauulltt token is encountered. + + ddeeffaauulltt This is the same as mmaacchhiinnee _n_a_m_e except that ddeeffaauulltt matches + any name. There can be only one ddeeffaauulltt token, and it must be + after all mmaacchhiinnee tokens. This is normally used as: + + default login anonymous password user@site + + thereby giving the user an automatic anonymous FTP login to ma- + chines not specified in _._n_e_t_r_c. This can be overridden by us- + ing the --nn flag to disable auto-login. + + llooggiinn _n_a_m_e + Identify a user on the remote machine. If this token is pre- + sent, the auto-login process will initiate a login using the + specified _n_a_m_e. + + ppaasssswwoorrdd _s_t_r_i_n_g + 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 _._n_e_t_r_c file for any user other + than _a_n_o_n_y_m_o_u_s, ffttpp will abort the auto-login process if the + _._n_e_t_r_c is readable by anyone besides the user. + + aaccccoouunntt _s_t_r_i_n_g + Supply an additional account password. If this token is pre- + sent, 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 ACCT command if it + does not. + + mmaaccddeeff _n_a_m_e + Define a macro. This token functions like the ffttpp mmaaccddeeff com- + mand functions. A macro is defined with the specified name; + its contents begin with the next _._n_e_t_r_c line and continue until + a blank line (consecutive new-line characters) is encountered. + If a macro named iinniitt is defined, it is automatically executed + as the last step in the auto-login process. + +CCOOMMMMAANNDD LLIINNEE EEDDIITTIINNGG + ffttpp supports interactive command line editing, via the editline(3) li- + brary. It is enabled with the eeddiitt 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. + + The editline(3) library is configured with a _._e_d_i_t_r_c file - refer to + editrc(5) for more information. + + An extra key binding is available to ffttpp to provide context sensitive + command and filename completion (including remote file completion). To + use this, bind a key to the editline(3) command ffttpp--ccoommpplleettee. By de- + fault, this is bound to the TAB key. + +CCOOMMMMAANNDD LLIINNEE PPRROOMMPPTT + By default, ffttpp displays a command line prompt of ``ftp>'' to the user. + This can be changed with the sseett pprroommpptt command. + + A prompt can be displayed on the right side of the screen (after the com- + mand input) with the sseett rrpprroommpptt command. + + The following formatting sequences are replaced by the given information: + + %/ The current remote working directory. + + %c[[0]_n], %.[[0]_n] + The trailing component of the current remote working directo- + ry, or _n trailing components if a digit _n is given. If _n be- + gins with `0', the number of skipped components precede the + trailing component(s) in the format ``/_<_s_k_i_p_p_e_d_>trailing'' + (for `%c') or ``...trailing'' (for `%.'). + + %M The remote host name. + + %m The remote host name, up to the first `.'. + + %n The remote user name. + + %% A single `%'. + +EENNVVIIRROONNMMEENNTT + ffttpp uses the following environment variables. + + FTPANONPASS Password to send in an anonymous FTP transfer. Defaults + to ```whoami`@''. + + FTPMODE Overrides the default operation mode. Support values are: + + active active mode FTP only + + auto automatic determination of passive or active + (this is the default) + + gate gate-ftp mode + + passive passive mode FTP only + + FTPPROMPT Command-line prompt to use. Defaults to ``ftp>''. Refer + to _C_O_M_M_A_N_D _L_I_N_E _P_R_O_M_P_T for more information. + + FTPRPROMPT Command-line right side prompt to use. Defaults to ``''. + Refer to _C_O_M_M_A_N_D _L_I_N_E _P_R_O_M_P_T for more information. + + FTPSERVER Host to use as gate-ftp server when ggaattee is enabled. + + FTPSERVERPORT Port to use when connecting to gate-ftp server when ggaattee + is enabled. Default is port returned by a ggeettsseerrvvbbyynnaammee() + lookup of ``ftpgate/tcp''. + + HOME For default location of a _._n_e_t_r_c file, if one exists. + + NETRC An alternate location of the _._n_e_t_r_c file. + + PAGER Used by various commands to display files. Defaults to + more(1) if empty or not set. + + SHELL For default shell. + + ftp_proxy URL of FTP proxy to use when making FTP URL requests (if + not defined, use the standard FTP protocol). + + _N_O_T_E: this is not used for interactive sessions, only for + command-line fetches. + + http_proxy URL of HTTP proxy to use when making HTTP URL requests. + If proxy authentication is required and there is a user- + name and password in this URL, they will automatically be + used in the first attempt to authenticate to the proxy. + + Note that the use of a username and password in ftp_proxy + and http_proxy may be incompatible with other programs + that use it (such as lynx(1)). + + _N_O_T_E: this is not used for interactive sessions, only for + command-line fetches. + + 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. + +SSEEEE AALLSSOO + getservbyname(3), editrc(5), services(5), ftpd(8) + +SSTTAANNDDAARRDDSS + ffttpp attempts to be compliant with RRFFCC 995599, RRFFCC 11112233, RRFFCC 11773388, RRFFCC 22006688, + RRFFCC 22338899, RRFFCC 22442288, RRFFCC 22773322, and ddrraafftt--iieettff--ffttppeexxtt--mmllsstt--1111. + +HHIISSTTOORRYY + The ffttpp command appeared in 4.2BSD. + + 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, con- + figurable command line prompt, and other enhancements over the standard + BSD ffttpp were implemented in NetBSD 1.3 and later releases by Luke Mewburn + <lukem@netbsd.org>. + + 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). + +BBUUGGSS + Correct execution of many commands depends upon proper behavior by the + remote server. + + An error in the treatment of carriage returns in the 4.2BSD ascii-mode + transfer code has been corrected. This correction may result in incor- + rect transfers of binary files to and from 4.2BSD servers using the ascii + type. Avoid this problem by using the binary image type. + + ffttpp assumes that all IPv4 mapped addresses (IPv6 addresses with a form + like ::ffff:10.1.1.1) indicate IPv4 destinations which can be handled by + 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 AF_INET6 sockets directly. For example, if your site + uses a SIIT translator for IPv6-to-IPv4 translation, ffttpp is unable to + support your configuration. + +NetBSD 1.6_BETA1 May 18, 2002 16 diff --git a/src/ftp_var.h b/src/ftp_var.h new file mode 100644 index 0000000..25f13d4 --- /dev/null +++ b/src/ftp_var.h @@ -0,0 +1,337 @@ +/* $NetBSD: ftp_var.h,v 1.71 2005/04/11 01:49:31 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2005 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. 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_var.h 8.4 (Berkeley) 10/9/94 + */ + +/* + * 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. + */ + +/* + * FTP global variables. + */ + +#ifdef SMALL +#undef NO_EDITCOMPLETE +#define NO_EDITCOMPLETE +#undef NO_PROGRESS +#define NO_PROGRESS +#endif + +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <poll.h> + +#include <setjmp.h> +#include <stringlist.h> + +#ifndef NO_EDITCOMPLETE +#include <histedit.h> +#endif /* !NO_EDITCOMPLETE */ + +#include "extern.h" +#include "progressbar.h" + +/* + * Format of command table. + */ +struct cmd { + char *c_name; /* name of command */ + const char *c_help; /* help string */ + char c_bell; /* give bell when command completes */ + char c_conn; /* must be connected to use command */ + char c_proxy; /* proxy server may execute */ +#ifndef NO_EDITCOMPLETE + const char *c_complete; /* context sensitive completion list */ +#endif /* !NO_EDITCOMPLETE */ + void (*c_handler)(int, char **); /* function to call */ +}; + +/* + * Format of macro table + */ +struct macel { + char mac_name[9]; /* macro name */ + char *mac_start; /* start of macro in macbuf */ + char *mac_end; /* end of macro in macbuf */ +}; + +/* + * Format of option table + */ +struct option { + char *name; + char *value; +}; + +/* + * Indices to features[]; an array containing status of remote server + * features; -1 not known (FEAT failed), 0 absent, 1 present. + */ +enum { + FEAT_FEAT = 0, /* FEAT, OPTS */ + FEAT_MDTM, /* MDTM */ + FEAT_MLST, /* MLSD, MLST */ + FEAT_REST_STREAM, /* RESTart STREAM */ + FEAT_SIZE, /* SIZE */ + FEAT_TVFS, /* TVFS (not used) */ + FEAT_max +}; + + +/* + * Global defines + */ +#define FTPBUFLEN MAXPATHLEN + 200 +#define MAX_IN_PORT_T 0xffffU + +#define HASHBYTES 1024 /* default mark for `hash' command */ +#define DEFAULTINCR 1024 /* default increment for `rate' command */ + +#define FTP_PORT 21 /* default if ! getservbyname("ftp/tcp") */ +#define HTTP_PORT 80 /* default if ! getservbyname("http/tcp") */ +#ifndef GATE_PORT +#define GATE_PORT 21 /* default if ! getservbyname("ftpgate/tcp") */ +#endif +#ifndef GATE_SERVER +#define GATE_SERVER "" /* default server */ +#endif + +#define DEFAULTPAGER "more" /* default pager if $PAGER isn't set */ +#define DEFAULTPROMPT "ftp> " /* default prompt if `set prompt' is empty */ +#define DEFAULTRPROMPT "" /* default rprompt if `set rprompt' is empty */ + +#define TMPFILE "ftpXXXXXXXXXX" + + +#ifndef GLOBAL +#define GLOBAL extern +#endif + +/* + * Options and other state info. + */ +GLOBAL int trace; /* trace packets exchanged */ +GLOBAL int hash; /* print # for each buffer transferred */ +GLOBAL int mark; /* number of bytes between hashes */ +GLOBAL int sendport; /* use PORT/LPRT cmd for each data connection */ +GLOBAL int connected; /* 1 = connected to server, -1 = logged in */ +GLOBAL int interactive; /* interactively prompt on m* cmds */ +GLOBAL int confirmrest; /* confirm rest of current m* cmd */ +GLOBAL int debug; /* debugging level */ +GLOBAL int bell; /* ring bell on cmd completion */ +GLOBAL int doglob; /* glob local file names */ +GLOBAL int autologin; /* establish user account on connection */ +GLOBAL int proxy; /* proxy server connection active */ +GLOBAL int proxflag; /* proxy connection exists */ +GLOBAL int gatemode; /* use gate-ftp */ +GLOBAL char *gateserver; /* server to use for gate-ftp */ +GLOBAL int sunique; /* store files on server with unique name */ +GLOBAL int runique; /* store local files with unique name */ +GLOBAL int mcase; /* map upper to lower case for mget names */ +GLOBAL int ntflag; /* use ntin ntout tables for name translation */ +GLOBAL int mapflag; /* use mapin mapout templates on file names */ +GLOBAL int preserve; /* preserve modification time on files */ +GLOBAL int code; /* return/reply code for ftp command */ +GLOBAL int crflag; /* if 1, strip car. rets. on ascii gets */ +GLOBAL int passivemode; /* passive mode enabled */ +GLOBAL int activefallback; /* fall back to active mode if passive fails */ +GLOBAL char *altarg; /* argv[1] with no shell-like preprocessing */ +GLOBAL char ntin[17]; /* input translation table */ +GLOBAL char ntout[17]; /* output translation table */ +GLOBAL char mapin[MAXPATHLEN]; /* input map template */ +GLOBAL char mapout[MAXPATHLEN]; /* output map template */ +GLOBAL char typename[32]; /* name of file transfer type */ +GLOBAL int type; /* requested file transfer type */ +GLOBAL int curtype; /* current file transfer type */ +GLOBAL char structname[32]; /* name of file transfer structure */ +GLOBAL int stru; /* file transfer structure */ +GLOBAL char formname[32]; /* name of file transfer format */ +GLOBAL int form; /* file transfer format */ +GLOBAL char modename[32]; /* name of file transfer mode */ +GLOBAL int mode; /* file transfer mode */ +GLOBAL char bytename[32]; /* local byte size in ascii */ +GLOBAL int bytesize; /* local byte size in binary */ +GLOBAL int anonftp; /* automatic anonymous login */ +GLOBAL int dirchange; /* remote directory changed by cd command */ +GLOBAL int flushcache; /* set HTTP cache flush headers with request */ +GLOBAL int rate_get; /* maximum get xfer rate */ +GLOBAL int rate_get_incr; /* increment for get xfer rate */ +GLOBAL int rate_put; /* maximum put xfer rate */ +GLOBAL int rate_put_incr; /* increment for put xfer rate */ +GLOBAL int retry_connect; /* seconds between retrying connection */ +GLOBAL char *tmpdir; /* temporary directory */ +GLOBAL int epsv4; /* use EPSV/EPRT on IPv4 connections */ +GLOBAL int epsv4bad; /* EPSV doesn't work on the current server */ +GLOBAL int editing; /* command line editing enabled */ +GLOBAL int features[FEAT_max]; /* remote FEATures supported */ + +#ifndef NO_EDITCOMPLETE +GLOBAL EditLine *el; /* editline(3) status structure */ +GLOBAL History *hist; /* editline(3) history structure */ +GLOBAL char *cursor_pos; /* cursor position we're looking for */ +GLOBAL size_t cursor_argc; /* location of cursor in margv */ +GLOBAL size_t cursor_argo; /* offset of cursor in margv[cursor_argc] */ +#endif /* !NO_EDITCOMPLETE */ + +GLOBAL char *direction; /* direction transfer is occurring */ + +GLOBAL char *hostname; /* name of host connected to */ +GLOBAL int unix_server; /* server is unix, can use binary for ascii */ +GLOBAL int unix_proxy; /* proxy is unix, can use binary for ascii */ +GLOBAL char localcwd[MAXPATHLEN]; /* local dir */ +GLOBAL char remotecwd[MAXPATHLEN]; /* remote dir */ +GLOBAL char *username; /* name of user logged in as. (dynamic) */ + +GLOBAL sa_family_t family; /* address family to use for connections */ +GLOBAL char *ftpport; /* port number to use for FTP connections */ +GLOBAL char *httpport; /* port number to use for HTTP connections */ +GLOBAL char *gateport; /* port number to use for gateftp connections */ + +GLOBAL char *outfile; /* filename to output URLs to */ +GLOBAL int restartautofetch; /* restart auto-fetch */ + +GLOBAL char line[FTPBUFLEN]; /* input line buffer */ +GLOBAL char *stringbase; /* current scan point in line buffer */ +GLOBAL char argbuf[FTPBUFLEN]; /* argument storage buffer */ +GLOBAL char *argbase; /* current storage point in arg buffer */ +GLOBAL StringList *marg_sl; /* stringlist containing margv */ +GLOBAL int margc; /* count of arguments on input line */ +#define margv (marg_sl->sl_str) /* args parsed from input line */ +GLOBAL int cpend; /* flag: if != 0, then pending server reply */ +GLOBAL int mflag; /* flag: if != 0, then active multi command */ + +GLOBAL int options; /* used during socket creation */ + +GLOBAL int sndbuf_size; /* socket send buffer size */ +GLOBAL int rcvbuf_size; /* socket receive buffer size */ + +GLOBAL int macnum; /* number of defined macros */ +GLOBAL struct macel macros[16]; +GLOBAL char macbuf[4096]; + +GLOBAL char *localhome; /* local home directory */ +GLOBAL char *localname; /* local user name */ +GLOBAL char netrc[MAXPATHLEN]; /* path to .netrc file */ +GLOBAL char reply_string[BUFSIZ]; /* first line of previous reply */ +GLOBAL void (*reply_callback)(const char *); + /* + * function to call for each line in + * the server's reply except for the + * first (`xxx-') and last (`xxx ') + */ + +GLOBAL volatile sig_atomic_t sigint_raised; + +GLOBAL FILE *cin; +GLOBAL FILE *cout; +GLOBAL int data; + +extern struct cmd cmdtab[]; +extern struct option optiontab[]; + + +#define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) +#define FREEPTR(x) if ((x) != NULL) { free(x); (x) = NULL; } + +#ifdef BSD4_4 +# define HAVE_SOCKADDR_SA_LEN 1 +#endif + +#ifdef NO_LONG_LONG +# define STRTOLL(x,y,z) strtol(x,y,z) +#else +# define STRTOLL(x,y,z) strtoll(x,y,z) +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5d60cdb --- /dev/null +++ b/src/main.c @@ -0,0 +1,1051 @@ +/* $NetBSD: main.c,v 1.94 2005/05/13 05:03:49 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2004 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. 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. + */ + +#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.94 2005/05/13 05:03:49 lukem Exp $"); +#endif +#endif /* not lint */ + +/* + * FTP User Program -- Command Interface. + */ +#include <sys/types.h> +#include <sys/socket.h> + +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <locale.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; + socklen_t slen; + + setlocale(LC_ALL, ""); + setprogname(argv[0]); + + sigint_raised = 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"); + slen = sizeof(rcvbuf_size); + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, + (void *)&rcvbuf_size, &slen) == -1) + err(1, "unable to get default rcvbuf size"); + slen = sizeof(sndbuf_size); + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, + (void *)&sndbuf_size, &slen) == -1) + err(1, "unable to get default sndbuf size"); + (void)close(s); + /* sanity check returned buffer sizes */ + if (rcvbuf_size <= 0) + rcvbuf_size = 8 * 1024; + if (sndbuf_size <= 0) + sndbuf_size = 8 * 1024; + + if (sndbuf_size > 8 * 1024 * 1024) + sndbuf_size = 8 * 1024 * 1024; + if (rcvbuf_size > 8 * 1024 * 1024) + rcvbuf_size = 8 * 1024 * 1024; + + 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); + sigint_or_rval_exit: + if (sigint_raised) { + (void)xsignal(SIGINT, SIG_DFL); + raise(SIGINT); + } + exit(rval); + } else if (strchr(argv[0], ':') != NULL + && ! isipv6addr(argv[0])) { + rval = auto_fetch(argc, argv); + if (rval >= 0) /* -1 == connected and cd-ed */ + goto sigint_or_rval_exit; + } else { + char *xargv[4], *user, *host; + + if ((rval = sigsetjmp(toplevel, 1))) + goto sigint_or_rval_exit; + (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 is 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; + + buf = el_gets(el, &num); + if (buf == NULL || num == 0) { + if (fromatty) + putc('\n', ttyout); + quit(0, NULL); + } + if (num >= sizeof(line)) { + fputs("Sorry, input line is too long.\n", + ttyout); + break; + } + memcpy(line, buf, num); + if (line[--num] == '\n') { + line[num] = '\0'; + if (num == 0) + break; + } + 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] [-q quittime]\n" +" [-r retry] [-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/src/progressbar.c b/src/progressbar.c new file mode 100644 index 0000000..d0bd35f --- /dev/null +++ b/src/progressbar.c @@ -0,0 +1,464 @@ +/* $NetBSD: progressbar.c,v 1.7 2005/04/11 01:49:31 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: progressbar.c,v 1.7 2005/04/11 01:49:31 lukem Exp $"); +#endif /* not lint */ + +/* + * FTP User Program -- Misc support routines + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "progressbar.h" + +#define SECSPERHOUR (60 * 60) +#define SECSPERDAY ((long)60 * 60 * 24) + +#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) + pgrp = getpgrp(); + + return (tcgetpgrp(fileno(ttyout)) == pgrp); +} +#endif /* !defined(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; +} + +/* + * 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, 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. + */ +#endif + int len; + char buf[256]; /* workspace for progress bar */ +#ifndef NO_PROGRESS +#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 (prefix) + len += snprintf(buf + len, BUFLEFT, "%s", prefix); + 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 (prefix) + barlength -= strlen(prefix); + 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) +{ + 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); +} + +/* + * 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)); +} diff --git a/src/progressbar.h b/src/progressbar.h new file mode 100644 index 0000000..9e004f4 --- /dev/null +++ b/src/progressbar.h @@ -0,0 +1,99 @@ +/* $NetBSD: progressbar.h,v 1.5 2005/02/10 16:00:38 jmc 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. + */ + +#ifndef STANDALONE_PROGRESS +#include <setjmp.h> +#endif /* !STANDALONE_PROGRESS */ + +#ifndef GLOBAL +#define GLOBAL extern +#endif + + +#define STALLTIME 5 /* # of seconds of no xfer before "stalling" */ + +typedef void (*sigfunc)(int); + + +GLOBAL FILE *ttyout; /* stdout, or stderr if retrieving to stdout */ + +GLOBAL int progress; /* display transfer progress bar */ +GLOBAL int ttywidth; /* width of tty */ + +GLOBAL off_t bytes; /* current # of bytes read */ +GLOBAL off_t filesize; /* size of file being transferred */ +GLOBAL off_t restart_point; /* offset to restart transfer */ +GLOBAL char *prefix; /* Text written left of progress bar */ + + +#ifndef STANDALONE_PROGRESS +GLOBAL int fromatty; /* input is from a terminal */ +GLOBAL int verbose; /* print messages coming back from server */ +GLOBAL int quit_time; /* maximum time to wait if stalled */ + +GLOBAL char *direction; /* direction transfer is occurring */ + +GLOBAL sigjmp_buf toplevel; /* non-local goto stuff for cmd scanner */ +#endif /* !STANDALONE_PROGRESS */ + +int foregroundproc(void); +void alarmtimer(int); +void progressmeter(int); +sigfunc xsignal(int, sigfunc); +sigfunc xsignal_restart(int, sigfunc, int); + +#ifndef STANDALONE_PROGRESS +void psummary(int); +void ptransfer(int); +#endif /* !STANDALONE_PROGRESS */ + +#ifdef NO_LONG_LONG +# define LLF "%ld" +# define LLFP(x) "%" x "ld" +# define LLT long +# define ULLF "%lu" +# define ULLFP(x) "%" x "lu" +# define ULLT unsigned long +#else +# define LLF "%lld" +# define LLFP(x) "%" x "lld" +# define LLT long long +# define ULLF "%llu" +# define ULLFP(x) "%" x "llu" +# define ULLT unsigned long long +#endif diff --git a/src/ruserpass.c b/src/ruserpass.c new file mode 100644 index 0000000..9cb318a --- /dev/null +++ b/src/ruserpass.c @@ -0,0 +1,293 @@ +/* $NetBSD: ruserpass.c,v 1.29 2003/08/07 11:13:57 agc Exp $ */ + +/* + * Copyright (c) 1985, 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. 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ruserpass.c 8.4 (Berkeley) 4/27/95"; +#else +__RCSID("$NetBSD: ruserpass.c,v 1.29 2003/08/07 11:13:57 agc Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ftp_var.h" + +static int token(void); +static FILE *cfile; + +#define DEFAULT 1 +#define LOGIN 2 +#define PASSWD 3 +#define ACCOUNT 4 +#define MACDEF 5 +#define ID 10 +#define MACH 11 + +static char tokval[100]; + +static struct toktab { + char *tokstr; + int tval; +} toktab[] = { + { "default", DEFAULT }, + { "login", LOGIN }, + { "password", PASSWD }, + { "passwd", PASSWD }, + { "account", ACCOUNT }, + { "machine", MACH }, + { "macdef", MACDEF }, + { NULL, 0 } +}; + +int +ruserpass(const char *host, const char **aname, const char **apass, + const char **aacct) +{ + char *tmp; + char myname[MAXHOSTNAMELEN + 1], *mydomain; + int t, i, c, usedefault = 0; + struct stat stb; + + if (netrc[0] == '\0') + return (0); + cfile = fopen(netrc, "r"); + if (cfile == NULL) { + if (errno != ENOENT) + warn("%s", netrc); + return (0); + } + if (gethostname(myname, sizeof(myname)) < 0) + myname[0] = '\0'; + myname[sizeof(myname) - 1] = '\0'; + if ((mydomain = strchr(myname, '.')) == NULL) + mydomain = ""; + next: + while ((t = token())) switch(t) { + + case DEFAULT: + usedefault = 1; + /* FALL THROUGH */ + + case MACH: + if (!usedefault) { + if (token() != ID) + continue; + /* + * Allow match either for user's input host name + * or official hostname. Also allow match of + * incompletely-specified host in local domain. + */ + if (strcasecmp(host, tokval) == 0) + goto match; + if (strcasecmp(hostname, tokval) == 0) + goto match; + if ((tmp = strchr(hostname, '.')) != NULL && + strcasecmp(tmp, mydomain) == 0 && + strncasecmp(hostname, tokval, tmp-hostname) == 0 && + tokval[tmp - hostname] == '\0') + goto match; + if ((tmp = strchr(host, '.')) != NULL && + strcasecmp(tmp, mydomain) == 0 && + strncasecmp(host, tokval, tmp - host) == 0 && + tokval[tmp - host] == '\0') + goto match; + continue; + } + match: + while ((t = token()) && t != MACH && t != DEFAULT) switch(t) { + + case LOGIN: + if (token()) { + if (*aname == NULL) + *aname = xstrdup(tokval); + else { + if (strcmp(*aname, tokval)) + goto next; + } + } + break; + case PASSWD: + if ((*aname == NULL || strcmp(*aname, "anonymous")) && + fstat(fileno(cfile), &stb) >= 0 && + (stb.st_mode & 077) != 0) { + warnx("Error: .netrc file is readable by others."); + warnx("Remove password or make file unreadable by others."); + goto bad; + } + if (token() && *apass == NULL) + *apass = xstrdup(tokval); + break; + case ACCOUNT: + if (fstat(fileno(cfile), &stb) >= 0 + && (stb.st_mode & 077) != 0) { + warnx("Error: .netrc file is readable by others."); + warnx("Remove account or make file unreadable by others."); + goto bad; + } + if (token() && *aacct == NULL) + *aacct = xstrdup(tokval); + break; + case MACDEF: + if (proxy) { + (void)fclose(cfile); + return (0); + } + while ((c = getc(cfile)) != EOF) + if (c != ' ' && c != '\t') + break; + if (c == EOF || c == '\n') { + fputs("Missing macdef name argument.\n", + ttyout); + goto bad; + } + if (macnum == 16) { + fputs( + "Limit of 16 macros have already been defined.\n", + ttyout); + goto bad; + } + tmp = macros[macnum].mac_name; + *tmp++ = c; + for (i = 0; i < 8 && (c = getc(cfile)) != EOF && + !isspace(c); ++i) { + *tmp++ = c; + } + if (c == EOF) { + fputs( + "Macro definition missing null line terminator.\n", + ttyout); + goto bad; + } + *tmp = '\0'; + if (c != '\n') { + while ((c = getc(cfile)) != EOF && c != '\n'); + } + if (c == EOF) { + fputs( + "Macro definition missing null line terminator.\n", + ttyout); + goto bad; + } + if (macnum == 0) { + macros[macnum].mac_start = macbuf; + } + else { + macros[macnum].mac_start = + macros[macnum-1].mac_end + 1; + } + tmp = macros[macnum].mac_start; + while (tmp != macbuf + 4096) { + if ((c = getc(cfile)) == EOF) { + fputs( + "Macro definition missing null line terminator.\n", + ttyout); + goto bad; + } + *tmp = c; + if (*tmp == '\n') { + if (*(tmp-1) == '\0') { + macros[macnum++].mac_end = tmp - 1; + break; + } + *tmp = '\0'; + } + tmp++; + } + if (tmp == macbuf + 4096) { + fputs("4K macro buffer exceeded.\n", + ttyout); + goto bad; + } + break; + default: + warnx("Unknown .netrc keyword %s", tokval); + break; + } + goto done; + } + done: + (void)fclose(cfile); + return (0); + bad: + (void)fclose(cfile); + return (-1); +} + +static int +token(void) +{ + char *cp; + int c; + struct toktab *t; + + if (feof(cfile) || ferror(cfile)) + return (0); + while ((c = getc(cfile)) != EOF && + (c == '\n' || c == '\t' || c == ' ' || c == ',')) + continue; + if (c == EOF) + return (0); + cp = tokval; + if (c == '"') { + while ((c = getc(cfile)) != EOF && c != '"') { + if (c == '\\') + c = getc(cfile); + *cp++ = c; + } + } else { + *cp++ = c; + while ((c = getc(cfile)) != EOF + && c != '\n' && c != '\t' && c != ' ' && c != ',') { + if (c == '\\') + c = getc(cfile); + *cp++ = c; + } + } + *cp = 0; + if (tokval[0] == 0) + return (0); + for (t = toktab; t->tokstr; t++) + if (!strcmp(t->tokstr, tokval)) + return (t->tval); + return (ID); +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..6f94bb0 --- /dev/null +++ b/src/util.c @@ -0,0 +1,1400 @@ +/* $NetBSD: util.c,v 1.123 2005/05/14 18:56:45 dsl Exp $ */ + +/*- + * Copyright (c) 1997-2005 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 Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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. 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.123 2005/05/14 18:56:45 dsl Exp $"); +#endif /* not lint */ + +/* + * FTP User Program -- Misc support routines + */ +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/ftp.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <glob.h> +#include <signal.h> +#include <limits.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <time.h> +#include <unistd.h> + +#include "ftp_var.h" + +#define TM_YEAR_BASE 1900 + +/* + * Connect to peer server and auto-login, if possible. + */ +void +setpeer(int argc, char *argv[]) +{ + char *host; + char *port; + + if (argc == 0) + goto usage; + if (connected) { + fprintf(ttyout, "Already connected to %s, use close first.\n", + hostname); + code = -1; + return; + } + if (argc < 2) + (void)another(&argc, &argv, "to"); + if (argc < 2 || argc > 3) { + usage: + fprintf(ttyout, "usage: %s host-name [port]\n", argv[0]); + code = -1; + return; + } + if (gatemode) + port = gateport; + else + port = ftpport; + if (argc > 2) + port = argv[2]; + + if (gatemode) { + if (gateserver == NULL || *gateserver == '\0') + errx(1, "gateserver not defined (shouldn't happen)"); + host = hookup(gateserver, port); + } else + host = hookup(argv[1], port); + + if (host) { + if (gatemode && verbose) { + fprintf(ttyout, + "Connecting via pass-through server %s\n", + gateserver); + } + + connected = 1; + /* + * Set up defaults for FTP. + */ + (void)strlcpy(typename, "ascii", sizeof(typename)); + type = TYPE_A; + curtype = TYPE_A; + (void)strlcpy(formname, "non-print", sizeof(formname)); + form = FORM_N; + (void)strlcpy(modename, "stream", sizeof(modename)); + mode = MODE_S; + (void)strlcpy(structname, "file", sizeof(structname)); + stru = STRU_F; + (void)strlcpy(bytename, "8", sizeof(bytename)); + bytesize = 8; + if (autologin) + (void)ftp_login(argv[1], NULL, NULL); + } +} + +static void +parse_feat(const char *line) +{ + + /* + * work-around broken ProFTPd servers that can't + * even obey RFC 2389. + */ + while (*line && isspace((int)*line)) + line++; + + if (strcasecmp(line, "MDTM") == 0) + features[FEAT_MDTM] = 1; + else if (strncasecmp(line, "MLST", sizeof("MLST") - 1) == 0) { + features[FEAT_MLST] = 1; + } else if (strcasecmp(line, "REST STREAM") == 0) + features[FEAT_REST_STREAM] = 1; + else if (strcasecmp(line, "SIZE") == 0) + features[FEAT_SIZE] = 1; + else if (strcasecmp(line, "TVFS") == 0) + features[FEAT_TVFS] = 1; +} + +/* + * Determine the remote system type (SYST) and features (FEAT). + * Call after a successful login (i.e, connected = -1) + */ +void +getremoteinfo(void) +{ + int overbose, i; + + overbose = verbose; + if (debug == 0) + verbose = -1; + + /* determine remote system type */ + if (command("SYST") == COMPLETE) { + if (overbose) { + char *cp, c; + + c = 0; + cp = strchr(reply_string + 4, ' '); + if (cp == NULL) + cp = strchr(reply_string + 4, '\r'); + if (cp) { + if (cp[-1] == '.') + cp--; + c = *cp; + *cp = '\0'; + } + + fprintf(ttyout, "Remote system type is %s.\n", + reply_string + 4); + if (cp) + *cp = c; + } + if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { + if (proxy) + unix_proxy = 1; + else + unix_server = 1; + /* + * Set type to 0 (not specified by user), + * meaning binary by default, but don't bother + * telling server. We can use binary + * for text files unless changed by the user. + */ + type = 0; + (void)strlcpy(typename, "binary", sizeof(typename)); + if (overbose) + fprintf(ttyout, + "Using %s mode to transfer files.\n", + typename); + } else { + if (proxy) + unix_proxy = 0; + else + unix_server = 0; + if (overbose && + !strncmp(reply_string, "215 TOPS20", 10)) + fputs( +"Remember to set tenex mode when transferring binary files from this machine.\n", + ttyout); + } + } + + /* determine features (if any) */ + for (i = 0; i < FEAT_max; i++) + features[i] = -1; + reply_callback = parse_feat; + if (command("FEAT") == COMPLETE) { + for (i = 0; i < FEAT_max; i++) { + if (features[i] == -1) + features[i] = 0; + } + features[FEAT_FEAT] = 1; + } else + features[FEAT_FEAT] = 0; + if (debug) { +#define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)]) + DEBUG_FEAT(FEAT_FEAT); + DEBUG_FEAT(FEAT_MDTM); + DEBUG_FEAT(FEAT_MLST); + DEBUG_FEAT(FEAT_REST_STREAM); + DEBUG_FEAT(FEAT_SIZE); + DEBUG_FEAT(FEAT_TVFS); +#undef DEBUG_FEAT + } + reply_callback = NULL; + + verbose = overbose; +} + +/* + * Reset the various variables that indicate connection state back to + * disconnected settings. + * The caller is responsible for issuing any commands to the remote server + * to perform a clean shutdown before this is invoked. + */ +void +cleanuppeer(void) +{ + + if (cout) + (void)fclose(cout); + cout = NULL; + connected = 0; + unix_server = 0; + unix_proxy = 0; + /* + * determine if anonftp was specifically set with -a + * (1), or implicitly set by auto_fetch() (2). in the + * latter case, disable after the current xfer + */ + if (anonftp == 2) + anonftp = 0; + data = -1; + epsv4bad = 0; + if (username) + free(username); + username = NULL; + if (!proxy) + macnum = 0; +} + +/* + * Top-level signal handler for interrupted commands. + */ +void +intr(int signo) +{ + + sigint_raised = 1; + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + siglongjmp(toplevel, 1); +} + +/* + * Signal handler for lost connections; cleanup various elements of + * the connection state, and call cleanuppeer() to finish it off. + */ +void +lostpeer(int dummy) +{ + int oerrno = errno; + + alarmtimer(0); + if (connected) { + if (cout != NULL) { + (void)shutdown(fileno(cout), 1+1); + (void)fclose(cout); + cout = NULL; + } + if (data >= 0) { + (void)shutdown(data, 1+1); + (void)close(data); + data = -1; + } + connected = 0; + } + pswitch(1); + if (connected) { + if (cout != NULL) { + (void)shutdown(fileno(cout), 1+1); + (void)fclose(cout); + cout = NULL; + } + connected = 0; + } + proxflag = 0; + pswitch(0); + cleanuppeer(); + errno = oerrno; +} + + +/* + * Login to remote host, using given username & password if supplied. + * Return non-zero if successful. + */ +int +ftp_login(const char *host, const char *user, const char *pass) +{ + char tmp[80]; + const char *acct; + int n, aflag, rval, freeuser, freepass, freeacct; + + acct = NULL; + aflag = rval = freeuser = freepass = freeacct = 0; + + if (debug) + fprintf(ttyout, "ftp_login: user `%s' pass `%s' host `%s'\n", + user ? user : "<null>", pass ? pass : "<null>", + host ? host : "<null>"); + + + /* + * Set up arguments for an anonymous FTP session, if necessary. + */ + if (anonftp) { + user = "anonymous"; /* as per RFC 1635 */ + pass = getoptionvalue("anonpass"); + } + + if (user == NULL) + freeuser = 1; + if (pass == NULL) + freepass = 1; + freeacct = 1; + if (ruserpass(host, &user, &pass, &acct) < 0) { + code = -1; + goto cleanup_ftp_login; + } + + while (user == NULL) { + if (localname) + fprintf(ttyout, "Name (%s:%s): ", host, localname); + else + fprintf(ttyout, "Name (%s): ", host); + *tmp = '\0'; + if (fgets(tmp, sizeof(tmp) - 1, stdin) == NULL) { + fprintf(ttyout, "\nEOF received; login aborted.\n"); + clearerr(stdin); + code = -1; + goto cleanup_ftp_login; + } + tmp[strlen(tmp) - 1] = '\0'; + freeuser = 0; + if (*tmp == '\0') + user = localname; + else + user = tmp; + } + + if (gatemode) { + char *nuser; + int len; + + len = strlen(user) + 1 + strlen(host) + 1; + nuser = xmalloc(len); + (void)strlcpy(nuser, user, len); + (void)strlcat(nuser, "@", len); + (void)strlcat(nuser, host, len); + freeuser = 1; + user = nuser; + } + + n = command("USER %s", user); + if (n == CONTINUE) { + if (pass == NULL) { + freepass = 0; + pass = getpass("Password:"); + } + n = command("PASS %s", pass); + } + if (n == CONTINUE) { + aflag++; + if (acct == NULL) { + freeacct = 0; + acct = getpass("Account:"); + } + if (acct[0] == '\0') { + warnx("Login failed."); + goto cleanup_ftp_login; + } + n = command("ACCT %s", acct); + } + if ((n != COMPLETE) || + (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) { + warnx("Login failed."); + goto cleanup_ftp_login; + } + rval = 1; + username = xstrdup(user); + if (proxy) + goto cleanup_ftp_login; + + connected = -1; + getremoteinfo(); + for (n = 0; n < macnum; ++n) { + if (!strcmp("init", macros[n].mac_name)) { + (void)strlcpy(line, "$init", sizeof(line)); + makeargv(); + domacro(margc, margv); + break; + } + } + updatelocalcwd(); + updateremotecwd(); + + cleanup_ftp_login: + if (user != NULL && freeuser) + free((char *)user); + if (pass != NULL && freepass) + free((char *)pass); + if (acct != NULL && freeacct) + free((char *)acct); + return (rval); +} + +/* + * `another' gets another argument, and stores the new argc and argv. + * It reverts to the top level (via intr()) on EOF/error. + * + * Returns false if no new arguments have been added. + */ +int +another(int *pargc, char ***pargv, const char *prompt) +{ + int len = strlen(line), ret; + + if (len >= sizeof(line) - 3) { + fputs("sorry, arguments too long.\n", ttyout); + intr(0); + } + fprintf(ttyout, "(%s) ", prompt); + line[len++] = ' '; + if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) { + clearerr(stdin); + intr(0); + } + len += strlen(&line[len]); + if (len > 0 && line[len - 1] == '\n') + line[len - 1] = '\0'; + makeargv(); + ret = margc > *pargc; + *pargc = margc; + *pargv = margv; + return (ret); +} + +/* + * glob files given in argv[] from the remote server. + * if errbuf isn't NULL, store error messages there instead + * of writing to the screen. + */ +char * +remglob(char *argv[], int doswitch, char **errbuf) +{ + char temp[MAXPATHLEN]; + static char buf[MAXPATHLEN]; + static FILE *ftemp = NULL; + static char **args; + int oldverbose, oldhash, oldprogress, fd, len; + char *cp, *mode; + + if (!mflag || !connected) { + if (!doglob) + args = NULL; + else { + if (ftemp) { + (void)fclose(ftemp); + ftemp = NULL; + } + } + return (NULL); + } + if (!doglob) { + if (args == NULL) + args = argv; + if ((cp = *++args) == NULL) + args = NULL; + return (cp); + } + if (ftemp == NULL) { + len = strlcpy(temp, tmpdir, sizeof(temp)); + if (temp[len - 1] != '/') + (void)strlcat(temp, "/", sizeof(temp)); + (void)strlcat(temp, TMPFILE, sizeof(temp)); + if ((fd = mkstemp(temp)) < 0) { + warn("unable to create temporary file %s", temp); + return (NULL); + } + close(fd); + oldverbose = verbose; + verbose = (errbuf != NULL) ? -1 : 0; + oldhash = hash; + oldprogress = progress; + hash = 0; + progress = 0; + if (doswitch) + pswitch(!proxy); + for (mode = "w"; *++argv != NULL; mode = "a") + recvrequest("NLST", temp, *argv, mode, 0, 0); + if ((code / 100) != COMPLETE) { + if (errbuf != NULL) + *errbuf = reply_string; + } + if (doswitch) + pswitch(!proxy); + verbose = oldverbose; + hash = oldhash; + progress = oldprogress; + ftemp = fopen(temp, "r"); + (void)unlink(temp); + if (ftemp == NULL) { + if (errbuf == NULL) + fputs( + "can't find list of remote files, oops.\n", + ttyout); + else + *errbuf = + "can't find list of remote files, oops."; + return (NULL); + } + } + if (fgets(buf, sizeof(buf), ftemp) == NULL) { + (void)fclose(ftemp); + ftemp = NULL; + return (NULL); + } + if ((cp = strchr(buf, '\n')) != NULL) + *cp = '\0'; + return (buf); +} + +/* + * Glob a local file name specification with the expectation of a single + * return value. Can't control multiple values being expanded from the + * expression, we return only the first. + * Returns NULL on error, or a pointer to a buffer containing the filename + * that's the caller's responsiblity to free(3) when finished with. + */ +char * +globulize(const char *pattern) +{ + glob_t gl; + int flags; + char *p; + + if (!doglob) + return (xstrdup(pattern)); + + flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; + memset(&gl, 0, sizeof(gl)); + if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) { + warnx("%s: not found", pattern); + globfree(&gl); + return (NULL); + } + p = xstrdup(gl.gl_pathv[0]); + globfree(&gl); + return (p); +} + +/* + * determine size of remote file + */ +off_t +remotesize(const char *file, int noisy) +{ + int overbose, r; + off_t size; + + overbose = verbose; + size = -1; + if (debug == 0) + verbose = -1; + if (! features[FEAT_SIZE]) { + if (noisy) + fprintf(ttyout, + "SIZE is not supported by remote server.\n"); + goto cleanup_remotesize; + } + r = command("SIZE %s", file); + if (r == COMPLETE) { + char *cp, *ep; + + cp = strchr(reply_string, ' '); + if (cp != NULL) { + cp++; + size = STRTOLL(cp, &ep, 10); + if (*ep != '\0' && !isspace((unsigned char)*ep)) + size = -1; + } + } else { + if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1) + features[FEAT_SIZE] = 0; + if (noisy && debug == 0) { + fputs(reply_string, ttyout); + putc('\n', ttyout); + } + } + cleanup_remotesize: + verbose = overbose; + return (size); +} + +/* + * determine last modification time (in GMT) of remote file + */ +time_t +remotemodtime(const char *file, int noisy) +{ + int overbose, ocode, r; + time_t rtime; + + overbose = verbose; + ocode = code; + rtime = -1; + if (debug == 0) + verbose = -1; + if (! features[FEAT_MDTM]) { + if (noisy) + fprintf(ttyout, + "MDTM is not supported by remote server.\n"); + goto cleanup_parse_time; + } + r = command("MDTM %s", file); + if (r == COMPLETE) { + struct tm timebuf; + char *timestr, *frac; + int yy, mo, day, hour, min, sec; + + /* + * time-val = 14DIGIT [ "." 1*DIGIT ] + * YYYYMMDDHHMMSS[.sss] + * mdtm-response = "213" SP time-val CRLF / error-response + */ + timestr = reply_string + 4; + + /* + * parse fraction. + * XXX: ignored for now + */ + frac = strchr(timestr, '\r'); + if (frac != NULL) + *frac = '\0'; + frac = strchr(timestr, '.'); + if (frac != NULL) + *frac++ = '\0'; + if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) { + /* + * XXX: Workaround for lame ftpd's that return + * `19100' instead of `2000' + */ + fprintf(ttyout, + "Y2K warning! Incorrect time-val `%s' received from server.\n", + timestr); + timestr++; + timestr[0] = '2'; + timestr[1] = '0'; + fprintf(ttyout, "Converted to `%s'\n", timestr); + } + if (strlen(timestr) != 14 || + sscanf(timestr, "%04d%02d%02d%02d%02d%02d", + &yy, &mo, &day, &hour, &min, &sec) != 6) { + bad_parse_time: + fprintf(ttyout, "Can't parse time `%s'.\n", timestr); + goto cleanup_parse_time; + } + memset(&timebuf, 0, sizeof(timebuf)); + timebuf.tm_sec = sec; + timebuf.tm_min = min; + timebuf.tm_hour = hour; + timebuf.tm_mday = day; + timebuf.tm_mon = mo - 1; + timebuf.tm_year = yy - TM_YEAR_BASE; + timebuf.tm_isdst = -1; + rtime = timegm(&timebuf); + if (rtime == -1) { + if (noisy || debug != 0) + goto bad_parse_time; + else + goto cleanup_parse_time; + } else if (debug) + fprintf(ttyout, "parsed date as: %s", ctime(&rtime)); + } else { + if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1) + features[FEAT_MDTM] = 0; + if (noisy && debug == 0) { + fputs(reply_string, ttyout); + putc('\n', ttyout); + } + } + cleanup_parse_time: + verbose = overbose; + if (rtime == -1) + code = ocode; + return (rtime); +} + +/* + * Update global `localcwd', which contains the state of the local cwd + */ +void +updatelocalcwd(void) +{ + + if (getcwd(localcwd, sizeof(localcwd)) == NULL) + localcwd[0] = '\0'; + if (debug) + fprintf(ttyout, "got localcwd as `%s'\n", localcwd); +} + +/* + * Update global `remotecwd', which contains the state of the remote cwd + */ +void +updateremotecwd(void) +{ + int overbose, ocode, i; + char *cp; + + overbose = verbose; + ocode = code; + if (debug == 0) + verbose = -1; + if (command("PWD") != COMPLETE) + goto badremotecwd; + cp = strchr(reply_string, ' '); + if (cp == NULL || cp[0] == '\0' || cp[1] != '"') + goto badremotecwd; + cp += 2; + for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) { + if (cp[0] == '"') { + if (cp[1] == '"') + cp++; + else + break; + } + remotecwd[i] = *cp; + } + remotecwd[i] = '\0'; + if (debug) + fprintf(ttyout, "got remotecwd as `%s'\n", remotecwd); + goto cleanupremotecwd; + badremotecwd: + remotecwd[0]='\0'; + cleanupremotecwd: + verbose = overbose; + code = ocode; +} + +/* + * Ensure file is in or under dir. + * Returns 1 if so, 0 if not (or an error occurred). + */ +int +fileindir(const char *file, const char *dir) +{ + char realfile[PATH_MAX+1]; + size_t dirlen; + + if (realpath(file, realfile) == NULL) { + warn("Unable to determine real path of `%s'", file); + return 0; + } + if (realfile[0] != '/') /* relative result */ + return 1; + dirlen = strlen(dir); +#if 0 +printf("file %s realfile %s dir %s [%d]\n", file, realfile, dir, dirlen); +#endif + if (strncmp(realfile, dir, dirlen) == 0 && realfile[dirlen] == '/') + return 1; + return 0; +} + +/* + * List words in stringlist, vertically arranged + */ +void +list_vertical(StringList *sl) +{ + int i, j, w; + int columns, width, lines; + char *p; + + width = 0; + + for (i = 0 ; i < sl->sl_cur ; i++) { + w = strlen(sl->sl_str[i]); + if (w > width) + width = w; + } + width = (width + 8) &~ 7; + + columns = ttywidth / width; + if (columns == 0) + columns = 1; + lines = (sl->sl_cur + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + p = sl->sl_str[j * lines + i]; + if (p) + fputs(p, ttyout); + if (j * lines + i + lines >= sl->sl_cur) { + putc('\n', ttyout); + break; + } + w = strlen(p); + while (w < width) { + w = (w + 8) &~ 7; + (void)putc('\t', ttyout); + } + } + } +} + +/* + * Update the global ttywidth value, using TIOCGWINSZ. + */ +void +setttywidth(int a) +{ + struct winsize winsize; + int oerrno = errno; + + if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 && + winsize.ws_col != 0) + ttywidth = winsize.ws_col; + else + ttywidth = 80; + errno = oerrno; +} + +/* + * Change the rate limit up (SIGUSR1) or down (SIGUSR2) + */ +void +crankrate(int sig) +{ + + switch (sig) { + case SIGUSR1: + if (rate_get) + rate_get += rate_get_incr; + if (rate_put) + rate_put += rate_put_incr; + break; + case SIGUSR2: + if (rate_get && rate_get > rate_get_incr) + rate_get -= rate_get_incr; + if (rate_put && rate_put > rate_put_incr) + rate_put -= rate_put_incr; + break; + default: + err(1, "crankrate invoked with unknown signal: %d", sig); + } +} + + +/* + * Setup or cleanup EditLine structures + */ +#ifndef NO_EDITCOMPLETE +void +controlediting(void) +{ + if (editing && el == NULL && hist == NULL) { + HistEvent ev; + int editmode; + + el = el_init(getprogname(), stdin, ttyout, stderr); + /* init editline */ + hist = history_init(); /* init the builtin history */ + history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */ + el_set(el, EL_HIST, history, hist); /* use history */ + + el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */ + el_set(el, EL_PROMPT, prompt); /* set the prompt functions */ + el_set(el, EL_RPROMPT, rprompt); + + /* add local file completion, bind to TAB */ + el_set(el, EL_ADDFN, "ftp-complete", + "Context sensitive argument completion", + complete); + el_set(el, EL_BIND, "^I", "ftp-complete", NULL); + el_source(el, NULL); /* read ~/.editrc */ + if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0) + editing = 0; /* the user doesn't want editing, + * so disable, and let statement + * below cleanup */ + else + el_set(el, EL_SIGNAL, 1); + } + if (!editing) { + if (hist) { + history_end(hist); + hist = NULL; + } + if (el) { + el_end(el); + el = NULL; + } + } +} +#endif /* !NO_EDITCOMPLETE */ + +/* + * Convert the string `arg' to an int, which may have an optional SI suffix + * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise. + */ +int +strsuftoi(const char *arg) +{ + char *cp; + long val; + + if (!isdigit((unsigned char)arg[0])) + return (-1); + + val = strtol(arg, &cp, 10); + if (cp != NULL) { + if (cp[0] != '\0' && cp[1] != '\0') + return (-1); + switch (tolower((unsigned char)cp[0])) { + case '\0': + case 'b': + break; + case 'k': + val <<= 10; + break; + case 'm': + val <<= 20; + break; + case 'g': + val <<= 30; + break; + default: + return (-1); + } + } + if (val < 0 || val > INT_MAX) + return (-1); + + return (val); +} + +/* + * Set up socket buffer sizes before a connection is made. + */ +void +setupsockbufsize(int sock) +{ + + if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, + (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1) + warn("unable to set sndbuf size %d", sndbuf_size); + + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1) + warn("unable to set rcvbuf size %d", rcvbuf_size); +} + +/* + * Copy characters from src into dst, \ quoting characters that require it + */ +void +ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) +{ + int di, si; + + for (di = si = 0; + src[si] != '\0' && di < dstlen && si < srclen; + di++, si++) { + switch (src[si]) { + case '\\': + case ' ': + case '\t': + case '\r': + case '\n': + case '"': + dst[di++] = '\\'; + if (di >= dstlen) + break; + /* FALLTHROUGH */ + default: + dst[di] = src[si]; + } + } + dst[di] = '\0'; +} + +/* + * Copy src into buf (which is len bytes long), expanding % sequences. + */ +void +formatbuf(char *buf, size_t len, const char *src) +{ + const char *p; + char *p2, *q; + int i, op, updirs, pdirs; + +#define ADDBUF(x) do { \ + if (i >= len - 1) \ + goto endbuf; \ + buf[i++] = (x); \ + } while (0) + + p = src; + for (i = 0; *p; p++) { + if (*p != '%') { + ADDBUF(*p); + continue; + } + p++; + + switch (op = *p) { + + case '/': + case '.': + case 'c': + p2 = connected ? remotecwd : ""; + updirs = pdirs = 0; + + /* option to determine fixed # of dirs from path */ + if (op == '.' || op == 'c') { + int skip; + + q = p2; + while (*p2) /* calc # of /'s */ + if (*p2++ == '/') + updirs++; + if (p[1] == '0') { /* print <x> or ... */ + pdirs = 1; + p++; + } + if (p[1] >= '1' && p[1] <= '9') { + /* calc # to skip */ + skip = p[1] - '0'; + p++; + } else + skip = 1; + + updirs -= skip; + while (skip-- > 0) { + while ((p2 > q) && (*p2 != '/')) + p2--; /* back up */ + if (skip && p2 > q) + p2--; + } + if (*p2 == '/' && p2 != q) + p2++; + } + + if (updirs > 0 && pdirs) { + if (i >= len - 5) + break; + if (op == '.') { + ADDBUF('.'); + ADDBUF('.'); + ADDBUF('.'); + } else { + ADDBUF('/'); + ADDBUF('<'); + if (updirs > 9) { + ADDBUF('9'); + ADDBUF('+'); + } else + ADDBUF('0' + updirs); + ADDBUF('>'); + } + } + for (; *p2; p2++) + ADDBUF(*p2); + break; + + case 'M': + case 'm': + for (p2 = connected && username ? username : "-"; + *p2 ; p2++) { + if (op == 'm' && *p2 == '.') + break; + ADDBUF(*p2); + } + break; + + case 'n': + for (p2 = connected ? username : "-"; *p2 ; p2++) + ADDBUF(*p2); + break; + + case '%': + ADDBUF('%'); + break; + + default: /* display unknown codes literally */ + ADDBUF('%'); + ADDBUF(op); + break; + + } + } + endbuf: + buf[i] = '\0'; +} + +/* + * Parse `port' into a TCP port number, defaulting to `defport' if `port' is + * an unknown service name. If defport != -1, print a warning upon bad parse. + */ +int +parseport(const char *port, int defport) +{ + int rv; + long nport; + char *p, *ep; + + p = xstrdup(port); + nport = strtol(p, &ep, 10); + if (*ep != '\0' && ep == p) { + struct servent *svp; + + svp = getservbyname(port, "tcp"); + if (svp == NULL) { + badparseport: + if (defport != -1) + warnx("Unknown port `%s', using port %d", + port, defport); + rv = defport; + } else + rv = ntohs(svp->s_port); + } else if (nport < 1 || nport > MAX_IN_PORT_T || *ep != '\0') + goto badparseport; + else + rv = nport; + free(p); + return (rv); +} + +/* + * Determine if given string is an IPv6 address or not. + * Return 1 for yes, 0 for no + */ +int +isipv6addr(const char *addr) +{ + int rv = 0; +#ifdef INET6 + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(addr, "0", &hints, &res) != 0) + rv = 0; + else { + rv = 1; + freeaddrinfo(res); + } + if (debug) + fprintf(ttyout, "isipv6addr: got %d for %s\n", rv, addr); +#endif + return (rv == 1) ? 1 : 0; +} + + +/* + * Internal version of connect(2); sets socket buffer sizes first and + * supports a connection timeout using a non-blocking connect(2) with + * a poll(2). + * Socket fcntl flags are temporarily updated to include O_NONBLOCK; + * these will not be reverted on connection failure. + * Returns -1 upon failure (with errno set to the problem), or 0 on success. + */ +int +xconnect(int sock, const struct sockaddr *name, socklen_t namelen) +{ + int flags, rv, timeout, error; + socklen_t slen; + struct timeval endtime, now, td; + struct pollfd pfd[1]; + + setupsockbufsize(sock); + + if ((flags = fcntl(sock, F_GETFL, 0)) == -1) + return -1; /* get current socket flags */ + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) + return -1; /* set non-blocking connect */ + + /* NOTE: we now must restore socket flags on successful exit */ + + pfd[0].fd = sock; + pfd[0].events = POLLIN|POLLOUT; + + if (quit_time > 0) { /* want a non default timeout */ + (void)gettimeofday(&endtime, NULL); + endtime.tv_sec += quit_time; /* determine end time */ + } + + rv = connect(sock, name, namelen); /* inititate the connection */ + if (rv == -1) { /* connection error */ + if (errno != EINPROGRESS) /* error isn't "please wait" */ + return -1; + + /* connect EINPROGRESS; wait */ + do { + if (quit_time > 0) { /* determine timeout */ + (void)gettimeofday(&now, NULL); + timersub(&endtime, &now, &td); + timeout = td.tv_sec * 1000 + td.tv_usec/1000; + if (timeout < 0) + timeout = 0; + } else { + timeout = INFTIM; + } + pfd[0].revents = 0; + rv = xpoll(pfd, 1, timeout); + /* loop until poll ! EINTR */ + } while (rv == -1 && errno == EINTR); + + if (rv == 0) { /* poll (connect) timed out */ + errno = ETIMEDOUT; + return -1; + } + + if (rv == -1) { /* poll error */ + return -1; + } else if (pfd[0].revents & (POLLIN|POLLOUT)) { + slen = sizeof(error); /* OK, or pending error */ + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, + &error, &slen) == -1) + return -1; /* Solaris pending error */ + if (error != 0) { + errno = error; /* BSD pending error */ + return -1; + } + } else { + errno = EBADF; /* this shouldn't happen ... */ + return -1; + } + } + + if (fcntl(sock, F_SETFL, flags) == -1) /* restore socket flags */ + return -1; + return 0; +} + +/* + * Internal version of listen(2); sets socket buffer sizes first. + */ +int +xlisten(int sock, int backlog) +{ + + setupsockbufsize(sock); + return (listen(sock, backlog)); +} + +/* + * Internal version of poll(2), to allow reimplementation by select(2) + * on platforms without the former. + */ +int +xpoll(struct pollfd *fds, int nfds, int timeout) +{ + return poll(fds, nfds, timeout); +} + +/* + * malloc() with inbuilt error checking + */ +void * +xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (p == NULL) + err(1, "Unable to allocate %ld bytes of memory", (long)size); + return (p); +} + +/* + * sl_init() with inbuilt error checking + */ +StringList * +xsl_init(void) +{ + StringList *p; + + p = sl_init(); + if (p == NULL) + err(1, "Unable to allocate memory for stringlist"); + return (p); +} + +/* + * sl_add() with inbuilt error checking + */ +void +xsl_add(StringList *sl, char *i) +{ + + if (sl_add(sl, i) == -1) + err(1, "Unable to add `%s' to stringlist", i); +} + +/* + * strdup() with inbuilt error checking + */ +char * +xstrdup(const char *str) +{ + char *s; + + if (str == NULL) + errx(1, "xstrdup() called with NULL argument"); + s = strdup(str); + if (s == NULL) + err(1, "Unable to allocate memory for string copy"); + return (s); +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..7b69df1 --- /dev/null +++ b/src/version.h @@ -0,0 +1,44 @@ +/* $NetBSD: version.h,v 1.50 2005/05/14 15:26:43 lukem Exp $ */ +/*- + * Copyright (c) 1999-2005 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. + */ + +#ifndef FTP_PRODUCT +#define FTP_PRODUCT "NetBSD-ftp" +#endif + +#ifndef FTP_VERSION +#define FTP_VERSION "20050514" +#endif |