various fixes suggested by Robert Elz:
* implement closedataconn() and use appropriately (including in mlsd()) * only put leading space in front of MLST output (not MLSD output) * MLSD: only output pdir and cdir entries when the type fact is requested. * change error code for giving MLSD a non-directory from 550 to 501 * remove MLSx Type fact support for UNIX.* for now; it's not standardised yet. * do a check_login when MLSD and MLST are given no args * detect & complain about null facts in OPTS MLST * cache getgroups() at login instead of calling each time in fact_perm() other mods: * implement cprintf(); as per fprintf() but increments total_bytes{,_out} * implement CPUTC(); as per putc() but increments total_bytes{,_out} * implement base64_encode() * fact_unique() display base64 encoding of dev_t and ino_t rather than hex output; should scale if size of those changes * change reply() so that a negative code acts as the initial line in a reply, code == 0 prefixes the line with 4 spaces, and code > 0 works as before. deprecate lreply(code, ) and lreply(0, ) in favour of reply(-code, ) and reply(0, ) respectively. * use cprintf() and CPUTC() appropriately (often instead of printf(), lreply(-2, ) or lreply(-1, ). now we actually account for the data sent by MLST and MLSD. * remove DEBUG support for sending MLSD output to control connection instead of data connection (my ftp client now supports MLSD :-)
This commit is contained in:
parent
4f82723a61
commit
73f082e2ea
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: cmds.c,v 1.2 2000/06/16 23:17:41 explorer Exp $ */
|
||||
/* $NetBSD: cmds.c,v 1.3 2000/06/19 15:15:03 lukem Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999-2000 The NetBSD Foundation, Inc.
|
||||
@ -101,7 +101,7 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: cmds.c,v 1.2 2000/06/16 23:17:41 explorer Exp $");
|
||||
__RCSID("$NetBSD: cmds.c,v 1.3 2000/06/19 15:15:03 lukem Exp $");
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -133,15 +133,13 @@ typedef struct {
|
||||
} factelem;
|
||||
|
||||
static void ack(const char *);
|
||||
static void base64_encode(const char *, size_t, char *, int);
|
||||
static void fact_type(const char *, FILE *, factelem *);
|
||||
static void fact_size(const char *, FILE *, factelem *);
|
||||
static void fact_modify(const char *, FILE *, factelem *);
|
||||
static void fact_perm(const char *, FILE *, factelem *);
|
||||
static void fact_unique(const char *, FILE *, factelem *);
|
||||
static void fact_unix_group(const char *, FILE *, factelem *);
|
||||
static void fact_unix_mode(const char *, FILE *, factelem *);
|
||||
static void fact_unix_owner(const char *, FILE *, factelem *);
|
||||
static int matchgroup(gid_t, gid_t *, int);
|
||||
static int matchgroup(gid_t);
|
||||
static void mlsname(FILE *, factelem *);
|
||||
static void replydirname(const char *, const char *);
|
||||
|
||||
@ -154,13 +152,11 @@ struct ftpfact {
|
||||
|
||||
struct ftpfact facttab[] = {
|
||||
{ "Type", 1, fact_type },
|
||||
#define FACT_TYPE 0
|
||||
{ "Size", 1, fact_size },
|
||||
{ "Modify", 1, fact_modify },
|
||||
{ "Perm", 1, fact_perm },
|
||||
{ "Unique", 1, fact_unique },
|
||||
{ "UNIX.mode", 1, fact_unix_mode },
|
||||
{ "UNIX.owner", 0, fact_unix_owner },
|
||||
{ "UNIX.group", 0, fact_unix_group },
|
||||
/* "Create" */
|
||||
/* "Lang" */
|
||||
/* "Media-Type" */
|
||||
@ -199,16 +195,16 @@ feat(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
lreply(211, "Features supported");
|
||||
lreply(-1, " MDTM");
|
||||
lreply(-2, " MLST ");
|
||||
reply(-211, "Features supported");
|
||||
cprintf(stdout, " MDTM\r\n");
|
||||
cprintf(stdout, " MLST ");
|
||||
for (i = 0; facttab[i].name; i++)
|
||||
lreply(-2, "%s%s;", facttab[i].name,
|
||||
cprintf(stdout, "%s%s;", facttab[i].name,
|
||||
facttab[i].enabled ? "*" : "");
|
||||
lreply(-1, "");
|
||||
lreply(-1, " REST STREAM");
|
||||
lreply(-1, " SIZE");
|
||||
lreply(-1, " TVFS");
|
||||
cprintf(stdout, "\r\n");
|
||||
cprintf(stdout, " REST STREAM\r\n");
|
||||
cprintf(stdout, " SIZE\r\n");
|
||||
cprintf(stdout, " TVFS\r\n");
|
||||
reply(211, "End");
|
||||
}
|
||||
|
||||
@ -228,13 +224,15 @@ makedir(const char *name)
|
||||
void
|
||||
mlsd(const char *path)
|
||||
{
|
||||
FILE *dout;
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
struct stat sb, pdirstat;
|
||||
char name[MAXPATHLEN];
|
||||
struct dirent *dp;
|
||||
struct stat sb, pdirstat;
|
||||
factelem f;
|
||||
FILE *dout;
|
||||
DIR *dirp;
|
||||
char name[MAXPATHLEN];
|
||||
int hastypefact;
|
||||
|
||||
hastypefact = facttab[FACT_TYPE].enabled;
|
||||
if (path == NULL)
|
||||
path = ".";
|
||||
if (stat(path, &pdirstat) == -1) {
|
||||
@ -244,53 +242,48 @@ mlsd(const char *path)
|
||||
}
|
||||
if (! S_ISDIR(pdirstat.st_mode)) {
|
||||
errno = ENOTDIR;
|
||||
goto mlsdperror;
|
||||
perror_reply(501, path);
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG /* send mlsd to ctrl connection not data connection */
|
||||
dout = stdout;
|
||||
lreply(250, "MLSD %s", path);
|
||||
#else
|
||||
dout = dataconn("MLSD", (off_t)-1, "w");
|
||||
if (dout == NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if ((dirp = opendir(path)) == NULL)
|
||||
goto mlsdperror;
|
||||
f.stat = &sb;
|
||||
while ((dp = readdir(dirp)) != NULL) {
|
||||
snprintf(name, sizeof(name), "%s/%s", path, dp->d_name);
|
||||
/* printf("got >%s< >%s<\n", dp->d_name, name); */
|
||||
if (ISDOTDIR(dp->d_name)) { /* special case curdir: */
|
||||
if (! hastypefact)
|
||||
continue;
|
||||
f.pdirstat = NULL; /* require stat of parent */
|
||||
f.display = path; /* set name to real name */
|
||||
f.iscurdir = 1; /* flag name is curdir */
|
||||
} else {
|
||||
if (ISDOTDOTDIR(dp->d_name)) {
|
||||
if (! hastypefact)
|
||||
continue;
|
||||
f.pdirstat = NULL;
|
||||
} else
|
||||
f.pdirstat = &pdirstat; /* cache parent stat */
|
||||
f.display = dp->d_name;
|
||||
f.iscurdir = 0;
|
||||
}
|
||||
if (stat(name, &sb) == -1)
|
||||
continue;
|
||||
f.path = name;
|
||||
if (ISDOTDIR(dp->d_name)) { /* special case curdir: */
|
||||
f.display = path; /* set name to real name */
|
||||
f.iscurdir = 1; /* flag name is curdir */
|
||||
f.pdirstat = NULL; /* require stat of parent */
|
||||
} else {
|
||||
f.display = dp->d_name;
|
||||
f.iscurdir = 0;
|
||||
if (ISDOTDOTDIR(dp->d_name))
|
||||
f.pdirstat = NULL;
|
||||
else
|
||||
f.pdirstat = &pdirstat; /* cache parent stat */
|
||||
}
|
||||
mlsname(dout, &f);
|
||||
}
|
||||
(void)closedir(dirp);
|
||||
|
||||
#ifdef DEBUG
|
||||
reply(250, "End");
|
||||
#else
|
||||
if (ferror(dout) != 0)
|
||||
perror_reply(550, "Data connection");
|
||||
else
|
||||
reply(226, "MLSD complete.");
|
||||
(void) fclose(dout);
|
||||
closedataconn(dout);
|
||||
total_xfers_out++;
|
||||
total_xfers++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -305,12 +298,13 @@ mlst(const char *path)
|
||||
perror_reply(550, path);
|
||||
return;
|
||||
}
|
||||
lreply(250, "MLST %s", path);
|
||||
reply(-250, "MLST %s", path);
|
||||
f.path = path;
|
||||
f.display = path;
|
||||
f.stat = &sb;
|
||||
f.pdirstat = NULL;
|
||||
f.iscurdir = 0;
|
||||
CPUTC(' ', stdout);
|
||||
mlsname(stdout, &f);
|
||||
reply(250, "End");
|
||||
}
|
||||
@ -341,37 +335,47 @@ opts(const char *command)
|
||||
|
||||
/* special case: MLST */
|
||||
if (strcasecmp(command, "MLST") == 0) {
|
||||
int enabled[sizeof(facttab) / sizeof(struct ftpfact)];
|
||||
int i, onedone;
|
||||
size_t len;
|
||||
char *p;
|
||||
|
||||
for (i = 0; facttab[i].name; i++)
|
||||
facttab[i].enabled = 0;
|
||||
for (i = 0; i < sizeof(enabled) / sizeof(int); i++)
|
||||
enabled[i] = 0;
|
||||
if (ep == NULL || *ep == '\0')
|
||||
goto displaymlstopts;
|
||||
|
||||
/* don't like spaces, and need trailing ; */
|
||||
if (strchr(ep, ' ') != NULL || ep[strlen(ep) - 1] != ';') {
|
||||
len = strlen(ep);
|
||||
if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') {
|
||||
badmlstopt:
|
||||
reply(501, "Invalid MLST options");
|
||||
return;
|
||||
}
|
||||
ep[len - 1] = '\0';
|
||||
while ((p = strsep(&ep, ";")) != NULL) {
|
||||
if (*p == '\0')
|
||||
goto badmlstopt;
|
||||
for (i = 0; facttab[i].name; i++)
|
||||
if (strcasecmp(p, facttab[i].name) == 0) {
|
||||
facttab[i].enabled = 1;
|
||||
enabled[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
displaymlstopts:
|
||||
lreply(-2, "200 MLST OPTS");
|
||||
for (i = 0; facttab[i].name; i++)
|
||||
facttab[i].enabled = enabled[i];
|
||||
cprintf(stdout, "200 MLST OPTS");
|
||||
for (i = onedone = 0; facttab[i].name; i++) {
|
||||
if (facttab[i].enabled) {
|
||||
lreply(-2, "%s%s;", onedone ? "" : " ",
|
||||
cprintf(stdout, "%s%s;", onedone ? "" : " ",
|
||||
facttab[i].name);
|
||||
onedone++;
|
||||
}
|
||||
}
|
||||
lreply(-1, "");
|
||||
cprintf(stdout, "\r\n");
|
||||
fflush(stdout);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -488,7 +492,8 @@ statfilecmd(const char *filename)
|
||||
|
||||
argv[2] = (char *)filename;
|
||||
fin = ftpd_popen(argv, "r", STDOUT_FILENO);
|
||||
lreply(211, "status of %s:", filename);
|
||||
reply(-211, "status of %s:", filename);
|
||||
/* XXX: use fgetln() or fparseln() here? */
|
||||
while ((c = getc(fin)) != EOF) {
|
||||
if (c == '\n') {
|
||||
if (ferror(stdout)){
|
||||
@ -502,13 +507,9 @@ statfilecmd(const char *filename)
|
||||
(void) ftpd_pclose(fin);
|
||||
return;
|
||||
}
|
||||
(void) putchar('\r');
|
||||
total_bytes++;
|
||||
total_bytes_out++;
|
||||
CPUTC('\r', stdout);
|
||||
}
|
||||
(void) putchar(c);
|
||||
total_bytes++;
|
||||
total_bytes_out++;
|
||||
CPUTC(c, stdout);
|
||||
}
|
||||
(void) ftpd_pclose(fin);
|
||||
reply(211, "End of Status");
|
||||
@ -523,13 +524,40 @@ ack(const char *s)
|
||||
reply(250, "%s command successful.", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode len bytes starting at clear using base64 encoding into encoded,
|
||||
* which should be at least ((len + 2) * 4 / 3 + 1) in size.
|
||||
* If nulterm is non-zero, terminate with \0 else pad to len with `='.
|
||||
*/
|
||||
static void
|
||||
base64_encode(const char *clear, size_t len, char *encoded, int nulterm)
|
||||
{
|
||||
static const char enc[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char *cp;
|
||||
int i;
|
||||
|
||||
cp = encoded;
|
||||
for (i = 0; i < len; i += 3) {
|
||||
*(cp++) = enc[((clear[i + 0] >> 2))];
|
||||
*(cp++) = enc[((clear[i + 0] << 4) & 0x30)
|
||||
| ((clear[i + 1] >> 4) & 0x0f)];
|
||||
*(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
|
||||
| ((clear[i + 2] >> 6) & 0x03)];
|
||||
*(cp++) = enc[((clear[i + 2] ) & 0x3f)];
|
||||
}
|
||||
*cp = '\0';
|
||||
while (i-- > len)
|
||||
*(--cp) = nulterm ? '\0' : '=';
|
||||
}
|
||||
|
||||
static void
|
||||
fact_modify(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
struct tm *t;
|
||||
|
||||
t = gmtime(&(fe->stat->st_mtime));
|
||||
fprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
|
||||
cprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
|
||||
TM_YEAR_BASE + t->tm_year,
|
||||
t->tm_mon+1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
@ -538,16 +566,14 @@ fact_modify(const char *fact, FILE *fd, factelem *fe)
|
||||
static void
|
||||
fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
int rok, wok, xok, pdirwok, ngid;
|
||||
int rok, wok, xok, pdirwok;
|
||||
struct stat *pdir;
|
||||
gid_t gids[NGROUPS_MAX];
|
||||
|
||||
ngid = getgroups(sizeof(gids), gids);
|
||||
if (fe->stat->st_uid == geteuid()) {
|
||||
rok = ((fe->stat->st_mode & S_IRUSR) != 0);
|
||||
wok = ((fe->stat->st_mode & S_IWUSR) != 0);
|
||||
xok = ((fe->stat->st_mode & S_IXUSR) != 0);
|
||||
} else if (matchgroup(fe->stat->st_gid, gids, ngid)) {
|
||||
} else if (matchgroup(fe->stat->st_gid)) {
|
||||
rok = ((fe->stat->st_mode & S_IRGRP) != 0);
|
||||
wok = ((fe->stat->st_mode & S_IWGRP) != 0);
|
||||
xok = ((fe->stat->st_mode & S_IXGRP) != 0);
|
||||
@ -557,7 +583,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||
xok = ((fe->stat->st_mode & S_IXOTH) != 0);
|
||||
}
|
||||
|
||||
fprintf(fd, "%s=", fact);
|
||||
cprintf(fd, "%s=", fact);
|
||||
|
||||
/*
|
||||
* if parent info not provided, look it up, but
|
||||
@ -571,7 +597,6 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||
struct stat dir;
|
||||
|
||||
len = strlcpy(realdir, fe->path, sizeof(realdir));
|
||||
/* printf("[path=%s]", fe->path); */
|
||||
if (len < sizeof(realdir) - 4) {
|
||||
if (S_ISDIR(fe->stat->st_mode))
|
||||
strlcat(realdir, "/..", sizeof(realdir));
|
||||
@ -585,7 +610,6 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||
} else
|
||||
strlcpy(realdir, "..", sizeof(realdir));
|
||||
}
|
||||
/* printf("[real=%s]", realdir); */
|
||||
if (stat(realdir, &dir) == 0)
|
||||
pdir = &dir;
|
||||
}
|
||||
@ -594,22 +618,19 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||
if (pdir != NULL) {
|
||||
if (pdir->st_uid == geteuid())
|
||||
pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
|
||||
else if (matchgroup(pdir->st_gid, gids, ngid))
|
||||
else if (matchgroup(pdir->st_gid))
|
||||
pdirwok = ((pdir->st_mode & S_IWGRP) != 0);
|
||||
else
|
||||
pdirwok = ((pdir->st_mode & S_IWOTH) != 0);
|
||||
}
|
||||
|
||||
/* printf("[euid=%d,r%d,w%d,x%d,pw%d]", geteuid(), rok, wok, xok, pdirwok);
|
||||
*/
|
||||
|
||||
/* 'a': can APPE to file */
|
||||
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
|
||||
fputs("a", fd);
|
||||
CPUTC('a', fd);
|
||||
|
||||
/* 'c': can create or append to files in directory */
|
||||
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
|
||||
fputs("c", fd);
|
||||
CPUTC('c', fd);
|
||||
|
||||
/* 'd': can delete file or directory */
|
||||
if (pdirwok && curclass.modify) {
|
||||
@ -634,38 +655,38 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
|
||||
}
|
||||
}
|
||||
if (candel)
|
||||
fputs("d", fd);
|
||||
CPUTC('d', fd);
|
||||
}
|
||||
|
||||
/* 'e': can enter directory */
|
||||
if (xok && S_ISDIR(fe->stat->st_mode))
|
||||
fputs("e", fd);
|
||||
CPUTC('e', fd);
|
||||
|
||||
/* 'f': can rename file or directory */
|
||||
if (pdirwok && curclass.modify)
|
||||
fputs("f", fd);
|
||||
CPUTC('f', fd);
|
||||
|
||||
/* 'l': can list directory */
|
||||
if (rok && xok && S_ISDIR(fe->stat->st_mode))
|
||||
fputs("l", fd);
|
||||
CPUTC('l', fd);
|
||||
|
||||
/* 'm': can create directory */
|
||||
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
|
||||
fputs("m", fd);
|
||||
CPUTC('m', fd);
|
||||
|
||||
/* 'p': can remove files in directory */
|
||||
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
|
||||
fputs("p", fd);
|
||||
CPUTC('p', fd);
|
||||
|
||||
/* 'r': can RETR file */
|
||||
if (rok && S_ISREG(fe->stat->st_mode))
|
||||
fputs("r", fd);
|
||||
CPUTC('r', fd);
|
||||
|
||||
/* 'w': can STOR file */
|
||||
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
|
||||
fputs("w", fd);
|
||||
CPUTC('w', fd);
|
||||
|
||||
fputc(';', fd);
|
||||
CPUTC(';', fd);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -673,82 +694,65 @@ fact_size(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
|
||||
if (S_ISREG(fe->stat->st_mode))
|
||||
fprintf(fd, "%s=%lld;", fact, (long long)fe->stat->st_size);
|
||||
cprintf(fd, "%s=%lld;", fact, (long long)fe->stat->st_size);
|
||||
}
|
||||
|
||||
static void
|
||||
fact_type(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
|
||||
fprintf(fd, "%s=", fact);
|
||||
cprintf(fd, "%s=", fact);
|
||||
switch (fe->stat->st_mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
if (fe->iscurdir || ISDOTDIR(fe->display))
|
||||
fputs("cdir", fd);
|
||||
cprintf(fd, "cdir");
|
||||
else if (ISDOTDOTDIR(fe->display))
|
||||
fputs("pdir", fd);
|
||||
cprintf(fd, "pdir");
|
||||
else
|
||||
fputs("dir", fd);
|
||||
cprintf(fd, "dir");
|
||||
break;
|
||||
case S_IFREG:
|
||||
fputs("file", fd);
|
||||
cprintf(fd, "file");
|
||||
break;
|
||||
case S_IFIFO:
|
||||
fputs("OS.unix=fifo", fd);
|
||||
cprintf(fd, "OS.unix=fifo");
|
||||
break;
|
||||
case S_IFLNK:
|
||||
fputs("OS.unix=slink", fd);
|
||||
case S_IFLNK: /* XXX: probably a NO-OP with stat() */
|
||||
cprintf(fd, "OS.unix=slink");
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
fputs("OS.unix=socket", fd);
|
||||
cprintf(fd, "OS.unix=socket");
|
||||
break;
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
fprintf(fd, "OS.unix=%s-%d/%d",
|
||||
cprintf(fd, "OS.unix=%s-%d/%d",
|
||||
S_ISBLK(fe->stat->st_mode) ? "blk" : "chr",
|
||||
major(fe->stat->st_rdev), minor(fe->stat->st_rdev));
|
||||
break;
|
||||
default:
|
||||
fprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
|
||||
cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
|
||||
break;
|
||||
}
|
||||
fputc(';', fd);
|
||||
CPUTC(';', fd);
|
||||
}
|
||||
|
||||
static void
|
||||
fact_unique(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
char obuf[(MAX(sizeof(dev_t),sizeof(ino_t)) + 2) * 4 / 3 + 2];
|
||||
|
||||
fprintf(fd, "%s=%04x%08x;", fact, fe->stat->st_dev, fe->stat->st_ino);
|
||||
}
|
||||
|
||||
static void
|
||||
fact_unix_mode(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
|
||||
fprintf(fd, "%s=%03o;", fact, fe->stat->st_mode & ACCESSPERMS);
|
||||
}
|
||||
|
||||
static void
|
||||
fact_unix_owner(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
|
||||
fprintf(fd, "%s=%d;", fact, (int)fe->stat->st_uid);
|
||||
}
|
||||
|
||||
static void
|
||||
fact_unix_group(const char *fact, FILE *fd, factelem *fe)
|
||||
{
|
||||
|
||||
fprintf(fd, "%s=%d;", fact, (int)fe->stat->st_gid);
|
||||
base64_encode((char *)&(fe->stat->st_dev), sizeof(dev_t), obuf, 1);
|
||||
cprintf(fd, "%s=%s", fact, obuf);
|
||||
base64_encode((char *)&(fe->stat->st_ino), sizeof(ino_t), obuf, 1);
|
||||
cprintf(fd, "%s;", obuf);
|
||||
}
|
||||
|
||||
static int
|
||||
matchgroup(gid_t gid, gid_t *gidlist, int ngids)
|
||||
matchgroup(gid_t gid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ngids; i++)
|
||||
for (i = 0; i < gidcount; i++)
|
||||
if (gid == gidlist[i])
|
||||
return(1);
|
||||
return (0);
|
||||
@ -759,12 +763,11 @@ mlsname(FILE *fp, factelem *fe)
|
||||
{
|
||||
int i;
|
||||
|
||||
fputs(" ", fp);
|
||||
for (i = 0; facttab[i].name; i++) {
|
||||
if (facttab[i].enabled)
|
||||
(facttab[i].display)(facttab[i].name, fp, fe);
|
||||
}
|
||||
fprintf(fp, " %s\r\n", fe->display);
|
||||
cprintf(fp, " %s\r\n", fe->display);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: conf.c,v 1.30 2000/05/20 02:20:18 lukem Exp $ */
|
||||
/* $NetBSD: conf.c,v 1.31 2000/06/19 15:15:03 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
|
||||
@ -38,7 +38,7 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: conf.c,v 1.30 2000/05/20 02:20:18 lukem Exp $");
|
||||
__RCSID("$NetBSD: conf.c,v 1.31 2000/06/19 15:15:03 lukem Exp $");
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -510,15 +510,15 @@ show_chdir_messages(int code)
|
||||
continue;
|
||||
then = st.st_mtime;
|
||||
if (code != 0) {
|
||||
lreply(code, "");
|
||||
reply(-code, "");
|
||||
code = 0;
|
||||
}
|
||||
lreply(code, "Please read the file %s", *rlist);
|
||||
reply(-code, "Please read the file %s", *rlist);
|
||||
t = localtime(&now);
|
||||
age = 365 * t->tm_year + t->tm_yday;
|
||||
t = localtime(&then);
|
||||
age -= 365 * t->tm_year + t->tm_yday;
|
||||
lreply(code, " it was last modified on %.24s - %d day%s ago",
|
||||
reply(-code, " it was last modified on %.24s - %d day%s ago",
|
||||
ctime(&then), age, PLURAL(age));
|
||||
}
|
||||
globfree(&gl);
|
||||
@ -530,27 +530,23 @@ format_file(const char *file, int code)
|
||||
FILE *f;
|
||||
char *buf, *p, *cwd;
|
||||
size_t len;
|
||||
off_t b;
|
||||
time_t now;
|
||||
|
||||
if (quietmessages)
|
||||
return (0);
|
||||
|
||||
#define PUTC(x) putchar(x), b++
|
||||
|
||||
if (EMPTYSTR(file))
|
||||
return(0);
|
||||
if ((f = fopen(file, "r")) == NULL)
|
||||
return (0);
|
||||
lreply(code, "");
|
||||
reply(-code, "");
|
||||
|
||||
b = 0;
|
||||
for (;
|
||||
(buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) {
|
||||
if (len > 0)
|
||||
if (buf[len - 1] == '\n')
|
||||
buf[--len] = '\0';
|
||||
b += printf(" ");
|
||||
cprintf(stdout, " ");
|
||||
|
||||
for (p = buf; *p; p++) {
|
||||
if (*p == '%') {
|
||||
@ -558,7 +554,7 @@ format_file(const char *file, int code)
|
||||
switch (*p) {
|
||||
|
||||
case 'c':
|
||||
b += printf("%s",
|
||||
cprintf(stdout, "%s",
|
||||
curclass.classname ?
|
||||
curclass.classname : "<unknown>");
|
||||
break;
|
||||
@ -570,7 +566,7 @@ format_file(const char *file, int code)
|
||||
strerror(errno));
|
||||
continue;
|
||||
}
|
||||
b += printf("%s", cwd);
|
||||
cprintf(stdout, "%s", cwd);
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
@ -578,51 +574,48 @@ format_file(const char *file, int code)
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
b += printf("%s", hostname);
|
||||
cprintf(stdout, "%s", hostname);
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (curclass.limit == -1)
|
||||
b += printf("unlimited");
|
||||
cprintf(stdout, "unlimited");
|
||||
else
|
||||
b += printf("%d",
|
||||
cprintf(stdout, "%d",
|
||||
curclass.limit);
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if (connections > 0)
|
||||
b += printf("%d", connections);
|
||||
cprintf(stdout, "%d",
|
||||
connections);
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
b += printf("%s", remotehost);
|
||||
cprintf(stdout, "%s", remotehost);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
now = time(NULL);
|
||||
b += printf("%.24s", ctime(&now));
|
||||
cprintf(stdout, "%.24s", ctime(&now));
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
b += printf("%s",
|
||||
cprintf(stdout, "%s",
|
||||
pw ? pw->pw_name : "<unknown>");
|
||||
break;
|
||||
|
||||
case '%':
|
||||
PUTC('%');
|
||||
CPUTC('%', stdout);
|
||||
break;
|
||||
|
||||
}
|
||||
} else {
|
||||
PUTC(*p);
|
||||
}
|
||||
} else
|
||||
CPUTC(*p, stdout);
|
||||
}
|
||||
PUTC('\r');
|
||||
PUTC('\n');
|
||||
cprintf(stdout, "\r\n");
|
||||
}
|
||||
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
(void)fflush(stdout);
|
||||
(void)fclose(f);
|
||||
return (1);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: extern.h,v 1.27 2000/06/14 13:44:22 lukem Exp $ */
|
||||
/* $NetBSD: extern.h,v 1.28 2000/06/19 15:15:03 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1992, 1993
|
||||
@ -101,9 +101,11 @@
|
||||
*/
|
||||
|
||||
void blkfree(char **);
|
||||
void closedataconn(FILE *);
|
||||
char *conffilename(const char *);
|
||||
char **copyblk(char **);
|
||||
void count_users(void);
|
||||
void cprintf(FILE *, const char *, ...);
|
||||
void cwd(const char *);
|
||||
FILE *dataconn(const char *, off_t, const char *);
|
||||
void delete(const char *);
|
||||
@ -120,7 +122,6 @@ void logcmd(const char *, off_t, const char *, const char *,
|
||||
const struct timeval *, const char *);
|
||||
void logwtmp(const char *, const char *, const char *);
|
||||
struct tab *lookup(struct tab *, const char *);
|
||||
void lreply(int, const char *, ...);
|
||||
void makedir(const char *);
|
||||
void mlsd(const char *);
|
||||
void mlst(const char *);
|
||||
@ -231,6 +232,8 @@ GLOBAL struct ftpclass curclass;
|
||||
GLOBAL int debug;
|
||||
GLOBAL jmp_buf errcatch;
|
||||
GLOBAL int form;
|
||||
GLOBAL int gidcount; /* number of entries in gidlist[] */
|
||||
GLOBAL gid_t gidlist[NGROUPS_MAX];
|
||||
GLOBAL int hasyyerrored;
|
||||
GLOBAL char hostname[MAXHOSTNAMELEN+1];
|
||||
#ifdef KERBEROS5
|
||||
@ -268,6 +271,10 @@ extern struct tab cmdtab[];
|
||||
#define CMD_IMPLEMENTED(x) ((x)->flags != 0)
|
||||
#define CMD_HAS_OPTIONS(x) ((x)->flags == 2)
|
||||
|
||||
#define CPUTC(c, f) do { \
|
||||
putc(c, f); total_bytes++; total_bytes_out++; \
|
||||
} while (0);
|
||||
|
||||
#define CURCLASSTYPE curclass.type == CLASS_GUEST ? "GUEST" : \
|
||||
curclass.type == CLASS_CHROOT ? "CHROOT" : \
|
||||
curclass.type == CLASS_REAL ? "REAL" : \
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ftpcmd.y,v 1.47 2000/06/14 13:44:23 lukem Exp $ */
|
||||
/* $NetBSD: ftpcmd.y,v 1.48 2000/06/19 15:15:03 lukem Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
|
||||
@ -83,7 +83,7 @@
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
|
||||
#else
|
||||
__RCSID("$NetBSD: ftpcmd.y,v 1.47 2000/06/14 13:44:23 lukem Exp $");
|
||||
__RCSID("$NetBSD: ftpcmd.y,v 1.48 2000/06/19 15:15:03 lukem Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
@ -225,12 +225,12 @@ cmd
|
||||
| QUIT CRLF
|
||||
{
|
||||
if (logged_in) {
|
||||
lreply(221, "");
|
||||
lreply(0,
|
||||
reply(-221, "");
|
||||
reply(0,
|
||||
"Data traffic for this session was %qd byte%s in %qd file%s.",
|
||||
(qdfmt_t)total_data, PLURAL(total_data),
|
||||
(qdfmt_t)total_files, PLURAL(total_files));
|
||||
lreply(0,
|
||||
reply(0,
|
||||
"Total traffic for this session was %qd byte%s in %qd transfer%s.",
|
||||
(qdfmt_t)total_bytes, PLURAL(total_bytes),
|
||||
(qdfmt_t)total_xfers, PLURAL(total_xfers));
|
||||
@ -1015,7 +1015,7 @@ cmd
|
||||
free($4);
|
||||
}
|
||||
|
||||
| MLST CRLF
|
||||
| MLST check_login CRLF
|
||||
{
|
||||
mlst(NULL);
|
||||
}
|
||||
@ -1028,7 +1028,7 @@ cmd
|
||||
free($4);
|
||||
}
|
||||
|
||||
| MLSD CRLF
|
||||
| MLSD check_login CRLF
|
||||
{
|
||||
mlsd(NULL);
|
||||
}
|
||||
@ -1502,7 +1502,6 @@ lookup(struct tab *p, const char *cmd)
|
||||
char *
|
||||
getline(char *s, int n, FILE *iop)
|
||||
{
|
||||
off_t b;
|
||||
int c;
|
||||
char *cs;
|
||||
|
||||
@ -1535,9 +1534,7 @@ getline(char *s, int n, FILE *iop)
|
||||
c = getc(iop);
|
||||
total_bytes++;
|
||||
total_bytes_in++;
|
||||
b = printf("%c%c%c", IAC, DONT, 0377&c);
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
|
||||
(void) fflush(stdout);
|
||||
continue;
|
||||
case DO:
|
||||
@ -1545,9 +1542,7 @@ getline(char *s, int n, FILE *iop)
|
||||
c = getc(iop);
|
||||
total_bytes++;
|
||||
total_bytes_in++;
|
||||
b = printf("%c%c%c", IAC, WONT, 0377&c);
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
|
||||
(void) fflush(stdout);
|
||||
continue;
|
||||
case IAC:
|
||||
@ -1872,41 +1867,35 @@ help(struct tab *ctab, const char *s)
|
||||
int i, j, w;
|
||||
int columns, lines;
|
||||
|
||||
lreply(214, "");
|
||||
lreply(0, "The following %scommands are recognized.", type);
|
||||
lreply(0, "(`-' = not implemented, `+' = supports options)");
|
||||
reply(-214, "");
|
||||
reply(0, "The following %scommands are recognized.", type);
|
||||
reply(0, "(`-' = not implemented, `+' = supports options)");
|
||||
columns = 76 / width;
|
||||
if (columns == 0)
|
||||
columns = 1;
|
||||
lines = (NCMDS + columns - 1) / columns;
|
||||
for (i = 0; i < lines; i++) {
|
||||
lreply(-2, " ");
|
||||
cprintf(stdout, " ");
|
||||
for (j = 0; j < columns; j++) {
|
||||
c = ctab + j * lines + i;
|
||||
lreply(-2, "%s", c->name);
|
||||
cprintf(stdout, "%s", c->name);
|
||||
w = strlen(c->name);
|
||||
if (! CMD_IMPLEMENTED(c)) {
|
||||
putchar('-');
|
||||
total_bytes++;
|
||||
total_bytes_out++;
|
||||
CPUTC('-', stdout);
|
||||
w++;
|
||||
}
|
||||
if (CMD_HAS_OPTIONS(c)) {
|
||||
putchar('+');
|
||||
total_bytes++;
|
||||
total_bytes_out++;
|
||||
CPUTC('+', stdout);
|
||||
w++;
|
||||
}
|
||||
if (c + lines >= &ctab[NCMDS])
|
||||
break;
|
||||
while (w < width) {
|
||||
putchar(' ');
|
||||
total_bytes++;
|
||||
total_bytes_out++;
|
||||
CPUTC(' ', stdout);
|
||||
w++;
|
||||
}
|
||||
}
|
||||
lreply(-2, "\r\n");
|
||||
cprintf(stdout, "\r\n");
|
||||
}
|
||||
(void) fflush(stdout);
|
||||
reply(214, "Direct comments to ftp-bugs@%s.", hostname);
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: ftpd.8,v 1.52 2000/06/14 13:44:24 lukem Exp $
|
||||
.\" $NetBSD: ftpd.8,v 1.53 2000/06/19 15:15:04 lukem Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
@ -67,7 +67,7 @@
|
||||
.\"
|
||||
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
|
||||
.\"
|
||||
.Dd June 14, 2000
|
||||
.Dd June 20, 2000
|
||||
.Dt FTPD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -197,9 +197,11 @@ The case of the requests is ignored.
|
||||
.It EPRT Ta "specify data connection port"
|
||||
.It FEAT Ta "list extra features that are not defined in" Cm "RFC 959"
|
||||
.It HELP Ta "give help information"
|
||||
.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
|
||||
.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lA"
|
||||
.It LPSV Ta "prepare for server-to-server transfer"
|
||||
.It LPRT Ta "specify data connection port"
|
||||
.It MLSD Ta "list contents of directory in a machine-processable form"
|
||||
.It MLST Ta "show a pathname in a machine-processable form"
|
||||
.It MKD Ta "make a directory"
|
||||
.It MDTM Ta "show last modification time of file"
|
||||
.It MODE Ta "specify data transfer" Em mode
|
||||
@ -553,11 +555,15 @@ The
|
||||
command appeared in
|
||||
.Bx 4.2 .
|
||||
.Pp
|
||||
The
|
||||
Various features such as the
|
||||
.Xr ftpd.conf 5
|
||||
functionality was implemented in
|
||||
functionality,
|
||||
.Cm RFC 2389 ,
|
||||
and
|
||||
.Cm draft-ietf-ftpext-mlst-10
|
||||
support was implemented in
|
||||
.Nx 1.3
|
||||
and later releases by Luke Mewburn, based on work by Simon Burge.
|
||||
and later releases by Luke Mewburn <lukem@netbsd.org>.
|
||||
.Sh BUGS
|
||||
The server must run as the super-user to create sockets with
|
||||
privileged port numbers.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ftpd.c,v 1.94 2000/06/14 13:55:15 itojun Exp $ */
|
||||
/* $NetBSD: ftpd.c,v 1.95 2000/06/19 15:15:04 lukem Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
|
||||
@ -109,7 +109,7 @@ __COPYRIGHT(
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
|
||||
#else
|
||||
__RCSID("$NetBSD: ftpd.c,v 1.94 2000/06/14 13:55:15 itojun Exp $");
|
||||
__RCSID("$NetBSD: ftpd.c,v 1.95 2000/06/19 15:15:04 lukem Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
|
||||
@ -243,6 +243,7 @@ main(int argc, char *argv[])
|
||||
usedefault = 1;
|
||||
(void)strcpy(confdir, _DEFAULT_CONFDIR);
|
||||
hostname[0] = '\0';
|
||||
gidcount = 0;
|
||||
|
||||
while ((ch = getopt(argc, argv, "a:c:C:dh:lst:T:u:Uv")) != -1) {
|
||||
switch (ch) {
|
||||
@ -343,7 +344,7 @@ main(int argc, char *argv[])
|
||||
while (fgets(line, sizeof(line), fd) != NULL) {
|
||||
if ((cp = strchr(line, '\n')) != NULL)
|
||||
*cp = '\0';
|
||||
lreply(530, "%s", line);
|
||||
reply(-530, "%s", line);
|
||||
}
|
||||
(void) fflush(stdout);
|
||||
(void) fclose(fd);
|
||||
@ -734,6 +735,7 @@ end_login(void)
|
||||
pw = NULL;
|
||||
logged_in = 0;
|
||||
quietmessages = 0;
|
||||
gidcount = 0;
|
||||
curclass.type = CLASS_REAL;
|
||||
}
|
||||
|
||||
@ -847,6 +849,7 @@ pass(const char *passwd)
|
||||
goto bad;
|
||||
}
|
||||
(void) initgroups(pw->pw_name, pw->pw_gid);
|
||||
gidcount = getgroups(sizeof(gidlist), gidlist);
|
||||
|
||||
/* open wtmp before chroot */
|
||||
logwtmp(ttyline, pw->pw_name, remotehost);
|
||||
@ -932,7 +935,7 @@ pass(const char *passwd)
|
||||
pw->pw_name, pw->pw_dir);
|
||||
goto bad;
|
||||
} else
|
||||
lreply(230,
|
||||
reply(-230,
|
||||
"No directory! Logging in with home=/");
|
||||
} else
|
||||
home = pw->pw_dir;
|
||||
@ -993,7 +996,7 @@ pass(const char *passwd)
|
||||
void
|
||||
retrieve(char *argv[], const char *name)
|
||||
{
|
||||
FILE *fin = NULL, *dout;
|
||||
FILE *fin, *dout;
|
||||
struct stat st;
|
||||
int (*closefunc)(FILE *) = NULL;
|
||||
int log, sendrv, closerv, stderrfd, isconversion, isdata, isls;
|
||||
@ -1004,6 +1007,7 @@ retrieve(char *argv[], const char *name)
|
||||
isconversion = isdata = isls = log = 0;
|
||||
tdp = NULL;
|
||||
dispname = name;
|
||||
fin = dout = NULL;
|
||||
if (argv == NULL) {
|
||||
log = 1;
|
||||
isdata = 1;
|
||||
@ -1075,10 +1079,10 @@ retrieve(char *argv[], const char *name)
|
||||
(void)gettimeofday(&start, NULL);
|
||||
sendrv = send_data(fin, dout, st.st_blksize, isdata);
|
||||
(void)gettimeofday(&finish, NULL);
|
||||
(void) fclose(dout);
|
||||
(void) fclose(dout); /* close now to affect timing stats */
|
||||
dout = NULL;
|
||||
timersub(&finish, &start, &td);
|
||||
tdp = &td;
|
||||
data = -1;
|
||||
done:
|
||||
if (log)
|
||||
logcmd("get", byte_count, name, NULL, tdp, NULL);
|
||||
@ -1088,7 +1092,7 @@ retrieve(char *argv[], const char *name)
|
||||
struct stat sb;
|
||||
|
||||
if (!isls && argv != NULL && closerv != 0) {
|
||||
lreply(226,
|
||||
reply(-226,
|
||||
"Command returned an exit status of %d",
|
||||
closerv);
|
||||
if (isconversion)
|
||||
@ -1101,12 +1105,12 @@ retrieve(char *argv[], const char *name)
|
||||
((err = fdopen(stderrfd, "r")) != NULL)) {
|
||||
char *cp, line[LINE_MAX];
|
||||
|
||||
lreply(226, "Command error messages:");
|
||||
reply(-226, "Command error messages:");
|
||||
rewind(err);
|
||||
while (fgets(line, sizeof(line), err) != NULL) {
|
||||
if ((cp = strchr(line, '\n')) != NULL)
|
||||
*cp = '\0';
|
||||
lreply(0, " %s", line);
|
||||
reply(0, " %s", line);
|
||||
}
|
||||
(void) fflush(stdout);
|
||||
(void) fclose(err);
|
||||
@ -1115,9 +1119,7 @@ retrieve(char *argv[], const char *name)
|
||||
reply(226, "Transfer complete.");
|
||||
}
|
||||
cleanupretrieve:
|
||||
if (pdata >= 0)
|
||||
(void)close(pdata);
|
||||
pdata = -1;
|
||||
closedataconn(dout);
|
||||
if (stderrfd != -1)
|
||||
(void)close(stderrfd);
|
||||
if (isconversion)
|
||||
@ -1133,6 +1135,7 @@ store(const char *name, const char *mode, int unique)
|
||||
struct timeval start, finish, td, *tdp;
|
||||
char *desc;
|
||||
|
||||
din = NULL;
|
||||
desc = (*mode == 'w') ? "put" : "append";
|
||||
if (unique && stat(name, &st) == 0 &&
|
||||
(name = gunique(name)) == NULL) {
|
||||
@ -1190,17 +1193,15 @@ store(const char *name, const char *mode, int unique)
|
||||
reply(226, "Transfer complete.");
|
||||
}
|
||||
(void)gettimeofday(&finish, NULL);
|
||||
(void) fclose(din);
|
||||
(void) fclose(din); /* close now to affect timing stats */
|
||||
din = NULL;
|
||||
timersub(&finish, &start, &td);
|
||||
tdp = &td;
|
||||
data = -1;
|
||||
done:
|
||||
logcmd(desc, byte_count, name, NULL, tdp, NULL);
|
||||
(*closefunc)(fout);
|
||||
cleanupstore:
|
||||
if (pdata >= 0)
|
||||
(void)close(pdata);
|
||||
pdata = -1;
|
||||
closedataconn(din);
|
||||
}
|
||||
|
||||
static FILE *
|
||||
@ -1338,6 +1339,18 @@ dataconn(const char *name, off_t size, const char *mode)
|
||||
return (file);
|
||||
}
|
||||
|
||||
void
|
||||
closedataconn(FILE *fd)
|
||||
{
|
||||
|
||||
if (fd != NULL)
|
||||
(void)fclose(fd);
|
||||
data = -1;
|
||||
if (pdata >= 0)
|
||||
(void)close(pdata);
|
||||
pdata = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tranfer the contents of "instr" to "outstr" peer using the appropriate
|
||||
* encapsulation of the data subject * to Mode, Structure, and Type.
|
||||
@ -1605,10 +1618,10 @@ receive_data(FILE *instr, FILE *outstr)
|
||||
if (ferror(outstr))
|
||||
goto file_err;
|
||||
if (bare_lfs) {
|
||||
lreply(226,
|
||||
reply(-226,
|
||||
"WARNING! %d bare linefeeds received in ASCII mode",
|
||||
bare_lfs);
|
||||
lreply(0, "File may not have transferred correctly.");
|
||||
reply(0, "File may not have transferred correctly.");
|
||||
}
|
||||
rval = 0;
|
||||
goto cleanup_recv_data;
|
||||
@ -1649,50 +1662,50 @@ statcmd(void)
|
||||
|
||||
a = p = (u_char *)NULL;
|
||||
|
||||
lreply(211, "%s FTP server status:", hostname);
|
||||
lreply(0, "Version: %s", FTPD_VERSION);
|
||||
reply(-211, "%s FTP server status:", hostname);
|
||||
reply(0, "Version: %s", FTPD_VERSION);
|
||||
ntop_buf[0] = '\0';
|
||||
if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
|
||||
ntop_buf, sizeof(ntop_buf), NULL, 0, NI_NUMERICHOST)
|
||||
&& strcmp(remotehost, ntop_buf) != 0) {
|
||||
lreply(0, "Connected to %s (%s)", remotehost, ntop_buf);
|
||||
reply(0, "Connected to %s (%s)", remotehost, ntop_buf);
|
||||
} else
|
||||
lreply(0, "Connected to %s", remotehost);
|
||||
reply(0, "Connected to %s", remotehost);
|
||||
if (logged_in) {
|
||||
if (curclass.type == CLASS_GUEST)
|
||||
lreply(0, "Logged in anonymously");
|
||||
reply(0, "Logged in anonymously");
|
||||
else
|
||||
lreply(0, "Logged in as %s%s", pw->pw_name,
|
||||
reply(0, "Logged in as %s%s", pw->pw_name,
|
||||
curclass.type == CLASS_CHROOT ? " (chroot)" : "");
|
||||
} else if (askpasswd)
|
||||
lreply(0, "Waiting for password");
|
||||
reply(0, "Waiting for password");
|
||||
else
|
||||
lreply(0, "Waiting for user name");
|
||||
lreply(-2, " TYPE: %s", typenames[type]);
|
||||
reply(0, "Waiting for user name");
|
||||
cprintf(stdout, " TYPE: %s", typenames[type]);
|
||||
if (type == TYPE_A || type == TYPE_E)
|
||||
lreply(-2, ", FORM: %s", formnames[form]);
|
||||
cprintf(stdout, ", FORM: %s", formnames[form]);
|
||||
if (type == TYPE_L) {
|
||||
#if NBBY == 8
|
||||
lreply(-2, " %d", NBBY);
|
||||
cprintf(stdout, " %d", NBBY);
|
||||
#else
|
||||
/* XXX: `bytesize' needs to be defined in this case */
|
||||
lreply(-2, " %d", bytesize);
|
||||
cprintf(stdout, " %d", bytesize);
|
||||
#endif
|
||||
}
|
||||
lreply(-1, "; STRUcture: %s; transfer MODE: %s",
|
||||
cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n",
|
||||
strunames[stru], modenames[mode]);
|
||||
ispassive = 0;
|
||||
if (data != -1) {
|
||||
lreply(0, "Data connection open");
|
||||
reply(0, "Data connection open");
|
||||
su = NULL;
|
||||
} else if (pdata != -1) {
|
||||
lreply(0, "in Passive mode");
|
||||
reply(0, "in Passive mode");
|
||||
su = (union sockunion *)&pasv_addr;
|
||||
ispassive = 1;
|
||||
goto printaddr;
|
||||
} else if (usedefault == 0) {
|
||||
if (epsvall) {
|
||||
lreply(0, "EPSV only mode (EPSV ALL)");
|
||||
reply(0, "EPSV only mode (EPSV ALL)");
|
||||
goto epsvonly;
|
||||
}
|
||||
su = (union sockunion *)&data_dest;
|
||||
@ -1702,7 +1715,7 @@ statcmd(void)
|
||||
a = (u_char *) &su->su_sin.sin_addr;
|
||||
p = (u_char *) &su->su_sin.sin_port;
|
||||
#define UC(b) (((int) b) & 0xff)
|
||||
lreply(0, "%s (%d,%d,%d,%d,%d,%d)",
|
||||
reply(0, "%s (%d,%d,%d,%d,%d,%d)",
|
||||
ispassive ? "PASV" : "PORT" ,
|
||||
UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
|
||||
UC(p[0]), UC(p[1]));
|
||||
@ -1731,11 +1744,11 @@ statcmd(void)
|
||||
break;
|
||||
}
|
||||
if (af) {
|
||||
lreply(-2, " %s (%d,%d",
|
||||
cprintf(stdout, " %s (%d,%d",
|
||||
ispassive ? "LPSV" : "LPRT", af, alen);
|
||||
for (i = 0; i < alen; i++)
|
||||
lreply(-2, ",%d", UC(a[i]));
|
||||
lreply(-1, ",%d,%d,%d)", 2, UC(p[0]), UC(p[1]));
|
||||
cprintf(stdout, ",%d", UC(a[i]));
|
||||
cprintf(stdout, ",%d,%d,%d)", 2, UC(p[0]), UC(p[1]));
|
||||
#undef UC
|
||||
}
|
||||
}
|
||||
@ -1757,88 +1770,86 @@ statcmd(void)
|
||||
if (getnameinfo((struct sockaddr *)su, su->su_len,
|
||||
ntop_buf, sizeof(ntop_buf), NULL, 0,
|
||||
NI_NUMERICHOST) == 0) {
|
||||
lreply(0, "%s (|%d|%s|%d|)",
|
||||
reply(0, "%s (|%d|%s|%d|)",
|
||||
ispassive ? "EPSV" : "EPRT",
|
||||
af, ntop_buf, ntohs(su->su_port));
|
||||
}
|
||||
}
|
||||
} else
|
||||
lreply(0, "No data connection");
|
||||
reply(0, "No data connection");
|
||||
|
||||
if (logged_in) {
|
||||
lreply(0, "Data sent: %qd byte%s in %qd file%s",
|
||||
reply(0, "Data sent: %qd byte%s in %qd file%s",
|
||||
(qdfmt_t)total_data_out, PLURAL(total_data_out),
|
||||
(qdfmt_t)total_files_out, PLURAL(total_files_out));
|
||||
lreply(0, "Data received: %qd byte%s in %qd file%s",
|
||||
reply(0, "Data received: %qd byte%s in %qd file%s",
|
||||
(qdfmt_t)total_data_in, PLURAL(total_data_in),
|
||||
(qdfmt_t)total_files_in, PLURAL(total_files_in));
|
||||
lreply(0, "Total data: %qd byte%s in %qd file%s",
|
||||
reply(0, "Total data: %qd byte%s in %qd file%s",
|
||||
(qdfmt_t)total_data, PLURAL(total_data),
|
||||
(qdfmt_t)total_files, PLURAL(total_files));
|
||||
}
|
||||
otbi = total_bytes_in;
|
||||
otbo = total_bytes_out;
|
||||
otb = total_bytes;
|
||||
lreply(0, "Traffic sent: %qd byte%s in %qd transfer%s",
|
||||
reply(0, "Traffic sent: %qd byte%s in %qd transfer%s",
|
||||
(qdfmt_t)otbo, PLURAL(otbo),
|
||||
(qdfmt_t)total_xfers_out, PLURAL(total_xfers_out));
|
||||
lreply(0, "Traffic received: %qd byte%s in %qd transfer%s",
|
||||
reply(0, "Traffic received: %qd byte%s in %qd transfer%s",
|
||||
(qdfmt_t)otbi, PLURAL(otbi),
|
||||
(qdfmt_t)total_xfers_in, PLURAL(total_xfers_in));
|
||||
lreply(0, "Total traffic: %qd byte%s in %qd transfer%s",
|
||||
reply(0, "Total traffic: %qd byte%s in %qd transfer%s",
|
||||
(qdfmt_t)otb, PLURAL(otb),
|
||||
(qdfmt_t)total_xfers, PLURAL(total_xfers));
|
||||
|
||||
if (logged_in) {
|
||||
struct ftpconv *cp;
|
||||
|
||||
lreply(0, "");
|
||||
lreply(0, "Class: %s, type: %s",
|
||||
reply(0, "");
|
||||
reply(0, "Class: %s, type: %s",
|
||||
curclass.classname, CURCLASSTYPE);
|
||||
lreply(0, "Check PORT/LPRT commands: %sabled",
|
||||
reply(0, "Check PORT/LPRT commands: %sabled",
|
||||
curclass.checkportcmd ? "en" : "dis");
|
||||
if (curclass.display != NULL)
|
||||
lreply(0, "Display file: %s", curclass.display);
|
||||
reply(0, "Display file: %s", curclass.display);
|
||||
if (curclass.notify != NULL)
|
||||
lreply(0, "Notify fileglob: %s", curclass.notify);
|
||||
lreply(0, "Idle timeout: %d, maximum timeout: %d",
|
||||
reply(0, "Notify fileglob: %s", curclass.notify);
|
||||
reply(0, "Idle timeout: %d, maximum timeout: %d",
|
||||
curclass.timeout, curclass.maxtimeout);
|
||||
lreply(0, "Current connections: %d", connections);
|
||||
reply(0, "Current connections: %d", connections);
|
||||
if (curclass.limit == -1)
|
||||
lreply(0, "Maximum connections: unlimited");
|
||||
reply(0, "Maximum connections: unlimited");
|
||||
else
|
||||
lreply(0, "Maximum connections: %d", curclass.limit);
|
||||
reply(0, "Maximum connections: %d", curclass.limit);
|
||||
if (curclass.limitfile)
|
||||
lreply(0, "Connection limit exceeded file: %s",
|
||||
reply(0, "Connection limit exceeded file: %s",
|
||||
curclass.limitfile);
|
||||
if (curclass.motd != NULL)
|
||||
lreply(0, "MotD file: %s", curclass.motd);
|
||||
lreply(0,
|
||||
reply(0, "MotD file: %s", curclass.motd);
|
||||
reply(0,
|
||||
"Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
|
||||
curclass.modify ? "en" : "dis");
|
||||
lreply(0,
|
||||
"Upload commands (APPE, STOR, STOU): %sabled",
|
||||
reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
|
||||
curclass.upload ? "en" : "dis");
|
||||
if (curclass.portmin && curclass.portmax)
|
||||
lreply(0, "PASV port range: %d - %d",
|
||||
reply(0, "PASV port range: %d - %d",
|
||||
curclass.portmin, curclass.portmax);
|
||||
if (curclass.rateget)
|
||||
lreply(0, "Rate get limit: %d bytes/sec",
|
||||
reply(0, "Rate get limit: %d bytes/sec",
|
||||
curclass.rateget);
|
||||
else
|
||||
lreply(0, "Rate get limit: disabled");
|
||||
reply(0, "Rate get limit: disabled");
|
||||
if (curclass.rateput)
|
||||
lreply(0, "Rate put limit: %d bytes/sec",
|
||||
reply(0, "Rate put limit: %d bytes/sec",
|
||||
curclass.rateput);
|
||||
else
|
||||
lreply(0, "Rate put limit: disabled");
|
||||
lreply(0, "Umask: %.04o", curclass.umask);
|
||||
reply(0, "Rate put limit: disabled");
|
||||
reply(0, "Umask: %.04o", curclass.umask);
|
||||
for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
|
||||
if (cp->suffix == NULL || cp->types == NULL ||
|
||||
cp->command == NULL)
|
||||
continue;
|
||||
lreply(0,
|
||||
"Conversion: %s [%s] disable: %s, command: %s",
|
||||
reply(0, "Conversion: %s [%s] disable: %s, command: %s",
|
||||
cp->suffix, cp->types, cp->disable, cp->command);
|
||||
}
|
||||
}
|
||||
@ -1858,8 +1869,11 @@ fatal(const char *s)
|
||||
|
||||
/*
|
||||
* reply() --
|
||||
* display the final line of a reply, with a trailing CRLF
|
||||
* i.e, n + " " + fmt + CRLF
|
||||
* depending on the value of n, display fmt with a trailing CRLF and
|
||||
* prefix of:
|
||||
* n < -1 prefix the message with abs(n) + "-" (initial line)
|
||||
* n == 0 prefix the message with 4 spaces (middle lines)
|
||||
* n > 0 prefix the message with n + " " (final line)
|
||||
*/
|
||||
void
|
||||
reply(int n, const char *fmt, ...)
|
||||
@ -1869,53 +1883,19 @@ reply(int n, const char *fmt, ...)
|
||||
|
||||
va_start(ap, fmt);
|
||||
b = 0;
|
||||
b += printf("%d ", n);
|
||||
b += vprintf(fmt, ap);
|
||||
b += printf("\r\n");
|
||||
if (n == 0)
|
||||
cprintf(stdout, " ");
|
||||
else if (n < 0)
|
||||
cprintf(stdout, "%d-", -n);
|
||||
else
|
||||
cprintf(stdout, "%d ", n);
|
||||
b = vprintf(fmt, ap);
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
cprintf(stdout, "\r\n");
|
||||
(void)fflush(stdout);
|
||||
if (debug) {
|
||||
syslog(LOG_DEBUG, "<--- %d ", n);
|
||||
vsyslog(LOG_DEBUG, fmt, ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* lreply() --
|
||||
* depending on the value of n, display a line with a trailing CRLF:
|
||||
* n == -2 freeform text, but no trailing CRLF
|
||||
* n == -1 freeform text
|
||||
* n == 0 prefix the message with 4 spaces
|
||||
* n > 0 prefix the message with n + "-"
|
||||
*/
|
||||
void
|
||||
lreply(int n, const char *fmt, ...)
|
||||
{
|
||||
off_t b;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
b = 0;
|
||||
switch (n) {
|
||||
case 0:
|
||||
b += printf(" ");
|
||||
case -1:
|
||||
case -2:
|
||||
break;
|
||||
default:
|
||||
b += printf("%d-", n);
|
||||
break;
|
||||
}
|
||||
b += vprintf(fmt, ap);
|
||||
if (n != -2)
|
||||
b += printf("\r\n");
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
if (n != -2)
|
||||
(void)fflush(stdout);
|
||||
if (debug && n >= 0) {
|
||||
syslog(LOG_DEBUG, "<--- %d- ", n);
|
||||
syslog(LOG_DEBUG, "<--- %d%c", abs(n), (n < 0) ? '-' : ' ');
|
||||
vsyslog(LOG_DEBUG, fmt, ap);
|
||||
}
|
||||
}
|
||||
@ -2290,15 +2270,7 @@ send_file_list(const char *whichf)
|
||||
goto out;
|
||||
}
|
||||
perror_reply(550, whichf);
|
||||
if (dout != NULL) {
|
||||
(void) fclose(dout);
|
||||
transflag = 0;
|
||||
data = -1;
|
||||
if (pdata >= 0)
|
||||
(void)close(pdata);
|
||||
pdata = -1;
|
||||
}
|
||||
goto out;
|
||||
goto cleanup_send_file_list;
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
@ -2368,13 +2340,9 @@ send_file_list(const char *whichf)
|
||||
else
|
||||
reply(226, "Transfer complete.");
|
||||
|
||||
cleanup_send_file_list:
|
||||
transflag = 0;
|
||||
if (dout != NULL)
|
||||
(void) fclose(dout);
|
||||
else if (pdata >= 0)
|
||||
(void)close(pdata);
|
||||
data = -1;
|
||||
pdata = -1;
|
||||
closedataconn(dout);
|
||||
out:
|
||||
total_xfers++;
|
||||
total_xfers_out++;
|
||||
@ -2461,3 +2429,19 @@ xstrdup(const char *s)
|
||||
/* NOTREACHED */
|
||||
return (new);
|
||||
}
|
||||
|
||||
/*
|
||||
* As per fprintf(), but increment total_bytes and total_bytes_out,
|
||||
* by the appropriate amount.
|
||||
*/
|
||||
void
|
||||
cprintf(FILE *fd, const char *fmt, ...)
|
||||
{
|
||||
off_t b;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
b = vfprintf(fd, fmt, ap);
|
||||
total_bytes += b;
|
||||
total_bytes_out += b;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: version.h,v 1.11 2000/06/14 13:44:25 lukem Exp $ */
|
||||
/* $NetBSD: version.h,v 1.12 2000/06/19 15:15:04 lukem Exp $ */
|
||||
/*-
|
||||
* Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
@ -36,5 +36,5 @@
|
||||
*/
|
||||
|
||||
#ifndef FTPD_VERSION
|
||||
#define FTPD_VERSION "NetBSD-ftpd 20000614"
|
||||
#define FTPD_VERSION "NetBSD-ftpd 20000620"
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user