* Grammar for FTP commands.
static char sccsid[] = "@(#)ftpcmd.y 4.12 (Berkeley) %G%";
extern struct sockaddr_in data_dest;
extern struct passwd *pw;
SP CRLF COMMA STRING NUMBER
USER PASS ACCT REIN QUIT PORT
PASV TYPE STRU MODE RETR STOR
APPE MLFL MAIL MSND MSOM MSAM
MRSQ MRCP ALLO REST RNFR RNTO
ABOR DELE CWD LIST NLST SITE
STAT HELP NOOP XMKD XRMD XPWD
cmd: USER SP username CRLF
extern struct passwd *getpwnam();
if (strcmp($3, "ftp") == 0 ||
strcmp($3, "anonymous") == 0) {
if ((pw = getpwnam("ftp")) != NULL) {
"Guest login ok, send ident as password.");
} else if (checkuser($3)) {
reply(331, "Password required for %s.", $3);
reply(530, "User %s unknown.", $3);
if (cmd_form == FORM_N) {
reply(200, "Type set to A.");
reply(504, "Form must be N.");
reply(504, "Type E not implemented.");
reply(200, "Type set to I.");
"Type set to L (byte size 8).");
reply(504, "Byte size must be 8.");
| STRU SP struct_code CRLF
reply(200, "STRU F ok.");
reply(502, "Unimplemented STRU type.");
reply(200, "MODE S ok.");
reply(502, "Unimplemented MODE type.");
| RETR check_login SP pathname CRLF
| STOR check_login SP pathname CRLF
| APPE check_login SP pathname CRLF
| NLST check_login SP pathname CRLF
retrieve("/bin/ls %s", $4);
retrieve("/bin/ls -lg", "");
| LIST check_login SP pathname CRLF
retrieve("/bin/ls -lg %s", $4);
| DELE check_login SP pathname CRLF
| CWD check_login SP pathname CRLF
| XMKD check_login SP pathname CRLF
| XRMD check_login SP pathname CRLF
host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
a = (char *)&data_dest.sin_addr;
a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
p = (char *)&data_dest.sin_port;
data_dest.sin_family = AF_INET;
/* this is for a bug in the BBN ftp */
if ($1 && strncmp($1, "~", 1) == 0) {
rename_cmd: rename_from rename_to
reply(503, "Bad sequence of commands.");
rename_from: RNFR check_login SP pathname CRLF
char *from = 0, *renamefrom();
rename_to: RNTO SP pathname CRLF
reply(530, "Please login with USER and PASS.");
#define CMD 0 /* beginning of command */
#define ARGS 1 /* expect miscellaneous arguments */
#define STR1 2 /* expect SP followed by STRING */
#define STR2 3 /* expect STRING */
#define OSTR 4 /* optional STRING */
short implemented; /* 1 if command is implemented */
struct tab cmdtab[] = { /* In order defined in RFC 765 */
{ "USER", USER, STR1, 1, "<sp> username" },
{ "PASS", PASS, STR1, 1, "<sp> password" },
{ "ACCT", ACCT, STR1, 0, "(specify account)" },
{ "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
{ "QUIT", QUIT, ARGS, 1, "(terminate service)", },
{ "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
{ "PASV", PASV, ARGS, 0, "(set server in passive mode)" },
{ "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
{ "STRU", STRU, ARGS, 1, "(specify file structure)" },
{ "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
{ "RETR", RETR, STR1, 1, "<sp> file-name" },
{ "STOR", STOR, STR1, 1, "<sp> file-name" },
{ "APPE", APPE, STR1, 1, "<sp> file-name" },
{ "MLFL", MLFL, OSTR, 0, "(mail file)" },
{ "MAIL", MAIL, OSTR, 0, "(mail to user)" },
{ "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
{ "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
{ "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
{ "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
{ "MRCP", MRCP, STR1, 0, "(mail recipient)" },
{ "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
{ "REST", REST, STR1, 0, "(restart command)" },
{ "RNFR", RNFR, STR1, 1, "<sp> file-name" },
{ "RNTO", RNTO, STR1, 1, "<sp> file-name" },
{ "ABOR", ABOR, ARGS, 0, "(abort operation)" },
{ "DELE", DELE, STR1, 1, "<sp> file-name" },
{ "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" },
{ "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
{ "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
{ "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
{ "SITE", SITE, STR1, 0, "(get site parameters)" },
{ "STAT", STAT, OSTR, 0, "(get server status)" },
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
{ "NOOP", NOOP, ARGS, 1, "" },
{ "XMKD", XMKD, STR1, 1, "<sp> path-name" },
{ "XRMD", XRMD, STR1, 1, "<sp> path-name" },
{ "XPWD", XPWD, ARGS, 1, "(return current directory)" },
{ "XCUP", XCUP, ARGS, 1, "(change to parent directory)" },
for (p = cmdtab; p->name != NULL; p++)
if (strcmp(cmd, p->name) == 0)
* getline - a hacked up version of fgets to ignore TELNET escape codes.
while (--n > 0 && (c = getc(iop)) >= 0) {
c = getc(iop); /* skip command */
c = getc(iop); /* try next char */
fprintf(stderr, "FTPD: command: %s", s);
"Timeout (%d seconds): closing control connection.", timeout);
"FTPD: User %s timed out after %d seconds at %s",
(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
signal(SIGALRM, toolong);
if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
reply(221, "You could at least say goodbye.");
cpos = index(cbuf, ' ') - cbuf;
if (p->implemented == 0) {
if (cbuf[cpos] == '\n') {
* Make sure the string is nonempty and \n terminated.
if (n > 1 && cbuf[cpos] == '\n') {
if (isdigit(cbuf[cpos])) {
while (isdigit(cbuf[++cpos]))
fatal("Unknown state in scanner.");
p = malloc(strlen(s) + 1);
fatal("Ran out of memory.");
register int width, NCMDS;
for (c = cmdtab; c->name != NULL; c++) {
int len = strlen(c->name);
width = (width + 8) &~ 7;
"The following commands are recognized (* =>'s unimplemented).");
lines = (NCMDS + columns - 1) / columns;
for (i = 0; i < lines; i++) {
for (j = 0; j < columns; j++) {
c = cmdtab + j * lines + i;
c->implemented ? ' ' : '*');
if (c + lines >= &cmdtab[NCMDS])
reply(214, "Direct comments to ftp-bugs@%s.", hostname);
if (c == (struct tab *)0) {
reply(504, "Unknown command %s.", s);
reply(214, "Syntax: %s %s", c->name, c->help);
reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);