110276Ssam /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 533738Sbostic * Redistribution and use in source and binary forms are permitted 634769Sbostic * provided that the above copyright notice and this paragraph are 734769Sbostic * duplicated in all such forms and that any documentation, 834769Sbostic * advertising materials, and other materials related to such 934769Sbostic * distribution and use acknowledge that the software was developed 1034769Sbostic * by the University of California, Berkeley. The name of the 1134769Sbostic * University may not be used to endorse or promote products derived 1234769Sbostic * from this software without specific prior written permission. 1334769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534769Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1634769Sbostic * 17*36324Sbostic * @(#)ftpcmd.y 5.16 (Berkeley) 12/08/88 1822501Sdist */ 1922501Sdist 2022501Sdist /* 2110276Ssam * Grammar for FTP commands. 2210276Ssam * See RFC 765. 2310276Ssam */ 2410276Ssam 2510276Ssam %{ 2610276Ssam 2710276Ssam #ifndef lint 28*36324Sbostic static char sccsid[] = "@(#)ftpcmd.y 5.16 (Berkeley) 12/08/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 transflag; 5926045Sminshall extern char tmpline[]; 6010276Ssam char **glob(); 6110276Ssam 6210276Ssam static int cmd_type; 6310276Ssam static int cmd_form; 6410276Ssam static int cmd_bytesz; 6536304Skarels char cbuf[512]; 6636304Skarels char *fromname; 6710276Ssam 6810276Ssam char *index(); 6910276Ssam %} 7010276Ssam 7110276Ssam %token 7210276Ssam A B C E F I 7310276Ssam L N P R S T 7410276Ssam 7510276Ssam SP CRLF COMMA STRING NUMBER 7610276Ssam 7710276Ssam USER PASS ACCT REIN QUIT PORT 7810276Ssam PASV TYPE STRU MODE RETR STOR 7910276Ssam APPE MLFL MAIL MSND MSOM MSAM 8010276Ssam MRSQ MRCP ALLO REST RNFR RNTO 8110276Ssam ABOR DELE CWD LIST NLST SITE 8210276Ssam STAT HELP NOOP XMKD XRMD XPWD 8326045Sminshall XCUP STOU 8410276Ssam 8510276Ssam LEXERR 8610276Ssam 8710276Ssam %start cmd_list 8810276Ssam 8910276Ssam %% 9010276Ssam 9110276Ssam cmd_list: /* empty */ 9210276Ssam | cmd_list cmd 9330945Scsvsj = { 9430945Scsvsj fromname = (char *) 0; 9530945Scsvsj } 9630945Scsvsj | cmd_list rcmd 9710276Ssam ; 9810276Ssam 9910276Ssam cmd: USER SP username CRLF 10010276Ssam = { 10136304Skarels user((char *) $3); 10226494Sminshall free((char *) $3); 10310276Ssam } 10410276Ssam | PASS SP password CRLF 10510276Ssam = { 10626494Sminshall pass((char *) $3); 10726494Sminshall free((char *) $3); 10810276Ssam } 10910276Ssam | PORT SP host_port CRLF 11010276Ssam = { 11110320Ssam usedefault = 0; 11236304Skarels if (pdata >= 0) { 11326045Sminshall (void) close(pdata); 11436304Skarels pdata = -1; 11526045Sminshall } 11627107Smckusick reply(200, "PORT command successful."); 11710276Ssam } 11826045Sminshall | PASV CRLF 11926045Sminshall = { 12026045Sminshall passive(); 12126045Sminshall } 12210276Ssam | TYPE SP type_code CRLF 12310276Ssam = { 12410276Ssam switch (cmd_type) { 12510276Ssam 12610276Ssam case TYPE_A: 12710276Ssam if (cmd_form == FORM_N) { 12810276Ssam reply(200, "Type set to A."); 12910276Ssam type = cmd_type; 13010276Ssam form = cmd_form; 13110276Ssam } else 13210276Ssam reply(504, "Form must be N."); 13310276Ssam break; 13410276Ssam 13510276Ssam case TYPE_E: 13610276Ssam reply(504, "Type E not implemented."); 13710276Ssam break; 13810276Ssam 13910276Ssam case TYPE_I: 14010276Ssam reply(200, "Type set to I."); 14110276Ssam type = cmd_type; 14210276Ssam break; 14310276Ssam 14410276Ssam case TYPE_L: 14510276Ssam if (cmd_bytesz == 8) { 14610276Ssam reply(200, 14710276Ssam "Type set to L (byte size 8)."); 14810276Ssam type = cmd_type; 14910276Ssam } else 15010276Ssam reply(504, "Byte size must be 8."); 15110276Ssam } 15210276Ssam } 15310276Ssam | STRU SP struct_code CRLF 15410276Ssam = { 15510276Ssam switch ($3) { 15610276Ssam 15710276Ssam case STRU_F: 15810276Ssam reply(200, "STRU F ok."); 15910276Ssam break; 16010276Ssam 16110276Ssam default: 16227107Smckusick reply(504, "Unimplemented STRU type."); 16310276Ssam } 16410276Ssam } 16510276Ssam | MODE SP mode_code CRLF 16610276Ssam = { 16710276Ssam switch ($3) { 16810276Ssam 16910276Ssam case MODE_S: 17010276Ssam reply(200, "MODE S ok."); 17110276Ssam break; 17210276Ssam 17310276Ssam default: 17410276Ssam reply(502, "Unimplemented MODE type."); 17510276Ssam } 17610276Ssam } 17710276Ssam | ALLO SP NUMBER CRLF 17810276Ssam = { 17927107Smckusick reply(202, "ALLO command ignored."); 18010276Ssam } 18110276Ssam | RETR check_login SP pathname CRLF 18210276Ssam = { 18310302Ssam if ($2 && $4 != NULL) 18426494Sminshall retrieve((char *) 0, (char *) $4); 18510302Ssam if ($4 != NULL) 18626494Sminshall free((char *) $4); 18710276Ssam } 18810276Ssam | STOR check_login SP pathname CRLF 18910276Ssam = { 19010302Ssam if ($2 && $4 != NULL) 19136304Skarels store((char *) $4, "w", 0); 19210302Ssam if ($4 != NULL) 19326494Sminshall free((char *) $4); 19410276Ssam } 19510276Ssam | APPE check_login SP pathname CRLF 19610276Ssam = { 19710302Ssam if ($2 && $4 != NULL) 19836304Skarels store((char *) $4, "a", 0); 19910302Ssam if ($4 != NULL) 20026494Sminshall free((char *) $4); 20110276Ssam } 20210276Ssam | NLST check_login CRLF 20310276Ssam = { 20410276Ssam if ($2) 20511217Ssam retrieve("/bin/ls", ""); 20610276Ssam } 20710276Ssam | NLST check_login SP pathname CRLF 20810276Ssam = { 20910302Ssam if ($2 && $4 != NULL) 21026494Sminshall retrieve("/bin/ls %s", (char *) $4); 21110302Ssam if ($4 != NULL) 21226494Sminshall free((char *) $4); 21310276Ssam } 21410276Ssam | LIST check_login CRLF 21510276Ssam = { 21610276Ssam if ($2) 21710318Ssam retrieve("/bin/ls -lg", ""); 21810276Ssam } 21910276Ssam | LIST check_login SP pathname CRLF 22010276Ssam = { 22110302Ssam if ($2 && $4 != NULL) 22226494Sminshall retrieve("/bin/ls -lg %s", (char *) $4); 22310302Ssam if ($4 != NULL) 22426494Sminshall free((char *) $4); 22510276Ssam } 22610276Ssam | DELE check_login SP pathname CRLF 22710276Ssam = { 22810302Ssam if ($2 && $4 != NULL) 22926494Sminshall delete((char *) $4); 23010302Ssam if ($4 != NULL) 23126494Sminshall free((char *) $4); 23210276Ssam } 23330945Scsvsj | RNTO SP pathname CRLF 23430945Scsvsj = { 23530945Scsvsj if (fromname) { 23630945Scsvsj renamecmd(fromname, (char *) $3); 23730945Scsvsj free(fromname); 23830945Scsvsj fromname = (char *) 0; 23930945Scsvsj } else { 24030945Scsvsj reply(503, "Bad sequence of commands."); 24130945Scsvsj } 24230945Scsvsj free((char *) $3); 24330945Scsvsj } 24426045Sminshall | ABOR CRLF 24526045Sminshall = { 24627107Smckusick reply(225, "ABOR command successful."); 24726045Sminshall } 24810276Ssam | CWD check_login CRLF 24910276Ssam = { 25010276Ssam if ($2) 25110276Ssam cwd(pw->pw_dir); 25210276Ssam } 25310276Ssam | CWD check_login SP pathname CRLF 25410276Ssam = { 25510302Ssam if ($2 && $4 != NULL) 25626494Sminshall cwd((char *) $4); 25710302Ssam if ($4 != NULL) 25826494Sminshall free((char *) $4); 25910276Ssam } 26010276Ssam | HELP CRLF 26110276Ssam = { 26226494Sminshall help((char *) 0); 26310276Ssam } 26410276Ssam | HELP SP STRING CRLF 26510276Ssam = { 26626494Sminshall help((char *) $3); 26710276Ssam } 26810276Ssam | NOOP CRLF 26910276Ssam = { 27027107Smckusick reply(200, "NOOP command successful."); 27110276Ssam } 27210276Ssam | XMKD check_login SP pathname CRLF 27310276Ssam = { 27410302Ssam if ($2 && $4 != NULL) 27526494Sminshall makedir((char *) $4); 27610302Ssam if ($4 != NULL) 27726494Sminshall free((char *) $4); 27810276Ssam } 27910276Ssam | XRMD check_login SP pathname CRLF 28010276Ssam = { 28110302Ssam if ($2 && $4 != NULL) 28226494Sminshall removedir((char *) $4); 28310302Ssam if ($4 != NULL) 28426494Sminshall free((char *) $4); 28510276Ssam } 28610276Ssam | XPWD check_login CRLF 28710276Ssam = { 28810276Ssam if ($2) 28910302Ssam pwd(); 29010276Ssam } 29110276Ssam | XCUP check_login CRLF 29210276Ssam = { 29310276Ssam if ($2) 29410276Ssam cwd(".."); 29510276Ssam } 29626045Sminshall | STOU check_login SP pathname CRLF 29726045Sminshall = { 29836304Skarels if ($2 && $4 != NULL) 29936304Skarels store((char *) $4, "w", 1); 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 31430945Scsvsj rcmd: RNFR check_login SP pathname CRLF 31530945Scsvsj = { 31630945Scsvsj char *renamefrom(); 31730945Scsvsj 31830945Scsvsj if ($2 && $4) { 31930945Scsvsj fromname = renamefrom((char *) $4); 32030945Scsvsj if (fromname == (char *) 0 && $4) { 32130945Scsvsj free((char *) $4); 32230945Scsvsj } 32330945Scsvsj } 32430945Scsvsj } 32530945Scsvsj ; 32630945Scsvsj 32710276Ssam username: STRING 32810276Ssam ; 32910276Ssam 33036304Skarels password: /* empty */ 33136304Skarels = { 33236304Skarels $$ = (int) ""; 33336304Skarels } 33436304Skarels | STRING 33510276Ssam ; 33610276Ssam 33710276Ssam byte_size: NUMBER 33810276Ssam ; 33910276Ssam 34010276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 34110276Ssam NUMBER COMMA NUMBER 34210276Ssam = { 34310276Ssam register char *a, *p; 34410276Ssam 34510276Ssam a = (char *)&data_dest.sin_addr; 34610276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 34710276Ssam p = (char *)&data_dest.sin_port; 34810276Ssam p[0] = $9; p[1] = $11; 34910324Ssam data_dest.sin_family = AF_INET; 35010276Ssam } 35110276Ssam ; 35210276Ssam 35310276Ssam form_code: N 35410276Ssam = { 35510276Ssam $$ = FORM_N; 35610276Ssam } 35710276Ssam | T 35810276Ssam = { 35910276Ssam $$ = FORM_T; 36010276Ssam } 36110276Ssam | C 36210276Ssam = { 36310276Ssam $$ = FORM_C; 36410276Ssam } 36510276Ssam ; 36610276Ssam 36710276Ssam type_code: A 36810276Ssam = { 36910276Ssam cmd_type = TYPE_A; 37010276Ssam cmd_form = FORM_N; 37110276Ssam } 37210276Ssam | A SP form_code 37310276Ssam = { 37410276Ssam cmd_type = TYPE_A; 37510276Ssam cmd_form = $3; 37610276Ssam } 37710276Ssam | E 37810276Ssam = { 37910276Ssam cmd_type = TYPE_E; 38010276Ssam cmd_form = FORM_N; 38110276Ssam } 38210276Ssam | E SP form_code 38310276Ssam = { 38410276Ssam cmd_type = TYPE_E; 38510276Ssam cmd_form = $3; 38610276Ssam } 38710276Ssam | I 38810276Ssam = { 38910276Ssam cmd_type = TYPE_I; 39010276Ssam } 39110276Ssam | L 39210276Ssam = { 39310276Ssam cmd_type = TYPE_L; 39410276Ssam cmd_bytesz = 8; 39510276Ssam } 39610276Ssam | L SP byte_size 39710276Ssam = { 39810276Ssam cmd_type = TYPE_L; 39910276Ssam cmd_bytesz = $3; 40010276Ssam } 40110276Ssam /* this is for a bug in the BBN ftp */ 40210276Ssam | L byte_size 40310276Ssam = { 40410276Ssam cmd_type = TYPE_L; 40510276Ssam cmd_bytesz = $2; 40610276Ssam } 40710276Ssam ; 40810276Ssam 40910276Ssam struct_code: F 41010276Ssam = { 41110276Ssam $$ = STRU_F; 41210276Ssam } 41310276Ssam | R 41410276Ssam = { 41510276Ssam $$ = STRU_R; 41610276Ssam } 41710276Ssam | P 41810276Ssam = { 41910276Ssam $$ = STRU_P; 42010276Ssam } 42110276Ssam ; 42210276Ssam 42310276Ssam mode_code: S 42410276Ssam = { 42510276Ssam $$ = MODE_S; 42610276Ssam } 42710276Ssam | B 42810276Ssam = { 42910276Ssam $$ = MODE_B; 43010276Ssam } 43110276Ssam | C 43210276Ssam = { 43310276Ssam $$ = MODE_C; 43410276Ssam } 43510276Ssam ; 43610276Ssam 43710276Ssam pathname: pathstring 43810276Ssam = { 43927107Smckusick /* 44027107Smckusick * Problem: this production is used for all pathname 44127107Smckusick * processing, but only gives a 550 error reply. 44227107Smckusick * This is a valid reply in some cases but not in others. 44327107Smckusick */ 44436304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 44526494Sminshall $$ = (int)*glob((char *) $1); 44610302Ssam if (globerr != NULL) { 44710276Ssam reply(550, globerr); 44810302Ssam $$ = NULL; 44910302Ssam } 45026494Sminshall free((char *) $1); 45110276Ssam } else 45210276Ssam $$ = $1; 45310276Ssam } 45410276Ssam ; 45510276Ssam 45610276Ssam pathstring: STRING 45710276Ssam ; 45810276Ssam 45910276Ssam check_login: /* empty */ 46010276Ssam = { 46110276Ssam if (logged_in) 46210276Ssam $$ = 1; 46310276Ssam else { 46410276Ssam reply(530, "Please login with USER and PASS."); 46510276Ssam $$ = 0; 46610276Ssam } 46710276Ssam } 46810276Ssam ; 46910276Ssam 47010276Ssam %% 47110276Ssam 47210276Ssam extern jmp_buf errcatch; 47310276Ssam 47410276Ssam #define CMD 0 /* beginning of command */ 47510276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 47610276Ssam #define STR1 2 /* expect SP followed by STRING */ 47710276Ssam #define STR2 3 /* expect STRING */ 47836304Skarels #define OSTR 4 /* optional SP then STRING */ 47936304Skarels #define ZSTR1 5 /* SP then optional STRING */ 48036304Skarels #define ZSTR2 6 /* optional STRING after SP */ 48110276Ssam 48210276Ssam struct tab { 48310276Ssam char *name; 48410276Ssam short token; 48510276Ssam short state; 48610276Ssam short implemented; /* 1 if command is implemented */ 48710276Ssam char *help; 48810276Ssam }; 48910276Ssam 49010276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 49110276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 49236304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 49310276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 49410276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 49510276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 49610276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 49726045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 49810276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 49910276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 50010276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 50110276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 50210276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 50310276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 50410276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 50510276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 50610276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 50710276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 50810276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 50910276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 51010276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 51110276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 51210276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 51310276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 51410276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 51526045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 51610276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 51736304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 51810276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 51910276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 52010276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 52110276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 52210276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 52310276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 52410276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 52526045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 52610276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 52726045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 52810276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 52926045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 53010276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 53126045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 53210276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 53326045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 53410276Ssam { NULL, 0, 0, 0, 0 } 53510276Ssam }; 53610276Ssam 53710276Ssam struct tab * 53810276Ssam lookup(cmd) 53910276Ssam char *cmd; 54010276Ssam { 54110276Ssam register struct tab *p; 54210276Ssam 54310276Ssam for (p = cmdtab; p->name != NULL; p++) 54410276Ssam if (strcmp(cmd, p->name) == 0) 54510276Ssam return (p); 54610276Ssam return (0); 54710276Ssam } 54810276Ssam 54913033Ssam #include <arpa/telnet.h> 55010276Ssam 55110276Ssam /* 55210276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 55310276Ssam */ 55410276Ssam char * 55510276Ssam getline(s, n, iop) 55610276Ssam char *s; 55710276Ssam register FILE *iop; 55810276Ssam { 55910276Ssam register c; 56026494Sminshall register char *cs; 56110276Ssam 56210276Ssam cs = s; 56327751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 56426045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 56526045Sminshall *cs++ = tmpline[c]; 56626045Sminshall if (tmpline[c] == '\n') { 56726045Sminshall *cs++ = '\0'; 56836304Skarels if (debug) 56936304Skarels syslog(LOG_DEBUG, "command: %s", s); 57026045Sminshall tmpline[0] = '\0'; 57126045Sminshall return(s); 57226045Sminshall } 57336304Skarels if (c == 0) 57426045Sminshall tmpline[0] = '\0'; 57526045Sminshall } 57636304Skarels while ((c = getc(iop)) != EOF) { 57736304Skarels c &= 0377; 57836304Skarels if (c == IAC) { 57936304Skarels if ((c = getc(iop)) != EOF) { 58036304Skarels c &= 0377; 58136304Skarels switch (c) { 58227751Sminshall case WILL: 58327751Sminshall case WONT: 58436277Sbostic c = getc(iop); 58536304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 58627751Sminshall (void) fflush(stdout); 58736304Skarels continue; 58827751Sminshall case DO: 58927751Sminshall case DONT: 59036277Sbostic c = getc(iop); 59136304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 59227751Sminshall (void) fflush(stdout); 59336304Skarels continue; 59436304Skarels case IAC: 59527751Sminshall break; 59627751Sminshall default: 59736304Skarels continue; /* ignore command */ 59827751Sminshall } 59936304Skarels } 60010276Ssam } 60136304Skarels *cs++ = c; 60236304Skarels if (--n <= 0 || c == '\n') 60310276Ssam break; 60410276Ssam } 60527751Sminshall if (c == EOF && cs == s) 60618303Sralph return (NULL); 60710276Ssam *cs++ = '\0'; 60836304Skarels if (debug) 60936304Skarels syslog(LOG_DEBUG, "command: %s", s); 61010276Ssam return (s); 61110276Ssam } 61210276Ssam 61311652Ssam static int 61411652Ssam toolong() 61511652Ssam { 61626494Sminshall time_t now; 61711652Ssam extern char *ctime(); 61826494Sminshall extern time_t time(); 61911652Ssam 62011652Ssam reply(421, 62111652Ssam "Timeout (%d seconds): closing control connection.", timeout); 62226494Sminshall (void) time(&now); 62311652Ssam if (logging) { 62426494Sminshall syslog(LOG_INFO, 62536304Skarels "User %s timed out after %d seconds at %s", 62611652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 62711652Ssam } 62813246Ssam dologout(1); 62911652Ssam } 63011652Ssam 63110276Ssam yylex() 63210276Ssam { 63310276Ssam static int cpos, state; 63410276Ssam register char *cp; 63510276Ssam register struct tab *p; 63610276Ssam int n; 63736277Sbostic char c, *strpbrk(); 63810276Ssam 63910276Ssam for (;;) { 64010276Ssam switch (state) { 64110276Ssam 64210276Ssam case CMD: 64326494Sminshall (void) signal(SIGALRM, toolong); 64426494Sminshall (void) alarm((unsigned) timeout); 64510276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 64610276Ssam reply(221, "You could at least say goodbye."); 64713246Ssam dologout(0); 64810276Ssam } 64926494Sminshall (void) alarm(0); 650*36324Sbostic if ((cp = index(cbuf, '\r'))) { 651*36324Sbostic *cp++ = '\n'; 652*36324Sbostic *cp = '\0'; 653*36324Sbostic } 65436277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 65536277Sbostic cpos = cp - cbuf; 65636304Skarels if (cpos == 0) 65710276Ssam cpos = 4; 65810276Ssam c = cbuf[cpos]; 65910276Ssam cbuf[cpos] = '\0'; 66010276Ssam upper(cbuf); 66110276Ssam p = lookup(cbuf); 66210276Ssam cbuf[cpos] = c; 66310276Ssam if (p != 0) { 66410276Ssam if (p->implemented == 0) { 66510276Ssam nack(p->name); 66626494Sminshall longjmp(errcatch,0); 66710276Ssam /* NOTREACHED */ 66810276Ssam } 66910276Ssam state = p->state; 67010276Ssam yylval = (int) p->name; 67110276Ssam return (p->token); 67210276Ssam } 67310276Ssam break; 67410276Ssam 67510276Ssam case OSTR: 67610276Ssam if (cbuf[cpos] == '\n') { 67710276Ssam state = CMD; 67810276Ssam return (CRLF); 67910276Ssam } 68036317Sbostic /* FALLTHROUGH */ 68110276Ssam 68210276Ssam case STR1: 68336304Skarels case ZSTR1: 68410276Ssam if (cbuf[cpos] == ' ') { 68510276Ssam cpos++; 68636317Sbostic state = state == OSTR ? STR2 : ++state; 68710276Ssam return (SP); 68810276Ssam } 68910276Ssam break; 69010276Ssam 69136304Skarels case ZSTR2: 69236304Skarels if (cbuf[cpos] == '\n') { 69336304Skarels state = CMD; 69436304Skarels return (CRLF); 69536304Skarels } 69636304Skarels /* FALL THRU */ 69736304Skarels 69810276Ssam case STR2: 69910276Ssam cp = &cbuf[cpos]; 70010276Ssam n = strlen(cp); 70110276Ssam cpos += n - 1; 70210276Ssam /* 70310276Ssam * Make sure the string is nonempty and \n terminated. 70410276Ssam */ 70510276Ssam if (n > 1 && cbuf[cpos] == '\n') { 70610276Ssam cbuf[cpos] = '\0'; 70710276Ssam yylval = copy(cp); 70810276Ssam cbuf[cpos] = '\n'; 70910276Ssam state = ARGS; 71010276Ssam return (STRING); 71110276Ssam } 71210276Ssam break; 71310276Ssam 71410276Ssam case ARGS: 71510276Ssam if (isdigit(cbuf[cpos])) { 71610276Ssam cp = &cbuf[cpos]; 71710276Ssam while (isdigit(cbuf[++cpos])) 71810276Ssam ; 71910276Ssam c = cbuf[cpos]; 72010276Ssam cbuf[cpos] = '\0'; 72110276Ssam yylval = atoi(cp); 72210276Ssam cbuf[cpos] = c; 72310276Ssam return (NUMBER); 72410276Ssam } 72510276Ssam switch (cbuf[cpos++]) { 72610276Ssam 72710276Ssam case '\n': 72810276Ssam state = CMD; 72910276Ssam return (CRLF); 73010276Ssam 73110276Ssam case ' ': 73210276Ssam return (SP); 73310276Ssam 73410276Ssam case ',': 73510276Ssam return (COMMA); 73610276Ssam 73710276Ssam case 'A': 73810276Ssam case 'a': 73910276Ssam return (A); 74010276Ssam 74110276Ssam case 'B': 74210276Ssam case 'b': 74310276Ssam return (B); 74410276Ssam 74510276Ssam case 'C': 74610276Ssam case 'c': 74710276Ssam return (C); 74810276Ssam 74910276Ssam case 'E': 75010276Ssam case 'e': 75110276Ssam return (E); 75210276Ssam 75310276Ssam case 'F': 75410276Ssam case 'f': 75510276Ssam return (F); 75610276Ssam 75710276Ssam case 'I': 75810276Ssam case 'i': 75910276Ssam return (I); 76010276Ssam 76110276Ssam case 'L': 76210276Ssam case 'l': 76310276Ssam return (L); 76410276Ssam 76510276Ssam case 'N': 76610276Ssam case 'n': 76710276Ssam return (N); 76810276Ssam 76910276Ssam case 'P': 77010276Ssam case 'p': 77110276Ssam return (P); 77210276Ssam 77310276Ssam case 'R': 77410276Ssam case 'r': 77510276Ssam return (R); 77610276Ssam 77710276Ssam case 'S': 77810276Ssam case 's': 77910276Ssam return (S); 78010276Ssam 78110276Ssam case 'T': 78210276Ssam case 't': 78310276Ssam return (T); 78410276Ssam 78510276Ssam } 78610276Ssam break; 78710276Ssam 78810276Ssam default: 78910276Ssam fatal("Unknown state in scanner."); 79010276Ssam } 79126494Sminshall yyerror((char *) 0); 79210276Ssam state = CMD; 79326494Sminshall longjmp(errcatch,0); 79410276Ssam } 79510276Ssam } 79610276Ssam 79710276Ssam upper(s) 79836277Sbostic register char *s; 79910276Ssam { 80010276Ssam while (*s != '\0') { 80110276Ssam if (islower(*s)) 80210276Ssam *s = toupper(*s); 80310276Ssam s++; 80410276Ssam } 80510276Ssam } 80610276Ssam 80710276Ssam copy(s) 80810276Ssam char *s; 80910276Ssam { 81010276Ssam char *p; 81126494Sminshall extern char *malloc(), *strcpy(); 81210276Ssam 81326494Sminshall p = malloc((unsigned) strlen(s) + 1); 81410276Ssam if (p == NULL) 81510276Ssam fatal("Ran out of memory."); 81626494Sminshall (void) strcpy(p, s); 81710276Ssam return ((int)p); 81810276Ssam } 81910276Ssam 82010276Ssam help(s) 82110276Ssam char *s; 82210276Ssam { 82310276Ssam register struct tab *c; 82410276Ssam register int width, NCMDS; 82510276Ssam 82610276Ssam width = 0, NCMDS = 0; 82710276Ssam for (c = cmdtab; c->name != NULL; c++) { 82831132Smckusick int len = strlen(c->name) + 1; 82910276Ssam 83010276Ssam if (len > width) 83110276Ssam width = len; 83210276Ssam NCMDS++; 83310276Ssam } 83410276Ssam width = (width + 8) &~ 7; 83510276Ssam if (s == 0) { 83610276Ssam register int i, j, w; 83710276Ssam int columns, lines; 83810276Ssam 83910276Ssam lreply(214, 84010276Ssam "The following commands are recognized (* =>'s unimplemented)."); 84110276Ssam columns = 76 / width; 84210276Ssam if (columns == 0) 84310276Ssam columns = 1; 84410276Ssam lines = (NCMDS + columns - 1) / columns; 84510276Ssam for (i = 0; i < lines; i++) { 84627107Smckusick printf(" "); 84710276Ssam for (j = 0; j < columns; j++) { 84810276Ssam c = cmdtab + j * lines + i; 84910276Ssam printf("%s%c", c->name, 85010276Ssam c->implemented ? ' ' : '*'); 85110302Ssam if (c + lines >= &cmdtab[NCMDS]) 85210276Ssam break; 85331132Smckusick w = strlen(c->name) + 1; 85410276Ssam while (w < width) { 85510276Ssam putchar(' '); 85610276Ssam w++; 85710276Ssam } 85810276Ssam } 85910276Ssam printf("\r\n"); 86010276Ssam } 86126494Sminshall (void) fflush(stdout); 86210276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 86310276Ssam return; 86410276Ssam } 86510276Ssam upper(s); 86610276Ssam c = lookup(s); 86710276Ssam if (c == (struct tab *)0) { 86827107Smckusick reply(502, "Unknown command %s.", s); 86910276Ssam return; 87010276Ssam } 87110276Ssam if (c->implemented) 87210276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 87310276Ssam else 87410276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 87510276Ssam } 876