summaryrefslogtreecommitdiff
path: root/net/tnftp
diff options
context:
space:
mode:
authorlukem <lukem>2003-07-31 07:13:01 +0000
committerlukem <lukem>2003-07-31 07:13:01 +0000
commita7e8208be04b08543dad35c84839295c3f5a508a (patch)
treed7a7d1f79ee44f24a8cd452a941175ed8336720a /net/tnftp
parent007f2ec1d474610ddb41d3973eab949ee811a8d8 (diff)
downloadpkgsrc-a7e8208be04b08543dad35c84839295c3f5a508a.tar.gz
Import of canonical tnftp 20030825 sources,
to make it easier to track new versions.
Diffstat (limited to 'net/tnftp')
-rw-r--r--net/tnftp/files/src/cmds.c2721
-rw-r--r--net/tnftp/files/src/ftp.c2123
-rw-r--r--net/tnftp/files/src/util.c1266
-rw-r--r--net/tnftp/files/src/version.h44
4 files changed, 6154 insertions, 0 deletions
diff --git a/net/tnftp/files/src/cmds.c b/net/tnftp/files/src/cmds.c
new file mode 100644
index 00000000000..5bc10ea6ac2
--- /dev/null
+++ b/net/tnftp/files/src/cmds.c
@@ -0,0 +1,2721 @@
+/* $NetBSD: cmds.c,v 1.1.1.1 2003/07/31 07:18:23 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1996-2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
+#else
+__RCSID("$NetBSD: cmds.c,v 1.1.1.1 2003/07/31 07:18:23 lukem Exp $");
+#endif
+#endif /* not lint */
+#endif
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+
+#include "tnftp.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;
+char *mname;
+
+static int confirm(const char *, 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(*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 *cmd;
+ int loc = 0;
+ char *locfile, *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");
+ if (loc && ntflag)
+ remfile = dotrans(remfile);
+ if (loc && mapflag)
+ remfile = domap(remfile);
+ sendrequest(cmd, locfile, remfile,
+ locfile != argv[1] || remfile != argv[2]);
+ free(locfile);
+}
+
+/*
+ * Send multiple files.
+ */
+void
+mput(int argc, char *argv[])
+{
+ int i;
+ sigfunc oldintr;
+ int ointer;
+ 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)) {
+ tp = cp;
+ if (mcase)
+ tp = docase(tp);
+ if (ntflag)
+ tp = dotrans(tp);
+ if (mapflag)
+ tp = domap(tp);
+ 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])) {
+ tp = (ntflag) ? dotrans(argv[i]) : argv[i];
+ tp = (mapflag) ? domap(tp) : tp;
+ 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)) {
+ tp = (ntflag) ? dotrans(*cpp) : *cpp;
+ tp = (mapflag) ? domap(tp) : tp;
+ 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, *locfile, *olocfile;
+
+ 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 = olocfile;
+ if (loc && mcase)
+ locfile = docase(locfile);
+ if (loc && ntflag)
+ locfile = dotrans(locfile);
+ if (loc && mapflag)
+ locfile = domap(locfile);
+ 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, *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) {
+ if (*cp == '\0' || !connected) {
+ mflag = 0;
+ continue;
+ }
+ if (! mflag || !confirm(argv[0], cp))
+ continue;
+ tp = cp;
+ if (mcase)
+ tp = docase(tp);
+ if (ntflag)
+ tp = dotrans(tp);
+ if (mapflag)
+ tp = domap(tp);
+ 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[])
+{
+ int i;
+
+ if (argc == 0) {
+ fprintf(ttyout, "usage: %s\n", argv[0]);
+ code = -1;
+ return;
+ }
+ 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 */
+ );
+ fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
+ if (macnum > 0) {
+ fputs("Macros:\n", ttyout);
+ for (i=0; i<macnum; i++) {
+ fprintf(ttyout, "\t%s\n", macros[i].mac_name);
+ }
+ }
+ 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;
+ updateremotepwd();
+ }
+}
+
+/*
+ * Set current working directory on local machine.
+ */
+void
+lcd(int argc, char *argv[])
+{
+ char buf[MAXPATHLEN];
+ 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) < 0)
+ warn("local: %s", locdir);
+ else {
+ if (getcwd(buf, sizeof(buf)) != NULL) {
+ fprintf(ttyout, "Local directory now %s\n", buf);
+ code = 0;
+ } else
+ warn("getcwd: %s", locdir);
+ }
+ (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 != '|') {
+ 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;
+ if (strcmp(dest, "-") && *dest != '|')
+ if (((dest = globulize(dest)) == NULL) ||
+ !confirm("output to local-file:", dest)) {
+ code = -1;
+ return;
+ }
+ dolist = strcmp(argv[0], "mls");
+ mname = argv[0];
+ 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 old1;
+ char shellnam[MAXPATHLEN], *shell, *namep;
+ int wait_status;
+
+ if (argc == 0) {
+ fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ old1 = 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, old1);
+ 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[])
+{
+ 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! */
+ if (command("PWD") == ERROR && code == 500) {
+ fputs("PWD command not recognized, trying XPWD.\n", ttyout);
+ (void)command("XPWD");
+ }
+ verbose = oldverbose;
+}
+
+/*
+ * Print working directory on local machine.
+ */
+void
+lpwd(int argc, char *argv[])
+{
+ char buf[MAXPATHLEN];
+
+ if (argc == 0) {
+ fprintf(ttyout, "usage: %s\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (getcwd(buf, sizeof(buf)) != NULL) {
+ fprintf(ttyout, "Local directory %s\n", buf);
+ code = 0;
+ } else {
+ warn("getcwd");
+ code = -1;
+ }
+}
+
+/*
+ * 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)
+{
+
+ 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
+ */
+char *
+docase(char *name)
+{
+ static char new[MAXPATHLEN];
+ int i, dochange;
+
+ dochange = 1;
+ for (i = 0; name[i] != '\0' && i < sizeof(new) - 1; i++) {
+ new[i] = name[i];
+ if (islower((unsigned char)new[i]))
+ dochange = 0;
+ }
+ new[i] = '\0';
+
+ if (dochange) {
+ for (i = 0; new[i] != '\0'; i++)
+ if (isupper((unsigned char)new[i]))
+ new[i] = tolower(new[i]);
+ }
+ return (new);
+}
+
+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));
+}
+
+char *
+dotrans(char *name)
+{
+ static char new[MAXPATHLEN];
+ char *cp1, *cp2 = new;
+ int i, ostop, found;
+
+ for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
+ continue;
+ for (cp1 = name; *cp1; cp1++) {
+ found = 0;
+ for (i = 0; *(ntin + i) && i < 16; i++) {
+ if (*cp1 == *(ntin + i)) {
+ found++;
+ if (i < ostop) {
+ *cp2++ = *(ntout + i);
+ }
+ break;
+ }
+ }
+ if (!found) {
+ *cp2++ = *cp1;
+ }
+ }
+ *cp2 = '\0';
+ return (new);
+}
+
+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);
+}
+
+char *
+domap(char *name)
+{
+ static char new[MAXPATHLEN];
+ char *cp1 = name, *cp2 = mapin;
+ 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;
+ }
+ cp1 = new;
+ *cp1 = '\0';
+ cp2 = mapout;
+ while (*cp2) {
+ match = 0;
+ switch (*cp2) {
+ case '\\':
+ if (*(cp2 + 1)) {
+ *cp1++ = *++cp2;
+ }
+ break;
+ case '[':
+LOOP:
+ if (*++cp2 == '$' &&
+ isdigit((unsigned char)*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ }
+ else {
+ while (*cp2 && *cp2 != ',' &&
+ *cp2 != ']') {
+ if (*cp2 == '\\') {
+ cp2++;
+ }
+ else if (*cp2 == '$' &&
+ isdigit((unsigned char)*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum =
+ *cp2 - '1']) {
+ char *cp3=tp[toknum];
+
+ while (cp3 !=
+ te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ }
+ else if (*cp2) {
+ *cp1++ = *cp2++;
+ }
+ }
+ if (!*cp2) {
+ fputs(
+ "nmap: unbalanced brackets.\n",
+ ttyout);
+ return (name);
+ }
+ match = 1;
+ cp2--;
+ }
+ if (match) {
+ while (*++cp2 && *cp2 != ']') {
+ if (*cp2 == '\\' && *(cp2 + 1)) {
+ cp2++;
+ }
+ }
+ if (!*cp2) {
+ fputs(
+ "nmap: unbalanced brackets.\n",
+ ttyout);
+ return (name);
+ }
+ break;
+ }
+ switch (*++cp2) {
+ case ',':
+ goto LOOP;
+ case ']':
+ break;
+ default:
+ cp2--;
+ goto LOOP;
+ }
+ break;
+ case '$':
+ if (isdigit((unsigned char)*(cp2 + 1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp1++ = *cp2;
+ break;
+ }
+ cp2++;
+ }
+ *cp1 = '\0';
+ if (!*new) {
+ return (name);
+ }
+ return (new);
+}
+
+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;
+ updateremotepwd();
+ }
+}
+
+/*
+ * 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/net/tnftp/files/src/ftp.c b/net/tnftp/files/src/ftp.c
new file mode 100644
index 00000000000..12f58f92d56
--- /dev/null
+++ b/net/tnftp/files/src/ftp.c
@@ -0,0 +1,2123 @@
+/* $NetBSD: ftp.c,v 1.1.1.1 2003/07/31 07:18:24 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1996-2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "tnftp.h"
+
+#if 0
+#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.1.1.1 2003/07/31 07:18:24 lukem Exp $");
+#endif
+#endif /* not lint */
+#endif
+
+#include <arpa/telnet.h>
+
+#include "ftp_var.h"
+
+volatile int abrtflag = 0;
+volatile int timeoutflag = 0;
+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, len, error, portnum;
+ struct addrinfo hints, *res, *res0;
+ char hbuf[MAXHOSTNAMELEN];
+ static char hostnamebuf[MAXHOSTNAMELEN];
+ char *cause = "unknown";
+
+ 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;
+ }
+ while ((error = xconnect(s, res->ai_addr, res->ai_addrlen)) < 0
+ && errno == EINTR) {
+ ;
+ }
+ 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) < 0) {
+ 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, (char *)&tos,
+ sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+ }
+#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;
+ }
+ {
+ int on = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))
+ < 0 && debug) {
+ warn("setsockopt");
+ }
+ }
+
+ return (hostname);
+ bad:
+ (void)close(s);
+ return (NULL);
+}
+
+void
+cmdabort(int notused)
+{
+ int oerrno = errno;
+
+ 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 < &current_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;
+ int nfd = 0;
+
+#ifdef USE_SELECT
+ struct timeval t;
+ fd_set rmask;
+
+ FD_ZERO(&rmask);
+ if (cin) {
+ if (nfd < fileno(cin))
+ nfd = fileno(cin);
+ FD_SET(fileno(cin), &rmask);
+ }
+ if (din) {
+ if (nfd < fileno(din))
+ nfd = fileno(din);
+ FD_SET(fileno(din), &rmask);
+ }
+
+ t.tv_sec = (long) sec;
+ t.tv_usec = 0;
+ if ((nr = select(nfd, &rmask, NULL, NULL, &t)) <= 0)
+ return nr;
+
+ nr = 0;
+ if (cin)
+ nr |= FD_ISSET(fileno(cin), &rmask) ? 1 : 0;
+ if (din)
+ nr |= FD_ISSET(fileno(din), &rmask) ? 2 : 0;
+
+#else
+ struct pollfd pfd[2];
+
+ if (cin) {
+ pfd[nfd].fd = fileno(cin);
+ pfd[nfd++].events = POLLIN;
+ }
+
+ if (din) {
+ pfd[nfd].fd = fileno(din);
+ pfd[nfd++].events = POLLIN;
+ }
+
+ if ((nr = poll(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;
+#endif
+ return nr;
+}
+
+sigjmp_buf xferabort;
+
+void
+abortxfer(int notused)
+{
+ char msgbuf[100];
+ int len;
+
+ 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, len, tmpno = 0;
+ int on = 1;
+ int error;
+ u_int addr[16], port[2];
+ u_int af, hal, pal;
+ 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, (char *)&on,
+ sizeof(on)) < 0)
+ warn("setsockopt (ignored)");
+ 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 (errno == EINTR)
+ continue;
+ if (activefallback) {
+ (void)close(data);
+ data = -1;
+ passivemode = 0;
+#if 0
+ activefallback = 0;
+#endif
+ goto reinit;
+ }
+ warn("connect");
+ goto bad;
+ }
+#ifdef IPTOS_THROUGHPUT
+ if (data_addr.su_family == AF_INET) {
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on,
+ sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+ }
+#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, (char *)&on,
+ sizeof(on)) < 0) {
+ warn("setsockopt (reuse address)");
+ 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, (char *)&on,
+ sizeof(on)) < 0)
+ warn("setsockopt (ignored)");
+ len = sizeof(data_addr.si_su);
+ memset((char *)&data_addr, 0, sizeof (data_addr));
+ if (getsockname(data, (struct sockaddr *)&data_addr.si_su, &len) < 0) {
+ 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, (char *)&on,
+ sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+ }
+#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, fromlen = myctladdr.su_len;
+
+ if (passivemode)
+ return (fdopen(data, lmode));
+
+ s = accept(data, (struct sockaddr *) &from.si_su, &fromlen);
+ if (s < 0) {
+ warn("accept");
+ (void)close(data), data = -1;
+ return (NULL);
+ }
+ (void)close(data);
+ data = s;
+#ifdef IPTOS_THROUGHPUT
+ if (from.su_family == AF_INET) {
+ int tos = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof(int)) < 0) {
+ warn("setsockopt TOS (ignored)");
+ }
+ }
+#endif
+ return (fdopen(data, lmode));
+}
+
+void
+psabort(int notused)
+{
+ int oerrno = errno;
+
+ 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)
+{
+
+ 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];
+ int len;
+
+ 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;
+ int 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/net/tnftp/files/src/util.c b/net/tnftp/files/src/util.c
new file mode 100644
index 00000000000..9b0a9aa11dd
--- /dev/null
+++ b/net/tnftp/files/src/util.c
@@ -0,0 +1,1266 @@
+/* $NetBSD: util.c,v 1.1.1.1 2003/07/31 07:18:24 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1997-2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: util.c,v 1.1.1.1 2003/07/31 07:18:24 lukem Exp $");
+#endif /* not lint */
+#endif
+
+/*
+ * FTP User Program -- Misc support routines
+ */
+
+#include "tnftp.h"
+
+#include "ftp_var.h"
+
+/*
+ * 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 dummy)
+{
+
+ 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;
+ }
+ }
+ updateremotepwd();
+
+ 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 `remotepwd', which contains the state of the remote cwd
+ */
+void
+updateremotepwd(void)
+{
+ int overbose, ocode, i;
+ char *cp;
+
+ overbose = verbose;
+ ocode = code;
+ if (debug == 0)
+ verbose = -1;
+ if (command("PWD") != COMPLETE)
+ goto badremotepwd;
+ cp = strchr(reply_string, ' ');
+ if (cp == NULL || cp[0] == '\0' || cp[1] != '"')
+ goto badremotepwd;
+ cp += 2;
+ for (i = 0; *cp && i < sizeof(remotepwd) - 1; i++, cp++) {
+ if (cp[0] == '"') {
+ if (cp[1] == '"')
+ cp++;
+ else
+ break;
+ }
+ remotepwd[i] = *cp;
+ }
+ remotepwd[i] = '\0';
+ if (debug)
+ fprintf(ttyout, "got remotepwd as `%s'\n", remotepwd);
+ goto cleanupremotepwd;
+ badremotepwd:
+ remotepwd[0]='\0';
+ cleanupremotepwd:
+ verbose = overbose;
+ code = ocode;
+}
+
+
+/*
+ * 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(rcvbuf_size)) < 0)
+ warn("unable to set sndbuf size %d", sndbuf_size);
+
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf_size,
+ sizeof(rcvbuf_size)) < 0)
+ 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 ? remotepwd : "";
+ 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.
+ */
+int
+xconnect(int sock, const struct sockaddr *name, int namelen)
+{
+
+ setupsockbufsize(sock);
+ return (connect(sock, name, namelen));
+}
+
+/*
+ * Internal version of listen(2); sets socket buffer sizes first.
+ */
+int
+xlisten(int sock, int backlog)
+{
+
+ setupsockbufsize(sock);
+ return (listen(sock, backlog));
+}
+
+/*
+ * 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/net/tnftp/files/src/version.h b/net/tnftp/files/src/version.h
new file mode 100644
index 00000000000..32dc885e7cb
--- /dev/null
+++ b/net/tnftp/files/src/version.h
@@ -0,0 +1,44 @@
+/* $NetBSD: version.h,v 1.1.1.1 2003/07/31 07:13:01 lukem Exp $ */
+/*-
+ * Copyright (c) 1999-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 FTP_PRODUCT
+#define FTP_PRODUCT "NetBSD-ftp"
+#endif
+
+#ifndef FTP_VERSION
+#define FTP_VERSION "20030731b"
+#endif