110276Ssam /* 226045Sminshall * Copyright (c) 1985 Regents of the University of California. 322501Sdist * All rights reserved. The Berkeley software License Agreement 422501Sdist * specifies the terms and conditions for redistribution. 522501Sdist */ 622501Sdist 722501Sdist /* 810276Ssam * Grammar for FTP commands. 910276Ssam * See RFC 765. 1010276Ssam */ 1110276Ssam 1210276Ssam %{ 1310276Ssam 1410276Ssam #ifndef lint 15*28865Smckusick static char sccsid[] = "@(#)ftpcmd.y 5.7 (Berkeley) 05/28/86"; 1610276Ssam #endif 1710276Ssam 1810276Ssam #include <sys/types.h> 1910276Ssam #include <sys/socket.h> 2010276Ssam 2110276Ssam #include <netinet/in.h> 2210276Ssam 2313033Ssam #include <arpa/ftp.h> 2413033Ssam 2510276Ssam #include <stdio.h> 2611652Ssam #include <signal.h> 2710276Ssam #include <ctype.h> 2810276Ssam #include <pwd.h> 2910276Ssam #include <setjmp.h> 3026494Sminshall #include <syslog.h> 3110276Ssam 3210276Ssam extern struct sockaddr_in data_dest; 3310276Ssam extern int logged_in; 3410276Ssam extern struct passwd *pw; 3510276Ssam extern int guest; 3610276Ssam extern int logging; 3710276Ssam extern int type; 3810276Ssam extern int form; 3910276Ssam extern int debug; 4011652Ssam extern int timeout; 4126045Sminshall extern int pdata; 4210276Ssam extern char hostname[]; 4310276Ssam extern char *globerr; 4410320Ssam extern int usedefault; 4526045Sminshall extern int unique; 4626045Sminshall extern int transflag; 4726045Sminshall extern char tmpline[]; 4810276Ssam char **glob(); 4910276Ssam 5010276Ssam static int cmd_type; 5110276Ssam static int cmd_form; 5210276Ssam static int cmd_bytesz; 5326045Sminshall char cbuf[512]; 5410276Ssam 5510276Ssam char *index(); 5610276Ssam %} 5710276Ssam 5810276Ssam %token 5910276Ssam A B C E F I 6010276Ssam L N P R S T 6110276Ssam 6210276Ssam SP CRLF COMMA STRING NUMBER 6310276Ssam 6410276Ssam USER PASS ACCT REIN QUIT PORT 6510276Ssam PASV TYPE STRU MODE RETR STOR 6610276Ssam APPE MLFL MAIL MSND MSOM MSAM 6710276Ssam MRSQ MRCP ALLO REST RNFR RNTO 6810276Ssam ABOR DELE CWD LIST NLST SITE 6910276Ssam STAT HELP NOOP XMKD XRMD XPWD 7026045Sminshall XCUP STOU 7110276Ssam 7210276Ssam LEXERR 7310276Ssam 7410276Ssam %start cmd_list 7510276Ssam 7610276Ssam %% 7710276Ssam 7810276Ssam cmd_list: /* empty */ 7910276Ssam | cmd_list cmd 8010276Ssam ; 8110276Ssam 8210276Ssam cmd: USER SP username CRLF 8310276Ssam = { 8410276Ssam extern struct passwd *getpwnam(); 8510276Ssam 8626045Sminshall logged_in = 0; 8726494Sminshall if (strcmp((char *) $3, "ftp") == 0 || 8826494Sminshall strcmp((char *) $3, "anonymous") == 0) { 8910324Ssam if ((pw = getpwnam("ftp")) != NULL) { 9010324Ssam guest = 1; 9110324Ssam reply(331, 9210276Ssam "Guest login ok, send ident as password."); 9310324Ssam } 9426045Sminshall else { 9526045Sminshall reply(530, "User %s unknown.", $3); 9626045Sminshall } 9726494Sminshall } else if (checkuser((char *) $3)) { 9810276Ssam guest = 0; 9926494Sminshall pw = getpwnam((char *) $3); 10026045Sminshall if (pw == NULL) { 10126045Sminshall reply(530, "User %s unknown.", $3); 10226045Sminshall } 10326045Sminshall else { 10426045Sminshall reply(331, "Password required for %s.", $3); 10526045Sminshall } 106*28865Smckusick } else { 107*28865Smckusick reply(530, "User %s access denied.", $3); 10810276Ssam } 10926494Sminshall free((char *) $3); 11010276Ssam } 11110276Ssam | PASS SP password CRLF 11210276Ssam = { 11326494Sminshall pass((char *) $3); 11426494Sminshall free((char *) $3); 11510276Ssam } 11610276Ssam | PORT SP host_port CRLF 11710276Ssam = { 11810320Ssam usedefault = 0; 11926045Sminshall if (pdata > 0) { 12026045Sminshall (void) close(pdata); 12126045Sminshall } 12226045Sminshall pdata = -1; 12327107Smckusick reply(200, "PORT command successful."); 12410276Ssam } 12526045Sminshall | PASV CRLF 12626045Sminshall = { 12726045Sminshall passive(); 12826045Sminshall } 12910276Ssam | TYPE SP type_code CRLF 13010276Ssam = { 13110276Ssam switch (cmd_type) { 13210276Ssam 13310276Ssam case TYPE_A: 13410276Ssam if (cmd_form == FORM_N) { 13510276Ssam reply(200, "Type set to A."); 13610276Ssam type = cmd_type; 13710276Ssam form = cmd_form; 13810276Ssam } else 13910276Ssam reply(504, "Form must be N."); 14010276Ssam break; 14110276Ssam 14210276Ssam case TYPE_E: 14310276Ssam reply(504, "Type E not implemented."); 14410276Ssam break; 14510276Ssam 14610276Ssam case TYPE_I: 14710276Ssam reply(200, "Type set to I."); 14810276Ssam type = cmd_type; 14910276Ssam break; 15010276Ssam 15110276Ssam case TYPE_L: 15210276Ssam if (cmd_bytesz == 8) { 15310276Ssam reply(200, 15410276Ssam "Type set to L (byte size 8)."); 15510276Ssam type = cmd_type; 15610276Ssam } else 15710276Ssam reply(504, "Byte size must be 8."); 15810276Ssam } 15910276Ssam } 16010276Ssam | STRU SP struct_code CRLF 16110276Ssam = { 16210276Ssam switch ($3) { 16310276Ssam 16410276Ssam case STRU_F: 16510276Ssam reply(200, "STRU F ok."); 16610276Ssam break; 16710276Ssam 16810276Ssam default: 16927107Smckusick reply(504, "Unimplemented STRU type."); 17010276Ssam } 17110276Ssam } 17210276Ssam | MODE SP mode_code CRLF 17310276Ssam = { 17410276Ssam switch ($3) { 17510276Ssam 17610276Ssam case MODE_S: 17710276Ssam reply(200, "MODE S ok."); 17810276Ssam break; 17910276Ssam 18010276Ssam default: 18110276Ssam reply(502, "Unimplemented MODE type."); 18210276Ssam } 18310276Ssam } 18410276Ssam | ALLO SP NUMBER CRLF 18510276Ssam = { 18627107Smckusick reply(202, "ALLO command ignored."); 18710276Ssam } 18810276Ssam | RETR check_login SP pathname CRLF 18910276Ssam = { 19010302Ssam if ($2 && $4 != NULL) 19126494Sminshall retrieve((char *) 0, (char *) $4); 19210302Ssam if ($4 != NULL) 19326494Sminshall free((char *) $4); 19410276Ssam } 19510276Ssam | STOR check_login SP pathname CRLF 19610276Ssam = { 19710302Ssam if ($2 && $4 != NULL) 19826494Sminshall store((char *) $4, "w"); 19910302Ssam if ($4 != NULL) 20026494Sminshall free((char *) $4); 20110276Ssam } 20210276Ssam | APPE check_login SP pathname CRLF 20310276Ssam = { 20410302Ssam if ($2 && $4 != NULL) 20526494Sminshall store((char *) $4, "a"); 20610302Ssam if ($4 != NULL) 20726494Sminshall free((char *) $4); 20810276Ssam } 20910276Ssam | NLST check_login CRLF 21010276Ssam = { 21110276Ssam if ($2) 21211217Ssam retrieve("/bin/ls", ""); 21310276Ssam } 21410276Ssam | NLST check_login SP pathname CRLF 21510276Ssam = { 21610302Ssam if ($2 && $4 != NULL) 21726494Sminshall retrieve("/bin/ls %s", (char *) $4); 21810302Ssam if ($4 != NULL) 21926494Sminshall free((char *) $4); 22010276Ssam } 22110276Ssam | LIST check_login CRLF 22210276Ssam = { 22310276Ssam if ($2) 22410318Ssam retrieve("/bin/ls -lg", ""); 22510276Ssam } 22610276Ssam | LIST check_login SP pathname CRLF 22710276Ssam = { 22810302Ssam if ($2 && $4 != NULL) 22926494Sminshall retrieve("/bin/ls -lg %s", (char *) $4); 23010302Ssam if ($4 != NULL) 23126494Sminshall free((char *) $4); 23210276Ssam } 23310276Ssam | DELE check_login SP pathname CRLF 23410276Ssam = { 23510302Ssam if ($2 && $4 != NULL) 23626494Sminshall delete((char *) $4); 23710302Ssam if ($4 != NULL) 23826494Sminshall free((char *) $4); 23910276Ssam } 24026045Sminshall | ABOR CRLF 24126045Sminshall = { 24227107Smckusick reply(225, "ABOR command successful."); 24326045Sminshall } 24410276Ssam | CWD check_login CRLF 24510276Ssam = { 24610276Ssam if ($2) 24710276Ssam cwd(pw->pw_dir); 24810276Ssam } 24910276Ssam | CWD check_login SP pathname CRLF 25010276Ssam = { 25110302Ssam if ($2 && $4 != NULL) 25226494Sminshall cwd((char *) $4); 25310302Ssam if ($4 != NULL) 25426494Sminshall free((char *) $4); 25510276Ssam } 25610276Ssam | rename_cmd 25710276Ssam | HELP CRLF 25810276Ssam = { 25926494Sminshall help((char *) 0); 26010276Ssam } 26110276Ssam | HELP SP STRING CRLF 26210276Ssam = { 26326494Sminshall help((char *) $3); 26410276Ssam } 26510276Ssam | NOOP CRLF 26610276Ssam = { 26727107Smckusick reply(200, "NOOP command successful."); 26810276Ssam } 26910276Ssam | XMKD check_login SP pathname CRLF 27010276Ssam = { 27110302Ssam if ($2 && $4 != NULL) 27226494Sminshall makedir((char *) $4); 27310302Ssam if ($4 != NULL) 27426494Sminshall free((char *) $4); 27510276Ssam } 27610276Ssam | XRMD check_login SP pathname CRLF 27710276Ssam = { 27810302Ssam if ($2 && $4 != NULL) 27926494Sminshall removedir((char *) $4); 28010302Ssam if ($4 != NULL) 28126494Sminshall free((char *) $4); 28210276Ssam } 28310276Ssam | XPWD check_login CRLF 28410276Ssam = { 28510276Ssam if ($2) 28610302Ssam pwd(); 28710276Ssam } 28810276Ssam | XCUP check_login CRLF 28910276Ssam = { 29010276Ssam if ($2) 29110276Ssam cwd(".."); 29210276Ssam } 29326045Sminshall | STOU check_login SP pathname CRLF 29426045Sminshall = { 29526045Sminshall if ($2 && $4 != NULL) { 29626045Sminshall unique++; 29726494Sminshall store((char *) $4, "w"); 29826045Sminshall unique = 0; 29926045Sminshall } 30026045Sminshall if ($4 != NULL) 30126494Sminshall free((char *) $4); 30226045Sminshall } 30310276Ssam | QUIT CRLF 30410276Ssam = { 30510276Ssam reply(221, "Goodbye."); 30613246Ssam dologout(0); 30710276Ssam } 30810276Ssam | error CRLF 30910276Ssam = { 31010276Ssam yyerrok; 31110276Ssam } 31210276Ssam ; 31310276Ssam 31410276Ssam username: STRING 31510276Ssam ; 31610276Ssam 31710276Ssam password: STRING 31810276Ssam ; 31910276Ssam 32010276Ssam byte_size: NUMBER 32110276Ssam ; 32210276Ssam 32310276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 32410276Ssam NUMBER COMMA NUMBER 32510276Ssam = { 32610276Ssam register char *a, *p; 32710276Ssam 32810276Ssam a = (char *)&data_dest.sin_addr; 32910276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 33010276Ssam p = (char *)&data_dest.sin_port; 33110276Ssam p[0] = $9; p[1] = $11; 33210324Ssam data_dest.sin_family = AF_INET; 33310276Ssam } 33410276Ssam ; 33510276Ssam 33610276Ssam form_code: N 33710276Ssam = { 33810276Ssam $$ = FORM_N; 33910276Ssam } 34010276Ssam | T 34110276Ssam = { 34210276Ssam $$ = FORM_T; 34310276Ssam } 34410276Ssam | C 34510276Ssam = { 34610276Ssam $$ = FORM_C; 34710276Ssam } 34810276Ssam ; 34910276Ssam 35010276Ssam type_code: A 35110276Ssam = { 35210276Ssam cmd_type = TYPE_A; 35310276Ssam cmd_form = FORM_N; 35410276Ssam } 35510276Ssam | A SP form_code 35610276Ssam = { 35710276Ssam cmd_type = TYPE_A; 35810276Ssam cmd_form = $3; 35910276Ssam } 36010276Ssam | E 36110276Ssam = { 36210276Ssam cmd_type = TYPE_E; 36310276Ssam cmd_form = FORM_N; 36410276Ssam } 36510276Ssam | E SP form_code 36610276Ssam = { 36710276Ssam cmd_type = TYPE_E; 36810276Ssam cmd_form = $3; 36910276Ssam } 37010276Ssam | I 37110276Ssam = { 37210276Ssam cmd_type = TYPE_I; 37310276Ssam } 37410276Ssam | L 37510276Ssam = { 37610276Ssam cmd_type = TYPE_L; 37710276Ssam cmd_bytesz = 8; 37810276Ssam } 37910276Ssam | L SP byte_size 38010276Ssam = { 38110276Ssam cmd_type = TYPE_L; 38210276Ssam cmd_bytesz = $3; 38310276Ssam } 38410276Ssam /* this is for a bug in the BBN ftp */ 38510276Ssam | L byte_size 38610276Ssam = { 38710276Ssam cmd_type = TYPE_L; 38810276Ssam cmd_bytesz = $2; 38910276Ssam } 39010276Ssam ; 39110276Ssam 39210276Ssam struct_code: F 39310276Ssam = { 39410276Ssam $$ = STRU_F; 39510276Ssam } 39610276Ssam | R 39710276Ssam = { 39810276Ssam $$ = STRU_R; 39910276Ssam } 40010276Ssam | P 40110276Ssam = { 40210276Ssam $$ = STRU_P; 40310276Ssam } 40410276Ssam ; 40510276Ssam 40610276Ssam mode_code: S 40710276Ssam = { 40810276Ssam $$ = MODE_S; 40910276Ssam } 41010276Ssam | B 41110276Ssam = { 41210276Ssam $$ = MODE_B; 41310276Ssam } 41410276Ssam | C 41510276Ssam = { 41610276Ssam $$ = MODE_C; 41710276Ssam } 41810276Ssam ; 41910276Ssam 42010276Ssam pathname: pathstring 42110276Ssam = { 42227107Smckusick /* 42327107Smckusick * Problem: this production is used for all pathname 42427107Smckusick * processing, but only gives a 550 error reply. 42527107Smckusick * This is a valid reply in some cases but not in others. 42627107Smckusick */ 42726494Sminshall if ($1 && strncmp((char *) $1, "~", 1) == 0) { 42826494Sminshall $$ = (int)*glob((char *) $1); 42910302Ssam if (globerr != NULL) { 43010276Ssam reply(550, globerr); 43110302Ssam $$ = NULL; 43210302Ssam } 43326494Sminshall free((char *) $1); 43410276Ssam } else 43510276Ssam $$ = $1; 43610276Ssam } 43710276Ssam ; 43810276Ssam 43910276Ssam pathstring: STRING 44010276Ssam ; 44110276Ssam 44210276Ssam rename_cmd: rename_from rename_to 44310276Ssam = { 44410276Ssam if ($1 && $2) 44526494Sminshall renamecmd((char *) $1, (char *) $2); 44610276Ssam else 44710276Ssam reply(503, "Bad sequence of commands."); 44810276Ssam if ($1) 44926494Sminshall free((char *) $1); 45010276Ssam if ($2) 45126494Sminshall free((char *) $2); 45210276Ssam } 45310276Ssam ; 45410276Ssam 45510276Ssam rename_from: RNFR check_login SP pathname CRLF 45610276Ssam = { 45710276Ssam char *from = 0, *renamefrom(); 45810276Ssam 45910302Ssam if ($2 && $4) 46026494Sminshall from = renamefrom((char *) $4); 46110302Ssam if (from == 0 && $4) 46226494Sminshall free((char *) $4); 46310276Ssam $$ = (int)from; 46410276Ssam } 46510276Ssam ; 46610276Ssam 46710276Ssam rename_to: RNTO SP pathname CRLF 46810276Ssam = { 46910276Ssam $$ = $3; 47010276Ssam } 47110276Ssam ; 47210276Ssam 47310276Ssam check_login: /* empty */ 47410276Ssam = { 47510276Ssam if (logged_in) 47610276Ssam $$ = 1; 47710276Ssam else { 47810276Ssam reply(530, "Please login with USER and PASS."); 47910276Ssam $$ = 0; 48010276Ssam } 48110276Ssam } 48210276Ssam ; 48310276Ssam 48410276Ssam %% 48510276Ssam 48610276Ssam extern jmp_buf errcatch; 48710276Ssam 48810276Ssam #define CMD 0 /* beginning of command */ 48910276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 49010276Ssam #define STR1 2 /* expect SP followed by STRING */ 49110276Ssam #define STR2 3 /* expect STRING */ 49210276Ssam #define OSTR 4 /* optional STRING */ 49310276Ssam 49410276Ssam struct tab { 49510276Ssam char *name; 49610276Ssam short token; 49710276Ssam short state; 49810276Ssam short implemented; /* 1 if command is implemented */ 49910276Ssam char *help; 50010276Ssam }; 50110276Ssam 50210276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 50310276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 50410276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 50510276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 50610276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 50710276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 50810276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 50926045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 51010276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 51110276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 51210276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 51310276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 51410276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 51510276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 51610276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 51710276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 51810276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 51910276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 52010276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 52110276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 52210276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 52310276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 52410276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 52510276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 52610276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 52726045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 52810276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 52910276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 53010276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 53110276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 53210276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 53310276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 53410276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 53510276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 53610276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 53726045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 53810276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 53926045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 54010276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 54126045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 54210276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 54326045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54410276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54526045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 54610276Ssam { NULL, 0, 0, 0, 0 } 54710276Ssam }; 54810276Ssam 54910276Ssam struct tab * 55010276Ssam lookup(cmd) 55110276Ssam char *cmd; 55210276Ssam { 55310276Ssam register struct tab *p; 55410276Ssam 55510276Ssam for (p = cmdtab; p->name != NULL; p++) 55610276Ssam if (strcmp(cmd, p->name) == 0) 55710276Ssam return (p); 55810276Ssam return (0); 55910276Ssam } 56010276Ssam 56113033Ssam #include <arpa/telnet.h> 56210276Ssam 56310276Ssam /* 56410276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 56510276Ssam */ 56610276Ssam char * 56710276Ssam getline(s, n, iop) 56810276Ssam char *s; 56910276Ssam register FILE *iop; 57010276Ssam { 57110276Ssam register c; 57226494Sminshall register char *cs; 57310276Ssam 57410276Ssam cs = s; 57527751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 57626045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 57726045Sminshall *cs++ = tmpline[c]; 57826045Sminshall if (tmpline[c] == '\n') { 57926045Sminshall *cs++ = '\0'; 58026045Sminshall if (debug) { 58126494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 58226045Sminshall } 58326045Sminshall tmpline[0] = '\0'; 58426045Sminshall return(s); 58526045Sminshall } 58626045Sminshall if (c == 0) { 58726045Sminshall tmpline[0] = '\0'; 58826045Sminshall } 58926045Sminshall } 59027751Sminshall while (--n > 0 && (c = getc(iop)) != EOF) { 59127751Sminshall c = 0377 & c; 59210276Ssam while (c == IAC) { 59327751Sminshall switch (c = 0377 & getc(iop)) { 59427751Sminshall case WILL: 59527751Sminshall case WONT: 59627751Sminshall c = 0377 & getc(iop); 59727751Sminshall printf("%c%c%c", IAC, WONT, c); 59827751Sminshall (void) fflush(stdout); 59927751Sminshall break; 60027751Sminshall case DO: 60127751Sminshall case DONT: 60227751Sminshall c = 0377 & getc(iop); 60327751Sminshall printf("%c%c%c", IAC, DONT, c); 60427751Sminshall (void) fflush(stdout); 60527751Sminshall break; 60627751Sminshall default: 60727751Sminshall break; 60827751Sminshall } 60927751Sminshall c = 0377 & getc(iop); /* try next character */ 61010276Ssam } 61110276Ssam *cs++ = c; 61210276Ssam if (c=='\n') 61310276Ssam break; 61410276Ssam } 61527751Sminshall if (c == EOF && cs == s) 61618303Sralph return (NULL); 61710276Ssam *cs++ = '\0'; 61811652Ssam if (debug) { 61926494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 62011652Ssam } 62110276Ssam return (s); 62210276Ssam } 62310276Ssam 62411652Ssam static int 62511652Ssam toolong() 62611652Ssam { 62726494Sminshall time_t now; 62811652Ssam extern char *ctime(); 62926494Sminshall extern time_t time(); 63011652Ssam 63111652Ssam reply(421, 63211652Ssam "Timeout (%d seconds): closing control connection.", timeout); 63326494Sminshall (void) time(&now); 63411652Ssam if (logging) { 63526494Sminshall syslog(LOG_INFO, 63611652Ssam "FTPD: User %s timed out after %d seconds at %s", 63711652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 63811652Ssam } 63913246Ssam dologout(1); 64011652Ssam } 64111652Ssam 64210276Ssam yylex() 64310276Ssam { 64410276Ssam static int cpos, state; 64510276Ssam register char *cp; 64610276Ssam register struct tab *p; 64710276Ssam int n; 64810276Ssam char c; 64910276Ssam 65010276Ssam for (;;) { 65110276Ssam switch (state) { 65210276Ssam 65310276Ssam case CMD: 65426494Sminshall (void) signal(SIGALRM, toolong); 65526494Sminshall (void) alarm((unsigned) timeout); 65610276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 65710276Ssam reply(221, "You could at least say goodbye."); 65813246Ssam dologout(0); 65910276Ssam } 66026494Sminshall (void) alarm(0); 66110276Ssam if (index(cbuf, '\r')) { 66210276Ssam cp = index(cbuf, '\r'); 66310276Ssam cp[0] = '\n'; cp[1] = 0; 66410276Ssam } 66510276Ssam if (index(cbuf, ' ')) 66610276Ssam cpos = index(cbuf, ' ') - cbuf; 66710276Ssam else 66826045Sminshall cpos = index(cbuf, '\n') - cbuf; 66926045Sminshall if (cpos == 0) { 67010276Ssam cpos = 4; 67126045Sminshall } 67210276Ssam c = cbuf[cpos]; 67310276Ssam cbuf[cpos] = '\0'; 67410276Ssam upper(cbuf); 67510276Ssam p = lookup(cbuf); 67610276Ssam cbuf[cpos] = c; 67710276Ssam if (p != 0) { 67810276Ssam if (p->implemented == 0) { 67910276Ssam nack(p->name); 68026494Sminshall longjmp(errcatch,0); 68110276Ssam /* NOTREACHED */ 68210276Ssam } 68310276Ssam state = p->state; 68410276Ssam yylval = (int) p->name; 68510276Ssam return (p->token); 68610276Ssam } 68710276Ssam break; 68810276Ssam 68910276Ssam case OSTR: 69010276Ssam if (cbuf[cpos] == '\n') { 69110276Ssam state = CMD; 69210276Ssam return (CRLF); 69310276Ssam } 69410276Ssam /* FALL THRU */ 69510276Ssam 69610276Ssam case STR1: 69710276Ssam if (cbuf[cpos] == ' ') { 69810276Ssam cpos++; 69910276Ssam state = STR2; 70010276Ssam return (SP); 70110276Ssam } 70210276Ssam break; 70310276Ssam 70410276Ssam case STR2: 70510276Ssam cp = &cbuf[cpos]; 70610276Ssam n = strlen(cp); 70710276Ssam cpos += n - 1; 70810276Ssam /* 70910276Ssam * Make sure the string is nonempty and \n terminated. 71010276Ssam */ 71110276Ssam if (n > 1 && cbuf[cpos] == '\n') { 71210276Ssam cbuf[cpos] = '\0'; 71310276Ssam yylval = copy(cp); 71410276Ssam cbuf[cpos] = '\n'; 71510276Ssam state = ARGS; 71610276Ssam return (STRING); 71710276Ssam } 71810276Ssam break; 71910276Ssam 72010276Ssam case ARGS: 72110276Ssam if (isdigit(cbuf[cpos])) { 72210276Ssam cp = &cbuf[cpos]; 72310276Ssam while (isdigit(cbuf[++cpos])) 72410276Ssam ; 72510276Ssam c = cbuf[cpos]; 72610276Ssam cbuf[cpos] = '\0'; 72710276Ssam yylval = atoi(cp); 72810276Ssam cbuf[cpos] = c; 72910276Ssam return (NUMBER); 73010276Ssam } 73110276Ssam switch (cbuf[cpos++]) { 73210276Ssam 73310276Ssam case '\n': 73410276Ssam state = CMD; 73510276Ssam return (CRLF); 73610276Ssam 73710276Ssam case ' ': 73810276Ssam return (SP); 73910276Ssam 74010276Ssam case ',': 74110276Ssam return (COMMA); 74210276Ssam 74310276Ssam case 'A': 74410276Ssam case 'a': 74510276Ssam return (A); 74610276Ssam 74710276Ssam case 'B': 74810276Ssam case 'b': 74910276Ssam return (B); 75010276Ssam 75110276Ssam case 'C': 75210276Ssam case 'c': 75310276Ssam return (C); 75410276Ssam 75510276Ssam case 'E': 75610276Ssam case 'e': 75710276Ssam return (E); 75810276Ssam 75910276Ssam case 'F': 76010276Ssam case 'f': 76110276Ssam return (F); 76210276Ssam 76310276Ssam case 'I': 76410276Ssam case 'i': 76510276Ssam return (I); 76610276Ssam 76710276Ssam case 'L': 76810276Ssam case 'l': 76910276Ssam return (L); 77010276Ssam 77110276Ssam case 'N': 77210276Ssam case 'n': 77310276Ssam return (N); 77410276Ssam 77510276Ssam case 'P': 77610276Ssam case 'p': 77710276Ssam return (P); 77810276Ssam 77910276Ssam case 'R': 78010276Ssam case 'r': 78110276Ssam return (R); 78210276Ssam 78310276Ssam case 'S': 78410276Ssam case 's': 78510276Ssam return (S); 78610276Ssam 78710276Ssam case 'T': 78810276Ssam case 't': 78910276Ssam return (T); 79010276Ssam 79110276Ssam } 79210276Ssam break; 79310276Ssam 79410276Ssam default: 79510276Ssam fatal("Unknown state in scanner."); 79610276Ssam } 79726494Sminshall yyerror((char *) 0); 79810276Ssam state = CMD; 79926494Sminshall longjmp(errcatch,0); 80010276Ssam } 80110276Ssam } 80210276Ssam 80310276Ssam upper(s) 80410276Ssam char *s; 80510276Ssam { 80610276Ssam while (*s != '\0') { 80710276Ssam if (islower(*s)) 80810276Ssam *s = toupper(*s); 80910276Ssam s++; 81010276Ssam } 81110276Ssam } 81210276Ssam 81310276Ssam copy(s) 81410276Ssam char *s; 81510276Ssam { 81610276Ssam char *p; 81726494Sminshall extern char *malloc(), *strcpy(); 81810276Ssam 81926494Sminshall p = malloc((unsigned) strlen(s) + 1); 82010276Ssam if (p == NULL) 82110276Ssam fatal("Ran out of memory."); 82226494Sminshall (void) strcpy(p, s); 82310276Ssam return ((int)p); 82410276Ssam } 82510276Ssam 82610276Ssam help(s) 82710276Ssam char *s; 82810276Ssam { 82910276Ssam register struct tab *c; 83010276Ssam register int width, NCMDS; 83110276Ssam 83210276Ssam width = 0, NCMDS = 0; 83310276Ssam for (c = cmdtab; c->name != NULL; c++) { 83410276Ssam int len = strlen(c->name); 83510276Ssam 83610276Ssam if (c->implemented == 0) 83710276Ssam len++; 83810276Ssam if (len > width) 83910276Ssam width = len; 84010276Ssam NCMDS++; 84110276Ssam } 84210276Ssam width = (width + 8) &~ 7; 84310276Ssam if (s == 0) { 84410276Ssam register int i, j, w; 84510276Ssam int columns, lines; 84610276Ssam 84710276Ssam lreply(214, 84810276Ssam "The following commands are recognized (* =>'s unimplemented)."); 84910276Ssam columns = 76 / width; 85010276Ssam if (columns == 0) 85110276Ssam columns = 1; 85210276Ssam lines = (NCMDS + columns - 1) / columns; 85310276Ssam for (i = 0; i < lines; i++) { 85427107Smckusick printf(" "); 85510276Ssam for (j = 0; j < columns; j++) { 85610276Ssam c = cmdtab + j * lines + i; 85710276Ssam printf("%s%c", c->name, 85810276Ssam c->implemented ? ' ' : '*'); 85910302Ssam if (c + lines >= &cmdtab[NCMDS]) 86010276Ssam break; 86110276Ssam w = strlen(c->name); 86210276Ssam while (w < width) { 86310276Ssam putchar(' '); 86410276Ssam w++; 86510276Ssam } 86610276Ssam } 86710276Ssam printf("\r\n"); 86810276Ssam } 86926494Sminshall (void) fflush(stdout); 87010276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 87110276Ssam return; 87210276Ssam } 87310276Ssam upper(s); 87410276Ssam c = lookup(s); 87510276Ssam if (c == (struct tab *)0) { 87627107Smckusick reply(502, "Unknown command %s.", s); 87710276Ssam return; 87810276Ssam } 87910276Ssam if (c->implemented) 88010276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 88110276Ssam else 88210276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 88310276Ssam } 884