110276Ssam /* 226045Sminshall * Copyright (c) 1985 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 533738Sbostic * Redistribution and use in source and binary forms are permitted 6*34769Sbostic * provided that the above copyright notice and this paragraph are 7*34769Sbostic * duplicated in all such forms and that any documentation, 8*34769Sbostic * advertising materials, and other materials related to such 9*34769Sbostic * distribution and use acknowledge that the software was developed 10*34769Sbostic * by the University of California, Berkeley. The name of the 11*34769Sbostic * University may not be used to endorse or promote products derived 12*34769Sbostic * from this software without specific prior written permission. 13*34769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34769Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16*34769Sbostic * 17*34769Sbostic * @(#)ftpcmd.y 5.11 (Berkeley) 06/18/88 1822501Sdist */ 1922501Sdist 2022501Sdist /* 2110276Ssam * Grammar for FTP commands. 2210276Ssam * See RFC 765. 2310276Ssam */ 2410276Ssam 2510276Ssam %{ 2610276Ssam 2710276Ssam #ifndef lint 28*34769Sbostic static char sccsid[] = "@(#)ftpcmd.y 5.11 (Berkeley) 06/18/88"; 2933738Sbostic #endif /* not lint */ 3010276Ssam 3110276Ssam #include <sys/types.h> 3210276Ssam #include <sys/socket.h> 3310276Ssam 3410276Ssam #include <netinet/in.h> 3510276Ssam 3613033Ssam #include <arpa/ftp.h> 3713033Ssam 3810276Ssam #include <stdio.h> 3911652Ssam #include <signal.h> 4010276Ssam #include <ctype.h> 4110276Ssam #include <pwd.h> 4210276Ssam #include <setjmp.h> 4326494Sminshall #include <syslog.h> 4410276Ssam 4510276Ssam extern struct sockaddr_in data_dest; 4610276Ssam extern int logged_in; 4710276Ssam extern struct passwd *pw; 4810276Ssam extern int guest; 4910276Ssam extern int logging; 5010276Ssam extern int type; 5110276Ssam extern int form; 5210276Ssam extern int debug; 5311652Ssam extern int timeout; 5426045Sminshall extern int pdata; 5510276Ssam extern char hostname[]; 5610276Ssam extern char *globerr; 5710320Ssam extern int usedefault; 5826045Sminshall extern int unique; 5926045Sminshall extern int transflag; 6026045Sminshall extern char tmpline[]; 6110276Ssam char **glob(); 6210276Ssam 6310276Ssam static int cmd_type; 6410276Ssam static int cmd_form; 6510276Ssam static int cmd_bytesz; 6626045Sminshall char cbuf[512]; 6730945Scsvsj char *fromname; 6810276Ssam 6910276Ssam char *index(); 7010276Ssam %} 7110276Ssam 7210276Ssam %token 7310276Ssam A B C E F I 7410276Ssam L N P R S T 7510276Ssam 7610276Ssam SP CRLF COMMA STRING NUMBER 7710276Ssam 7810276Ssam USER PASS ACCT REIN QUIT PORT 7910276Ssam PASV TYPE STRU MODE RETR STOR 8010276Ssam APPE MLFL MAIL MSND MSOM MSAM 8110276Ssam MRSQ MRCP ALLO REST RNFR RNTO 8210276Ssam ABOR DELE CWD LIST NLST SITE 8310276Ssam STAT HELP NOOP XMKD XRMD XPWD 8426045Sminshall XCUP STOU 8510276Ssam 8610276Ssam LEXERR 8710276Ssam 8810276Ssam %start cmd_list 8910276Ssam 9010276Ssam %% 9110276Ssam 9210276Ssam cmd_list: /* empty */ 9310276Ssam | cmd_list cmd 9430945Scsvsj = { 9530945Scsvsj fromname = (char *) 0; 9630945Scsvsj } 9730945Scsvsj | cmd_list rcmd 9810276Ssam ; 9910276Ssam 10010276Ssam cmd: USER SP username CRLF 10110276Ssam = { 10210276Ssam extern struct passwd *getpwnam(); 10310276Ssam 10426045Sminshall logged_in = 0; 10526494Sminshall if (strcmp((char *) $3, "ftp") == 0 || 10626494Sminshall strcmp((char *) $3, "anonymous") == 0) { 10710324Ssam if ((pw = getpwnam("ftp")) != NULL) { 10810324Ssam guest = 1; 10910324Ssam reply(331, 11010276Ssam "Guest login ok, send ident as password."); 11110324Ssam } 11226045Sminshall else { 11326045Sminshall reply(530, "User %s unknown.", $3); 11426045Sminshall } 11526494Sminshall } else if (checkuser((char *) $3)) { 11610276Ssam guest = 0; 11726494Sminshall pw = getpwnam((char *) $3); 11826045Sminshall if (pw == NULL) { 11926045Sminshall reply(530, "User %s unknown.", $3); 12026045Sminshall } 12126045Sminshall else { 12226045Sminshall reply(331, "Password required for %s.", $3); 12326045Sminshall } 12428865Smckusick } else { 12528865Smckusick reply(530, "User %s access denied.", $3); 12610276Ssam } 12726494Sminshall free((char *) $3); 12810276Ssam } 12910276Ssam | PASS SP password CRLF 13010276Ssam = { 13126494Sminshall pass((char *) $3); 13226494Sminshall free((char *) $3); 13310276Ssam } 13410276Ssam | PORT SP host_port CRLF 13510276Ssam = { 13610320Ssam usedefault = 0; 13726045Sminshall if (pdata > 0) { 13826045Sminshall (void) close(pdata); 13926045Sminshall } 14026045Sminshall pdata = -1; 14127107Smckusick reply(200, "PORT command successful."); 14210276Ssam } 14326045Sminshall | PASV CRLF 14426045Sminshall = { 14526045Sminshall passive(); 14626045Sminshall } 14710276Ssam | TYPE SP type_code CRLF 14810276Ssam = { 14910276Ssam switch (cmd_type) { 15010276Ssam 15110276Ssam case TYPE_A: 15210276Ssam if (cmd_form == FORM_N) { 15310276Ssam reply(200, "Type set to A."); 15410276Ssam type = cmd_type; 15510276Ssam form = cmd_form; 15610276Ssam } else 15710276Ssam reply(504, "Form must be N."); 15810276Ssam break; 15910276Ssam 16010276Ssam case TYPE_E: 16110276Ssam reply(504, "Type E not implemented."); 16210276Ssam break; 16310276Ssam 16410276Ssam case TYPE_I: 16510276Ssam reply(200, "Type set to I."); 16610276Ssam type = cmd_type; 16710276Ssam break; 16810276Ssam 16910276Ssam case TYPE_L: 17010276Ssam if (cmd_bytesz == 8) { 17110276Ssam reply(200, 17210276Ssam "Type set to L (byte size 8)."); 17310276Ssam type = cmd_type; 17410276Ssam } else 17510276Ssam reply(504, "Byte size must be 8."); 17610276Ssam } 17710276Ssam } 17810276Ssam | STRU SP struct_code CRLF 17910276Ssam = { 18010276Ssam switch ($3) { 18110276Ssam 18210276Ssam case STRU_F: 18310276Ssam reply(200, "STRU F ok."); 18410276Ssam break; 18510276Ssam 18610276Ssam default: 18727107Smckusick reply(504, "Unimplemented STRU type."); 18810276Ssam } 18910276Ssam } 19010276Ssam | MODE SP mode_code CRLF 19110276Ssam = { 19210276Ssam switch ($3) { 19310276Ssam 19410276Ssam case MODE_S: 19510276Ssam reply(200, "MODE S ok."); 19610276Ssam break; 19710276Ssam 19810276Ssam default: 19910276Ssam reply(502, "Unimplemented MODE type."); 20010276Ssam } 20110276Ssam } 20210276Ssam | ALLO SP NUMBER CRLF 20310276Ssam = { 20427107Smckusick reply(202, "ALLO command ignored."); 20510276Ssam } 20610276Ssam | RETR check_login SP pathname CRLF 20710276Ssam = { 20810302Ssam if ($2 && $4 != NULL) 20926494Sminshall retrieve((char *) 0, (char *) $4); 21010302Ssam if ($4 != NULL) 21126494Sminshall free((char *) $4); 21210276Ssam } 21310276Ssam | STOR check_login SP pathname CRLF 21410276Ssam = { 21510302Ssam if ($2 && $4 != NULL) 21626494Sminshall store((char *) $4, "w"); 21710302Ssam if ($4 != NULL) 21826494Sminshall free((char *) $4); 21910276Ssam } 22010276Ssam | APPE check_login SP pathname CRLF 22110276Ssam = { 22210302Ssam if ($2 && $4 != NULL) 22326494Sminshall store((char *) $4, "a"); 22410302Ssam if ($4 != NULL) 22526494Sminshall free((char *) $4); 22610276Ssam } 22710276Ssam | NLST check_login CRLF 22810276Ssam = { 22910276Ssam if ($2) 23011217Ssam retrieve("/bin/ls", ""); 23110276Ssam } 23210276Ssam | NLST check_login SP pathname CRLF 23310276Ssam = { 23410302Ssam if ($2 && $4 != NULL) 23526494Sminshall retrieve("/bin/ls %s", (char *) $4); 23610302Ssam if ($4 != NULL) 23726494Sminshall free((char *) $4); 23810276Ssam } 23910276Ssam | LIST check_login CRLF 24010276Ssam = { 24110276Ssam if ($2) 24210318Ssam retrieve("/bin/ls -lg", ""); 24310276Ssam } 24410276Ssam | LIST check_login SP pathname CRLF 24510276Ssam = { 24610302Ssam if ($2 && $4 != NULL) 24726494Sminshall retrieve("/bin/ls -lg %s", (char *) $4); 24810302Ssam if ($4 != NULL) 24926494Sminshall free((char *) $4); 25010276Ssam } 25110276Ssam | DELE check_login SP pathname CRLF 25210276Ssam = { 25310302Ssam if ($2 && $4 != NULL) 25426494Sminshall delete((char *) $4); 25510302Ssam if ($4 != NULL) 25626494Sminshall free((char *) $4); 25710276Ssam } 25830945Scsvsj | RNTO SP pathname CRLF 25930945Scsvsj = { 26030945Scsvsj if (fromname) { 26130945Scsvsj renamecmd(fromname, (char *) $3); 26230945Scsvsj free(fromname); 26330945Scsvsj fromname = (char *) 0; 26430945Scsvsj } else { 26530945Scsvsj reply(503, "Bad sequence of commands."); 26630945Scsvsj } 26730945Scsvsj free((char *) $3); 26830945Scsvsj } 26926045Sminshall | ABOR CRLF 27026045Sminshall = { 27127107Smckusick reply(225, "ABOR command successful."); 27226045Sminshall } 27310276Ssam | CWD check_login CRLF 27410276Ssam = { 27510276Ssam if ($2) 27610276Ssam cwd(pw->pw_dir); 27710276Ssam } 27810276Ssam | CWD check_login SP pathname CRLF 27910276Ssam = { 28010302Ssam if ($2 && $4 != NULL) 28126494Sminshall cwd((char *) $4); 28210302Ssam if ($4 != NULL) 28326494Sminshall free((char *) $4); 28410276Ssam } 28510276Ssam | HELP CRLF 28610276Ssam = { 28726494Sminshall help((char *) 0); 28810276Ssam } 28910276Ssam | HELP SP STRING CRLF 29010276Ssam = { 29126494Sminshall help((char *) $3); 29210276Ssam } 29310276Ssam | NOOP CRLF 29410276Ssam = { 29527107Smckusick reply(200, "NOOP command successful."); 29610276Ssam } 29710276Ssam | XMKD check_login SP pathname CRLF 29810276Ssam = { 29910302Ssam if ($2 && $4 != NULL) 30026494Sminshall makedir((char *) $4); 30110302Ssam if ($4 != NULL) 30226494Sminshall free((char *) $4); 30310276Ssam } 30410276Ssam | XRMD check_login SP pathname CRLF 30510276Ssam = { 30610302Ssam if ($2 && $4 != NULL) 30726494Sminshall removedir((char *) $4); 30810302Ssam if ($4 != NULL) 30926494Sminshall free((char *) $4); 31010276Ssam } 31110276Ssam | XPWD check_login CRLF 31210276Ssam = { 31310276Ssam if ($2) 31410302Ssam pwd(); 31510276Ssam } 31610276Ssam | XCUP check_login CRLF 31710276Ssam = { 31810276Ssam if ($2) 31910276Ssam cwd(".."); 32010276Ssam } 32126045Sminshall | STOU check_login SP pathname CRLF 32226045Sminshall = { 32326045Sminshall if ($2 && $4 != NULL) { 32426045Sminshall unique++; 32526494Sminshall store((char *) $4, "w"); 32626045Sminshall unique = 0; 32726045Sminshall } 32826045Sminshall if ($4 != NULL) 32926494Sminshall free((char *) $4); 33026045Sminshall } 33110276Ssam | QUIT CRLF 33210276Ssam = { 33310276Ssam reply(221, "Goodbye."); 33413246Ssam dologout(0); 33510276Ssam } 33610276Ssam | error CRLF 33710276Ssam = { 33810276Ssam yyerrok; 33910276Ssam } 34010276Ssam ; 34110276Ssam 34230945Scsvsj rcmd: RNFR check_login SP pathname CRLF 34330945Scsvsj = { 34430945Scsvsj char *renamefrom(); 34530945Scsvsj 34630945Scsvsj if ($2 && $4) { 34730945Scsvsj fromname = renamefrom((char *) $4); 34830945Scsvsj if (fromname == (char *) 0 && $4) { 34930945Scsvsj free((char *) $4); 35030945Scsvsj } 35130945Scsvsj } 35230945Scsvsj } 35330945Scsvsj ; 35430945Scsvsj 35510276Ssam username: STRING 35610276Ssam ; 35710276Ssam 35810276Ssam password: STRING 35910276Ssam ; 36010276Ssam 36110276Ssam byte_size: NUMBER 36210276Ssam ; 36310276Ssam 36410276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 36510276Ssam NUMBER COMMA NUMBER 36610276Ssam = { 36710276Ssam register char *a, *p; 36810276Ssam 36910276Ssam a = (char *)&data_dest.sin_addr; 37010276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 37110276Ssam p = (char *)&data_dest.sin_port; 37210276Ssam p[0] = $9; p[1] = $11; 37310324Ssam data_dest.sin_family = AF_INET; 37410276Ssam } 37510276Ssam ; 37610276Ssam 37710276Ssam form_code: N 37810276Ssam = { 37910276Ssam $$ = FORM_N; 38010276Ssam } 38110276Ssam | T 38210276Ssam = { 38310276Ssam $$ = FORM_T; 38410276Ssam } 38510276Ssam | C 38610276Ssam = { 38710276Ssam $$ = FORM_C; 38810276Ssam } 38910276Ssam ; 39010276Ssam 39110276Ssam type_code: A 39210276Ssam = { 39310276Ssam cmd_type = TYPE_A; 39410276Ssam cmd_form = FORM_N; 39510276Ssam } 39610276Ssam | A SP form_code 39710276Ssam = { 39810276Ssam cmd_type = TYPE_A; 39910276Ssam cmd_form = $3; 40010276Ssam } 40110276Ssam | E 40210276Ssam = { 40310276Ssam cmd_type = TYPE_E; 40410276Ssam cmd_form = FORM_N; 40510276Ssam } 40610276Ssam | E SP form_code 40710276Ssam = { 40810276Ssam cmd_type = TYPE_E; 40910276Ssam cmd_form = $3; 41010276Ssam } 41110276Ssam | I 41210276Ssam = { 41310276Ssam cmd_type = TYPE_I; 41410276Ssam } 41510276Ssam | L 41610276Ssam = { 41710276Ssam cmd_type = TYPE_L; 41810276Ssam cmd_bytesz = 8; 41910276Ssam } 42010276Ssam | L SP byte_size 42110276Ssam = { 42210276Ssam cmd_type = TYPE_L; 42310276Ssam cmd_bytesz = $3; 42410276Ssam } 42510276Ssam /* this is for a bug in the BBN ftp */ 42610276Ssam | L byte_size 42710276Ssam = { 42810276Ssam cmd_type = TYPE_L; 42910276Ssam cmd_bytesz = $2; 43010276Ssam } 43110276Ssam ; 43210276Ssam 43310276Ssam struct_code: F 43410276Ssam = { 43510276Ssam $$ = STRU_F; 43610276Ssam } 43710276Ssam | R 43810276Ssam = { 43910276Ssam $$ = STRU_R; 44010276Ssam } 44110276Ssam | P 44210276Ssam = { 44310276Ssam $$ = STRU_P; 44410276Ssam } 44510276Ssam ; 44610276Ssam 44710276Ssam mode_code: S 44810276Ssam = { 44910276Ssam $$ = MODE_S; 45010276Ssam } 45110276Ssam | B 45210276Ssam = { 45310276Ssam $$ = MODE_B; 45410276Ssam } 45510276Ssam | C 45610276Ssam = { 45710276Ssam $$ = MODE_C; 45810276Ssam } 45910276Ssam ; 46010276Ssam 46110276Ssam pathname: pathstring 46210276Ssam = { 46327107Smckusick /* 46427107Smckusick * Problem: this production is used for all pathname 46527107Smckusick * processing, but only gives a 550 error reply. 46627107Smckusick * This is a valid reply in some cases but not in others. 46727107Smckusick */ 46826494Sminshall if ($1 && strncmp((char *) $1, "~", 1) == 0) { 46926494Sminshall $$ = (int)*glob((char *) $1); 47010302Ssam if (globerr != NULL) { 47110276Ssam reply(550, globerr); 47210302Ssam $$ = NULL; 47310302Ssam } 47426494Sminshall free((char *) $1); 47510276Ssam } else 47610276Ssam $$ = $1; 47710276Ssam } 47810276Ssam ; 47910276Ssam 48010276Ssam pathstring: STRING 48110276Ssam ; 48210276Ssam 48310276Ssam check_login: /* empty */ 48410276Ssam = { 48510276Ssam if (logged_in) 48610276Ssam $$ = 1; 48710276Ssam else { 48810276Ssam reply(530, "Please login with USER and PASS."); 48910276Ssam $$ = 0; 49010276Ssam } 49110276Ssam } 49210276Ssam ; 49310276Ssam 49410276Ssam %% 49510276Ssam 49610276Ssam extern jmp_buf errcatch; 49710276Ssam 49810276Ssam #define CMD 0 /* beginning of command */ 49910276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 50010276Ssam #define STR1 2 /* expect SP followed by STRING */ 50110276Ssam #define STR2 3 /* expect STRING */ 50210276Ssam #define OSTR 4 /* optional STRING */ 50310276Ssam 50410276Ssam struct tab { 50510276Ssam char *name; 50610276Ssam short token; 50710276Ssam short state; 50810276Ssam short implemented; /* 1 if command is implemented */ 50910276Ssam char *help; 51010276Ssam }; 51110276Ssam 51210276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 51310276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 51410276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 51510276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 51610276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 51710276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 51810276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 51926045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 52010276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 52110276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 52210276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 52310276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 52410276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 52510276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 52610276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 52710276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 52810276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 52910276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 53010276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 53110276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 53210276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 53310276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 53410276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 53510276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 53610276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 53726045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 53810276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 53910276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 54010276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 54110276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 54210276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 54310276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 54410276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 54510276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 54610276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 54726045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 54810276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 54926045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 55010276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 55126045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 55210276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 55326045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 55410276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 55526045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 55610276Ssam { NULL, 0, 0, 0, 0 } 55710276Ssam }; 55810276Ssam 55910276Ssam struct tab * 56010276Ssam lookup(cmd) 56110276Ssam char *cmd; 56210276Ssam { 56310276Ssam register struct tab *p; 56410276Ssam 56510276Ssam for (p = cmdtab; p->name != NULL; p++) 56610276Ssam if (strcmp(cmd, p->name) == 0) 56710276Ssam return (p); 56810276Ssam return (0); 56910276Ssam } 57010276Ssam 57113033Ssam #include <arpa/telnet.h> 57210276Ssam 57310276Ssam /* 57410276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 57510276Ssam */ 57610276Ssam char * 57710276Ssam getline(s, n, iop) 57810276Ssam char *s; 57910276Ssam register FILE *iop; 58010276Ssam { 58110276Ssam register c; 58226494Sminshall register char *cs; 58310276Ssam 58410276Ssam cs = s; 58527751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 58626045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 58726045Sminshall *cs++ = tmpline[c]; 58826045Sminshall if (tmpline[c] == '\n') { 58926045Sminshall *cs++ = '\0'; 59026045Sminshall if (debug) { 59126494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 59226045Sminshall } 59326045Sminshall tmpline[0] = '\0'; 59426045Sminshall return(s); 59526045Sminshall } 59626045Sminshall if (c == 0) { 59726045Sminshall tmpline[0] = '\0'; 59826045Sminshall } 59926045Sminshall } 60027751Sminshall while (--n > 0 && (c = getc(iop)) != EOF) { 60127751Sminshall c = 0377 & c; 60210276Ssam while (c == IAC) { 60327751Sminshall switch (c = 0377 & getc(iop)) { 60427751Sminshall case WILL: 60527751Sminshall case WONT: 60627751Sminshall c = 0377 & getc(iop); 60727751Sminshall printf("%c%c%c", IAC, WONT, c); 60827751Sminshall (void) fflush(stdout); 60927751Sminshall break; 61027751Sminshall case DO: 61127751Sminshall case DONT: 61227751Sminshall c = 0377 & getc(iop); 61327751Sminshall printf("%c%c%c", IAC, DONT, c); 61427751Sminshall (void) fflush(stdout); 61527751Sminshall break; 61627751Sminshall default: 61727751Sminshall break; 61827751Sminshall } 61927751Sminshall c = 0377 & getc(iop); /* try next character */ 62010276Ssam } 62110276Ssam *cs++ = c; 62210276Ssam if (c=='\n') 62310276Ssam break; 62410276Ssam } 62527751Sminshall if (c == EOF && cs == s) 62618303Sralph return (NULL); 62710276Ssam *cs++ = '\0'; 62811652Ssam if (debug) { 62926494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 63011652Ssam } 63110276Ssam return (s); 63210276Ssam } 63310276Ssam 63411652Ssam static int 63511652Ssam toolong() 63611652Ssam { 63726494Sminshall time_t now; 63811652Ssam extern char *ctime(); 63926494Sminshall extern time_t time(); 64011652Ssam 64111652Ssam reply(421, 64211652Ssam "Timeout (%d seconds): closing control connection.", timeout); 64326494Sminshall (void) time(&now); 64411652Ssam if (logging) { 64526494Sminshall syslog(LOG_INFO, 64611652Ssam "FTPD: User %s timed out after %d seconds at %s", 64711652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 64811652Ssam } 64913246Ssam dologout(1); 65011652Ssam } 65111652Ssam 65210276Ssam yylex() 65310276Ssam { 65410276Ssam static int cpos, state; 65510276Ssam register char *cp; 65610276Ssam register struct tab *p; 65710276Ssam int n; 65810276Ssam char c; 65910276Ssam 66010276Ssam for (;;) { 66110276Ssam switch (state) { 66210276Ssam 66310276Ssam case CMD: 66426494Sminshall (void) signal(SIGALRM, toolong); 66526494Sminshall (void) alarm((unsigned) timeout); 66610276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 66710276Ssam reply(221, "You could at least say goodbye."); 66813246Ssam dologout(0); 66910276Ssam } 67026494Sminshall (void) alarm(0); 67110276Ssam if (index(cbuf, '\r')) { 67210276Ssam cp = index(cbuf, '\r'); 67310276Ssam cp[0] = '\n'; cp[1] = 0; 67410276Ssam } 67510276Ssam if (index(cbuf, ' ')) 67610276Ssam cpos = index(cbuf, ' ') - cbuf; 67710276Ssam else 67826045Sminshall cpos = index(cbuf, '\n') - cbuf; 67926045Sminshall if (cpos == 0) { 68010276Ssam cpos = 4; 68126045Sminshall } 68210276Ssam c = cbuf[cpos]; 68310276Ssam cbuf[cpos] = '\0'; 68410276Ssam upper(cbuf); 68510276Ssam p = lookup(cbuf); 68610276Ssam cbuf[cpos] = c; 68710276Ssam if (p != 0) { 68810276Ssam if (p->implemented == 0) { 68910276Ssam nack(p->name); 69026494Sminshall longjmp(errcatch,0); 69110276Ssam /* NOTREACHED */ 69210276Ssam } 69310276Ssam state = p->state; 69410276Ssam yylval = (int) p->name; 69510276Ssam return (p->token); 69610276Ssam } 69710276Ssam break; 69810276Ssam 69910276Ssam case OSTR: 70010276Ssam if (cbuf[cpos] == '\n') { 70110276Ssam state = CMD; 70210276Ssam return (CRLF); 70310276Ssam } 70410276Ssam /* FALL THRU */ 70510276Ssam 70610276Ssam case STR1: 70710276Ssam if (cbuf[cpos] == ' ') { 70810276Ssam cpos++; 70910276Ssam state = STR2; 71010276Ssam return (SP); 71110276Ssam } 71210276Ssam break; 71310276Ssam 71410276Ssam case STR2: 71510276Ssam cp = &cbuf[cpos]; 71610276Ssam n = strlen(cp); 71710276Ssam cpos += n - 1; 71810276Ssam /* 71910276Ssam * Make sure the string is nonempty and \n terminated. 72010276Ssam */ 72110276Ssam if (n > 1 && cbuf[cpos] == '\n') { 72210276Ssam cbuf[cpos] = '\0'; 72310276Ssam yylval = copy(cp); 72410276Ssam cbuf[cpos] = '\n'; 72510276Ssam state = ARGS; 72610276Ssam return (STRING); 72710276Ssam } 72810276Ssam break; 72910276Ssam 73010276Ssam case ARGS: 73110276Ssam if (isdigit(cbuf[cpos])) { 73210276Ssam cp = &cbuf[cpos]; 73310276Ssam while (isdigit(cbuf[++cpos])) 73410276Ssam ; 73510276Ssam c = cbuf[cpos]; 73610276Ssam cbuf[cpos] = '\0'; 73710276Ssam yylval = atoi(cp); 73810276Ssam cbuf[cpos] = c; 73910276Ssam return (NUMBER); 74010276Ssam } 74110276Ssam switch (cbuf[cpos++]) { 74210276Ssam 74310276Ssam case '\n': 74410276Ssam state = CMD; 74510276Ssam return (CRLF); 74610276Ssam 74710276Ssam case ' ': 74810276Ssam return (SP); 74910276Ssam 75010276Ssam case ',': 75110276Ssam return (COMMA); 75210276Ssam 75310276Ssam case 'A': 75410276Ssam case 'a': 75510276Ssam return (A); 75610276Ssam 75710276Ssam case 'B': 75810276Ssam case 'b': 75910276Ssam return (B); 76010276Ssam 76110276Ssam case 'C': 76210276Ssam case 'c': 76310276Ssam return (C); 76410276Ssam 76510276Ssam case 'E': 76610276Ssam case 'e': 76710276Ssam return (E); 76810276Ssam 76910276Ssam case 'F': 77010276Ssam case 'f': 77110276Ssam return (F); 77210276Ssam 77310276Ssam case 'I': 77410276Ssam case 'i': 77510276Ssam return (I); 77610276Ssam 77710276Ssam case 'L': 77810276Ssam case 'l': 77910276Ssam return (L); 78010276Ssam 78110276Ssam case 'N': 78210276Ssam case 'n': 78310276Ssam return (N); 78410276Ssam 78510276Ssam case 'P': 78610276Ssam case 'p': 78710276Ssam return (P); 78810276Ssam 78910276Ssam case 'R': 79010276Ssam case 'r': 79110276Ssam return (R); 79210276Ssam 79310276Ssam case 'S': 79410276Ssam case 's': 79510276Ssam return (S); 79610276Ssam 79710276Ssam case 'T': 79810276Ssam case 't': 79910276Ssam return (T); 80010276Ssam 80110276Ssam } 80210276Ssam break; 80310276Ssam 80410276Ssam default: 80510276Ssam fatal("Unknown state in scanner."); 80610276Ssam } 80726494Sminshall yyerror((char *) 0); 80810276Ssam state = CMD; 80926494Sminshall longjmp(errcatch,0); 81010276Ssam } 81110276Ssam } 81210276Ssam 81310276Ssam upper(s) 81410276Ssam char *s; 81510276Ssam { 81610276Ssam while (*s != '\0') { 81710276Ssam if (islower(*s)) 81810276Ssam *s = toupper(*s); 81910276Ssam s++; 82010276Ssam } 82110276Ssam } 82210276Ssam 82310276Ssam copy(s) 82410276Ssam char *s; 82510276Ssam { 82610276Ssam char *p; 82726494Sminshall extern char *malloc(), *strcpy(); 82810276Ssam 82926494Sminshall p = malloc((unsigned) strlen(s) + 1); 83010276Ssam if (p == NULL) 83110276Ssam fatal("Ran out of memory."); 83226494Sminshall (void) strcpy(p, s); 83310276Ssam return ((int)p); 83410276Ssam } 83510276Ssam 83610276Ssam help(s) 83710276Ssam char *s; 83810276Ssam { 83910276Ssam register struct tab *c; 84010276Ssam register int width, NCMDS; 84110276Ssam 84210276Ssam width = 0, NCMDS = 0; 84310276Ssam for (c = cmdtab; c->name != NULL; c++) { 84431132Smckusick int len = strlen(c->name) + 1; 84510276Ssam 84610276Ssam if (len > width) 84710276Ssam width = len; 84810276Ssam NCMDS++; 84910276Ssam } 85010276Ssam width = (width + 8) &~ 7; 85110276Ssam if (s == 0) { 85210276Ssam register int i, j, w; 85310276Ssam int columns, lines; 85410276Ssam 85510276Ssam lreply(214, 85610276Ssam "The following commands are recognized (* =>'s unimplemented)."); 85710276Ssam columns = 76 / width; 85810276Ssam if (columns == 0) 85910276Ssam columns = 1; 86010276Ssam lines = (NCMDS + columns - 1) / columns; 86110276Ssam for (i = 0; i < lines; i++) { 86227107Smckusick printf(" "); 86310276Ssam for (j = 0; j < columns; j++) { 86410276Ssam c = cmdtab + j * lines + i; 86510276Ssam printf("%s%c", c->name, 86610276Ssam c->implemented ? ' ' : '*'); 86710302Ssam if (c + lines >= &cmdtab[NCMDS]) 86810276Ssam break; 86931132Smckusick w = strlen(c->name) + 1; 87010276Ssam while (w < width) { 87110276Ssam putchar(' '); 87210276Ssam w++; 87310276Ssam } 87410276Ssam } 87510276Ssam printf("\r\n"); 87610276Ssam } 87726494Sminshall (void) fflush(stdout); 87810276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 87910276Ssam return; 88010276Ssam } 88110276Ssam upper(s); 88210276Ssam c = lookup(s); 88310276Ssam if (c == (struct tab *)0) { 88427107Smckusick reply(502, "Unknown command %s.", s); 88510276Ssam return; 88610276Ssam } 88710276Ssam if (c->implemented) 88810276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 88910276Ssam else 89010276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 89110276Ssam } 892