/*-
* Copyright (c) 1996-2021 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1985, 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (C) 1997 and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Confirm if "cmd" is to be performed upon "file".
* If "file" is NULL, generate a "Continue with" prompt instead.
*/
static int
confirm(const char *cmd, const char *file)
{
const char *errormsg;
char cline[BUFSIZ];
const char *promptleft, *promptright;
if (!interactive || confirmrest)
return (1);
if (file == NULL) {
promptleft = "Continue with";
promptright = cmd;
} else {
promptleft = cmd;
promptright = file;
}
for (;;) {
fprintf(ttyout, "%s %s [anpqy?]? ", promptleft, promptright);
(void)fflush(ttyout);
if (get_line(stdin, cline, sizeof(cline), &errormsg) < 0) {
mflag = 0;
fprintf(ttyout, "%s; %s aborted\n", errormsg, cmd);
return (0);
}
switch (tolower((unsigned char)*cline)) {
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", cmd);
/* 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;
/*
* 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 (ftp_debug == 0 && show == 0)
verbose = 0;
for (p = types; p->t_name; p++)
if (newtype == p->t_type)
break;
if (p->t_name == 0) {
errx(1, "changetype: unknown type %d", newtype);
}
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;
}
/*
* Set binary transfer type.
*/
/*VARARGS*/
void
setbinary(int argc, char *argv[])
{
/*
* 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 *gmode)
{
int loc, rval;
char *remfile, *olocfile, *locfile;
char buf[MAXPATHLEN];
loc = rval = 0;
if (argc == 2) {
argc++;
argv[2] = argv[1];
loc++;
}
if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file")))
goto usage;
if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
usage:
UPRINTF("usage: %s remote-file [local-file]\n", argv[0]);
code = -1;
return (0);
}
remfile = argv[1];
if ((olocfile = globulize(argv[2])) == NULL) {
code = -1;
return (0);
}
locfile = doprocess(buf, sizeof(buf), olocfile,
loc && mcase, loc && ntflag, loc && mapflag);
if (restartit) {
struct stat stbuf;
int ret;
if (! features[FEAT_REST_STREAM]) {
fprintf(ttyout,
"Restart is not supported by the remote server.\n");
return (0);
}
ret = stat(locfile, &stbuf);
if (restartit == 1) {
if (ret < 0) {
if (errno != ENOENT) {
warn("Can't stat `%s'", locfile);
goto freegetit;
}
restart_point = 0;
}
else
restart_point = stbuf.st_size;
} else {
if (ret == 0) {
time_t mtime;
/*
* Read list of filenames from a local file and get those
*/
void
fget(int argc, char *argv[])
{
const char *gmode;
FILE *fp;
char buf[MAXPATHLEN], cmdbuf[MAX_C_NAME];
/*
* Get a directory listing of remote files.
* Supports being invoked as:
* cmd runs
* --- ----
* dir, ls LIST
* mlsd MLSD
* nlist NLST
* pdir, pls LIST |$PAGER
* pmlsd MLSD |$PAGER
*/
void
ls(int argc, char *argv[])
{
const char *cmd;
char *remdir, *locbuf, *locfile;
int pagecmd, mlsdcmd;
remdir = NULL;
locbuf = NULL;
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";
/*
* Print working directory on local machine.
*/
void
lpwd(int argc, char *argv[])
{
code = -1;
if (argc != 1) {
UPRINTF("usage: %s\n", argv[0]);
return;
}
if (! localcwd[0])
updatelocalcwd();
if (! localcwd[0])
fprintf(ttyout, "Unable to determine local directory\n");
else {
fprintf(ttyout, "Local directory: %s\n", localcwd);
code = 0;
}
}
/*
* Make a directory.
*/
void
makedir(int argc, char *argv[])
{
int r;
if (argc == 0 || argc > 2 ||
(argc == 1 && !another(&argc, &argv, "directory-name"))) {
UPRINTF("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"))) {
UPRINTF("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"))) {
UPRINTF("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"))){
UPRINTF("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;
}
/*
* 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) {
UPRINTF("usage: %s\n", argv[0]);
code = -1;
return;
}
if (connected)
disconnect(0, NULL);
pswitch(1);
if (connected)
disconnect(0, NULL);
exit(0);
}
void __dead
justquit(void)
{
quit(0, NULL);
/*
* quit is not __dead, but for our invocation it never will return,
* but some compilers are not smart enough to find this out.
*/
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) {
UPRINTF("usage: %s\n", argv[0]);
code = -1;
return;
}
if (!connected)
return;
(void)command("QUIT");
cleanuppeer();
}
/*
* convert the given name to lower case if it's all upper case, into
* a static buffer which is returned to the caller
*/
static char *
docase(char *dst, size_t dlen, const char *src)
{
size_t i;
int dochange = 1;
for (i = 0; src[i] != '\0' && i < dlen - 1; i++) {
dst[i] = src[i];
if (islower((unsigned char)dst[i]))
dochange = 0;
}
dst[i] = '\0';
if (dochange) {
for (i = 0; dst[i] != '\0'; i++)
if (isupper((unsigned char)dst[i]))
dst[i] = tolower((unsigned char)dst[i]);
}
return dst;
}
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;
o = getoption(option);
if (o == NULL) {
fprintf(ttyout, "No such option `%s'.\n", option);
return;
}
FREEPTR(o->value);
o->value = ftp_strdup(value);
if (verbose && doverbose)
fprintf(ttyout, "Setting `%s' to `%s'.\n",
o->name, o->value);
}
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) {
UPRINTF("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) {
UPRINTF("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) {
UPRINTF("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;
}