110276Ssam /* 226045Sminshall * Copyright (c) 1985 Regents of the University of California. 3*33738Sbostic * All rights reserved. 4*33738Sbostic * 5*33738Sbostic * Redistribution and use in source and binary forms are permitted 6*33738Sbostic * provided that this notice is preserved and that due credit is given 7*33738Sbostic * to the University of California at Berkeley. The name of the University 8*33738Sbostic * may not be used to endorse or promote products derived from this 9*33738Sbostic * software without specific prior written permission. This software 10*33738Sbostic * is provided ``as is'' without express or implied warranty. 1122501Sdist */ 1222501Sdist 1322501Sdist /* 1410276Ssam * Grammar for FTP commands. 1510276Ssam * See RFC 765. 1610276Ssam */ 1710276Ssam 1810276Ssam %{ 1910276Ssam 2010276Ssam #ifndef lint 21*33738Sbostic static char sccsid[] = "@(#)ftpcmd.y 5.10 (Berkeley) 03/14/88"; 22*33738Sbostic #endif /* not lint */ 2310276Ssam 2410276Ssam #include <sys/types.h> 2510276Ssam #include <sys/socket.h> 2610276Ssam 2710276Ssam #include <netinet/in.h> 2810276Ssam 2913033Ssam #include <arpa/ftp.h> 3013033Ssam 3110276Ssam #include <stdio.h> 3211652Ssam #include <signal.h> 3310276Ssam #include <ctype.h> 3410276Ssam #include <pwd.h> 3510276Ssam #include <setjmp.h> 3626494Sminshall #include <syslog.h> 3710276Ssam 3810276Ssam extern struct sockaddr_in data_dest; 3910276Ssam extern int logged_in; 4010276Ssam extern struct passwd *pw; 4110276Ssam extern int guest; 4210276Ssam extern int logging; 4310276Ssam extern int type; 4410276Ssam extern int form; 4510276Ssam extern int debug; 4611652Ssam extern int timeout; 4726045Sminshall extern int pdata; 4810276Ssam extern char hostname[]; 4910276Ssam extern char *globerr; 5010320Ssam extern int usedefault; 5126045Sminshall extern int unique; 5226045Sminshall extern int transflag; 5326045Sminshall extern char tmpline[]; 5410276Ssam char **glob(); 5510276Ssam 5610276Ssam static int cmd_type; 5710276Ssam static int cmd_form; 5810276Ssam static int cmd_bytesz; 5926045Sminshall char cbuf[512]; 6030945Scsvsj char *fromname; 6110276Ssam 6210276Ssam char *index(); 6310276Ssam %} 6410276Ssam 6510276Ssam %token 6610276Ssam A B C E F I 6710276Ssam L N P R S T 6810276Ssam 6910276Ssam SP CRLF COMMA STRING NUMBER 7010276Ssam 7110276Ssam USER PASS ACCT REIN QUIT PORT 7210276Ssam PASV TYPE STRU MODE RETR STOR 7310276Ssam APPE MLFL MAIL MSND MSOM MSAM 7410276Ssam MRSQ MRCP ALLO REST RNFR RNTO 7510276Ssam ABOR DELE CWD LIST NLST SITE 7610276Ssam STAT HELP NOOP XMKD XRMD XPWD 7726045Sminshall XCUP STOU 7810276Ssam 7910276Ssam LEXERR 8010276Ssam 8110276Ssam %start cmd_list 8210276Ssam 8310276Ssam %% 8410276Ssam 8510276Ssam cmd_list: /* empty */ 8610276Ssam | cmd_list cmd 8730945Scsvsj = { 8830945Scsvsj fromname = (char *) 0; 8930945Scsvsj } 9030945Scsvsj | cmd_list rcmd 9110276Ssam ; 9210276Ssam 9310276Ssam cmd: USER SP username CRLF 9410276Ssam = { 9510276Ssam extern struct passwd *getpwnam(); 9610276Ssam 9726045Sminshall logged_in = 0; 9826494Sminshall if (strcmp((char *) $3, "ftp") == 0 || 9926494Sminshall strcmp((char *) $3, "anonymous") == 0) { 10010324Ssam if ((pw = getpwnam("ftp")) != NULL) { 10110324Ssam guest = 1; 10210324Ssam reply(331, 10310276Ssam "Guest login ok, send ident as password."); 10410324Ssam } 10526045Sminshall else { 10626045Sminshall reply(530, "User %s unknown.", $3); 10726045Sminshall } 10826494Sminshall } else if (checkuser((char *) $3)) { 10910276Ssam guest = 0; 11026494Sminshall pw = getpwnam((char *) $3); 11126045Sminshall if (pw == NULL) { 11226045Sminshall reply(530, "User %s unknown.", $3); 11326045Sminshall } 11426045Sminshall else { 11526045Sminshall reply(331, "Password required for %s.", $3); 11626045Sminshall } 11728865Smckusick } else { 11828865Smckusick reply(530, "User %s access denied.", $3); 11910276Ssam } 12026494Sminshall free((char *) $3); 12110276Ssam } 12210276Ssam | PASS SP password CRLF 12310276Ssam = { 12426494Sminshall pass((char *) $3); 12526494Sminshall free((char *) $3); 12610276Ssam } 12710276Ssam | PORT SP host_port CRLF 12810276Ssam = { 12910320Ssam usedefault = 0; 13026045Sminshall if (pdata > 0) { 13126045Sminshall (void) close(pdata); 13226045Sminshall } 13326045Sminshall pdata = -1; 13427107Smckusick reply(200, "PORT command successful."); 13510276Ssam } 13626045Sminshall | PASV CRLF 13726045Sminshall = { 13826045Sminshall passive(); 13926045Sminshall } 14010276Ssam | TYPE SP type_code CRLF 14110276Ssam = { 14210276Ssam switch (cmd_type) { 14310276Ssam 14410276Ssam case TYPE_A: 14510276Ssam if (cmd_form == FORM_N) { 14610276Ssam reply(200, "Type set to A."); 14710276Ssam type = cmd_type; 14810276Ssam form = cmd_form; 14910276Ssam } else 15010276Ssam reply(504, "Form must be N."); 15110276Ssam break; 15210276Ssam 15310276Ssam case TYPE_E: 15410276Ssam reply(504, "Type E not implemented."); 15510276Ssam break; 15610276Ssam 15710276Ssam case TYPE_I: 15810276Ssam reply(200, "Type set to I."); 15910276Ssam type = cmd_type; 16010276Ssam break; 16110276Ssam 16210276Ssam case TYPE_L: 16310276Ssam if (cmd_bytesz == 8) { 16410276Ssam reply(200, 16510276Ssam "Type set to L (byte size 8)."); 16610276Ssam type = cmd_type; 16710276Ssam } else 16810276Ssam reply(504, "Byte size must be 8."); 16910276Ssam } 17010276Ssam } 17110276Ssam | STRU SP struct_code CRLF 17210276Ssam = { 17310276Ssam switch ($3) { 17410276Ssam 17510276Ssam case STRU_F: 17610276Ssam reply(200, "STRU F ok."); 17710276Ssam break; 17810276Ssam 17910276Ssam default: 18027107Smckusick reply(504, "Unimplemented STRU type."); 18110276Ssam } 18210276Ssam } 18310276Ssam | MODE SP mode_code CRLF 18410276Ssam = { 18510276Ssam switch ($3) { 18610276Ssam 18710276Ssam case MODE_S: 18810276Ssam reply(200, "MODE S ok."); 18910276Ssam break; 19010276Ssam 19110276Ssam default: 19210276Ssam reply(502, "Unimplemented MODE type."); 19310276Ssam } 19410276Ssam } 19510276Ssam | ALLO SP NUMBER CRLF 19610276Ssam = { 19727107Smckusick reply(202, "ALLO command ignored."); 19810276Ssam } 19910276Ssam | RETR check_login SP pathname CRLF 20010276Ssam = { 20110302Ssam if ($2 && $4 != NULL) 20226494Sminshall retrieve((char *) 0, (char *) $4); 20310302Ssam if ($4 != NULL) 20426494Sminshall free((char *) $4); 20510276Ssam } 20610276Ssam | STOR check_login SP pathname CRLF 20710276Ssam = { 20810302Ssam if ($2 && $4 != NULL) 20926494Sminshall store((char *) $4, "w"); 21010302Ssam if ($4 != NULL) 21126494Sminshall free((char *) $4); 21210276Ssam } 21310276Ssam | APPE check_login SP pathname CRLF 21410276Ssam = { 21510302Ssam if ($2 && $4 != NULL) 21626494Sminshall store((char *) $4, "a"); 21710302Ssam if ($4 != NULL) 21826494Sminshall free((char *) $4); 21910276Ssam } 22010276Ssam | NLST check_login CRLF 22110276Ssam = { 22210276Ssam if ($2) 22311217Ssam retrieve("/bin/ls", ""); 22410276Ssam } 22510276Ssam | NLST check_login SP pathname CRLF 22610276Ssam = { 22710302Ssam if ($2 && $4 != NULL) 22826494Sminshall retrieve("/bin/ls %s", (char *) $4); 22910302Ssam if ($4 != NULL) 23026494Sminshall free((char *) $4); 23110276Ssam } 23210276Ssam | LIST check_login CRLF 23310276Ssam = { 23410276Ssam if ($2) 23510318Ssam retrieve("/bin/ls -lg", ""); 23610276Ssam } 23710276Ssam | LIST check_login SP pathname CRLF 23810276Ssam = { 23910302Ssam if ($2 && $4 != NULL) 24026494Sminshall retrieve("/bin/ls -lg %s", (char *) $4); 24110302Ssam if ($4 != NULL) 24226494Sminshall free((char *) $4); 24310276Ssam } 24410276Ssam | DELE check_login SP pathname CRLF 24510276Ssam = { 24610302Ssam if ($2 && $4 != NULL) 24726494Sminshall delete((char *) $4); 24810302Ssam if ($4 != NULL) 24926494Sminshall free((char *) $4); 25010276Ssam } 25130945Scsvsj | RNTO SP pathname CRLF 25230945Scsvsj = { 25330945Scsvsj if (fromname) { 25430945Scsvsj renamecmd(fromname, (char *) $3); 25530945Scsvsj free(fromname); 25630945Scsvsj fromname = (char *) 0; 25730945Scsvsj } else { 25830945Scsvsj reply(503, "Bad sequence of commands."); 25930945Scsvsj } 26030945Scsvsj free((char *) $3); 26130945Scsvsj } 26226045Sminshall | ABOR CRLF 26326045Sminshall = { 26427107Smckusick reply(225, "ABOR command successful."); 26526045Sminshall } 26610276Ssam | CWD check_login CRLF 26710276Ssam = { 26810276Ssam if ($2) 26910276Ssam cwd(pw->pw_dir); 27010276Ssam } 27110276Ssam | CWD check_login SP pathname CRLF 27210276Ssam = { 27310302Ssam if ($2 && $4 != NULL) 27426494Sminshall cwd((char *) $4); 27510302Ssam if ($4 != NULL) 27626494Sminshall free((char *) $4); 27710276Ssam } 27810276Ssam | HELP CRLF 27910276Ssam = { 28026494Sminshall help((char *) 0); 28110276Ssam } 28210276Ssam | HELP SP STRING CRLF 28310276Ssam = { 28426494Sminshall help((char *) $3); 28510276Ssam } 28610276Ssam | NOOP CRLF 28710276Ssam = { 28827107Smckusick reply(200, "NOOP command successful."); 28910276Ssam } 29010276Ssam | XMKD check_login SP pathname CRLF 29110276Ssam = { 29210302Ssam if ($2 && $4 != NULL) 29326494Sminshall makedir((char *) $4); 29410302Ssam if ($4 != NULL) 29526494Sminshall free((char *) $4); 29610276Ssam } 29710276Ssam | XRMD check_login SP pathname CRLF 29810276Ssam = { 29910302Ssam if ($2 && $4 != NULL) 30026494Sminshall removedir((char *) $4); 30110302Ssam if ($4 != NULL) 30226494Sminshall free((char *) $4); 30310276Ssam } 30410276Ssam | XPWD check_login CRLF 30510276Ssam = { 30610276Ssam if ($2) 30710302Ssam pwd(); 30810276Ssam } 30910276Ssam | XCUP check_login CRLF 31010276Ssam = { 31110276Ssam if ($2) 31210276Ssam cwd(".."); 31310276Ssam } 31426045Sminshall | STOU check_login SP pathname CRLF 31526045Sminshall = { 31626045Sminshall if ($2 && $4 != NULL) { 31726045Sminshall unique++; 31826494Sminshall store((char *) $4, "w"); 31926045Sminshall unique = 0; 32026045Sminshall } 32126045Sminshall if ($4 != NULL) 32226494Sminshall free((char *) $4); 32326045Sminshall } 32410276Ssam | QUIT CRLF 32510276Ssam = { 32610276Ssam reply(221, "Goodbye."); 32713246Ssam dologout(0); 32810276Ssam } 32910276Ssam | error CRLF 33010276Ssam = { 33110276Ssam yyerrok; 33210276Ssam } 33310276Ssam ; 33410276Ssam 33530945Scsvsj rcmd: RNFR check_login SP pathname CRLF 33630945Scsvsj = { 33730945Scsvsj char *renamefrom(); 33830945Scsvsj 33930945Scsvsj if ($2 && $4) { 34030945Scsvsj fromname = renamefrom((char *) $4); 34130945Scsvsj if (fromname == (char *) 0 && $4) { 34230945Scsvsj free((char *) $4); 34330945Scsvsj } 34430945Scsvsj } 34530945Scsvsj } 34630945Scsvsj ; 34730945Scsvsj 34810276Ssam username: STRING 34910276Ssam ; 35010276Ssam 35110276Ssam password: STRING 35210276Ssam ; 35310276Ssam 35410276Ssam byte_size: NUMBER 35510276Ssam ; 35610276Ssam 35710276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 35810276Ssam NUMBER COMMA NUMBER 35910276Ssam = { 36010276Ssam register char *a, *p; 36110276Ssam 36210276Ssam a = (char *)&data_dest.sin_addr; 36310276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 36410276Ssam p = (char *)&data_dest.sin_port; 36510276Ssam p[0] = $9; p[1] = $11; 36610324Ssam data_dest.sin_family = AF_INET; 36710276Ssam } 36810276Ssam ; 36910276Ssam 37010276Ssam form_code: N 37110276Ssam = { 37210276Ssam $$ = FORM_N; 37310276Ssam } 37410276Ssam | T 37510276Ssam = { 37610276Ssam $$ = FORM_T; 37710276Ssam } 37810276Ssam | C 37910276Ssam = { 38010276Ssam $$ = FORM_C; 38110276Ssam } 38210276Ssam ; 38310276Ssam 38410276Ssam type_code: A 38510276Ssam = { 38610276Ssam cmd_type = TYPE_A; 38710276Ssam cmd_form = FORM_N; 38810276Ssam } 38910276Ssam | A SP form_code 39010276Ssam = { 39110276Ssam cmd_type = TYPE_A; 39210276Ssam cmd_form = $3; 39310276Ssam } 39410276Ssam | E 39510276Ssam = { 39610276Ssam cmd_type = TYPE_E; 39710276Ssam cmd_form = FORM_N; 39810276Ssam } 39910276Ssam | E SP form_code 40010276Ssam = { 40110276Ssam cmd_type = TYPE_E; 40210276Ssam cmd_form = $3; 40310276Ssam } 40410276Ssam | I 40510276Ssam = { 40610276Ssam cmd_type = TYPE_I; 40710276Ssam } 40810276Ssam | L 40910276Ssam = { 41010276Ssam cmd_type = TYPE_L; 41110276Ssam cmd_bytesz = 8; 41210276Ssam } 41310276Ssam | L SP byte_size 41410276Ssam = { 41510276Ssam cmd_type = TYPE_L; 41610276Ssam cmd_bytesz = $3; 41710276Ssam } 41810276Ssam /* this is for a bug in the BBN ftp */ 41910276Ssam | L byte_size 42010276Ssam = { 42110276Ssam cmd_type = TYPE_L; 42210276Ssam cmd_bytesz = $2; 42310276Ssam } 42410276Ssam ; 42510276Ssam 42610276Ssam struct_code: F 42710276Ssam = { 42810276Ssam $$ = STRU_F; 42910276Ssam } 43010276Ssam | R 43110276Ssam = { 43210276Ssam $$ = STRU_R; 43310276Ssam } 43410276Ssam | P 43510276Ssam = { 43610276Ssam $$ = STRU_P; 43710276Ssam } 43810276Ssam ; 43910276Ssam 44010276Ssam mode_code: S 44110276Ssam = { 44210276Ssam $$ = MODE_S; 44310276Ssam } 44410276Ssam | B 44510276Ssam = { 44610276Ssam $$ = MODE_B; 44710276Ssam } 44810276Ssam | C 44910276Ssam = { 45010276Ssam $$ = MODE_C; 45110276Ssam } 45210276Ssam ; 45310276Ssam 45410276Ssam pathname: pathstring 45510276Ssam = { 45627107Smckusick /* 45727107Smckusick * Problem: this production is used for all pathname 45827107Smckusick * processing, but only gives a 550 error reply. 45927107Smckusick * This is a valid reply in some cases but not in others. 46027107Smckusick */ 46126494Sminshall if ($1 && strncmp((char *) $1, "~", 1) == 0) { 46226494Sminshall $$ = (int)*glob((char *) $1); 46310302Ssam if (globerr != NULL) { 46410276Ssam reply(550, globerr); 46510302Ssam $$ = NULL; 46610302Ssam } 46726494Sminshall free((char *) $1); 46810276Ssam } else 46910276Ssam $$ = $1; 47010276Ssam } 47110276Ssam ; 47210276Ssam 47310276Ssam pathstring: STRING 47410276Ssam ; 47510276Ssam 47610276Ssam check_login: /* empty */ 47710276Ssam = { 47810276Ssam if (logged_in) 47910276Ssam $$ = 1; 48010276Ssam else { 48110276Ssam reply(530, "Please login with USER and PASS."); 48210276Ssam $$ = 0; 48310276Ssam } 48410276Ssam } 48510276Ssam ; 48610276Ssam 48710276Ssam %% 48810276Ssam 48910276Ssam extern jmp_buf errcatch; 49010276Ssam 49110276Ssam #define CMD 0 /* beginning of command */ 49210276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 49310276Ssam #define STR1 2 /* expect SP followed by STRING */ 49410276Ssam #define STR2 3 /* expect STRING */ 49510276Ssam #define OSTR 4 /* optional STRING */ 49610276Ssam 49710276Ssam struct tab { 49810276Ssam char *name; 49910276Ssam short token; 50010276Ssam short state; 50110276Ssam short implemented; /* 1 if command is implemented */ 50210276Ssam char *help; 50310276Ssam }; 50410276Ssam 50510276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 50610276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 50710276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 50810276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 50910276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 51010276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 51110276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 51226045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 51310276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 51410276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 51510276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 51610276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 51710276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 51810276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 51910276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 52010276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 52110276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 52210276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 52310276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 52410276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 52510276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 52610276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 52710276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 52810276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 52910276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 53026045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 53110276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 53210276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 53310276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 53410276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 53510276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 53610276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 53710276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 53810276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 53910276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 54026045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 54110276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 54226045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 54310276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 54426045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 54510276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 54626045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54710276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54826045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 54910276Ssam { NULL, 0, 0, 0, 0 } 55010276Ssam }; 55110276Ssam 55210276Ssam struct tab * 55310276Ssam lookup(cmd) 55410276Ssam char *cmd; 55510276Ssam { 55610276Ssam register struct tab *p; 55710276Ssam 55810276Ssam for (p = cmdtab; p->name != NULL; p++) 55910276Ssam if (strcmp(cmd, p->name) == 0) 56010276Ssam return (p); 56110276Ssam return (0); 56210276Ssam } 56310276Ssam 56413033Ssam #include <arpa/telnet.h> 56510276Ssam 56610276Ssam /* 56710276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 56810276Ssam */ 56910276Ssam char * 57010276Ssam getline(s, n, iop) 57110276Ssam char *s; 57210276Ssam register FILE *iop; 57310276Ssam { 57410276Ssam register c; 57526494Sminshall register char *cs; 57610276Ssam 57710276Ssam cs = s; 57827751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 57926045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 58026045Sminshall *cs++ = tmpline[c]; 58126045Sminshall if (tmpline[c] == '\n') { 58226045Sminshall *cs++ = '\0'; 58326045Sminshall if (debug) { 58426494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 58526045Sminshall } 58626045Sminshall tmpline[0] = '\0'; 58726045Sminshall return(s); 58826045Sminshall } 58926045Sminshall if (c == 0) { 59026045Sminshall tmpline[0] = '\0'; 59126045Sminshall } 59226045Sminshall } 59327751Sminshall while (--n > 0 && (c = getc(iop)) != EOF) { 59427751Sminshall c = 0377 & c; 59510276Ssam while (c == IAC) { 59627751Sminshall switch (c = 0377 & getc(iop)) { 59727751Sminshall case WILL: 59827751Sminshall case WONT: 59927751Sminshall c = 0377 & getc(iop); 60027751Sminshall printf("%c%c%c", IAC, WONT, c); 60127751Sminshall (void) fflush(stdout); 60227751Sminshall break; 60327751Sminshall case DO: 60427751Sminshall case DONT: 60527751Sminshall c = 0377 & getc(iop); 60627751Sminshall printf("%c%c%c", IAC, DONT, c); 60727751Sminshall (void) fflush(stdout); 60827751Sminshall break; 60927751Sminshall default: 61027751Sminshall break; 61127751Sminshall } 61227751Sminshall c = 0377 & getc(iop); /* try next character */ 61310276Ssam } 61410276Ssam *cs++ = c; 61510276Ssam if (c=='\n') 61610276Ssam break; 61710276Ssam } 61827751Sminshall if (c == EOF && cs == s) 61918303Sralph return (NULL); 62010276Ssam *cs++ = '\0'; 62111652Ssam if (debug) { 62226494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 62311652Ssam } 62410276Ssam return (s); 62510276Ssam } 62610276Ssam 62711652Ssam static int 62811652Ssam toolong() 62911652Ssam { 63026494Sminshall time_t now; 63111652Ssam extern char *ctime(); 63226494Sminshall extern time_t time(); 63311652Ssam 63411652Ssam reply(421, 63511652Ssam "Timeout (%d seconds): closing control connection.", timeout); 63626494Sminshall (void) time(&now); 63711652Ssam if (logging) { 63826494Sminshall syslog(LOG_INFO, 63911652Ssam "FTPD: User %s timed out after %d seconds at %s", 64011652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 64111652Ssam } 64213246Ssam dologout(1); 64311652Ssam } 64411652Ssam 64510276Ssam yylex() 64610276Ssam { 64710276Ssam static int cpos, state; 64810276Ssam register char *cp; 64910276Ssam register struct tab *p; 65010276Ssam int n; 65110276Ssam char c; 65210276Ssam 65310276Ssam for (;;) { 65410276Ssam switch (state) { 65510276Ssam 65610276Ssam case CMD: 65726494Sminshall (void) signal(SIGALRM, toolong); 65826494Sminshall (void) alarm((unsigned) timeout); 65910276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 66010276Ssam reply(221, "You could at least say goodbye."); 66113246Ssam dologout(0); 66210276Ssam } 66326494Sminshall (void) alarm(0); 66410276Ssam if (index(cbuf, '\r')) { 66510276Ssam cp = index(cbuf, '\r'); 66610276Ssam cp[0] = '\n'; cp[1] = 0; 66710276Ssam } 66810276Ssam if (index(cbuf, ' ')) 66910276Ssam cpos = index(cbuf, ' ') - cbuf; 67010276Ssam else 67126045Sminshall cpos = index(cbuf, '\n') - cbuf; 67226045Sminshall if (cpos == 0) { 67310276Ssam cpos = 4; 67426045Sminshall } 67510276Ssam c = cbuf[cpos]; 67610276Ssam cbuf[cpos] = '\0'; 67710276Ssam upper(cbuf); 67810276Ssam p = lookup(cbuf); 67910276Ssam cbuf[cpos] = c; 68010276Ssam if (p != 0) { 68110276Ssam if (p->implemented == 0) { 68210276Ssam nack(p->name); 68326494Sminshall longjmp(errcatch,0); 68410276Ssam /* NOTREACHED */ 68510276Ssam } 68610276Ssam state = p->state; 68710276Ssam yylval = (int) p->name; 68810276Ssam return (p->token); 68910276Ssam } 69010276Ssam break; 69110276Ssam 69210276Ssam case OSTR: 69310276Ssam if (cbuf[cpos] == '\n') { 69410276Ssam state = CMD; 69510276Ssam return (CRLF); 69610276Ssam } 69710276Ssam /* FALL THRU */ 69810276Ssam 69910276Ssam case STR1: 70010276Ssam if (cbuf[cpos] == ' ') { 70110276Ssam cpos++; 70210276Ssam state = STR2; 70310276Ssam return (SP); 70410276Ssam } 70510276Ssam break; 70610276Ssam 70710276Ssam case STR2: 70810276Ssam cp = &cbuf[cpos]; 70910276Ssam n = strlen(cp); 71010276Ssam cpos += n - 1; 71110276Ssam /* 71210276Ssam * Make sure the string is nonempty and \n terminated. 71310276Ssam */ 71410276Ssam if (n > 1 && cbuf[cpos] == '\n') { 71510276Ssam cbuf[cpos] = '\0'; 71610276Ssam yylval = copy(cp); 71710276Ssam cbuf[cpos] = '\n'; 71810276Ssam state = ARGS; 71910276Ssam return (STRING); 72010276Ssam } 72110276Ssam break; 72210276Ssam 72310276Ssam case ARGS: 72410276Ssam if (isdigit(cbuf[cpos])) { 72510276Ssam cp = &cbuf[cpos]; 72610276Ssam while (isdigit(cbuf[++cpos])) 72710276Ssam ; 72810276Ssam c = cbuf[cpos]; 72910276Ssam cbuf[cpos] = '\0'; 73010276Ssam yylval = atoi(cp); 73110276Ssam cbuf[cpos] = c; 73210276Ssam return (NUMBER); 73310276Ssam } 73410276Ssam switch (cbuf[cpos++]) { 73510276Ssam 73610276Ssam case '\n': 73710276Ssam state = CMD; 73810276Ssam return (CRLF); 73910276Ssam 74010276Ssam case ' ': 74110276Ssam return (SP); 74210276Ssam 74310276Ssam case ',': 74410276Ssam return (COMMA); 74510276Ssam 74610276Ssam case 'A': 74710276Ssam case 'a': 74810276Ssam return (A); 74910276Ssam 75010276Ssam case 'B': 75110276Ssam case 'b': 75210276Ssam return (B); 75310276Ssam 75410276Ssam case 'C': 75510276Ssam case 'c': 75610276Ssam return (C); 75710276Ssam 75810276Ssam case 'E': 75910276Ssam case 'e': 76010276Ssam return (E); 76110276Ssam 76210276Ssam case 'F': 76310276Ssam case 'f': 76410276Ssam return (F); 76510276Ssam 76610276Ssam case 'I': 76710276Ssam case 'i': 76810276Ssam return (I); 76910276Ssam 77010276Ssam case 'L': 77110276Ssam case 'l': 77210276Ssam return (L); 77310276Ssam 77410276Ssam case 'N': 77510276Ssam case 'n': 77610276Ssam return (N); 77710276Ssam 77810276Ssam case 'P': 77910276Ssam case 'p': 78010276Ssam return (P); 78110276Ssam 78210276Ssam case 'R': 78310276Ssam case 'r': 78410276Ssam return (R); 78510276Ssam 78610276Ssam case 'S': 78710276Ssam case 's': 78810276Ssam return (S); 78910276Ssam 79010276Ssam case 'T': 79110276Ssam case 't': 79210276Ssam return (T); 79310276Ssam 79410276Ssam } 79510276Ssam break; 79610276Ssam 79710276Ssam default: 79810276Ssam fatal("Unknown state in scanner."); 79910276Ssam } 80026494Sminshall yyerror((char *) 0); 80110276Ssam state = CMD; 80226494Sminshall longjmp(errcatch,0); 80310276Ssam } 80410276Ssam } 80510276Ssam 80610276Ssam upper(s) 80710276Ssam char *s; 80810276Ssam { 80910276Ssam while (*s != '\0') { 81010276Ssam if (islower(*s)) 81110276Ssam *s = toupper(*s); 81210276Ssam s++; 81310276Ssam } 81410276Ssam } 81510276Ssam 81610276Ssam copy(s) 81710276Ssam char *s; 81810276Ssam { 81910276Ssam char *p; 82026494Sminshall extern char *malloc(), *strcpy(); 82110276Ssam 82226494Sminshall p = malloc((unsigned) strlen(s) + 1); 82310276Ssam if (p == NULL) 82410276Ssam fatal("Ran out of memory."); 82526494Sminshall (void) strcpy(p, s); 82610276Ssam return ((int)p); 82710276Ssam } 82810276Ssam 82910276Ssam help(s) 83010276Ssam char *s; 83110276Ssam { 83210276Ssam register struct tab *c; 83310276Ssam register int width, NCMDS; 83410276Ssam 83510276Ssam width = 0, NCMDS = 0; 83610276Ssam for (c = cmdtab; c->name != NULL; c++) { 83731132Smckusick int len = strlen(c->name) + 1; 83810276Ssam 83910276Ssam if (len > width) 84010276Ssam width = len; 84110276Ssam NCMDS++; 84210276Ssam } 84310276Ssam width = (width + 8) &~ 7; 84410276Ssam if (s == 0) { 84510276Ssam register int i, j, w; 84610276Ssam int columns, lines; 84710276Ssam 84810276Ssam lreply(214, 84910276Ssam "The following commands are recognized (* =>'s unimplemented)."); 85010276Ssam columns = 76 / width; 85110276Ssam if (columns == 0) 85210276Ssam columns = 1; 85310276Ssam lines = (NCMDS + columns - 1) / columns; 85410276Ssam for (i = 0; i < lines; i++) { 85527107Smckusick printf(" "); 85610276Ssam for (j = 0; j < columns; j++) { 85710276Ssam c = cmdtab + j * lines + i; 85810276Ssam printf("%s%c", c->name, 85910276Ssam c->implemented ? ' ' : '*'); 86010302Ssam if (c + lines >= &cmdtab[NCMDS]) 86110276Ssam break; 86231132Smckusick w = strlen(c->name) + 1; 86310276Ssam while (w < width) { 86410276Ssam putchar(' '); 86510276Ssam w++; 86610276Ssam } 86710276Ssam } 86810276Ssam printf("\r\n"); 86910276Ssam } 87026494Sminshall (void) fflush(stdout); 87110276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 87210276Ssam return; 87310276Ssam } 87410276Ssam upper(s); 87510276Ssam c = lookup(s); 87610276Ssam if (c == (struct tab *)0) { 87727107Smckusick reply(502, "Unknown command %s.", s); 87810276Ssam return; 87910276Ssam } 88010276Ssam if (c->implemented) 88110276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 88210276Ssam else 88310276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 88410276Ssam } 885