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*36620Srick * @(#)ftpcmd.y 5.18 (Berkeley) 01/25/89 1822501Sdist */ 1922501Sdist 2022501Sdist /* 2110276Ssam * Grammar for FTP commands. 2210276Ssam * See RFC 765. 2310276Ssam */ 2410276Ssam 2510276Ssam %{ 2610276Ssam 2710276Ssam #ifndef lint 28*36620Srick static char sccsid[] = "@(#)ftpcmd.y 5.18 (Berkeley) 01/25/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; 55*36620Srick 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 84*36620Srick STAT HELP NOOP MKD RMD PWD 85*36620Srick 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) 189*36620Srick 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) 210*36620Srick send_file_list("."); 21110276Ssam } 212*36620Srick | NLST check_login SP STRING CRLF 21310276Ssam = { 214*36620Srick if ($2 && $4 != NULL) 215*36620Srick send_file_list((char *) $4); 21610302Ssam if ($4 != NULL) 21726494Sminshall free((char *) $4); 21810276Ssam } 21910276Ssam | LIST check_login CRLF 22010276Ssam = { 22110276Ssam if ($2) 222*36620Srick retrieve("/bin/ls -lgA", ""); 22310276Ssam } 22410276Ssam | LIST check_login SP pathname CRLF 22510276Ssam = { 22610302Ssam if ($2 && $4 != NULL) 227*36620Srick 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 } 277*36620Srick | 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 } 284*36620Srick | 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 } 291*36620Srick | PWD check_login CRLF 29210276Ssam = { 29310276Ssam if ($2) 29410302Ssam pwd(); 29510276Ssam } 296*36620Srick | 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 = { 31036552Sbostic reply(215, "UNIX Type: L%d Version: BSD-%d", 31136552Sbostic NBBY, BSD); 31236552Sbostic } 31310276Ssam | QUIT CRLF 31410276Ssam = { 31510276Ssam reply(221, "Goodbye."); 31613246Ssam dologout(0); 31710276Ssam } 31810276Ssam | error CRLF 31910276Ssam = { 32010276Ssam yyerrok; 32110276Ssam } 32210276Ssam ; 32330945Scsvsj rcmd: RNFR check_login SP pathname CRLF 32430945Scsvsj = { 32530945Scsvsj char *renamefrom(); 32630945Scsvsj 32736552Sbostic restart_point = (off_t) 0; 32830945Scsvsj if ($2 && $4) { 32930945Scsvsj fromname = renamefrom((char *) $4); 33030945Scsvsj if (fromname == (char *) 0 && $4) { 33130945Scsvsj free((char *) $4); 33230945Scsvsj } 33330945Scsvsj } 33430945Scsvsj } 33536552Sbostic | REST SP byte_size CRLF 33636552Sbostic = { 33736552Sbostic long atol(); 33836552Sbostic 33936552Sbostic fromname = (char *) 0; 34036552Sbostic restart_point = $3; 34136552Sbostic reply(350, "Restarting at %ld. Send STORE or RETRIEVE to initiate transfer.", restart_point); 34236552Sbostic } 34330945Scsvsj ; 34430945Scsvsj 34510276Ssam username: STRING 34610276Ssam ; 34710276Ssam 34836304Skarels password: /* empty */ 34936304Skarels = { 35036304Skarels $$ = (int) ""; 35136304Skarels } 35236304Skarels | STRING 35310276Ssam ; 35410276Ssam 35510276Ssam byte_size: NUMBER 35610276Ssam ; 35710276Ssam 35810276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 35910276Ssam NUMBER COMMA NUMBER 36010276Ssam = { 36110276Ssam register char *a, *p; 36210276Ssam 36310276Ssam a = (char *)&data_dest.sin_addr; 36410276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 36510276Ssam p = (char *)&data_dest.sin_port; 36610276Ssam p[0] = $9; p[1] = $11; 36710324Ssam data_dest.sin_family = AF_INET; 36810276Ssam } 36910276Ssam ; 37010276Ssam 37110276Ssam form_code: N 37210276Ssam = { 37310276Ssam $$ = FORM_N; 37410276Ssam } 37510276Ssam | T 37610276Ssam = { 37710276Ssam $$ = FORM_T; 37810276Ssam } 37910276Ssam | C 38010276Ssam = { 38110276Ssam $$ = FORM_C; 38210276Ssam } 38310276Ssam ; 38410276Ssam 38510276Ssam type_code: A 38610276Ssam = { 38710276Ssam cmd_type = TYPE_A; 38810276Ssam cmd_form = FORM_N; 38910276Ssam } 39010276Ssam | A SP form_code 39110276Ssam = { 39210276Ssam cmd_type = TYPE_A; 39310276Ssam cmd_form = $3; 39410276Ssam } 39510276Ssam | E 39610276Ssam = { 39710276Ssam cmd_type = TYPE_E; 39810276Ssam cmd_form = FORM_N; 39910276Ssam } 40010276Ssam | E SP form_code 40110276Ssam = { 40210276Ssam cmd_type = TYPE_E; 40310276Ssam cmd_form = $3; 40410276Ssam } 40510276Ssam | I 40610276Ssam = { 40710276Ssam cmd_type = TYPE_I; 40810276Ssam } 40910276Ssam | L 41010276Ssam = { 41110276Ssam cmd_type = TYPE_L; 41236552Sbostic cmd_bytesz = NBBY; 41310276Ssam } 41410276Ssam | L SP byte_size 41510276Ssam = { 41610276Ssam cmd_type = TYPE_L; 41710276Ssam cmd_bytesz = $3; 41810276Ssam } 41910276Ssam /* this is for a bug in the BBN ftp */ 42010276Ssam | L byte_size 42110276Ssam = { 42210276Ssam cmd_type = TYPE_L; 42310276Ssam cmd_bytesz = $2; 42410276Ssam } 42510276Ssam ; 42610276Ssam 42710276Ssam struct_code: F 42810276Ssam = { 42910276Ssam $$ = STRU_F; 43010276Ssam } 43110276Ssam | R 43210276Ssam = { 43310276Ssam $$ = STRU_R; 43410276Ssam } 43510276Ssam | P 43610276Ssam = { 43710276Ssam $$ = STRU_P; 43810276Ssam } 43910276Ssam ; 44010276Ssam 44110276Ssam mode_code: S 44210276Ssam = { 44310276Ssam $$ = MODE_S; 44410276Ssam } 44510276Ssam | B 44610276Ssam = { 44710276Ssam $$ = MODE_B; 44810276Ssam } 44910276Ssam | C 45010276Ssam = { 45110276Ssam $$ = MODE_C; 45210276Ssam } 45310276Ssam ; 45410276Ssam 45510276Ssam pathname: pathstring 45610276Ssam = { 45727107Smckusick /* 45827107Smckusick * Problem: this production is used for all pathname 45927107Smckusick * processing, but only gives a 550 error reply. 46027107Smckusick * This is a valid reply in some cases but not in others. 46127107Smckusick */ 46236304Skarels if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 46326494Sminshall $$ = (int)*glob((char *) $1); 46410302Ssam if (globerr != NULL) { 46510276Ssam reply(550, globerr); 46610302Ssam $$ = NULL; 46710302Ssam } 46826494Sminshall free((char *) $1); 46910276Ssam } else 47010276Ssam $$ = $1; 47110276Ssam } 47210276Ssam ; 47310276Ssam 47410276Ssam pathstring: STRING 47510276Ssam ; 47610276Ssam 47710276Ssam check_login: /* empty */ 47810276Ssam = { 47910276Ssam if (logged_in) 48010276Ssam $$ = 1; 48110276Ssam else { 48210276Ssam reply(530, "Please login with USER and PASS."); 48310276Ssam $$ = 0; 48410276Ssam } 48510276Ssam } 48610276Ssam ; 48710276Ssam 48810276Ssam %% 48910276Ssam 49010276Ssam extern jmp_buf errcatch; 49110276Ssam 49210276Ssam #define CMD 0 /* beginning of command */ 49310276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 49410276Ssam #define STR1 2 /* expect SP followed by STRING */ 49510276Ssam #define STR2 3 /* expect STRING */ 49636304Skarels #define OSTR 4 /* optional SP then STRING */ 49736304Skarels #define ZSTR1 5 /* SP then optional STRING */ 49836304Skarels #define ZSTR2 6 /* optional STRING after SP */ 49910276Ssam 50010276Ssam struct tab { 50110276Ssam char *name; 50210276Ssam short token; 50310276Ssam short state; 50410276Ssam short implemented; /* 1 if command is implemented */ 50510276Ssam char *help; 50610276Ssam }; 50710276Ssam 50810276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 50910276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 51036304Skarels { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 51110276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 51210276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 51310276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 51410276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 51526045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 51610276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 51710276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 51810276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 51910276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 52010276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 52110276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 52210276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 52310276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 52410276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 52510276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 52610276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 52710276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 52810276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 52910276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 53036552Sbostic { "REST", REST, ARGS, 1, "(restart command)" }, 53110276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 53210276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 53326045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 53410276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 53536304Skarels { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 53610276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 53710276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 53810276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 53910276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 54036552Sbostic { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 54110276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 54210276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 54310276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 544*36620Srick { "MKD", MKD, STR1, 1, "<sp> path-name" }, 545*36620Srick { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 546*36620Srick { "RMD", RMD, STR1, 1, "<sp> path-name" }, 547*36620Srick { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 548*36620Srick { "PWD", PWD, ARGS, 1, "(return current directory)" }, 549*36620Srick { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 550*36620Srick { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 551*36620Srick { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 55226045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 55310276Ssam { NULL, 0, 0, 0, 0 } 55410276Ssam }; 55510276Ssam 55610276Ssam struct tab * 55710276Ssam lookup(cmd) 55810276Ssam char *cmd; 55910276Ssam { 56010276Ssam register struct tab *p; 56110276Ssam 56210276Ssam for (p = cmdtab; p->name != NULL; p++) 56310276Ssam if (strcmp(cmd, p->name) == 0) 56410276Ssam return (p); 56510276Ssam return (0); 56610276Ssam } 56710276Ssam 56813033Ssam #include <arpa/telnet.h> 56910276Ssam 57010276Ssam /* 57110276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 57210276Ssam */ 57310276Ssam char * 57410276Ssam getline(s, n, iop) 57510276Ssam char *s; 57610276Ssam register FILE *iop; 57710276Ssam { 57810276Ssam register c; 57926494Sminshall register char *cs; 58010276Ssam 58110276Ssam cs = s; 58227751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 58326045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 58426045Sminshall *cs++ = tmpline[c]; 58526045Sminshall if (tmpline[c] == '\n') { 58626045Sminshall *cs++ = '\0'; 58736304Skarels if (debug) 58836304Skarels syslog(LOG_DEBUG, "command: %s", s); 58926045Sminshall tmpline[0] = '\0'; 59026045Sminshall return(s); 59126045Sminshall } 59236304Skarels if (c == 0) 59326045Sminshall tmpline[0] = '\0'; 59426045Sminshall } 59536304Skarels while ((c = getc(iop)) != EOF) { 59636304Skarels c &= 0377; 59736304Skarels if (c == IAC) { 59836304Skarels if ((c = getc(iop)) != EOF) { 59936304Skarels c &= 0377; 60036304Skarels switch (c) { 60127751Sminshall case WILL: 60227751Sminshall case WONT: 60336277Sbostic c = getc(iop); 60436304Skarels printf("%c%c%c", IAC, DONT, 0377&c); 60527751Sminshall (void) fflush(stdout); 60636304Skarels continue; 60727751Sminshall case DO: 60827751Sminshall case DONT: 60936277Sbostic c = getc(iop); 61036304Skarels printf("%c%c%c", IAC, WONT, 0377&c); 61127751Sminshall (void) fflush(stdout); 61236304Skarels continue; 61336304Skarels case IAC: 61427751Sminshall break; 61527751Sminshall default: 61636304Skarels continue; /* ignore command */ 61727751Sminshall } 61836304Skarels } 61910276Ssam } 62036304Skarels *cs++ = c; 62136304Skarels if (--n <= 0 || c == '\n') 62210276Ssam break; 62310276Ssam } 62427751Sminshall if (c == EOF && cs == s) 62518303Sralph return (NULL); 62610276Ssam *cs++ = '\0'; 62736304Skarels if (debug) 62836304Skarels syslog(LOG_DEBUG, "command: %s", s); 62910276Ssam return (s); 63010276Ssam } 63110276Ssam 63211652Ssam static int 63311652Ssam toolong() 63411652Ssam { 63526494Sminshall time_t now; 63611652Ssam extern char *ctime(); 63726494Sminshall extern time_t time(); 63811652Ssam 63911652Ssam reply(421, 64011652Ssam "Timeout (%d seconds): closing control connection.", timeout); 64126494Sminshall (void) time(&now); 64211652Ssam if (logging) { 64326494Sminshall syslog(LOG_INFO, 64436304Skarels "User %s timed out after %d seconds at %s", 64511652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 64611652Ssam } 64713246Ssam dologout(1); 64811652Ssam } 64911652Ssam 65010276Ssam yylex() 65110276Ssam { 65210276Ssam static int cpos, state; 65310276Ssam register char *cp; 65410276Ssam register struct tab *p; 65510276Ssam int n; 65636277Sbostic char c, *strpbrk(); 65710276Ssam 65810276Ssam for (;;) { 65910276Ssam switch (state) { 66010276Ssam 66110276Ssam case CMD: 66226494Sminshall (void) signal(SIGALRM, toolong); 66326494Sminshall (void) alarm((unsigned) timeout); 66410276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 66510276Ssam reply(221, "You could at least say goodbye."); 66613246Ssam dologout(0); 66710276Ssam } 66826494Sminshall (void) alarm(0); 669*36620Srick #ifdef SETPROCTITLE 670*36620Srick if (strncasecmp(cbuf, "PASS", 4) != NULL) { 671*36620Srick setproctitle("%s: %s", remotehost, cbuf); 672*36620Srick } 673*36620Srick #endif /* SETPROCTITLE */ 67436324Sbostic if ((cp = index(cbuf, '\r'))) { 67536324Sbostic *cp++ = '\n'; 67636324Sbostic *cp = '\0'; 67736324Sbostic } 67836277Sbostic if ((cp = strpbrk(cbuf, " \n"))) 67936277Sbostic cpos = cp - cbuf; 68036304Skarels if (cpos == 0) 68110276Ssam cpos = 4; 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 } 70436317Sbostic /* FALLTHROUGH */ 70510276Ssam 70610276Ssam case STR1: 70736304Skarels case ZSTR1: 70810276Ssam if (cbuf[cpos] == ' ') { 70910276Ssam cpos++; 71036317Sbostic state = state == OSTR ? STR2 : ++state; 71110276Ssam return (SP); 71210276Ssam } 71310276Ssam break; 71410276Ssam 71536304Skarels case ZSTR2: 71636304Skarels if (cbuf[cpos] == '\n') { 71736304Skarels state = CMD; 71836304Skarels return (CRLF); 71936304Skarels } 72036304Skarels /* FALL THRU */ 72136304Skarels 72210276Ssam case STR2: 72310276Ssam cp = &cbuf[cpos]; 72410276Ssam n = strlen(cp); 72510276Ssam cpos += n - 1; 72610276Ssam /* 72710276Ssam * Make sure the string is nonempty and \n terminated. 72810276Ssam */ 72910276Ssam if (n > 1 && cbuf[cpos] == '\n') { 73010276Ssam cbuf[cpos] = '\0'; 73110276Ssam yylval = copy(cp); 73210276Ssam cbuf[cpos] = '\n'; 73310276Ssam state = ARGS; 73410276Ssam return (STRING); 73510276Ssam } 73610276Ssam break; 73710276Ssam 73810276Ssam case ARGS: 73910276Ssam if (isdigit(cbuf[cpos])) { 74010276Ssam cp = &cbuf[cpos]; 74110276Ssam while (isdigit(cbuf[++cpos])) 74210276Ssam ; 74310276Ssam c = cbuf[cpos]; 74410276Ssam cbuf[cpos] = '\0'; 74510276Ssam yylval = atoi(cp); 74610276Ssam cbuf[cpos] = c; 74710276Ssam return (NUMBER); 74810276Ssam } 74910276Ssam switch (cbuf[cpos++]) { 75010276Ssam 75110276Ssam case '\n': 75210276Ssam state = CMD; 75310276Ssam return (CRLF); 75410276Ssam 75510276Ssam case ' ': 75610276Ssam return (SP); 75710276Ssam 75810276Ssam case ',': 75910276Ssam return (COMMA); 76010276Ssam 76110276Ssam case 'A': 76210276Ssam case 'a': 76310276Ssam return (A); 76410276Ssam 76510276Ssam case 'B': 76610276Ssam case 'b': 76710276Ssam return (B); 76810276Ssam 76910276Ssam case 'C': 77010276Ssam case 'c': 77110276Ssam return (C); 77210276Ssam 77310276Ssam case 'E': 77410276Ssam case 'e': 77510276Ssam return (E); 77610276Ssam 77710276Ssam case 'F': 77810276Ssam case 'f': 77910276Ssam return (F); 78010276Ssam 78110276Ssam case 'I': 78210276Ssam case 'i': 78310276Ssam return (I); 78410276Ssam 78510276Ssam case 'L': 78610276Ssam case 'l': 78710276Ssam return (L); 78810276Ssam 78910276Ssam case 'N': 79010276Ssam case 'n': 79110276Ssam return (N); 79210276Ssam 79310276Ssam case 'P': 79410276Ssam case 'p': 79510276Ssam return (P); 79610276Ssam 79710276Ssam case 'R': 79810276Ssam case 'r': 79910276Ssam return (R); 80010276Ssam 80110276Ssam case 'S': 80210276Ssam case 's': 80310276Ssam return (S); 80410276Ssam 80510276Ssam case 'T': 80610276Ssam case 't': 80710276Ssam return (T); 80810276Ssam 80910276Ssam } 81010276Ssam break; 81110276Ssam 81210276Ssam default: 81310276Ssam fatal("Unknown state in scanner."); 81410276Ssam } 81526494Sminshall yyerror((char *) 0); 81610276Ssam state = CMD; 81726494Sminshall longjmp(errcatch,0); 81810276Ssam } 81910276Ssam } 82010276Ssam 82110276Ssam upper(s) 82236277Sbostic register char *s; 82310276Ssam { 82410276Ssam while (*s != '\0') { 82510276Ssam if (islower(*s)) 82610276Ssam *s = toupper(*s); 82710276Ssam s++; 82810276Ssam } 82910276Ssam } 83010276Ssam 83110276Ssam copy(s) 83210276Ssam char *s; 83310276Ssam { 83410276Ssam char *p; 83526494Sminshall extern char *malloc(), *strcpy(); 83610276Ssam 83726494Sminshall p = malloc((unsigned) strlen(s) + 1); 83810276Ssam if (p == NULL) 83910276Ssam fatal("Ran out of memory."); 84026494Sminshall (void) strcpy(p, s); 84110276Ssam return ((int)p); 84210276Ssam } 84310276Ssam 84410276Ssam help(s) 84510276Ssam char *s; 84610276Ssam { 84710276Ssam register struct tab *c; 84810276Ssam register int width, NCMDS; 84910276Ssam 85010276Ssam width = 0, NCMDS = 0; 85110276Ssam for (c = cmdtab; c->name != NULL; c++) { 85231132Smckusick int len = strlen(c->name) + 1; 85310276Ssam 85410276Ssam if (len > width) 85510276Ssam width = len; 85610276Ssam NCMDS++; 85710276Ssam } 85810276Ssam width = (width + 8) &~ 7; 85910276Ssam if (s == 0) { 86010276Ssam register int i, j, w; 86110276Ssam int columns, lines; 86210276Ssam 86310276Ssam lreply(214, 86410276Ssam "The following commands are recognized (* =>'s unimplemented)."); 86510276Ssam columns = 76 / width; 86610276Ssam if (columns == 0) 86710276Ssam columns = 1; 86810276Ssam lines = (NCMDS + columns - 1) / columns; 86910276Ssam for (i = 0; i < lines; i++) { 87027107Smckusick printf(" "); 87110276Ssam for (j = 0; j < columns; j++) { 87210276Ssam c = cmdtab + j * lines + i; 87310276Ssam printf("%s%c", c->name, 87410276Ssam c->implemented ? ' ' : '*'); 87510302Ssam if (c + lines >= &cmdtab[NCMDS]) 87610276Ssam break; 87731132Smckusick w = strlen(c->name) + 1; 87810276Ssam while (w < width) { 87910276Ssam putchar(' '); 88010276Ssam w++; 88110276Ssam } 88210276Ssam } 88310276Ssam printf("\r\n"); 88410276Ssam } 88526494Sminshall (void) fflush(stdout); 88610276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 88710276Ssam return; 88810276Ssam } 88910276Ssam upper(s); 89010276Ssam c = lookup(s); 89110276Ssam if (c == (struct tab *)0) { 89227107Smckusick reply(502, "Unknown command %s.", s); 89310276Ssam return; 89410276Ssam } 89510276Ssam if (c->implemented) 89610276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 89710276Ssam else 89810276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 89910276Ssam } 900