110276Ssam /* 2*36304Skarels * 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*36304Skarels * @(#)ftpcmd.y 5.14 (Berkeley) 12/07/88 1822501Sdist */ 1922501Sdist 2022501Sdist /* 2110276Ssam * Grammar for FTP commands. 2210276Ssam * See RFC 765. 2310276Ssam */ 2410276Ssam 2510276Ssam %{ 2610276Ssam 2710276Ssam #ifndef lint 28*36304Skarels static char sccsid[] = "@(#)ftpcmd.y 5.14 (Berkeley) 12/07/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; 65*36304Skarels char cbuf[512]; 66*36304Skarels 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 = { 101*36304Skarels 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; 112*36304Skarels if (pdata >= 0) { 11326045Sminshall (void) close(pdata); 114*36304Skarels 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) 191*36304Skarels 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) 198*36304Skarels 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 = { 298*36304Skarels if ($2 && $4 != NULL) 299*36304Skarels 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 330*36304Skarels password: /* empty */ 331*36304Skarels = { 332*36304Skarels $$ = (int) ""; 333*36304Skarels } 334*36304Skarels | 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 */ 444*36304Skarels 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 */ 478*36304Skarels #define OSTR 4 /* optional SP then STRING */ 479*36304Skarels #define ZSTR1 5 /* SP then optional STRING */ 480*36304Skarels #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" }, 492*36304Skarels { "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" }, 517*36304Skarels { "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'; 568*36304Skarels if (debug) 569*36304Skarels syslog(LOG_DEBUG, "command: %s", s); 57026045Sminshall tmpline[0] = '\0'; 57126045Sminshall return(s); 57226045Sminshall } 573*36304Skarels if (c == 0) 57426045Sminshall tmpline[0] = '\0'; 57526045Sminshall } 576*36304Skarels while ((c = getc(iop)) != EOF) { 577*36304Skarels c &= 0377; 578*36304Skarels if (c == IAC) { 579*36304Skarels if ((c = getc(iop)) != EOF) { 580*36304Skarels c &= 0377; 581*36304Skarels switch (c) { 58227751Sminshall case WILL: 58327751Sminshall case WONT: 58436277Sbostic c = getc(iop); 585*36304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 58627751Sminshall (void) fflush(stdout); 587*36304Skarels continue; 58827751Sminshall case DO: 58927751Sminshall case DONT: 59036277Sbostic c = getc(iop); 591*36304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 59227751Sminshall (void) fflush(stdout); 593*36304Skarels continue; 594*36304Skarels case IAC: 59527751Sminshall break; 59627751Sminshall default: 597*36304Skarels continue; /* ignore command */ 59827751Sminshall } 599*36304Skarels } 60010276Ssam } 601*36304Skarels *cs++ = c; 602*36304Skarels if (--n <= 0 || c == '\n') 60310276Ssam break; 60410276Ssam } 60527751Sminshall if (c == EOF && cs == s) 60618303Sralph return (NULL); 60710276Ssam *cs++ = '\0'; 608*36304Skarels if (debug) 609*36304Skarels 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, 625*36304Skarels "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*36304Skarels if ((cp = index(cbuf, '\r'))) 65136277Sbostic *cp++ = '\n'; *cp = '\0'; 65236277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 65336277Sbostic cpos = cp - cbuf; 654*36304Skarels if (cpos == 0) 65510276Ssam cpos = 4; 65610276Ssam c = cbuf[cpos]; 65710276Ssam cbuf[cpos] = '\0'; 65810276Ssam upper(cbuf); 65910276Ssam p = lookup(cbuf); 66010276Ssam cbuf[cpos] = c; 66110276Ssam if (p != 0) { 66210276Ssam if (p->implemented == 0) { 66310276Ssam nack(p->name); 66426494Sminshall longjmp(errcatch,0); 66510276Ssam /* NOTREACHED */ 66610276Ssam } 66710276Ssam state = p->state; 66810276Ssam yylval = (int) p->name; 66910276Ssam return (p->token); 67010276Ssam } 67110276Ssam break; 67210276Ssam 67310276Ssam case OSTR: 67410276Ssam if (cbuf[cpos] == '\n') { 67510276Ssam state = CMD; 67610276Ssam return (CRLF); 67710276Ssam } 67810276Ssam /* FALL THRU */ 67910276Ssam 68010276Ssam case STR1: 681*36304Skarels case ZSTR1: 68210276Ssam if (cbuf[cpos] == ' ') { 68310276Ssam cpos++; 684*36304Skarels state++; 68510276Ssam return (SP); 68610276Ssam } 68710276Ssam break; 68810276Ssam 689*36304Skarels case ZSTR2: 690*36304Skarels if (cbuf[cpos] == '\n') { 691*36304Skarels state = CMD; 692*36304Skarels return (CRLF); 693*36304Skarels } 694*36304Skarels /* FALL THRU */ 695*36304Skarels 69610276Ssam case STR2: 69710276Ssam cp = &cbuf[cpos]; 69810276Ssam n = strlen(cp); 69910276Ssam cpos += n - 1; 70010276Ssam /* 70110276Ssam * Make sure the string is nonempty and \n terminated. 70210276Ssam */ 70310276Ssam if (n > 1 && cbuf[cpos] == '\n') { 70410276Ssam cbuf[cpos] = '\0'; 70510276Ssam yylval = copy(cp); 70610276Ssam cbuf[cpos] = '\n'; 70710276Ssam state = ARGS; 70810276Ssam return (STRING); 70910276Ssam } 71010276Ssam break; 71110276Ssam 71210276Ssam case ARGS: 71310276Ssam if (isdigit(cbuf[cpos])) { 71410276Ssam cp = &cbuf[cpos]; 71510276Ssam while (isdigit(cbuf[++cpos])) 71610276Ssam ; 71710276Ssam c = cbuf[cpos]; 71810276Ssam cbuf[cpos] = '\0'; 71910276Ssam yylval = atoi(cp); 72010276Ssam cbuf[cpos] = c; 72110276Ssam return (NUMBER); 72210276Ssam } 72310276Ssam switch (cbuf[cpos++]) { 72410276Ssam 72510276Ssam case '\n': 72610276Ssam state = CMD; 72710276Ssam return (CRLF); 72810276Ssam 72910276Ssam case ' ': 73010276Ssam return (SP); 73110276Ssam 73210276Ssam case ',': 73310276Ssam return (COMMA); 73410276Ssam 73510276Ssam case 'A': 73610276Ssam case 'a': 73710276Ssam return (A); 73810276Ssam 73910276Ssam case 'B': 74010276Ssam case 'b': 74110276Ssam return (B); 74210276Ssam 74310276Ssam case 'C': 74410276Ssam case 'c': 74510276Ssam return (C); 74610276Ssam 74710276Ssam case 'E': 74810276Ssam case 'e': 74910276Ssam return (E); 75010276Ssam 75110276Ssam case 'F': 75210276Ssam case 'f': 75310276Ssam return (F); 75410276Ssam 75510276Ssam case 'I': 75610276Ssam case 'i': 75710276Ssam return (I); 75810276Ssam 75910276Ssam case 'L': 76010276Ssam case 'l': 76110276Ssam return (L); 76210276Ssam 76310276Ssam case 'N': 76410276Ssam case 'n': 76510276Ssam return (N); 76610276Ssam 76710276Ssam case 'P': 76810276Ssam case 'p': 76910276Ssam return (P); 77010276Ssam 77110276Ssam case 'R': 77210276Ssam case 'r': 77310276Ssam return (R); 77410276Ssam 77510276Ssam case 'S': 77610276Ssam case 's': 77710276Ssam return (S); 77810276Ssam 77910276Ssam case 'T': 78010276Ssam case 't': 78110276Ssam return (T); 78210276Ssam 78310276Ssam } 78410276Ssam break; 78510276Ssam 78610276Ssam default: 78710276Ssam fatal("Unknown state in scanner."); 78810276Ssam } 78926494Sminshall yyerror((char *) 0); 79010276Ssam state = CMD; 79126494Sminshall longjmp(errcatch,0); 79210276Ssam } 79310276Ssam } 79410276Ssam 79510276Ssam upper(s) 79636277Sbostic register char *s; 79710276Ssam { 79810276Ssam while (*s != '\0') { 79910276Ssam if (islower(*s)) 80010276Ssam *s = toupper(*s); 80110276Ssam s++; 80210276Ssam } 80310276Ssam } 80410276Ssam 80510276Ssam copy(s) 80610276Ssam char *s; 80710276Ssam { 80810276Ssam char *p; 80926494Sminshall extern char *malloc(), *strcpy(); 81010276Ssam 81126494Sminshall p = malloc((unsigned) strlen(s) + 1); 81210276Ssam if (p == NULL) 81310276Ssam fatal("Ran out of memory."); 81426494Sminshall (void) strcpy(p, s); 81510276Ssam return ((int)p); 81610276Ssam } 81710276Ssam 81810276Ssam help(s) 81910276Ssam char *s; 82010276Ssam { 82110276Ssam register struct tab *c; 82210276Ssam register int width, NCMDS; 82310276Ssam 82410276Ssam width = 0, NCMDS = 0; 82510276Ssam for (c = cmdtab; c->name != NULL; c++) { 82631132Smckusick int len = strlen(c->name) + 1; 82710276Ssam 82810276Ssam if (len > width) 82910276Ssam width = len; 83010276Ssam NCMDS++; 83110276Ssam } 83210276Ssam width = (width + 8) &~ 7; 83310276Ssam if (s == 0) { 83410276Ssam register int i, j, w; 83510276Ssam int columns, lines; 83610276Ssam 83710276Ssam lreply(214, 83810276Ssam "The following commands are recognized (* =>'s unimplemented)."); 83910276Ssam columns = 76 / width; 84010276Ssam if (columns == 0) 84110276Ssam columns = 1; 84210276Ssam lines = (NCMDS + columns - 1) / columns; 84310276Ssam for (i = 0; i < lines; i++) { 84427107Smckusick printf(" "); 84510276Ssam for (j = 0; j < columns; j++) { 84610276Ssam c = cmdtab + j * lines + i; 84710276Ssam printf("%s%c", c->name, 84810276Ssam c->implemented ? ' ' : '*'); 84910302Ssam if (c + lines >= &cmdtab[NCMDS]) 85010276Ssam break; 85131132Smckusick w = strlen(c->name) + 1; 85210276Ssam while (w < width) { 85310276Ssam putchar(' '); 85410276Ssam w++; 85510276Ssam } 85610276Ssam } 85710276Ssam printf("\r\n"); 85810276Ssam } 85926494Sminshall (void) fflush(stdout); 86010276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 86110276Ssam return; 86210276Ssam } 86310276Ssam upper(s); 86410276Ssam c = lookup(s); 86510276Ssam if (c == (struct tab *)0) { 86627107Smckusick reply(502, "Unknown command %s.", s); 86710276Ssam return; 86810276Ssam } 86910276Ssam if (c->implemented) 87010276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 87110276Ssam else 87210276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 87310276Ssam } 874