/* ftpcmd.y - grammar for FTP commands */
* $Header: /f/osi/ftp-ftam/RCS/ftpcmd.y,v 7.1 91/02/22 09:24:20 mrose Interim $
* Revision 7.1 91/02/22 09:24:20 mrose
* Revision 7.0 89/11/23 21:55:19 mrose
* Acquisition, use, and distribution of this module and related
* materials are subject to the restrictions of a license agreement.
* Consult the Preface in the User's Manual for the full terms of
* Shamelessly taken from UCB
* Grammar for FTP commands.
static char *rcsid = "$Header: /f/osi/ftp-ftam/RCS/ftpcmd.y,v 7.1 91/02/22 09:24:20 mrose Interim $";
extern struct sockaddr_in data_dest;
extern char *ftp_account;
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
/* remote host information may appear in user
* name as user@osihost. Save user and hostname until
* all neccessary information is gathered.
ftp_user = savestr((char*)$3);
if (op = index (ftp_user, '@')) {
if (strcmp(ftp_user, "ftp") == 0 ||
strcmp(ftp_user, "anonymous") == 0) {
"Guest login ok, send ident as password.");
} else if (checkuser(ftp_user)) {
reply(331, "Password required for \"%s\".", $3);
reply(500,"User disallowed");
/* Try and login. dologin() checks if it has
* all the neccessary information to try and login.
* Appropriate response codes are generated.
ftp_passwd = savestr((char*)$3);
| SITE SP osi_hostname CRLF
osi_host = savestr((char*)$3);
logged_in = ftp_passwd ? dologin() : 0;
ftp_account = savestr((char*)$3);
/* The ISODE supports three file types:
* Binary and Text are selected here.
* Directory file types are used for LIST and NLST
if (cmd_form == FORM_N &&
f_type(TYPE_A) != NOTOK) {
reply(200, "Type set to A.");
reply(504, "TYPE set error.");
reply(504, "Type E not implemented.");
if (f_type(TYPE_I) == OK){
reply(200, "Type set to I.");
reply(504, "TYPE set error.");
if (cmd_bytesz == 8 && f_type(TYPE_L) == OK) {
"Type set to L (byte size 8).");
reply(504, "TYPE set error.");
| 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
ftp_store((char*)$4, "w");
| APPE check_login SP pathname CRLF
ftp_store((char *)$4, "a");
| NLST check_login SP pathname CRLF
directory("NLST", (char*)$4);
| LIST check_login SP pathname CRLF
directory("LIST", (char*)$4);
| DELE check_login SP pathname CRLF
| XMKD check_login SP pathname CRLF
(void)makedir((char*)$4);
| 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 */
rename_cmd: rename_from rename_to
renamecmd((char*)$1, (char*)$2);
reply(503, "Bad sequence of commands.");
rename_from: RNFR check_login SP pathname CRLF
char *from = 0, *renamefrom();
from = renamefrom((char*)$4);
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, 1, "(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" },
/* Most directory oriented commands (except XMKD and XRMD) are
* not supported. The ISODE FTAM requires more knowledge
* about the remote filesystem type than is available through FTP.
{ "CWD", CWD, OSTR, 0, "[ <sp> directory-name]" },
{ "XCWD", CWD, OSTR, 0, "[ <sp> directory-name ]" },
{ "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
{ "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
{ "SITE", SITE, STR1, 1, "(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, 0, "(return current directory)" },
{ "XCUP", XCUP, ARGS, 0, "(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 */
advise (NULLCP, "---> %s", s);
"Timeout (%d seconds): closing control connection.", timeout);
"user %s timed out after %d seconds at %s",
ftp_user, timeout, ctime(&now));
(void)signal(SIGALRM, toolong);
(void)alarm((unsigned)timeout);
if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
cpos = index(cbuf, ' ') - cbuf;
if (p->implemented == 0) {
if (cbuf[cpos] == '\n') {
/* trim leading blanks */
for(;cbuf[cpos] == ' ';cpos++);
* 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((unsigned) (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);