110276Ssam /* 226045Sminshall * Copyright (c) 1985 Regents of the University of California. 322501Sdist * All rights reserved. The Berkeley software License Agreement 422501Sdist * specifies the terms and conditions for redistribution. 522501Sdist */ 622501Sdist 722501Sdist /* 810276Ssam * Grammar for FTP commands. 910276Ssam * See RFC 765. 1010276Ssam */ 1110276Ssam 1210276Ssam %{ 1310276Ssam 1410276Ssam #ifndef lint 15*31132Smckusick static char sccsid[] = "@(#)ftpcmd.y 5.9 (Berkeley) 05/15/87"; 1610276Ssam #endif 1710276Ssam 1810276Ssam #include <sys/types.h> 1910276Ssam #include <sys/socket.h> 2010276Ssam 2110276Ssam #include <netinet/in.h> 2210276Ssam 2313033Ssam #include <arpa/ftp.h> 2413033Ssam 2510276Ssam #include <stdio.h> 2611652Ssam #include <signal.h> 2710276Ssam #include <ctype.h> 2810276Ssam #include <pwd.h> 2910276Ssam #include <setjmp.h> 3026494Sminshall #include <syslog.h> 3110276Ssam 3210276Ssam extern struct sockaddr_in data_dest; 3310276Ssam extern int logged_in; 3410276Ssam extern struct passwd *pw; 3510276Ssam extern int guest; 3610276Ssam extern int logging; 3710276Ssam extern int type; 3810276Ssam extern int form; 3910276Ssam extern int debug; 4011652Ssam extern int timeout; 4126045Sminshall extern int pdata; 4210276Ssam extern char hostname[]; 4310276Ssam extern char *globerr; 4410320Ssam extern int usedefault; 4526045Sminshall extern int unique; 4626045Sminshall extern int transflag; 4726045Sminshall extern char tmpline[]; 4810276Ssam char **glob(); 4910276Ssam 5010276Ssam static int cmd_type; 5110276Ssam static int cmd_form; 5210276Ssam static int cmd_bytesz; 5326045Sminshall char cbuf[512]; 5430945Scsvsj char *fromname; 5510276Ssam 5610276Ssam char *index(); 5710276Ssam %} 5810276Ssam 5910276Ssam %token 6010276Ssam A B C E F I 6110276Ssam L N P R S T 6210276Ssam 6310276Ssam SP CRLF COMMA STRING NUMBER 6410276Ssam 6510276Ssam USER PASS ACCT REIN QUIT PORT 6610276Ssam PASV TYPE STRU MODE RETR STOR 6710276Ssam APPE MLFL MAIL MSND MSOM MSAM 6810276Ssam MRSQ MRCP ALLO REST RNFR RNTO 6910276Ssam ABOR DELE CWD LIST NLST SITE 7010276Ssam STAT HELP NOOP XMKD XRMD XPWD 7126045Sminshall XCUP STOU 7210276Ssam 7310276Ssam LEXERR 7410276Ssam 7510276Ssam %start cmd_list 7610276Ssam 7710276Ssam %% 7810276Ssam 7910276Ssam cmd_list: /* empty */ 8010276Ssam | cmd_list cmd 8130945Scsvsj = { 8230945Scsvsj fromname = (char *) 0; 8330945Scsvsj } 8430945Scsvsj | cmd_list rcmd 8510276Ssam ; 8610276Ssam 8710276Ssam cmd: USER SP username CRLF 8810276Ssam = { 8910276Ssam extern struct passwd *getpwnam(); 9010276Ssam 9126045Sminshall logged_in = 0; 9226494Sminshall if (strcmp((char *) $3, "ftp") == 0 || 9326494Sminshall strcmp((char *) $3, "anonymous") == 0) { 9410324Ssam if ((pw = getpwnam("ftp")) != NULL) { 9510324Ssam guest = 1; 9610324Ssam reply(331, 9710276Ssam "Guest login ok, send ident as password."); 9810324Ssam } 9926045Sminshall else { 10026045Sminshall reply(530, "User %s unknown.", $3); 10126045Sminshall } 10226494Sminshall } else if (checkuser((char *) $3)) { 10310276Ssam guest = 0; 10426494Sminshall pw = getpwnam((char *) $3); 10526045Sminshall if (pw == NULL) { 10626045Sminshall reply(530, "User %s unknown.", $3); 10726045Sminshall } 10826045Sminshall else { 10926045Sminshall reply(331, "Password required for %s.", $3); 11026045Sminshall } 11128865Smckusick } else { 11228865Smckusick reply(530, "User %s access denied.", $3); 11310276Ssam } 11426494Sminshall free((char *) $3); 11510276Ssam } 11610276Ssam | PASS SP password CRLF 11710276Ssam = { 11826494Sminshall pass((char *) $3); 11926494Sminshall free((char *) $3); 12010276Ssam } 12110276Ssam | PORT SP host_port CRLF 12210276Ssam = { 12310320Ssam usedefault = 0; 12426045Sminshall if (pdata > 0) { 12526045Sminshall (void) close(pdata); 12626045Sminshall } 12726045Sminshall pdata = -1; 12827107Smckusick reply(200, "PORT command successful."); 12910276Ssam } 13026045Sminshall | PASV CRLF 13126045Sminshall = { 13226045Sminshall passive(); 13326045Sminshall } 13410276Ssam | TYPE SP type_code CRLF 13510276Ssam = { 13610276Ssam switch (cmd_type) { 13710276Ssam 13810276Ssam case TYPE_A: 13910276Ssam if (cmd_form == FORM_N) { 14010276Ssam reply(200, "Type set to A."); 14110276Ssam type = cmd_type; 14210276Ssam form = cmd_form; 14310276Ssam } else 14410276Ssam reply(504, "Form must be N."); 14510276Ssam break; 14610276Ssam 14710276Ssam case TYPE_E: 14810276Ssam reply(504, "Type E not implemented."); 14910276Ssam break; 15010276Ssam 15110276Ssam case TYPE_I: 15210276Ssam reply(200, "Type set to I."); 15310276Ssam type = cmd_type; 15410276Ssam break; 15510276Ssam 15610276Ssam case TYPE_L: 15710276Ssam if (cmd_bytesz == 8) { 15810276Ssam reply(200, 15910276Ssam "Type set to L (byte size 8)."); 16010276Ssam type = cmd_type; 16110276Ssam } else 16210276Ssam reply(504, "Byte size must be 8."); 16310276Ssam } 16410276Ssam } 16510276Ssam | STRU SP struct_code CRLF 16610276Ssam = { 16710276Ssam switch ($3) { 16810276Ssam 16910276Ssam case STRU_F: 17010276Ssam reply(200, "STRU F ok."); 17110276Ssam break; 17210276Ssam 17310276Ssam default: 17427107Smckusick reply(504, "Unimplemented STRU type."); 17510276Ssam } 17610276Ssam } 17710276Ssam | MODE SP mode_code CRLF 17810276Ssam = { 17910276Ssam switch ($3) { 18010276Ssam 18110276Ssam case MODE_S: 18210276Ssam reply(200, "MODE S ok."); 18310276Ssam break; 18410276Ssam 18510276Ssam default: 18610276Ssam reply(502, "Unimplemented MODE type."); 18710276Ssam } 18810276Ssam } 18910276Ssam | ALLO SP NUMBER CRLF 19010276Ssam = { 19127107Smckusick reply(202, "ALLO command ignored."); 19210276Ssam } 19310276Ssam | RETR check_login SP pathname CRLF 19410276Ssam = { 19510302Ssam if ($2 && $4 != NULL) 19626494Sminshall retrieve((char *) 0, (char *) $4); 19710302Ssam if ($4 != NULL) 19826494Sminshall free((char *) $4); 19910276Ssam } 20010276Ssam | STOR check_login SP pathname CRLF 20110276Ssam = { 20210302Ssam if ($2 && $4 != NULL) 20326494Sminshall store((char *) $4, "w"); 20410302Ssam if ($4 != NULL) 20526494Sminshall free((char *) $4); 20610276Ssam } 20710276Ssam | APPE check_login SP pathname CRLF 20810276Ssam = { 20910302Ssam if ($2 && $4 != NULL) 21026494Sminshall store((char *) $4, "a"); 21110302Ssam if ($4 != NULL) 21226494Sminshall free((char *) $4); 21310276Ssam } 21410276Ssam | NLST check_login CRLF 21510276Ssam = { 21610276Ssam if ($2) 21711217Ssam retrieve("/bin/ls", ""); 21810276Ssam } 21910276Ssam | NLST check_login SP pathname CRLF 22010276Ssam = { 22110302Ssam if ($2 && $4 != NULL) 22226494Sminshall retrieve("/bin/ls %s", (char *) $4); 22310302Ssam if ($4 != NULL) 22426494Sminshall free((char *) $4); 22510276Ssam } 22610276Ssam | LIST check_login CRLF 22710276Ssam = { 22810276Ssam if ($2) 22910318Ssam retrieve("/bin/ls -lg", ""); 23010276Ssam } 23110276Ssam | LIST check_login SP pathname CRLF 23210276Ssam = { 23310302Ssam if ($2 && $4 != NULL) 23426494Sminshall retrieve("/bin/ls -lg %s", (char *) $4); 23510302Ssam if ($4 != NULL) 23626494Sminshall free((char *) $4); 23710276Ssam } 23810276Ssam | DELE check_login SP pathname CRLF 23910276Ssam = { 24010302Ssam if ($2 && $4 != NULL) 24126494Sminshall delete((char *) $4); 24210302Ssam if ($4 != NULL) 24326494Sminshall free((char *) $4); 24410276Ssam } 24530945Scsvsj | RNTO SP pathname CRLF 24630945Scsvsj = { 24730945Scsvsj if (fromname) { 24830945Scsvsj renamecmd(fromname, (char *) $3); 24930945Scsvsj free(fromname); 25030945Scsvsj fromname = (char *) 0; 25130945Scsvsj } else { 25230945Scsvsj reply(503, "Bad sequence of commands."); 25330945Scsvsj } 25430945Scsvsj free((char *) $3); 25530945Scsvsj } 25626045Sminshall | ABOR CRLF 25726045Sminshall = { 25827107Smckusick reply(225, "ABOR command successful."); 25926045Sminshall } 26010276Ssam | CWD check_login CRLF 26110276Ssam = { 26210276Ssam if ($2) 26310276Ssam cwd(pw->pw_dir); 26410276Ssam } 26510276Ssam | CWD check_login SP pathname CRLF 26610276Ssam = { 26710302Ssam if ($2 && $4 != NULL) 26826494Sminshall cwd((char *) $4); 26910302Ssam if ($4 != NULL) 27026494Sminshall free((char *) $4); 27110276Ssam } 27210276Ssam | HELP CRLF 27310276Ssam = { 27426494Sminshall help((char *) 0); 27510276Ssam } 27610276Ssam | HELP SP STRING CRLF 27710276Ssam = { 27826494Sminshall help((char *) $3); 27910276Ssam } 28010276Ssam | NOOP CRLF 28110276Ssam = { 28227107Smckusick reply(200, "NOOP command successful."); 28310276Ssam } 28410276Ssam | XMKD check_login SP pathname CRLF 28510276Ssam = { 28610302Ssam if ($2 && $4 != NULL) 28726494Sminshall makedir((char *) $4); 28810302Ssam if ($4 != NULL) 28926494Sminshall free((char *) $4); 29010276Ssam } 29110276Ssam | XRMD check_login SP pathname CRLF 29210276Ssam = { 29310302Ssam if ($2 && $4 != NULL) 29426494Sminshall removedir((char *) $4); 29510302Ssam if ($4 != NULL) 29626494Sminshall free((char *) $4); 29710276Ssam } 29810276Ssam | XPWD check_login CRLF 29910276Ssam = { 30010276Ssam if ($2) 30110302Ssam pwd(); 30210276Ssam } 30310276Ssam | XCUP check_login CRLF 30410276Ssam = { 30510276Ssam if ($2) 30610276Ssam cwd(".."); 30710276Ssam } 30826045Sminshall | STOU check_login SP pathname CRLF 30926045Sminshall = { 31026045Sminshall if ($2 && $4 != NULL) { 31126045Sminshall unique++; 31226494Sminshall store((char *) $4, "w"); 31326045Sminshall unique = 0; 31426045Sminshall } 31526045Sminshall if ($4 != NULL) 31626494Sminshall free((char *) $4); 31726045Sminshall } 31810276Ssam | QUIT CRLF 31910276Ssam = { 32010276Ssam reply(221, "Goodbye."); 32113246Ssam dologout(0); 32210276Ssam } 32310276Ssam | error CRLF 32410276Ssam = { 32510276Ssam yyerrok; 32610276Ssam } 32710276Ssam ; 32810276Ssam 32930945Scsvsj rcmd: RNFR check_login SP pathname CRLF 33030945Scsvsj = { 33130945Scsvsj char *renamefrom(); 33230945Scsvsj 33330945Scsvsj if ($2 && $4) { 33430945Scsvsj fromname = renamefrom((char *) $4); 33530945Scsvsj if (fromname == (char *) 0 && $4) { 33630945Scsvsj free((char *) $4); 33730945Scsvsj } 33830945Scsvsj } 33930945Scsvsj } 34030945Scsvsj ; 34130945Scsvsj 34210276Ssam username: STRING 34310276Ssam ; 34410276Ssam 34510276Ssam password: STRING 34610276Ssam ; 34710276Ssam 34810276Ssam byte_size: NUMBER 34910276Ssam ; 35010276Ssam 35110276Ssam host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 35210276Ssam NUMBER COMMA NUMBER 35310276Ssam = { 35410276Ssam register char *a, *p; 35510276Ssam 35610276Ssam a = (char *)&data_dest.sin_addr; 35710276Ssam a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 35810276Ssam p = (char *)&data_dest.sin_port; 35910276Ssam p[0] = $9; p[1] = $11; 36010324Ssam data_dest.sin_family = AF_INET; 36110276Ssam } 36210276Ssam ; 36310276Ssam 36410276Ssam form_code: N 36510276Ssam = { 36610276Ssam $$ = FORM_N; 36710276Ssam } 36810276Ssam | T 36910276Ssam = { 37010276Ssam $$ = FORM_T; 37110276Ssam } 37210276Ssam | C 37310276Ssam = { 37410276Ssam $$ = FORM_C; 37510276Ssam } 37610276Ssam ; 37710276Ssam 37810276Ssam type_code: A 37910276Ssam = { 38010276Ssam cmd_type = TYPE_A; 38110276Ssam cmd_form = FORM_N; 38210276Ssam } 38310276Ssam | A SP form_code 38410276Ssam = { 38510276Ssam cmd_type = TYPE_A; 38610276Ssam cmd_form = $3; 38710276Ssam } 38810276Ssam | E 38910276Ssam = { 39010276Ssam cmd_type = TYPE_E; 39110276Ssam cmd_form = FORM_N; 39210276Ssam } 39310276Ssam | E SP form_code 39410276Ssam = { 39510276Ssam cmd_type = TYPE_E; 39610276Ssam cmd_form = $3; 39710276Ssam } 39810276Ssam | I 39910276Ssam = { 40010276Ssam cmd_type = TYPE_I; 40110276Ssam } 40210276Ssam | L 40310276Ssam = { 40410276Ssam cmd_type = TYPE_L; 40510276Ssam cmd_bytesz = 8; 40610276Ssam } 40710276Ssam | L SP byte_size 40810276Ssam = { 40910276Ssam cmd_type = TYPE_L; 41010276Ssam cmd_bytesz = $3; 41110276Ssam } 41210276Ssam /* this is for a bug in the BBN ftp */ 41310276Ssam | L byte_size 41410276Ssam = { 41510276Ssam cmd_type = TYPE_L; 41610276Ssam cmd_bytesz = $2; 41710276Ssam } 41810276Ssam ; 41910276Ssam 42010276Ssam struct_code: F 42110276Ssam = { 42210276Ssam $$ = STRU_F; 42310276Ssam } 42410276Ssam | R 42510276Ssam = { 42610276Ssam $$ = STRU_R; 42710276Ssam } 42810276Ssam | P 42910276Ssam = { 43010276Ssam $$ = STRU_P; 43110276Ssam } 43210276Ssam ; 43310276Ssam 43410276Ssam mode_code: S 43510276Ssam = { 43610276Ssam $$ = MODE_S; 43710276Ssam } 43810276Ssam | B 43910276Ssam = { 44010276Ssam $$ = MODE_B; 44110276Ssam } 44210276Ssam | C 44310276Ssam = { 44410276Ssam $$ = MODE_C; 44510276Ssam } 44610276Ssam ; 44710276Ssam 44810276Ssam pathname: pathstring 44910276Ssam = { 45027107Smckusick /* 45127107Smckusick * Problem: this production is used for all pathname 45227107Smckusick * processing, but only gives a 550 error reply. 45327107Smckusick * This is a valid reply in some cases but not in others. 45427107Smckusick */ 45526494Sminshall if ($1 && strncmp((char *) $1, "~", 1) == 0) { 45626494Sminshall $$ = (int)*glob((char *) $1); 45710302Ssam if (globerr != NULL) { 45810276Ssam reply(550, globerr); 45910302Ssam $$ = NULL; 46010302Ssam } 46126494Sminshall free((char *) $1); 46210276Ssam } else 46310276Ssam $$ = $1; 46410276Ssam } 46510276Ssam ; 46610276Ssam 46710276Ssam pathstring: STRING 46810276Ssam ; 46910276Ssam 47010276Ssam check_login: /* empty */ 47110276Ssam = { 47210276Ssam if (logged_in) 47310276Ssam $$ = 1; 47410276Ssam else { 47510276Ssam reply(530, "Please login with USER and PASS."); 47610276Ssam $$ = 0; 47710276Ssam } 47810276Ssam } 47910276Ssam ; 48010276Ssam 48110276Ssam %% 48210276Ssam 48310276Ssam extern jmp_buf errcatch; 48410276Ssam 48510276Ssam #define CMD 0 /* beginning of command */ 48610276Ssam #define ARGS 1 /* expect miscellaneous arguments */ 48710276Ssam #define STR1 2 /* expect SP followed by STRING */ 48810276Ssam #define STR2 3 /* expect STRING */ 48910276Ssam #define OSTR 4 /* optional STRING */ 49010276Ssam 49110276Ssam struct tab { 49210276Ssam char *name; 49310276Ssam short token; 49410276Ssam short state; 49510276Ssam short implemented; /* 1 if command is implemented */ 49610276Ssam char *help; 49710276Ssam }; 49810276Ssam 49910276Ssam struct tab cmdtab[] = { /* In order defined in RFC 765 */ 50010276Ssam { "USER", USER, STR1, 1, "<sp> username" }, 50110276Ssam { "PASS", PASS, STR1, 1, "<sp> password" }, 50210276Ssam { "ACCT", ACCT, STR1, 0, "(specify account)" }, 50310276Ssam { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 50410276Ssam { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 50510276Ssam { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 50626045Sminshall { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 50710276Ssam { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 50810276Ssam { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 50910276Ssam { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 51010276Ssam { "RETR", RETR, STR1, 1, "<sp> file-name" }, 51110276Ssam { "STOR", STOR, STR1, 1, "<sp> file-name" }, 51210276Ssam { "APPE", APPE, STR1, 1, "<sp> file-name" }, 51310276Ssam { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 51410276Ssam { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 51510276Ssam { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 51610276Ssam { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 51710276Ssam { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 51810276Ssam { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 51910276Ssam { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 52010276Ssam { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 52110276Ssam { "REST", REST, STR1, 0, "(restart command)" }, 52210276Ssam { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 52310276Ssam { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 52426045Sminshall { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 52510276Ssam { "DELE", DELE, STR1, 1, "<sp> file-name" }, 52610276Ssam { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, 52710276Ssam { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 52810276Ssam { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 52910276Ssam { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 53010276Ssam { "SITE", SITE, STR1, 0, "(get site parameters)" }, 53110276Ssam { "STAT", STAT, OSTR, 0, "(get server status)" }, 53210276Ssam { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 53310276Ssam { "NOOP", NOOP, ARGS, 1, "" }, 53426045Sminshall { "MKD", XMKD, STR1, 1, "<sp> path-name" }, 53510276Ssam { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, 53626045Sminshall { "RMD", XRMD, STR1, 1, "<sp> path-name" }, 53710276Ssam { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, 53826045Sminshall { "PWD", XPWD, ARGS, 1, "(return current directory)" }, 53910276Ssam { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, 54026045Sminshall { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54110276Ssam { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, 54226045Sminshall { "STOU", STOU, STR1, 1, "<sp> file-name" }, 54310276Ssam { NULL, 0, 0, 0, 0 } 54410276Ssam }; 54510276Ssam 54610276Ssam struct tab * 54710276Ssam lookup(cmd) 54810276Ssam char *cmd; 54910276Ssam { 55010276Ssam register struct tab *p; 55110276Ssam 55210276Ssam for (p = cmdtab; p->name != NULL; p++) 55310276Ssam if (strcmp(cmd, p->name) == 0) 55410276Ssam return (p); 55510276Ssam return (0); 55610276Ssam } 55710276Ssam 55813033Ssam #include <arpa/telnet.h> 55910276Ssam 56010276Ssam /* 56110276Ssam * getline - a hacked up version of fgets to ignore TELNET escape codes. 56210276Ssam */ 56310276Ssam char * 56410276Ssam getline(s, n, iop) 56510276Ssam char *s; 56610276Ssam register FILE *iop; 56710276Ssam { 56810276Ssam register c; 56926494Sminshall register char *cs; 57010276Ssam 57110276Ssam cs = s; 57227751Sminshall /* tmpline may contain saved command from urgent mode interruption */ 57326045Sminshall for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 57426045Sminshall *cs++ = tmpline[c]; 57526045Sminshall if (tmpline[c] == '\n') { 57626045Sminshall *cs++ = '\0'; 57726045Sminshall if (debug) { 57826494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 57926045Sminshall } 58026045Sminshall tmpline[0] = '\0'; 58126045Sminshall return(s); 58226045Sminshall } 58326045Sminshall if (c == 0) { 58426045Sminshall tmpline[0] = '\0'; 58526045Sminshall } 58626045Sminshall } 58727751Sminshall while (--n > 0 && (c = getc(iop)) != EOF) { 58827751Sminshall c = 0377 & c; 58910276Ssam while (c == IAC) { 59027751Sminshall switch (c = 0377 & getc(iop)) { 59127751Sminshall case WILL: 59227751Sminshall case WONT: 59327751Sminshall c = 0377 & getc(iop); 59427751Sminshall printf("%c%c%c", IAC, WONT, c); 59527751Sminshall (void) fflush(stdout); 59627751Sminshall break; 59727751Sminshall case DO: 59827751Sminshall case DONT: 59927751Sminshall c = 0377 & getc(iop); 60027751Sminshall printf("%c%c%c", IAC, DONT, c); 60127751Sminshall (void) fflush(stdout); 60227751Sminshall break; 60327751Sminshall default: 60427751Sminshall break; 60527751Sminshall } 60627751Sminshall c = 0377 & getc(iop); /* try next character */ 60710276Ssam } 60810276Ssam *cs++ = c; 60910276Ssam if (c=='\n') 61010276Ssam break; 61110276Ssam } 61227751Sminshall if (c == EOF && cs == s) 61318303Sralph return (NULL); 61410276Ssam *cs++ = '\0'; 61511652Ssam if (debug) { 61626494Sminshall syslog(LOG_DEBUG, "FTPD: command: %s", s); 61711652Ssam } 61810276Ssam return (s); 61910276Ssam } 62010276Ssam 62111652Ssam static int 62211652Ssam toolong() 62311652Ssam { 62426494Sminshall time_t now; 62511652Ssam extern char *ctime(); 62626494Sminshall extern time_t time(); 62711652Ssam 62811652Ssam reply(421, 62911652Ssam "Timeout (%d seconds): closing control connection.", timeout); 63026494Sminshall (void) time(&now); 63111652Ssam if (logging) { 63226494Sminshall syslog(LOG_INFO, 63311652Ssam "FTPD: User %s timed out after %d seconds at %s", 63411652Ssam (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 63511652Ssam } 63613246Ssam dologout(1); 63711652Ssam } 63811652Ssam 63910276Ssam yylex() 64010276Ssam { 64110276Ssam static int cpos, state; 64210276Ssam register char *cp; 64310276Ssam register struct tab *p; 64410276Ssam int n; 64510276Ssam char c; 64610276Ssam 64710276Ssam for (;;) { 64810276Ssam switch (state) { 64910276Ssam 65010276Ssam case CMD: 65126494Sminshall (void) signal(SIGALRM, toolong); 65226494Sminshall (void) alarm((unsigned) timeout); 65310276Ssam if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 65410276Ssam reply(221, "You could at least say goodbye."); 65513246Ssam dologout(0); 65610276Ssam } 65726494Sminshall (void) alarm(0); 65810276Ssam if (index(cbuf, '\r')) { 65910276Ssam cp = index(cbuf, '\r'); 66010276Ssam cp[0] = '\n'; cp[1] = 0; 66110276Ssam } 66210276Ssam if (index(cbuf, ' ')) 66310276Ssam cpos = index(cbuf, ' ') - cbuf; 66410276Ssam else 66526045Sminshall cpos = index(cbuf, '\n') - cbuf; 66626045Sminshall if (cpos == 0) { 66710276Ssam cpos = 4; 66826045Sminshall } 66910276Ssam c = cbuf[cpos]; 67010276Ssam cbuf[cpos] = '\0'; 67110276Ssam upper(cbuf); 67210276Ssam p = lookup(cbuf); 67310276Ssam cbuf[cpos] = c; 67410276Ssam if (p != 0) { 67510276Ssam if (p->implemented == 0) { 67610276Ssam nack(p->name); 67726494Sminshall longjmp(errcatch,0); 67810276Ssam /* NOTREACHED */ 67910276Ssam } 68010276Ssam state = p->state; 68110276Ssam yylval = (int) p->name; 68210276Ssam return (p->token); 68310276Ssam } 68410276Ssam break; 68510276Ssam 68610276Ssam case OSTR: 68710276Ssam if (cbuf[cpos] == '\n') { 68810276Ssam state = CMD; 68910276Ssam return (CRLF); 69010276Ssam } 69110276Ssam /* FALL THRU */ 69210276Ssam 69310276Ssam case STR1: 69410276Ssam if (cbuf[cpos] == ' ') { 69510276Ssam cpos++; 69610276Ssam state = STR2; 69710276Ssam return (SP); 69810276Ssam } 69910276Ssam break; 70010276Ssam 70110276Ssam case STR2: 70210276Ssam cp = &cbuf[cpos]; 70310276Ssam n = strlen(cp); 70410276Ssam cpos += n - 1; 70510276Ssam /* 70610276Ssam * Make sure the string is nonempty and \n terminated. 70710276Ssam */ 70810276Ssam if (n > 1 && cbuf[cpos] == '\n') { 70910276Ssam cbuf[cpos] = '\0'; 71010276Ssam yylval = copy(cp); 71110276Ssam cbuf[cpos] = '\n'; 71210276Ssam state = ARGS; 71310276Ssam return (STRING); 71410276Ssam } 71510276Ssam break; 71610276Ssam 71710276Ssam case ARGS: 71810276Ssam if (isdigit(cbuf[cpos])) { 71910276Ssam cp = &cbuf[cpos]; 72010276Ssam while (isdigit(cbuf[++cpos])) 72110276Ssam ; 72210276Ssam c = cbuf[cpos]; 72310276Ssam cbuf[cpos] = '\0'; 72410276Ssam yylval = atoi(cp); 72510276Ssam cbuf[cpos] = c; 72610276Ssam return (NUMBER); 72710276Ssam } 72810276Ssam switch (cbuf[cpos++]) { 72910276Ssam 73010276Ssam case '\n': 73110276Ssam state = CMD; 73210276Ssam return (CRLF); 73310276Ssam 73410276Ssam case ' ': 73510276Ssam return (SP); 73610276Ssam 73710276Ssam case ',': 73810276Ssam return (COMMA); 73910276Ssam 74010276Ssam case 'A': 74110276Ssam case 'a': 74210276Ssam return (A); 74310276Ssam 74410276Ssam case 'B': 74510276Ssam case 'b': 74610276Ssam return (B); 74710276Ssam 74810276Ssam case 'C': 74910276Ssam case 'c': 75010276Ssam return (C); 75110276Ssam 75210276Ssam case 'E': 75310276Ssam case 'e': 75410276Ssam return (E); 75510276Ssam 75610276Ssam case 'F': 75710276Ssam case 'f': 75810276Ssam return (F); 75910276Ssam 76010276Ssam case 'I': 76110276Ssam case 'i': 76210276Ssam return (I); 76310276Ssam 76410276Ssam case 'L': 76510276Ssam case 'l': 76610276Ssam return (L); 76710276Ssam 76810276Ssam case 'N': 76910276Ssam case 'n': 77010276Ssam return (N); 77110276Ssam 77210276Ssam case 'P': 77310276Ssam case 'p': 77410276Ssam return (P); 77510276Ssam 77610276Ssam case 'R': 77710276Ssam case 'r': 77810276Ssam return (R); 77910276Ssam 78010276Ssam case 'S': 78110276Ssam case 's': 78210276Ssam return (S); 78310276Ssam 78410276Ssam case 'T': 78510276Ssam case 't': 78610276Ssam return (T); 78710276Ssam 78810276Ssam } 78910276Ssam break; 79010276Ssam 79110276Ssam default: 79210276Ssam fatal("Unknown state in scanner."); 79310276Ssam } 79426494Sminshall yyerror((char *) 0); 79510276Ssam state = CMD; 79626494Sminshall longjmp(errcatch,0); 79710276Ssam } 79810276Ssam } 79910276Ssam 80010276Ssam upper(s) 80110276Ssam char *s; 80210276Ssam { 80310276Ssam while (*s != '\0') { 80410276Ssam if (islower(*s)) 80510276Ssam *s = toupper(*s); 80610276Ssam s++; 80710276Ssam } 80810276Ssam } 80910276Ssam 81010276Ssam copy(s) 81110276Ssam char *s; 81210276Ssam { 81310276Ssam char *p; 81426494Sminshall extern char *malloc(), *strcpy(); 81510276Ssam 81626494Sminshall p = malloc((unsigned) strlen(s) + 1); 81710276Ssam if (p == NULL) 81810276Ssam fatal("Ran out of memory."); 81926494Sminshall (void) strcpy(p, s); 82010276Ssam return ((int)p); 82110276Ssam } 82210276Ssam 82310276Ssam help(s) 82410276Ssam char *s; 82510276Ssam { 82610276Ssam register struct tab *c; 82710276Ssam register int width, NCMDS; 82810276Ssam 82910276Ssam width = 0, NCMDS = 0; 83010276Ssam for (c = cmdtab; c->name != NULL; c++) { 831*31132Smckusick int len = strlen(c->name) + 1; 83210276Ssam 83310276Ssam if (len > width) 83410276Ssam width = len; 83510276Ssam NCMDS++; 83610276Ssam } 83710276Ssam width = (width + 8) &~ 7; 83810276Ssam if (s == 0) { 83910276Ssam register int i, j, w; 84010276Ssam int columns, lines; 84110276Ssam 84210276Ssam lreply(214, 84310276Ssam "The following commands are recognized (* =>'s unimplemented)."); 84410276Ssam columns = 76 / width; 84510276Ssam if (columns == 0) 84610276Ssam columns = 1; 84710276Ssam lines = (NCMDS + columns - 1) / columns; 84810276Ssam for (i = 0; i < lines; i++) { 84927107Smckusick printf(" "); 85010276Ssam for (j = 0; j < columns; j++) { 85110276Ssam c = cmdtab + j * lines + i; 85210276Ssam printf("%s%c", c->name, 85310276Ssam c->implemented ? ' ' : '*'); 85410302Ssam if (c + lines >= &cmdtab[NCMDS]) 85510276Ssam break; 856*31132Smckusick w = strlen(c->name) + 1; 85710276Ssam while (w < width) { 85810276Ssam putchar(' '); 85910276Ssam w++; 86010276Ssam } 86110276Ssam } 86210276Ssam printf("\r\n"); 86310276Ssam } 86426494Sminshall (void) fflush(stdout); 86510276Ssam reply(214, "Direct comments to ftp-bugs@%s.", hostname); 86610276Ssam return; 86710276Ssam } 86810276Ssam upper(s); 86910276Ssam c = lookup(s); 87010276Ssam if (c == (struct tab *)0) { 87127107Smckusick reply(502, "Unknown command %s.", s); 87210276Ssam return; 87310276Ssam } 87410276Ssam if (c->implemented) 87510276Ssam reply(214, "Syntax: %s %s", c->name, c->help); 87610276Ssam else 87710276Ssam reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); 87810276Ssam } 879