X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/ff8adb766ce5c2f6385e55c921160b6a1be807cd..94ae9fcacb9cd5e70c293a55d838de48cda0d985:/usr/src/libexec/ftpd/ftpcmd.y diff --git a/usr/src/libexec/ftpd/ftpcmd.y b/usr/src/libexec/ftpd/ftpcmd.y index 66c3da3369..437e7c6ed5 100644 --- a/usr/src/libexec/ftpd/ftpcmd.y +++ b/usr/src/libexec/ftpd/ftpcmd.y @@ -1,46 +1,44 @@ /* - * Copyright (c) 1985, 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1985, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * %sccs.include.redist.c% * - * @(#)ftpcmd.y 5.19 (Berkeley) %G% + * @(#)ftpcmd.y 8.2 (Berkeley) %G% */ /* * Grammar for FTP commands. - * See RFC 765. + * See RFC 959. */ %{ #ifndef lint -static char sccsid[] = "@(#)ftpcmd.y 5.19 (Berkeley) %G%"; +static char sccsid[] = "@(#)ftpcmd.y 8.2 (Berkeley) %G%"; #endif /* not lint */ #include #include +#include #include - #include -#include -#include #include +#include +#include #include #include +#include +#include +#include +#include #include +#include +#include + +#include "extern.h" extern struct sockaddr_in data_dest; extern int logged_in; @@ -51,15 +49,13 @@ extern int type; extern int form; extern int debug; extern int timeout; +extern int maxtimeout; extern int pdata; extern char hostname[], remotehost[]; -extern char *globerr; +extern char proctitle[]; extern int usedefault; extern int transflag; extern char tmpline[]; -char **glob(); - -off_t restart_point; static int cmd_type; static int cmd_form; @@ -67,14 +63,18 @@ static int cmd_bytesz; char cbuf[512]; char *fromname; -char *index(); %} +%union { + int i; + char *s; +} + %token A B C E F I L N P R S T - SP CRLF COMMA STRING NUMBER + SP CRLF COMMA USER PASS ACCT REIN QUIT PORT PASV TYPE STRU MODE RETR STOR @@ -82,35 +82,45 @@ char *index(); MRSQ MRCP ALLO REST RNFR RNTO ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD - CDUP STOU SYST + CDUP STOU SMNT SYST SIZE MDTM + + UMASK IDLE CHMOD LEXERR +%token STRING +%token NUMBER + +%type check_login octal_number byte_size +%type struct_code mode_code type_code form_code +%type pathstring pathname password username + %start cmd_list %% -cmd_list: /* empty */ - | cmd_list cmd - = { +cmd_list + : /* empty */ + | cmd_list cmd + { fromname = (char *) 0; - restart_point = (off_t) 0; } - | cmd_list rcmd + | cmd_list rcmd ; -cmd: USER SP username CRLF - = { - user((char *) $3); - free((char *) $3); +cmd + : USER SP username CRLF + { + user($3); + free($3); } - | PASS SP password CRLF - = { - pass((char *) $3); - free((char *) $3); + | PASS SP password CRLF + { + pass($3); + free($3); } - | PORT SP host_port CRLF - = { + | PORT SP host_port CRLF + { usedefault = 0; if (pdata >= 0) { (void) close(pdata); @@ -118,12 +128,12 @@ cmd: USER SP username CRLF } reply(200, "PORT command successful."); } - | PASV CRLF - = { + | PASV CRLF + { passive(); } - | TYPE SP type_code CRLF - = { + | TYPE SP type_code CRLF + { switch (cmd_type) { case TYPE_A: @@ -145,18 +155,20 @@ cmd: USER SP username CRLF break; case TYPE_L: - if (cmd_bytesz == NBBY) { +#if NBBY == 8 + if (cmd_bytesz == 8) { reply(200, - "Type set to L (byte size %d).", - NBBY); + "Type set to L (byte size 8)."); type = cmd_type; } else - reply(504, "Byte size must be %d.", - NBBY); + reply(504, "Byte size must be 8."); +#else /* NBBY == 8 */ + UNIMPLEMENTED for NBBY != 8 +#endif /* NBBY == 8 */ } } - | STRU SP struct_code CRLF - = { + | STRU SP struct_code CRLF + { switch ($3) { case STRU_F: @@ -167,8 +179,8 @@ cmd: USER SP username CRLF reply(504, "Unimplemented STRU type."); } } - | MODE SP mode_code CRLF - = { + | MODE SP mode_code CRLF + { switch ($3) { case MODE_S: @@ -179,190 +191,329 @@ cmd: USER SP username CRLF reply(502, "Unimplemented MODE type."); } } - | ALLO SP NUMBER CRLF - = { + | ALLO SP NUMBER CRLF + { + reply(202, "ALLO command ignored."); + } + | ALLO SP NUMBER SP R SP NUMBER CRLF + { reply(202, "ALLO command ignored."); } - | RETR check_login SP pathname CRLF - = { + | RETR check_login SP pathname CRLF + { if ($2 && $4 != NULL) - retrieve((char *) 0, (char *)$4); + retrieve((char *) 0, $4); if ($4 != NULL) - free((char *) $4); + free($4); } - | STOR check_login SP pathname CRLF - = { + | STOR check_login SP pathname CRLF + { if ($2 && $4 != NULL) - store((char *) $4, "w", 0); + store($4, "w", 0); if ($4 != NULL) - free((char *) $4); + free($4); } - | APPE check_login SP pathname CRLF - = { + | APPE check_login SP pathname CRLF + { if ($2 && $4 != NULL) - store((char *) $4, "a", 0); + store($4, "a", 0); if ($4 != NULL) - free((char *) $4); + free($4); } - | NLST check_login CRLF - = { + | NLST check_login CRLF + { if ($2) send_file_list("."); } - | NLST check_login SP STRING CRLF - = { - if ($2 && $4 != NULL) - send_file_list((char *) $4); + | NLST check_login SP STRING CRLF + { + if ($2 && $4 != NULL) + send_file_list($4); if ($4 != NULL) - free((char *) $4); + free($4); } - | LIST check_login CRLF - = { + | LIST check_login CRLF + { if ($2) retrieve("/bin/ls -lgA", ""); } - | LIST check_login SP pathname CRLF - = { + | LIST check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + retrieve("/bin/ls -lgA %s", $4); + if ($4 != NULL) + free($4); + } + | STAT check_login SP pathname CRLF + { if ($2 && $4 != NULL) - retrieve("/bin/ls -lgA %s", (char *) $4); + statfilecmd($4); if ($4 != NULL) - free((char *) $4); + free($4); + } + | STAT CRLF + { + statcmd(); } - | DELE check_login SP pathname CRLF - = { + | DELE check_login SP pathname CRLF + { if ($2 && $4 != NULL) - delete((char *) $4); + delete($4); if ($4 != NULL) - free((char *) $4); + free($4); } - | RNTO SP pathname CRLF - = { + | RNTO SP pathname CRLF + { if (fromname) { - renamecmd(fromname, (char *) $3); + renamecmd(fromname, $3); free(fromname); fromname = (char *) 0; } else { reply(503, "Bad sequence of commands."); } - free((char *) $3); + free($3); } - | ABOR CRLF - = { + | ABOR CRLF + { reply(225, "ABOR command successful."); } - | CWD check_login CRLF - = { + | CWD check_login CRLF + { if ($2) cwd(pw->pw_dir); } - | CWD check_login SP pathname CRLF - = { + | CWD check_login SP pathname CRLF + { if ($2 && $4 != NULL) - cwd((char *) $4); + cwd($4); if ($4 != NULL) - free((char *) $4); + free($4); } - | HELP CRLF - = { - help((char *) 0); + | HELP CRLF + { + help(cmdtab, (char *) 0); } - | HELP SP STRING CRLF - = { - help((char *) $3); + | HELP SP STRING CRLF + { + char *cp = $3; + + if (strncasecmp(cp, "SITE", 4) == 0) { + cp = $3 + 4; + if (*cp == ' ') + cp++; + if (*cp) + help(sitetab, cp); + else + help(sitetab, (char *) 0); + } else + help(cmdtab, $3); } - | NOOP CRLF - = { + | NOOP CRLF + { reply(200, "NOOP command successful."); } - | MKD check_login SP pathname CRLF - = { + | MKD check_login SP pathname CRLF + { if ($2 && $4 != NULL) - makedir((char *) $4); + makedir($4); if ($4 != NULL) - free((char *) $4); + free($4); } - | RMD check_login SP pathname CRLF - = { + | RMD check_login SP pathname CRLF + { if ($2 && $4 != NULL) - removedir((char *) $4); + removedir($4); if ($4 != NULL) - free((char *) $4); + free($4); } - | PWD check_login CRLF - = { + | PWD check_login CRLF + { if ($2) pwd(); } - | CDUP check_login CRLF - = { + | CDUP check_login CRLF + { if ($2) cwd(".."); } - | STOU check_login SP pathname CRLF - = { + | SITE SP HELP CRLF + { + help(sitetab, (char *) 0); + } + | SITE SP HELP SP STRING CRLF + { + help(sitetab, $5); + } + | SITE SP UMASK check_login CRLF + { + int oldmask; + + if ($4) { + oldmask = umask(0); + (void) umask(oldmask); + reply(200, "Current UMASK is %03o", oldmask); + } + } + | SITE SP UMASK check_login SP octal_number CRLF + { + int oldmask; + + if ($4) { + if (($6 == -1) || ($6 > 0777)) { + reply(501, "Bad UMASK value"); + } else { + oldmask = umask($6); + reply(200, + "UMASK set to %03o (was %03o)", + $6, oldmask); + } + } + } + | SITE SP CHMOD check_login SP octal_number SP pathname CRLF + { + if ($4 && ($8 != NULL)) { + if ($6 > 0777) + reply(501, + "CHMOD: Mode value must be between 0 and 0777"); + else if (chmod($8, $6) < 0) + perror_reply(550, $8); + else + reply(200, "CHMOD command successful."); + } + if ($8 != NULL) + free($8); + } + | SITE SP IDLE CRLF + { + reply(200, + "Current IDLE time limit is %d seconds; max %d", + timeout, maxtimeout); + } + | SITE SP IDLE SP NUMBER CRLF + { + if ($5 < 30 || $5 > maxtimeout) { + reply(501, + "Maximum IDLE time must be between 30 and %d seconds", + maxtimeout); + } else { + timeout = $5; + (void) alarm((unsigned) timeout); + reply(200, + "Maximum IDLE time set to %d seconds", + timeout); + } + } + | STOU check_login SP pathname CRLF + { if ($2 && $4 != NULL) - store((char *) $4, "w", 1); + store($4, "w", 1); if ($4 != NULL) - free((char *) $4); + free($4); } - | SYST CRLF - = { + | SYST CRLF + { #ifdef unix +#ifdef BSD reply(215, "UNIX Type: L%d Version: BSD-%d", -#else - reply(215, "UNKNOWN Type: L%d Version: BSD-%d", -#endif NBBY, BSD); +#else /* BSD */ + reply(215, "UNIX Type: L%d", NBBY); +#endif /* BSD */ +#else /* unix */ + reply(215, "UNKNOWN Type: L%d", NBBY); +#endif /* unix */ + } + + /* + * SIZE is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + | SIZE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + sizecmd($4); + if ($4 != NULL) + free($4); } - | QUIT CRLF - = { + + /* + * MDTM is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return modification time of file as an ISO 3307 + * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx + * where xxx is the fractional second (of any precision, + * not necessarily 3 digits) + */ + | MDTM check_login SP pathname CRLF + { + if ($2 && $4 != NULL) { + struct stat stbuf; + if (stat($4, &stbuf) < 0) + reply(550, "%s: %s", + $4, strerror(errno)); + else if (!S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", $4); + } else { + struct tm *t; + t = gmtime(&stbuf.st_mtime); + reply(213, + "19%02d%02d%02d%02d%02d%02d", + t->tm_year, t->tm_mon+1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + } + if ($4 != NULL) + free($4); + } + | QUIT CRLF + { reply(221, "Goodbye."); dologout(0); } - | error CRLF - = { + | error CRLF + { yyerrok; } ; -rcmd: RNFR check_login SP pathname CRLF - = { +rcmd + : RNFR check_login SP pathname CRLF + { char *renamefrom(); - restart_point = (off_t) 0; if ($2 && $4) { - fromname = renamefrom((char *) $4); + fromname = renamefrom($4); if (fromname == (char *) 0 && $4) { - free((char *) $4); + free($4); } } } - | REST SP byte_size CRLF - = { - long atol(); - - fromname = (char *) 0; - restart_point = $3; - reply(350, "Restarting at %ld. Send STORE or RETRIEVE to initiate transfer.", restart_point); - } ; - -username: STRING + +username + : STRING ; -password: /* empty */ - = { - $$ = (int) ""; +password + : /* empty */ + { + $$ = (char *)calloc(1, sizeof(char)); } - | STRING + | STRING ; -byte_size: NUMBER +byte_size + : NUMBER ; -host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA +host_port + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER - = { - register char *a, *p; + { + char *a, *p; a = (char *)&data_dest.sin_addr; a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; @@ -372,121 +523,161 @@ host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA } ; -form_code: N - = { - $$ = FORM_N; - } - | T - = { - $$ = FORM_T; - } - | C - = { - $$ = FORM_C; - } +form_code + : N + { + $$ = FORM_N; + } + | T + { + $$ = FORM_T; + } + | C + { + $$ = FORM_C; + } ; -type_code: A - = { - cmd_type = TYPE_A; - cmd_form = FORM_N; - } - | A SP form_code - = { - cmd_type = TYPE_A; - cmd_form = $3; - } - | E - = { - cmd_type = TYPE_E; - cmd_form = FORM_N; - } - | E SP form_code - = { - cmd_type = TYPE_E; - cmd_form = $3; - } - | I - = { - cmd_type = TYPE_I; - } - | L - = { - cmd_type = TYPE_L; - cmd_bytesz = NBBY; - } - | L SP byte_size - = { - cmd_type = TYPE_L; - cmd_bytesz = $3; - } - /* this is for a bug in the BBN ftp */ - | L byte_size - = { - cmd_type = TYPE_L; - cmd_bytesz = $2; - } +type_code + : A + { + cmd_type = TYPE_A; + cmd_form = FORM_N; + } + | A SP form_code + { + cmd_type = TYPE_A; + cmd_form = $3; + } + | E + { + cmd_type = TYPE_E; + cmd_form = FORM_N; + } + | E SP form_code + { + cmd_type = TYPE_E; + cmd_form = $3; + } + | I + { + cmd_type = TYPE_I; + } + | L + { + cmd_type = TYPE_L; + cmd_bytesz = NBBY; + } + | L SP byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $3; + } + /* this is for a bug in the BBN ftp */ + | L byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $2; + } ; -struct_code: F - = { - $$ = STRU_F; - } - | R - = { - $$ = STRU_R; - } - | P - = { - $$ = STRU_P; - } +struct_code + : F + { + $$ = STRU_F; + } + | R + { + $$ = STRU_R; + } + | P + { + $$ = STRU_P; + } ; -mode_code: S - = { - $$ = MODE_S; - } - | B - = { - $$ = MODE_B; - } - | C - = { - $$ = MODE_C; - } +mode_code + : S + { + $$ = MODE_S; + } + | B + { + $$ = MODE_B; + } + | C + { + $$ = MODE_C; + } ; -pathname: pathstring - = { - /* - * Problem: this production is used for all pathname - * processing, but only gives a 550 error reply. - * This is a valid reply in some cases but not in others. - */ - if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { - $$ = (int)*glob((char *) $1); - if (globerr != NULL) { - reply(550, globerr); - $$ = NULL; - } - free((char *) $1); - } else - $$ = $1; - } +pathname + : pathstring + { + /* + * Problem: this production is used for all pathname + * processing, but only gives a 550 error reply. + * This is a valid reply in some cases but not in others. + */ + if (logged_in && $1 && *$1 == '~') { + glob_t gl; + int flags = GLOB_BRACE|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + if (glob($1, flags, NULL, &gl)) { + reply(550, "not found"); + $$ = NULL; + } else { + $$ = strdup(gl.gl_pathv[0]); + } + globfree(&gl); + free($1); + } else + $$ = $1; + } ; -pathstring: STRING +pathstring + : STRING ; -check_login: /* empty */ - = { - if (logged_in) - $$ = 1; - else { - reply(530, "Please login with USER and PASS."); - $$ = 0; +octal_number + : NUMBER + { + int ret, dec, multby, digit; + + /* + * Convert a number that was read as decimal number + * to what it would be if it had been read as octal. + */ + dec = $1; + multby = 1; + ret = 0; + while (dec) { + digit = dec%10; + if (digit > 7) { + ret = -1; + break; + } + ret += digit * multby; + multby *= 8; + dec /= 10; + } + $$ = ret; + } + ; + + +check_login + : /* empty */ + { + if (logged_in) + $$ = 1; + else { + reply(530, "Please login with USER and PASS."); + $$ = 0; + } } - } ; %% @@ -500,6 +691,8 @@ extern jmp_buf errcatch; #define OSTR 4 /* optional SP then STRING */ #define ZSTR1 5 /* SP then optional STRING */ #define ZSTR2 6 /* optional STRING after SP */ +#define SITECMD 7 /* SITE command */ +#define NSTR 8 /* Number followed by a string */ struct tab { char *name; @@ -513,6 +706,7 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "USER", USER, STR1, 1, " username" }, { "PASS", PASS, ZSTR1, 1, " password" }, { "ACCT", ACCT, STR1, 0, "(specify account)" }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4" }, @@ -531,7 +725,7 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, - { "REST", REST, ARGS, 1, "(restart command)" }, + { "REST", REST, ARGS, 0, "(restart command)" }, { "RNFR", RNFR, STR1, 1, " file-name" }, { "RNTO", RNTO, STR1, 1, " file-name" }, { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, @@ -540,9 +734,9 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "XCWD", CWD, OSTR, 1, "[ directory-name ]" }, { "LIST", LIST, OSTR, 1, "[ path-name ]" }, { "NLST", NLST, OSTR, 1, "[ path-name ]" }, - { "SITE", SITE, STR1, 0, "(get site parameters)" }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]" }, { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, - { "STAT", STAT, OSTR, 0, "(get server status)" }, + { "STAT", STAT, OSTR, 1, "[ path-name ]" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { "NOOP", NOOP, ARGS, 1, "" }, { "MKD", MKD, STR1, 1, " path-name" }, @@ -554,16 +748,34 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "STOU", STOU, STR1, 1, " file-name" }, + { "SIZE", SIZE, OSTR, 1, " path-name" }, + { "MDTM", MDTM, OSTR, 1, " path-name" }, + { NULL, 0, 0, 0, 0 } +}; + +struct tab sitetab[] = { + { "UMASK", UMASK, ARGS, 1, "[ umask ]" }, + { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, + { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, + { "HELP", HELP, OSTR, 1, "[ ]" }, { NULL, 0, 0, 0, 0 } }; -struct tab * -lookup(cmd) +static char *copy __P((char *)); +static void help __P((struct tab *, char *)); +static struct tab * + lookup __P((struct tab *, char *)); +static void sizecmd __P((char *)); +static void toolong __P((int)); +static int yylex __P((void)); + +static struct tab * +lookup(p, cmd) + struct tab *p; char *cmd; { - register struct tab *p; - for (p = cmdtab; p->name != NULL; p++) + for (; p->name != NULL; p++) if (strcmp(cmd, p->name) == 0) return (p); return (0); @@ -577,9 +789,10 @@ lookup(cmd) char * getline(s, n, iop) char *s; - register FILE *iop; + int n; + FILE *iop; { - register c; + int c; register char *cs; cs = s; @@ -628,36 +841,48 @@ getline(s, n, iop) if (c == EOF && cs == s) return (NULL); *cs++ = '\0'; - if (debug) - syslog(LOG_DEBUG, "command: %s", s); + if (debug) { + if (!guest && strncasecmp("pass ", s, 5) == 0) { + /* Don't syslog passwords */ + syslog(LOG_DEBUG, "command: %.5s ???", s); + } else { + register char *cp; + register int len; + + /* Don't syslog trailing CR-LF */ + len = strlen(s); + cp = s + len - 1; + while (cp >= s && (*cp == '\n' || *cp == '\r')) { + --cp; + --len; + } + syslog(LOG_DEBUG, "command: %.*s", len, s); + } + } return (s); } -static int -toolong() +static void +toolong(signo) + int signo; { - time_t now; - extern char *ctime(); - extern time_t time(); reply(421, - "Timeout (%d seconds): closing control connection.", timeout); - (void) time(&now); - if (logging) { - syslog(LOG_INFO, - "User %s timed out after %d seconds at %s", - (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); - } + "Timeout (%d seconds): closing control connection.", timeout); + if (logging) + syslog(LOG_INFO, "User %s timed out after %d seconds", + (pw ? pw -> pw_name : "unknown"), timeout); dologout(1); } +static int yylex() { static int cpos, state; - register char *cp; - register struct tab *p; + char *cp, *cp2; + struct tab *p; int n; - char c, *strpbrk(); + char c; for (;;) { switch (state) { @@ -671,11 +896,10 @@ yylex() } (void) alarm(0); #ifdef SETPROCTITLE - if (strncasecmp(cbuf, "PASS", 4) != NULL) { - setproctitle("%s: %s", remotehost, cbuf); - } + if (strncasecmp(cbuf, "PASS", 4) != NULL) + setproctitle("%s: %s", proctitle, cbuf); #endif /* SETPROCTITLE */ - if ((cp = index(cbuf, '\r'))) { + if ((cp = strchr(cbuf, '\r'))) { *cp++ = '\n'; *cp = '\0'; } @@ -686,18 +910,45 @@ yylex() c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cbuf); - p = lookup(cbuf); + p = lookup(cmdtab, cbuf); + cbuf[cpos] = c; + if (p != 0) { + if (p->implemented == 0) { + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + yylval.s = p->name; + return (p->token); + } + break; + + case SITECMD: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + cp = &cbuf[cpos]; + if ((cp2 = strpbrk(cp, " \n"))) + cpos = cp2 - cbuf; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cp); + p = lookup(sitetab, cp); cbuf[cpos] = c; if (p != 0) { if (p->implemented == 0) { + state = CMD; nack(p->name); longjmp(errcatch,0); /* NOTREACHED */ } state = p->state; - yylval = (int) p->name; + yylval.s = p->name; return (p->token); } + state = CMD; break; case OSTR: @@ -709,6 +960,7 @@ yylex() case STR1: case ZSTR1: + dostr1: if (cbuf[cpos] == ' ') { cpos++; state = state == OSTR ? STR2 : ++state; @@ -721,7 +973,7 @@ yylex() state = CMD; return (CRLF); } - /* FALL THRU */ + /* FALLTHROUGH */ case STR2: cp = &cbuf[cpos]; @@ -732,13 +984,32 @@ yylex() */ if (n > 1 && cbuf[cpos] == '\n') { cbuf[cpos] = '\0'; - yylval = copy(cp); + yylval.s = copy(cp); cbuf[cpos] = '\n'; state = ARGS; return (STRING); } break; + case NSTR: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval.i = atoi(cp); + cbuf[cpos] = c; + state = STR1; + return (NUMBER); + } + state = STR1; + goto dostr1; + case ARGS: if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; @@ -746,7 +1017,7 @@ yylex() ; c = cbuf[cpos]; cbuf[cpos] = '\0'; - yylval = atoi(cp); + yylval.i = atoi(cp); cbuf[cpos] = c; return (NUMBER); } @@ -822,8 +1093,9 @@ yylex() } } +void upper(s) - register char *s; + char *s; { while (*s != '\0') { if (islower(*s)) @@ -832,28 +1104,35 @@ upper(s) } } +static char * copy(s) char *s; { char *p; - extern char *malloc(), *strcpy(); p = malloc((unsigned) strlen(s) + 1); if (p == NULL) fatal("Ran out of memory."); (void) strcpy(p, s); - return ((int)p); + return (p); } -help(s) +static void +help(ctab, s) + struct tab *ctab; char *s; { - register struct tab *c; - register int width, NCMDS; + struct tab *c; + int width, NCMDS; + char *type; + if (ctab == sitetab) + type = "SITE "; + else + type = ""; width = 0, NCMDS = 0; - for (c = cmdtab; c->name != NULL; c++) { - int len = strlen(c->name) + 1; + for (c = ctab; c->name != NULL; c++) { + int len = strlen(c->name); if (len > width) width = len; @@ -861,11 +1140,11 @@ help(s) } width = (width + 8) &~ 7; if (s == 0) { - register int i, j, w; + int i, j, w; int columns, lines; - lreply(214, - "The following commands are recognized (* =>'s unimplemented)."); + lreply(214, "The following %scommands are recognized %s.", + type, "(* =>'s unimplemented)"); columns = 76 / width; if (columns == 0) columns = 1; @@ -873,10 +1152,10 @@ help(s) for (i = 0; i < lines; i++) { printf(" "); for (j = 0; j < columns; j++) { - c = cmdtab + j * lines + i; + c = ctab + j * lines + i; printf("%s%c", c->name, c->implemented ? ' ' : '*'); - if (c + lines >= &cmdtab[NCMDS]) + if (c + lines >= &ctab[NCMDS]) break; w = strlen(c->name) + 1; while (w < width) { @@ -891,13 +1170,58 @@ help(s) return; } upper(s); - c = lookup(s); + c = lookup(ctab, s); if (c == (struct tab *)0) { reply(502, "Unknown command %s.", s); return; } if (c->implemented) - reply(214, "Syntax: %s %s", c->name, c->help); + reply(214, "Syntax: %s%s %s", type, c->name, c->help); else - reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); + reply(214, "%s%-*s\t%s; unimplemented.", type, width, + c->name, c->help); +} + +static void +sizecmd(filename) + char *filename; +{ + switch (type) { + case TYPE_L: + case TYPE_I: { + struct stat stbuf; + if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) + reply(550, "%s: not a plain file.", filename); + else + reply(213, "%qu", stbuf.st_size); + break; } + case TYPE_A: { + FILE *fin; + int c; + off_t count; + struct stat stbuf; + fin = fopen(filename, "r"); + if (fin == NULL) { + perror_reply(550, filename); + return; + } + if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", filename); + (void) fclose(fin); + return; + } + + count = 0; + while((c=getc(fin)) != EOF) { + if (c == '\n') /* will get expanded to \r\n */ + count++; + count++; + } + (void) fclose(fin); + + reply(213, "%qd", count); + break; } + default: + reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); + } }