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*36640Srick * @(#)ftpcmd.y 5.19 (Berkeley) 01/26/89 1822501Sdist */ 1922501Sdist 2022501Sdist /* 2110276Ssam * Grammar for FTP commands. 2210276Ssam * See RFC 765. 2310276Ssam */ 2410276Ssam 2510276Ssam %{ 2610276Ssam 2710276Ssam #ifndef lint 28*36640Srick static char sccsid[] = "@(#)ftpcmd.y 5.19 (Berkeley) 01/26/89"; 2933738Sbostic #endif /* not lint */ 3010276Ssam 3136552Sbostic #include <sys/param.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; 5536620Srick extern char hostname[], remotehost[]; 5610276Ssam extern char *globerr; 5710320Ssam extern int usedefault; 5826045Sminshall extern int transflag; 5926045Sminshall extern char tmpline[]; 6010276Ssam char **glob(); 6110276Ssam 6236552Sbostic off_t restart_point; 6336552Sbostic 6410276Ssam static int cmd_type; 6510276Ssam static int cmd_form; 6610276Ssam static int cmd_bytesz; 6736304Skarels char cbuf[512]; 6836304Skarels char *fromname; 6910276Ssam 7010276Ssam char *index(); 7110276Ssam %} 7210276Ssam 7310276Ssam %token 7410276Ssam A B C E F I 7510276Ssam L N P R S T 7610276Ssam 7710276Ssam SP CRLF COMMA STRING NUMBER 7810276Ssam 7910276Ssam USER PASS ACCT REIN QUIT PORT 8010276Ssam PASV TYPE STRU MODE RETR STOR 8110276Ssam APPE MLFL MAIL MSND MSOM MSAM 8210276Ssam MRSQ MRCP ALLO REST RNFR RNTO 8310276Ssam ABOR DELE CWD LIST NLST SITE 8436620Srick STAT HELP NOOP MKD RMD PWD 8536620Srick CDUP STOU SYST 8610276Ssam 8710276Ssam LEXERR 8810276Ssam 8910276Ssam %start cmd_list 9010276Ssam 9110276Ssam %% 9210276Ssam 9310276Ssam cmd_list: /* empty */ 9410276Ssam | cmd_list cmd 9530945Scsvsj = { 9630945Scsvsj fromname = (char *) 0; 9736552Sbostic restart_point = (off_t) 0; 9830945Scsvsj } 9930945Scsvsj | cmd_list rcmd 10010276Ssam ; 10110276Ssam 10210276Ssam cmd: USER SP username CRLF 10310276Ssam = { 10436304Skarels user((char *) $3); 10526494Sminshall free((char *) $3); 10610276Ssam } 10710276Ssam | PASS SP password CRLF 10810276Ssam = { 10926494Sminshall pass((char *) $3); 11026494Sminshall free((char *) $3); 11110276Ssam } 11210276Ssam | PORT SP host_port CRLF 11310276Ssam = { 11410320Ssam usedefault = 0; 11536304Skarels if (pdata >= 0) { 11626045Sminshall (void) close(pdata); 11736304Skarels pdata = -1; 11826045Sminshall } 11927107Smckusick reply(200, "PORT command successful."); 12010276Ssam } 12126045Sminshall | PASV CRLF 12226045Sminshall = { 12326045Sminshall passive(); 12426045Sminshall } 12510276Ssam | TYPE SP type_code CRLF 12610276Ssam = { 12710276Ssam switch (cmd_type) { 12810276Ssam 12910276Ssam case TYPE_A: 13010276Ssam if (cmd_form == FORM_N) { 13110276Ssam reply(200, "Type set to A."); 13210276Ssam type = cmd_type; 13310276Ssam form = cmd_form; 13410276Ssam } else 13510276Ssam reply(504, "Form must be N."); 13610276Ssam break; 13710276Ssam 13810276Ssam case TYPE_E: 13910276Ssam reply(504, "Type E not implemented."); 14010276Ssam break; 14110276Ssam 14210276Ssam case TYPE_I: 14310276Ssam reply(200, "Type set to I."); 14410276Ssam type = cmd_type; 14510276Ssam break; 14610276Ssam 14710276Ssam case TYPE_L: 14836552Sbostic if (cmd_bytesz == NBBY) { 14910276Ssam reply(200, 15036552Sbostic "Type set to L (byte size %d).", 15136552Sbostic NBBY); 15210276Ssam type = cmd_type; 15310276Ssam } else 15436552Sbostic reply(504, "Byte size must be %d.", 15536552Sbostic NBBY); 15610276Ssam } 15710276Ssam } 15810276Ssam | STRU SP struct_code CRLF 15910276Ssam = { 16010276Ssam switch ($3) { 16110276Ssam 16210276Ssam case STRU_F: 16310276Ssam reply(200, "STRU F ok."); 16410276Ssam break; 16510276Ssam 16610276Ssam default: 16727107Smckusick reply(504, "Unimplemented STRU type."); 16810276Ssam } 16910276Ssam } 17010276Ssam | MODE SP mode_code CRLF 17110276Ssam = { 17210276Ssam switch ($3) { 17310276Ssam 17410276Ssam case MODE_S: 17510276Ssam reply(200, "MODE S ok."); 17610276Ssam break; 17710276Ssam 17810276Ssam default: 17910276Ssam reply(502, "Unimplemented MODE type."); 18010276Ssam } 18110276Ssam } 18210276Ssam | ALLO SP NUMBER CRLF 18310276Ssam = { 18427107Smckusick reply(202, "ALLO command ignored."); 18510276Ssam } 18610276Ssam | RETR check_login SP pathname CRLF 18710276Ssam = { 18810302Ssam if ($2 && $4 != NULL) 18936620Srick retrieve((char *) 0, (char *)$4); 19010302Ssam if ($4 != NULL) 19126494Sminshall free((char *) $4); 19210276Ssam } 19310276Ssam | STOR check_login SP pathname CRLF 19410276Ssam = { 19510302Ssam if ($2 && $4 != NULL) 19636304Skarels store((char *) $4, "w", 0); 19710302Ssam if ($4 != NULL) 19826494Sminshall free((char *) $4); 19910276Ssam } 20010276Ssam | APPE check_login SP pathname CRLF 20110276Ssam = { 20210302Ssam if ($2 && $4 != NULL) 20336304Skarels store((char *) $4, "a", 0); 20410302Ssam if ($4 != NULL) 20526494Sminshall free((char *) $4); 20610276Ssam } 20710276Ssam | NLST check_login CRLF 20810276Ssam = { 20910276Ssam if ($2) 21036620Srick send_file_list("."); 21110276Ssam } 21236620Srick | NLST check_login SP STRING CRLF 21310276Ssam = { 21436620Srick if ($2 && $4 != NULL) 21536620Srick send_file_list((char *) $4); 21610302Ssam if ($4 != NULL) 21726494Sminshall free((char *) $4); 21810276Ssam } 21910276Ssam | LIST check_login CRLF 22010276Ssam = { 22110276Ssam if ($2) 22236620Srick retrieve("/bin/ls -lgA", ""); 22310276Ssam } 22410276Ssam | LIST check_login SP pathname CRLF 22510276Ssam = { 22610302Ssam if ($2 && $4 != NULL) 22736620Srick retrieve("/bin/ls -lgA %s", (char *) $4); 22810302Ssam if ($4 != NULL) 22926494Sminshall free((char *) $4); 23010276Ssam } 23110276Ssam | DELE check_login SP pathname CRLF 23210276Ssam = { 23310302Ssam if ($2 && $4 != NULL) 23426494Sminshall delete((char *) $4); 23510302Ssam if ($4 != NULL) 23626494Sminshall free((char *) $4); 23710276Ssam } 23830945Scsvsj | RNTO SP pathname CRLF 23930945Scsvsj = { 24030945Scsvsj if (fromname) { 24130945Scsvsj renamecmd(fromname, (char *) $3); 24230945Scsvsj free(fromname); 24330945Scsvsj fromname = (char *) 0; 24430945Scsvsj } else { 24530945Scsvsj reply(503, "Bad sequence of commands."); 24630945Scsvsj } 24730945Scsvsj free((char *) $3); 24830945Scsvsj } 24926045Sminshall | ABOR CRLF 25026045Sminshall = { 25127107Smckusick reply(225, "ABOR command successful."); 25226045Sminshall } 25310276Ssam | CWD check_login CRLF 25410276Ssam = { 25510276Ssam if ($2) 25610276Ssam cwd(pw->pw_dir); 25710276Ssam } 25810276Ssam | CWD check_login SP pathname CRLF 25910276Ssam = { 26010302Ssam if ($2 && $4 != NULL) 26126494Sminshall cwd((char *) $4); 26210302Ssam if ($4 != NULL) 26326494Sminshall free((char *) $4); 26410276Ssam } 26510276Ssam | HELP CRLF 26610276Ssam = { 26726494Sminshall help((char *) 0); 26810276Ssam } 26910276Ssam | HELP SP STRING CRLF 27010276Ssam = { 27126494Sminshall help((char *) $3); 27210276Ssam } 27310276Ssam | NOOP CRLF 27410276Ssam = { 27527107Smckusick reply(200, "NOOP command successful."); 27610276Ssam } 27736620Srick | MKD check_login SP pathname CRLF 27810276Ssam = { 27910302Ssam if ($2 && $4 != NULL) 28026494Sminshall makedir((char *) $4); 28110302Ssam if ($4 != NULL) 28226494Sminshall free((char *) $4); 28310276Ssam } 28436620Srick | RMD check_login SP pathname CRLF 28510276Ssam = { 28610302Ssam if ($2 && $4 != NULL) 28726494Sminshall removedir((char *) $4); 28810302Ssam if ($4 != NULL) 28926494Sminshall free((char *) $4); 29010276Ssam } 29136620Srick | PWD check_login CRLF 29210276Ssam = { 29310276Ssam if ($2) 29410302Ssam pwd(); 29510276Ssam } 29636620Srick | CDUP check_login CRLF 29710276Ssam = { 29810276Ssam if ($2) 29910276Ssam cwd(".."); 30010276Ssam } 30126045Sminshall | STOU check_login SP pathname CRLF 30226045Sminshall = { 30336304Skarels if ($2 && $4 != NULL) 30436304Skarels store((char *) $4, "w", 1); 30526045Sminshall if ($4 != NULL) 30626494Sminshall free((char *) $4); 30726045Sminshall } 30836552Sbostic | SYST CRLF 30936552Sbostic = { 310*36640Srick #ifdef unix 31136552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 312*36640Srick #else 313*36640Srick reply(215, "UNKNOWN Type: L%d Version: BSD-%d", 314*36640Srick #endif 31536552Sbostic NBBY, BSD); 31636552Sbostic } 31710276Ssam | QUIT CRLF 31810276Ssam = { 31910276Ssam reply(221, "Goodbye."); 32013246Ssam dologout(0); 32110276Ssam } 32210276Ssam | error CRLF 32310276Ssam = { 32410276Ssam yyerrok; 32510276Ssam } 32610276Ssam ; 32730945Scsvsj rcmd: RNFR check_login SP pathname CRLF 32830945Scsvsj = { 32930945Scsvsj char *renamefrom(); 33030945Scsvsj 33136552Sbostic restart_point = (off_t) 0; 33230945Scsvsj if ($2 && $4) { 33330945Scsvsj fromname = renamefrom((char *) $4); 33430945Scsvsj if (fromname == (char *) 0 && $4) { 33530945Scsvsj free((char *) $4); 33630945Scsvsj } 33730945Scsvsj } 33830945Scsvsj } 33936552Sbostic | REST SP byte_size CRLF 34036552Sbostic = { 34136552Sbostic long atol(); 34236552Sbostic 34336552Sbostic fromname = (char *) 0; 34436552Sbostic restart_point = $3; 34536552Sbostic reply(350, "Restarting at %ld. Send STORE or RETRIEVE to initiate transfer.", restart_point); 34636552Sbostic } 34730945Scsvsj ; 34830945Scsvsj 34910276Ssam username: STRING 35010276Ssam ; 35110276Ssam 35236304Skarels password: /* empty */ 35336304Skarels = { 35436304Skarels $$ = (int) ""; 35536304Skarels } 35636304Skarels | STRING 35710276Ssam ; 35810276Ssam 35910276Ssam byte_size: NUMBER 36010276Ssam ; 36110276Ssam 36210276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 36310276Ssam NUMBER COMMA NUMBER 36410276Ssam = { 36510276Ssam register char *a, *p; 36610276Ssam 36710276Ssam a = (char *)&data_dest.sin_addr; 36810276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 36910276Ssam p = (char *)&data_dest.sin_port; 37010276Ssam p[0] = $9; p[1] = $11; 37110324Ssam data_dest.sin_family = AF_INET; 37210276Ssam } 37310276Ssam ; 37410276Ssam 37510276Ssam form_code: N 37610276Ssam = { 37710276Ssam $$ = FORM_N; 37810276Ssam } 37910276Ssam | T 38010276Ssam = { 38110276Ssam $$ = FORM_T; 38210276Ssam } 38310276Ssam | C 38410276Ssam = { 38510276Ssam $$ = FORM_C; 38610276Ssam } 38710276Ssam ; 38810276Ssam 38910276Ssam type_code: A 39010276Ssam = { 39110276Ssam cmd_type = TYPE_A; 39210276Ssam cmd_form = FORM_N; 39310276Ssam } 39410276Ssam | A SP form_code 39510276Ssam = { 39610276Ssam cmd_type = TYPE_A; 39710276Ssam cmd_form = $3; 39810276Ssam } 39910276Ssam | E 40010276Ssam = { 40110276Ssam cmd_type = TYPE_E; 40210276Ssam cmd_form = FORM_N; 40310276Ssam } 40410276Ssam | E SP form_code 40510276Ssam = { 40610276Ssam cmd_type = TYPE_E; 40710276Ssam cmd_form = $3; 40810276Ssam } 40910276Ssam | I 41010276Ssam = { 41110276Ssam cmd_type = TYPE_I; 41210276Ssam } 41310276Ssam | L 41410276Ssam = { 41510276Ssam cmd_type = TYPE_L; 41636552Sbostic cmd_bytesz = NBBY; 41710276Ssam } 41810276Ssam | L SP byte_size 41910276Ssam = { 42010276Ssam cmd_type = TYPE_L; 42110276Ssam cmd_bytesz = $3; 42210276Ssam } 42310276Ssam /* this is for a bug in the BBN ftp */ 42410276Ssam | L byte_size 42510276Ssam = { 42610276Ssam cmd_type = TYPE_L; 42710276Ssam cmd_bytesz = $2; 42810276Ssam } 42910276Ssam ; 43010276Ssam 43110276Ssam struct_code: F 43210276Ssam = { 43310276Ssam $$ = STRU_F; 43410276Ssam } 43510276Ssam | R 43610276Ssam = { 43710276Ssam $$ = STRU_R; 43810276Ssam } 43910276Ssam | P 44010276Ssam = { 44110276Ssam $$ = STRU_P; 44210276Ssam } 44310276Ssam ; 44410276Ssam 44510276Ssam mode_code: S 44610276Ssam = { 44710276Ssam $$ = MODE_S; 44810276Ssam } 44910276Ssam | B 45010276Ssam = { 45110276Ssam $$ = MODE_B; 45210276Ssam } 45310276Ssam | C 45410276Ssam = { 45510276Ssam $$ = MODE_C; 45610276Ssam } 45710276Ssam ; 45810276Ssam 45910276Ssam pathname: pathstring 46010276Ssam = { 46127107Smckusick /* 46227107Smckusick * Problem: this production is used for all pathname 46327107Smckusick * processing, but only gives a 550 error reply. 46427107Smckusick * This is a valid reply in some cases but not in others. 46527107Smckusick */ 46636304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 46726494Sminshall $$ = (int)*glob((char *) $1); 46810302Ssam if (globerr != NULL) { 46910276Ssam reply(550, globerr); 47010302Ssam $$ = NULL; 47110302Ssam } 47226494Sminshall free((char *) $1); 47310276Ssam } else 47410276Ssam $$ = $1; 47510276Ssam } 47610276Ssam ; 47710276Ssam 47810276Ssam pathstring: STRING 47910276Ssam ; 48010276Ssam 48110276Ssam check_login: /* empty */ 48210276Ssam = { 48310276Ssam if (logged_in) 48410276Ssam $$ = 1; 48510276Ssam else { 48610276Ssam reply(530, "Please login with USER and PASS."); 48710276Ssam $$ = 0; 48810276Ssam } 48910276Ssam } 49010276Ssam ; 49110276Ssam 49210276Ssam %% 49310276Ssam 49410276Ssam extern jmp_buf errcatch; 49510276Ssam 49610276Ssam #define CMD 0 /* beginning of command */ 49710276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 49810276Ssam #define STR1 2 /* expect SP followed by STRING */ 49910276Ssam #define STR2 3 /* expect STRING */ 50036304Skarels #define OSTR 4 /* optional SP then STRING */ 50136304Skarels #define ZSTR1 5 /* SP then optional STRING */ 50236304Skarels #define ZSTR2 6 /* optional STRING after SP */ 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" }, 51436304Skarels { "PASS", PASS, ZSTR1, 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)" }, 53436552Sbostic { "REST", REST, ARGS, 1, "(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" }, 53936304Skarels { "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)" }, 54436552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 54510276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 54610276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 54710276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 54836620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 54936620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 55036620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 55136620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 55236620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 55336620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 55436620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 55536620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 55626045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 55710276Ssam { NULL, 0, 0, 0, 0 } 55810276Ssam }; 55910276Ssam 56010276Ssam struct tab * 56110276Ssam lookup(cmd) 56210276Ssam char *cmd; 56310276Ssam { 56410276Ssam register struct tab *p; 56510276Ssam 56610276Ssam for (p = cmdtab; p->name != NULL; p++) 56710276Ssam if (strcmp(cmd, p->name) == 0) 56810276Ssam return (p); 56910276Ssam return (0); 57010276Ssam } 57110276Ssam 57213033Ssam #include <arpa/telnet.h> 57310276Ssam 57410276Ssam /* 57510276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 57610276Ssam */ 57710276Ssam char * 57810276Ssam getline(s, n, iop) 57910276Ssam char *s; 58010276Ssam register FILE *iop; 58110276Ssam { 58210276Ssam register c; 58326494Sminshall register char *cs; 58410276Ssam 58510276Ssam cs = s; 58627751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 58726045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 58826045Sminshall *cs++ = tmpline[c]; 58926045Sminshall if (tmpline[c] == '\n') { 59026045Sminshall *cs++ = '\0'; 59136304Skarels if (debug) 59236304Skarels syslog(LOG_DEBUG, "command: %s", s); 59326045Sminshall tmpline[0] = '\0'; 59426045Sminshall return(s); 59526045Sminshall } 59636304Skarels if (c == 0) 59726045Sminshall tmpline[0] = '\0'; 59826045Sminshall } 59936304Skarels while ((c = getc(iop)) != EOF) { 60036304Skarels c &= 0377; 60136304Skarels if (c == IAC) { 60236304Skarels if ((c = getc(iop)) != EOF) { 60336304Skarels c &= 0377; 60436304Skarels switch (c) { 60527751Sminshall case WILL: 60627751Sminshall case WONT: 60736277Sbostic c = getc(iop); 60836304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 60927751Sminshall (void) fflush(stdout); 61036304Skarels continue; 61127751Sminshall case DO: 61227751Sminshall case DONT: 61336277Sbostic c = getc(iop); 61436304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 61527751Sminshall (void) fflush(stdout); 61636304Skarels continue; 61736304Skarels case IAC: 61827751Sminshall break; 61927751Sminshall default: 62036304Skarels continue; /* ignore command */ 62127751Sminshall } 62236304Skarels } 62310276Ssam } 62436304Skarels *cs++ = c; 62536304Skarels if (--n <= 0 || c == '\n') 62610276Ssam break; 62710276Ssam } 62827751Sminshall if (c == EOF && cs == s) 62918303Sralph return (NULL); 63010276Ssam *cs++ = '\0'; 63136304Skarels if (debug) 63236304Skarels syslog(LOG_DEBUG, "command: %s", s); 63310276Ssam return (s); 63410276Ssam } 63510276Ssam 63611652Ssam static int 63711652Ssam toolong() 63811652Ssam { 63926494Sminshall time_t now; 64011652Ssam extern char *ctime(); 64126494Sminshall extern time_t time(); 64211652Ssam 64311652Ssam reply(421, 64411652Ssam "Timeout (%d seconds): closing control connection.", timeout); 64526494Sminshall (void) time(&now); 64611652Ssam if (logging) { 64726494Sminshall syslog(LOG_INFO, 64836304Skarels "User %s timed out after %d seconds at %s", 64911652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 65011652Ssam } 65113246Ssam dologout(1); 65211652Ssam } 65311652Ssam 65410276Ssam yylex() 65510276Ssam { 65610276Ssam static int cpos, state; 65710276Ssam register char *cp; 65810276Ssam register struct tab *p; 65910276Ssam int n; 66036277Sbostic char c, *strpbrk(); 66110276Ssam 66210276Ssam for (;;) { 66310276Ssam switch (state) { 66410276Ssam 66510276Ssam case CMD: 66626494Sminshall (void) signal(SIGALRM, toolong); 66726494Sminshall (void) alarm((unsigned) timeout); 66810276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 66910276Ssam reply(221, "You could at least say goodbye."); 67013246Ssam dologout(0); 67110276Ssam } 67226494Sminshall (void) alarm(0); 67336620Srick #ifdef SETPROCTITLE 67436620Srick if (strncasecmp(cbuf, "PASS", 4) != NULL) { 67536620Srick setproctitle("%s: %s", remotehost, cbuf); 67636620Srick } 67736620Srick #endif /* SETPROCTITLE */ 67836324Sbostic if ((cp = index(cbuf, '\r'))) { 67936324Sbostic *cp++ = '\n'; 68036324Sbostic *cp = '\0'; 68136324Sbostic } 68236277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 68336277Sbostic cpos = cp - cbuf; 68436304Skarels if (cpos == 0) 68510276Ssam cpos = 4; 68610276Ssam c = cbuf[cpos]; 68710276Ssam cbuf[cpos] = '\0'; 68810276Ssam upper(cbuf); 68910276Ssam p = lookup(cbuf); 69010276Ssam cbuf[cpos] = c; 69110276Ssam if (p != 0) { 69210276Ssam if (p->implemented == 0) { 69310276Ssam nack(p->name); 69426494Sminshall longjmp(errcatch,0); 69510276Ssam /* NOTREACHED */ 69610276Ssam } 69710276Ssam state = p->state; 69810276Ssam yylval = (int) p->name; 69910276Ssam return (p->token); 70010276Ssam } 70110276Ssam break; 70210276Ssam 70310276Ssam case OSTR: 70410276Ssam if (cbuf[cpos] == '\n') { 70510276Ssam state = CMD; 70610276Ssam return (CRLF); 70710276Ssam } 70836317Sbostic /* FALLTHROUGH */ 70910276Ssam 71010276Ssam case STR1: 71136304Skarels case ZSTR1: 71210276Ssam if (cbuf[cpos] == ' ') { 71310276Ssam cpos++; 71436317Sbostic state = state == OSTR ? STR2 : ++state; 71510276Ssam return (SP); 71610276Ssam } 71710276Ssam break; 71810276Ssam 71936304Skarels case ZSTR2: 72036304Skarels if (cbuf[cpos] == '\n') { 72136304Skarels state = CMD; 72236304Skarels return (CRLF); 72336304Skarels } 72436304Skarels /* FALL THRU */ 72536304Skarels 72610276Ssam case STR2: 72710276Ssam cp = &cbuf[cpos]; 72810276Ssam n = strlen(cp); 72910276Ssam cpos += n - 1; 73010276Ssam /* 73110276Ssam * Make sure the string is nonempty and \n terminated. 73210276Ssam */ 73310276Ssam if (n > 1 && cbuf[cpos] == '\n') { 73410276Ssam cbuf[cpos] = '\0'; 73510276Ssam yylval = copy(cp); 73610276Ssam cbuf[cpos] = '\n'; 73710276Ssam state = ARGS; 73810276Ssam return (STRING); 73910276Ssam } 74010276Ssam break; 74110276Ssam 74210276Ssam case ARGS: 74310276Ssam if (isdigit(cbuf[cpos])) { 74410276Ssam cp = &cbuf[cpos]; 74510276Ssam while (isdigit(cbuf[++cpos])) 74610276Ssam ; 74710276Ssam c = cbuf[cpos]; 74810276Ssam cbuf[cpos] = '\0'; 74910276Ssam yylval = atoi(cp); 75010276Ssam cbuf[cpos] = c; 75110276Ssam return (NUMBER); 75210276Ssam } 75310276Ssam switch (cbuf[cpos++]) { 75410276Ssam 75510276Ssam case '\n': 75610276Ssam state = CMD; 75710276Ssam return (CRLF); 75810276Ssam 75910276Ssam case ' ': 76010276Ssam return (SP); 76110276Ssam 76210276Ssam case ',': 76310276Ssam return (COMMA); 76410276Ssam 76510276Ssam case 'A': 76610276Ssam case 'a': 76710276Ssam return (A); 76810276Ssam 76910276Ssam case 'B': 77010276Ssam case 'b': 77110276Ssam return (B); 77210276Ssam 77310276Ssam case 'C': 77410276Ssam case 'c': 77510276Ssam return (C); 77610276Ssam 77710276Ssam case 'E': 77810276Ssam case 'e': 77910276Ssam return (E); 78010276Ssam 78110276Ssam case 'F': 78210276Ssam case 'f': 78310276Ssam return (F); 78410276Ssam 78510276Ssam case 'I': 78610276Ssam case 'i': 78710276Ssam return (I); 78810276Ssam 78910276Ssam case 'L': 79010276Ssam case 'l': 79110276Ssam return (L); 79210276Ssam 79310276Ssam case 'N': 79410276Ssam case 'n': 79510276Ssam return (N); 79610276Ssam 79710276Ssam case 'P': 79810276Ssam case 'p': 79910276Ssam return (P); 80010276Ssam 80110276Ssam case 'R': 80210276Ssam case 'r': 80310276Ssam return (R); 80410276Ssam 80510276Ssam case 'S': 80610276Ssam case 's': 80710276Ssam return (S); 80810276Ssam 80910276Ssam case 'T': 81010276Ssam case 't': 81110276Ssam return (T); 81210276Ssam 81310276Ssam } 81410276Ssam break; 81510276Ssam 81610276Ssam default: 81710276Ssam fatal("Unknown state in scanner."); 81810276Ssam } 81926494Sminshall yyerror((char *) 0); 82010276Ssam state = CMD; 82126494Sminshall longjmp(errcatch,0); 82210276Ssam } 82310276Ssam } 82410276Ssam 82510276Ssam upper(s) 82636277Sbostic register char *s; 82710276Ssam { 82810276Ssam while (*s != '\0') { 82910276Ssam if (islower(*s)) 83010276Ssam *s = toupper(*s); 83110276Ssam s++; 83210276Ssam } 83310276Ssam } 83410276Ssam 83510276Ssam copy(s) 83610276Ssam char *s; 83710276Ssam { 83810276Ssam char *p; 83926494Sminshall extern char *malloc(), *strcpy(); 84010276Ssam 84126494Sminshall p = malloc((unsigned) strlen(s) + 1); 84210276Ssam if (p == NULL) 84310276Ssam fatal("Ran out of memory."); 84426494Sminshall (void) strcpy(p, s); 84510276Ssam return ((int)p); 84610276Ssam } 84710276Ssam 84810276Ssam help(s) 84910276Ssam char *s; 85010276Ssam { 85110276Ssam register struct tab *c; 85210276Ssam register int width, NCMDS; 85310276Ssam 85410276Ssam width = 0, NCMDS = 0; 85510276Ssam for (c = cmdtab; c->name != NULL; c++) { 85631132Smckusick int len = strlen(c->name) + 1; 85710276Ssam 85810276Ssam if (len > width) 85910276Ssam width = len; 86010276Ssam NCMDS++; 86110276Ssam } 86210276Ssam width = (width + 8) &~ 7; 86310276Ssam if (s == 0) { 86410276Ssam register int i, j, w; 86510276Ssam int columns, lines; 86610276Ssam 86710276Ssam lreply(214, 86810276Ssam "The following commands are recognized (* =>'s unimplemented)."); 86910276Ssam columns = 76 / width; 87010276Ssam if (columns == 0) 87110276Ssam columns = 1; 87210276Ssam lines = (NCMDS + columns - 1) / columns; 87310276Ssam for (i = 0; i < lines; i++) { 87427107Smckusick printf(" "); 87510276Ssam for (j = 0; j < columns; j++) { 87610276Ssam c = cmdtab + j * lines + i; 87710276Ssam printf("%s%c", c->name, 87810276Ssam c->implemented ? ' ' : '*'); 87910302Ssam if (c + lines >= &cmdtab[NCMDS]) 88010276Ssam break; 88131132Smckusick w = strlen(c->name) + 1; 88210276Ssam while (w < width) { 88310276Ssam putchar(' '); 88410276Ssam w++; 88510276Ssam } 88610276Ssam } 88710276Ssam printf("\r\n"); 88810276Ssam } 88926494Sminshall (void) fflush(stdout); 89010276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 89110276Ssam return; 89210276Ssam } 89310276Ssam upper(s); 89410276Ssam c = lookup(s); 89510276Ssam if (c == (struct tab *)0) { 89627107Smckusick reply(502, "Unknown command %s.", s); 89710276Ssam return; 89810276Ssam } 89910276Ssam if (c->implemented) 90010276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 90110276Ssam else 90210276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 90310276Ssam } 904