File: [local] / src / usr.bin / ftp / small.c (download)
Revision 1.13, Wed Mar 8 04:43:11 2023 UTC (15 months ago) by guenther
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, HEAD Changes since 1.12: +1 -4 lines
Delete obsolete /* ARGSUSED */ lint comments.
ok miod@ millert@
|
/* $OpenBSD: small.c,v 1.13 2023/03/08 04:43:11 guenther Exp $ */
/* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */
/*
* 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.
*/
/*
* Copyright (c) 1985, 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* FTP User Program -- Command Routines.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <arpa/ftp.h>
#include <ctype.h>
#include <err.h>
#include <fnmatch.h>
#include <glob.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ftp_var.h"
#include "pathnames.h"
#include "small.h"
jmp_buf jabort;
char *mname;
char *home = "/";
struct types {
char *t_name;
char *t_mode;
int t_type;
} types[] = {
{ "ascii", "A", TYPE_A },
{ "binary", "I", TYPE_I },
{ "image", "I", TYPE_I },
{ NULL }
};
/*
* Set transfer type.
*/
void
settype(int argc, char *argv[])
{
struct types *p;
int comret;
if (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;
}
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 (
#ifndef SMALL
!debug &&
#endif /* !SMALL */
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.
*/
void
setbinary(int argc, char *argv[])
{
stype[1] = "binary";
settype(2, stype);
}
void
get(int argc, char *argv[])
{
(void)getit(argc, argv, 0, restart_point ? "a+w" : "w" );
}
/*
* Receive one file.
*/
int
getit(int argc, char *argv[], int restartit, const char *mode)
{
int loc = 0;
int rval = 0;
char *oldargv1, *oldargv2, *globargv2;
if (argc == 2) {
argc++;
argv[2] = argv[1];
loc++;
}
#ifndef SMALL
if (argc < 2 && !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);
}
#endif /* !SMALL */
oldargv1 = argv[1];
oldargv2 = argv[2];
if (!globulize(&argv[2])) {
code = -1;
return (0);
}
globargv2 = argv[2];
if (loc && mcase) {
char *tp = argv[1], *tp2, tmpbuf[PATH_MAX];
while (*tp && !islower((unsigned char)*tp)) {
tp++;
}
if (!*tp) {
tp = argv[2];
tp2 = tmpbuf;
while ((*tp2 = *tp) != '\0') {
if (isupper((unsigned char)*tp2)) {
*tp2 = tolower((unsigned char)*tp2);
}
tp++;
tp2++;
}
argv[2] = tmpbuf;
}
}
if (loc && ntflag)
argv[2] = dotrans(argv[2]);
if (loc && mapflag)
argv[2] = domap(argv[2]);
#ifndef SMALL
if (restartit) {
struct stat stbuf;
int ret;
ret = stat(argv[2], &stbuf);
if (restartit == 1) {
restart_point = (ret < 0) ? 0 : 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;
fprintf(ttyout,
"Local file \"%s\" is newer "\
"than remote file \"%s\".\n",
argv[2], argv[1]);
goto freegetit;
}
}
}
}
#endif /* !SMALL */
recvrequest("RETR", argv[2], argv[1], mode,
argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc);
restart_point = 0;
#ifndef SMALL
freegetit:
#endif
if (oldargv2 != globargv2) /* free up after globulize() */
free(globargv2);
return (rval);
}
/* XXX - Signal race. */
void
mabort(int signo)
{
int save_errno = errno;
alarmtimer(0);
(void) write(fileno(ttyout), "\n\r", 2);
#ifndef SMALL
if (mflag && fromatty) {
/* XXX signal race, crazy unbelievable stdio misuse */
if (confirm(mname, NULL)) {
errno = save_errno;
longjmp(jabort, 1);
}
}
#endif /* !SMALL */
mflag = 0;
errno = save_errno;
longjmp(jabort, 1);
}
/*
* Get multiple files.
*/
void
mget(int argc, char *argv[])
{
extern int optind, optreset;
sig_t oldintr;
int xargc = 2;
char *cp, localcwd[PATH_MAX], *xargv[] = { argv[0], NULL, NULL };
static int restartit = 0;
#ifndef SMALL
extern char *optarg;
const char *errstr;
int ch, i = 1;
char type = 0, *dummyargv[] = { argv[0], ".", NULL };
FILE *ftemp = NULL;
static int depth = 0, max_depth = 0;
optind = optreset = 1;
if (depth)
depth++;
while ((ch = getopt(argc, argv, "cd:nr")) != -1) {
switch(ch) {
case 'c':
restartit = 1;
break;
case 'd':
max_depth = strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr != NULL) {
fprintf(ttyout, "bad depth value, %s: %s\n",
errstr, optarg);
code = -1;
return;
}
break;
case 'n':
restartit = -1;
break;
case 'r':
depth = 1;
break;
default:
goto usage;
}
}
if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) {
usage:
fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n",
argv[0]);
code = -1;
return;
}
argv[optind - 1] = argv[0];
argc -= optind - 1;
argv += optind - 1;
#endif /* !SMALL */
mname = argv[0];
mflag = 1;
if (getcwd(localcwd, sizeof(localcwd)) == NULL)
err(1, "can't get cwd");
oldintr = signal(SIGINT, mabort);
(void)setjmp(jabort);
while ((cp =
#ifdef SMALL
remglob(argv, proxy, NULL)) != NULL
) {
#else /* SMALL */
depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) :
remglob(argv, proxy, NULL)) != NULL
|| (mflag && depth && ++i < argc)
) {
if (cp == NULL)
continue;
#endif /* SMALL */
if (*cp == '\0') {
mflag = 0;
continue;
}
if (!mflag)
continue;
#ifndef SMALL
if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0)
continue;
#endif /* !SMALL */
if (!fileindir(cp, localcwd)) {
fprintf(ttyout, "Skipping non-relative filename `%s'\n",
cp);
continue;
}
#ifndef SMALL
if (type == 'd' && depth == max_depth)
continue;
if (!confirm(argv[0], cp))
continue;
if (type == 'd') {
mkdir(cp, 0755);
if (chdir(cp) != 0) {
warn("local: %s", cp);
continue;
}
xargv[1] = cp;
cd(xargc, xargv);
if (dirchange != 1)
goto out;
xargv[1] = "*";
mget(xargc, xargv);
xargv[1] = "..";
cd(xargc, xargv);
if (dirchange != 1) {
mflag = 0;
goto out;
}
out:
if (chdir("..") != 0) {
warn("local: %s", cp);
mflag = 0;
}
continue;
}
if (type == 's')
/* Currently ignored. */
continue;
#endif /* !SMALL */
xargv[1] = cp;
(void)getit(xargc, xargv, restartit,
(restartit == 1 || restart_point) ? "a+w" : "w");
#ifndef SMALL
if (!mflag && fromatty) {
if (confirm(argv[0], NULL))
mflag = 1;
}
#endif /* !SMALL */
}
(void)signal(SIGINT, oldintr);
#ifndef SMALL
if (depth)
depth--;
if (depth == 0 || mflag == 0)
depth = max_depth = mflag = restartit = 0;
#else /* !SMALL */
mflag = 0;
#endif /* !SMALL */
}
/*
* Set current working directory on remote machine.
*/
void
cd(int argc, char *argv[])
{
int r;
#ifndef SMALL
if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
argc > 2) {
fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
code = -1;
return;
}
#endif /* !SMALL */
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 == ERROR && code == 550) {
dirchange = 0;
return;
}
if (r == COMPLETE)
dirchange = 1;
}
/*
* Terminate session, but don't exit.
*/
void
disconnect(int argc, char *argv[])
{
if (!connected)
return;
(void)command("QUIT");
if (cout) {
(void)fclose(cout);
}
cout = NULL;
connected = 0;
data = -1;
#ifndef SMALL
if (!proxy) {
macnum = 0;
}
#endif /* !SMALL */
}
char *
dotrans(char *name)
{
static char new[PATH_MAX];
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);
}
char *
domap(char *name)
{
static char new[PATH_MAX];
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;
}
/* FALLTHROUGH */
default:
*cp1++ = *cp2;
break;
}
cp2++;
}
*cp1 = '\0';
if (!*new) {
return (name);
}
return (new);
}