1 /* $NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $ */
4 * Copyright (c) 1985, 1988, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
39 * Grammar for FTP commands.
45 #include "ftpd_locl.h"
46 RCSID("$Id: ftpcmd.y,v 1.61 2001/08/05 06:39:29 assar Exp $");
52 static int cmd_bytesz;
60 short implemented; /* 1 if command is implemented */
64 extern struct tab cmdtab[];
65 extern struct tab sitetab[];
67 static char *copy (char *);
68 static void help (struct tab *, char *);
70 lookup (struct tab *, char *);
71 static void sizecmd (char *);
72 static RETSIGTYPE toolong (int);
73 static int yylex (void);
75 /* This is for bison */
77 #if !defined(alloca) && !defined(HAVE_ALLOCA)
78 #define alloca(x) malloc(x)
94 USER PASS ACCT REIN QUIT PORT
95 PASV TYPE STRU MODE RETR STOR
96 APPE MLFL MAIL MSND MSOM MSAM
97 MRSQ MRCP ALLO REST RNFR RNTO
98 ABOR DELE CWD LIST NLST SITE
99 sTAT HELP NOOP MKD RMD PWD
100 CDUP STOU SMNT SYST SIZE MDTM
105 AUTH ADAT PROT PBSZ CCC MIC
108 KAUTH KLIST KDESTROY KRBTKFILE AFSLOG
118 %type <i> check_login check_login_no_guest check_secure octal_number byte_size
119 %type <i> struct_code mode_code type_code form_code
120 %type <s> pathstring pathname password username
130 fromname = (char *) 0;
131 restart_point = (off_t) 0;
137 : USER SP username CRLF
142 | PASS SP password CRLF
145 memset ($3, 0, strlen($3));
148 | PORT SP host_port CRLF
155 reply(200, "PORT command successful.");
157 | EPRT SP STRING CRLF
162 | PASV CRLF check_login
167 | EPSV CRLF check_login
172 | EPSV SP STRING CRLF check_login
178 | TYPE SP type_code CRLF
183 if (cmd_form == FORM_N) {
184 reply(200, "Type set to A.");
188 reply(504, "Form must be N.");
192 reply(504, "Type E not implemented.");
196 reply(200, "Type set to I.");
202 if (cmd_bytesz == 8) {
204 "Type set to L (byte size 8).");
207 reply(504, "Byte size must be 8.");
208 #else /* NBBY == 8 */
209 UNIMPLEMENTED for NBBY != 8
210 #endif /* NBBY == 8 */
213 | STRU SP struct_code CRLF
218 reply(200, "STRU F ok.");
222 reply(504, "Unimplemented STRU type.");
225 | MODE SP mode_code CRLF
230 reply(200, "MODE S ok.");
234 reply(502, "Unimplemented MODE type.");
237 | ALLO SP NUMBER CRLF
239 reply(202, "ALLO command ignored.");
241 | ALLO SP NUMBER SP R SP NUMBER CRLF
243 reply(202, "ALLO command ignored.");
245 | RETR SP pathname CRLF check_login
249 if ($5 && name != NULL)
254 | STOR SP pathname CRLF check_login
258 if ($5 && name != NULL)
259 do_store(name, "w", 0);
263 | APPE SP pathname CRLF check_login
267 if ($5 && name != NULL)
268 do_store(name, "a", 0);
272 | NLST CRLF check_login
277 | NLST SP STRING CRLF check_login
281 if ($5 && name != NULL)
282 send_file_list(name);
286 | LIST CRLF check_login
291 | LIST SP pathname CRLF check_login
297 | sTAT SP pathname CRLF check_login
299 if ($5 && $3 != NULL)
307 if (file_size != (off_t) -1)
308 reply(213, "Status: %lu of %lu bytes transferred",
309 (unsigned long)byte_count,
310 (unsigned long)file_size);
312 reply(213, "Status: %lu bytes transferred",
313 (unsigned long)byte_count);
317 | DELE SP pathname CRLF check_login_no_guest
319 if ($5 && $3 != NULL)
324 | RNTO SP pathname CRLF check_login_no_guest
328 renamecmd(fromname, $3);
330 fromname = (char *) 0;
332 reply(503, "Bad sequence of commands.");
341 reply(426, "Transfer aborted. Data connection closed.");
342 reply(226, "Abort successful");
344 longjmp(urgcatch, 1);
346 reply(225, "ABOR command successful.");
348 | CWD CRLF check_login
353 | CWD SP pathname CRLF check_login
355 if ($5 && $3 != NULL)
362 help(cmdtab, (char *) 0);
364 | HELP SP STRING CRLF
368 if (strncasecmp(cp, "SITE", 4) == 0) {
375 help(sitetab, (char *) 0);
381 reply(200, "NOOP command successful.");
383 | MKD SP pathname CRLF check_login
385 if ($5 && $3 != NULL)
390 | RMD SP pathname CRLF check_login_no_guest
392 if ($5 && $3 != NULL)
397 | PWD CRLF check_login
402 | CDUP CRLF check_login
409 lreply(211, "Supported features:");
411 lreply(0, " REST STREAM");
415 | OPTS SP STRING CRLF
418 reply(501, "Bad options");
423 help(sitetab, (char *) 0);
425 | SITE SP HELP SP STRING CRLF
429 | SITE SP UMASK CRLF check_login
432 int oldmask = umask(0);
434 reply(200, "Current UMASK is %03o", oldmask);
437 | SITE SP UMASK SP octal_number CRLF check_login_no_guest
440 if (($5 == -1) || ($5 > 0777)) {
441 reply(501, "Bad UMASK value");
443 int oldmask = umask($5);
445 "UMASK set to %03o (was %03o)",
450 | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
452 if ($9 && $7 != NULL) {
455 "CHMOD: Mode value must be between 0 and 0777");
456 else if (chmod($7, $5) < 0)
457 perror_reply(550, $7);
459 reply(200, "CHMOD command successful.");
467 "Current IDLE time limit is %d seconds; max %d",
468 ftpd_timeout, maxtimeout);
470 | SITE SP IDLE SP NUMBER CRLF
472 if ($5 < 30 || $5 > maxtimeout) {
474 "Maximum IDLE time must be between 30 and %d seconds",
478 alarm((unsigned) ftpd_timeout);
480 "Maximum IDLE time set to %d seconds",
485 | SITE SP KAUTH SP STRING CRLF check_login
491 reply(500, "Can't be done as guest.");
493 if($7 && $5 != NULL){
494 p = strpbrk($5, " \t");
497 kauth($5, p + strspn(p, " \t"));
505 reply(500, "Command not implemented.");
508 | SITE SP KLIST CRLF check_login
514 reply(500, "Command not implemented.");
517 | SITE SP KDESTROY CRLF check_login
523 reply(500, "Command not implemented.");
526 | SITE SP KRBTKFILE SP STRING CRLF check_login
530 reply(500, "Can't be done as guest.");
536 reply(500, "Command not implemented.");
539 | SITE SP AFSLOG CRLF check_login
543 reply(500, "Can't be done as guest.");
547 reply(500, "Command not implemented.");
550 | SITE SP AFSLOG SP STRING CRLF check_login
554 reply(500, "Can't be done as guest.");
560 reply(500, "Command not implemented.");
563 | SITE SP LOCATE SP STRING CRLF check_login
572 reply(200, "http://www.pdc.kth.se/kth-krb/");
574 | STOU SP pathname CRLF check_login
576 if ($5 && $3 != NULL)
577 do_store($3, "w", 1);
583 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
584 reply(215, "UNIX Type: L%d", NBBY);
586 reply(215, "UNKNOWN Type: L%d", NBBY);
591 * SIZE is not in RFC959, but Postel has blessed it and
592 * it will be in the updated RFC.
594 * Return size of file in a format suitable for
595 * using with RESTART (we just count bytes).
597 | SIZE SP pathname CRLF check_login
599 if ($5 && $3 != NULL)
606 * MDTM is not in RFC959, but Postel has blessed it and
607 * it will be in the updated RFC.
609 * Return modification time of file as an ISO 3307
610 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
611 * where xxx is the fractional second (of any precision,
612 * not necessarily 3 digits)
614 | MDTM SP pathname CRLF check_login
616 if ($5 && $3 != NULL) {
618 if (stat($3, &stbuf) < 0)
620 $3, strerror(errno));
621 else if (!S_ISREG(stbuf.st_mode)) {
623 "%s: not a plain file.", $3);
626 time_t mtime = stbuf.st_mtime;
630 "%04d%02d%02d%02d%02d%02d",
644 reply(221, "Goodbye.");
653 : RNFR SP pathname CRLF check_login_no_guest
655 restart_point = (off_t) 0;
657 fromname = renamefrom($3);
658 if (fromname == (char *) 0 && $3) {
663 | REST SP byte_size CRLF
665 fromname = (char *) 0;
666 restart_point = $3; /* XXX $3 is only "int" */
667 reply(350, "Restarting at %ld. %s",
669 "Send STORE or RETRIEVE to initiate transfer.");
671 | AUTH SP STRING CRLF
676 | ADAT SP STRING CRLF
681 | PBSZ SP NUMBER CRLF
685 | PROT SP STRING CRLF
698 | CONF SP STRING CRLF
700 mec($3, prot_confidential);
705 mec($3, prot_private);
717 $$ = (char *)calloc(1, sizeof(char));
727 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
730 struct sockaddr_in *sin = (struct sockaddr_in *)data_dest;
732 sin->sin_family = AF_INET;
733 sin->sin_port = htons($9 * 256 + $11);
734 sin->sin_addr.s_addr =
735 htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
789 /* this is for a bug in the BBN ftp */
831 * Problem: this production is used for all pathname
832 * processing, but only gives a 550 error reply.
833 * This is a valid reply in some cases but not in others.
835 if (logged_in && $1 && *$1 == '~') {
838 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
840 memset(&gl, 0, sizeof(gl));
841 if (glob($1, flags, NULL, &gl) ||
843 reply(550, "not found");
846 $$ = strdup(gl.gl_pathv[0]);
862 int ret, dec, multby, digit;
865 * Convert a number that was read as decimal number
866 * to what it would be if it had been read as octal.
877 ret += digit * multby;
886 check_login_no_guest : check_login
890 reply(550, "Permission denied");
894 check_login : check_secure
897 if(($$ = logged_in) == 0)
898 reply(530, "Please login with USER and PASS.");
904 check_secure : /* empty */
907 if(sec_complete && !secure_command()) {
909 reply(533, "Command protection level denied "
910 "for paranoid reasons.");
917 extern jmp_buf errcatch;
919 #define CMD 0 /* beginning of command */
920 #define ARGS 1 /* expect miscellaneous arguments */
921 #define STR1 2 /* expect SP followed by STRING */
922 #define STR2 3 /* expect STRING */
923 #define OSTR 4 /* optional SP then STRING */
924 #define ZSTR1 5 /* SP then optional STRING */
925 #define ZSTR2 6 /* optional STRING after SP */
926 #define SITECMD 7 /* SITE command */
927 #define NSTR 8 /* Number followed by a string */
929 struct tab cmdtab[] = { /* In order defined in RFC 765 */
930 { "USER", USER, STR1, 1, "<sp> username" },
931 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
932 { "ACCT", ACCT, STR1, 0, "(specify account)" },
933 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
934 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
935 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
936 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
937 { "EPRT", EPRT, STR1, 1, "<sp> string" },
938 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
939 { "EPSV", EPSV, OSTR, 1, "[<sp> foo]" },
940 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
941 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
942 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
943 { "RETR", RETR, STR1, 1, "<sp> file-name" },
944 { "STOR", STOR, STR1, 1, "<sp> file-name" },
945 { "APPE", APPE, STR1, 1, "<sp> file-name" },
946 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
947 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
948 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
949 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
950 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
951 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
952 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
953 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
954 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
955 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
956 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
957 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
958 { "DELE", DELE, STR1, 1, "<sp> file-name" },
959 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
960 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
961 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
962 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
963 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
964 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
965 { "STAT", sTAT, OSTR, 1, "[ <sp> path-name ]" },
966 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
967 { "NOOP", NOOP, ARGS, 1, "" },
968 { "MKD", MKD, STR1, 1, "<sp> path-name" },
969 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
970 { "RMD", RMD, STR1, 1, "<sp> path-name" },
971 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
972 { "PWD", PWD, ARGS, 1, "(return current directory)" },
973 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
974 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
975 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
976 { "STOU", STOU, STR1, 1, "<sp> file-name" },
977 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
978 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
980 /* extensions from RFC2228 */
981 { "AUTH", AUTH, STR1, 1, "<sp> auth-type" },
982 { "ADAT", ADAT, STR1, 1, "<sp> auth-data" },
983 { "PBSZ", PBSZ, ARGS, 1, "<sp> buffer-size" },
984 { "PROT", PROT, STR1, 1, "<sp> prot-level" },
985 { "CCC", CCC, ARGS, 1, "" },
986 { "MIC", MIC, STR1, 1, "<sp> integrity command" },
987 { "CONF", CONF, STR1, 1, "<sp> confidentiality command" },
988 { "ENC", ENC, STR1, 1, "<sp> privacy command" },
991 { "FEAT", FEAT, ARGS, 1, "" },
992 { "OPTS", OPTS, ARGS, 1, "<sp> command [<sp> options]" },
997 struct tab sitetab[] = {
998 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
999 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1000 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1001 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1003 { "KAUTH", KAUTH, STR1, 1, "<sp> principal [ <sp> ticket ]" },
1004 { "KLIST", KLIST, ARGS, 1, "(show ticket file)" },
1005 { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1006 { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1007 { "AFSLOG", AFSLOG, OSTR, 1, "[<sp> cell]" },
1009 { "LOCATE", LOCATE, STR1, 1, "<sp> globexpr" },
1010 { "FIND", LOCATE, STR1, 1, "<sp> globexpr" },
1012 { "URL", URL, ARGS, 1, "?" },
1014 { NULL, 0, 0, 0, 0 }
1018 lookup(struct tab *p, char *cmd)
1021 for (; p->name != NULL; p++)
1022 if (strcmp(cmd, p->name) == 0)
1028 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1031 ftpd_getline(char *s, int n)
1037 /* tmpline may contain saved command from urgent mode interruption */
1039 strlcpy(s, ftp_command, n);
1041 syslog(LOG_DEBUG, "command: %s", s);
1043 fprintf(stderr, "%s\n", s);
1047 while ((c = getc(stdin)) != EOF) {
1050 if ((c = getc(stdin)) != EOF) {
1056 printf("%c%c%c", IAC, DONT, 0377&c);
1062 printf("%c%c%c", IAC, WONT, 0377&c);
1068 continue; /* ignore command */
1073 if (--n <= 0 || c == '\n')
1076 if (c == EOF && cs == s)
1080 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1081 /* Don't syslog passwords */
1082 syslog(LOG_DEBUG, "command: %.5s ???", s);
1087 /* Don't syslog trailing CR-LF */
1090 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1094 syslog(LOG_DEBUG, "command: %.*s", len, s);
1098 fprintf(stderr, "%s\n", s);
1108 "Timeout (%d seconds): closing control connection.",
1111 syslog(LOG_INFO, "User %s timed out after %d seconds",
1112 (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1120 static int cpos, state;
1130 signal(SIGALRM, toolong);
1131 alarm((unsigned) ftpd_timeout);
1132 if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1133 reply(221, "You could at least say goodbye.");
1137 #ifdef HAVE_SETPROCTITLE
1138 if (strncasecmp(cbuf, "PASS", 4) != NULL)
1139 setproctitle("%s: %s", proctitle, cbuf);
1140 #endif /* HAVE_SETPROCTITLE */
1141 if ((cp = strchr(cbuf, '\r'))) {
1145 if ((cp = strpbrk(cbuf, " \n")))
1152 p = lookup(cmdtab, cbuf);
1155 if (p->implemented == 0) {
1157 longjmp(errcatch,0);
1167 if (cbuf[cpos] == ' ') {
1172 if ((cp2 = strpbrk(cp, " \n")))
1177 p = lookup(sitetab, cp);
1180 if (p->implemented == 0) {
1183 longjmp(errcatch,0);
1194 if (cbuf[cpos] == '\n') {
1203 if (cbuf[cpos] == ' ') {
1214 if (cbuf[cpos] == '\n') {
1225 * Make sure the string is nonempty and \n terminated.
1227 if (n > 1 && cbuf[cpos] == '\n') {
1229 yylval.s = copy(cp);
1237 if (cbuf[cpos] == ' ') {
1241 if (isdigit((unsigned char)cbuf[cpos])) {
1243 while (isdigit((unsigned char)cbuf[++cpos]))
1247 yylval.i = atoi(cp);
1256 if (isdigit((unsigned char)cbuf[cpos])) {
1258 while (isdigit((unsigned char)cbuf[++cpos]))
1262 yylval.i = atoi(cp);
1266 switch (cbuf[cpos++]) {
1330 fatal("Unknown state in scanner.");
1332 yyerror((char *) 0);
1334 longjmp(errcatch,0);
1345 fatal("Ran out of memory.");
1350 help(struct tab *ctab, char *s)
1357 if (ctab == sitetab)
1361 width = 0, NCMDS = 0;
1362 for (c = ctab; c->name != NULL; c++) {
1363 int len = strlen(c->name);
1369 width = (width + 8) &~ 7;
1374 lreply(214, "The following %scommands are recognized %s.",
1375 type, "(* =>'s unimplemented)");
1376 columns = 76 / width;
1379 lines = (NCMDS + columns - 1) / columns;
1380 for (i = 0; i < lines; i++) {
1381 strlcpy (buf, " ", sizeof(buf));
1382 for (j = 0; j < columns; j++) {
1383 c = ctab + j * lines + i;
1384 snprintf (buf + strlen(buf),
1385 sizeof(buf) - strlen(buf),
1388 c->implemented ? ' ' : '*');
1389 if (c + lines >= &ctab[NCMDS])
1391 w = strlen(c->name) + 1;
1399 lreply(214, "%s", buf);
1401 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1405 c = lookup(ctab, s);
1406 if (c == (struct tab *)0) {
1407 reply(502, "Unknown command %s.", s);
1411 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1413 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1418 sizecmd(char *filename)
1424 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1425 reply(550, "%s: not a plain file.", filename);
1427 reply(213, "%lu", (unsigned long)stbuf.st_size);
1435 fin = fopen(filename, "r");
1437 perror_reply(550, filename);
1440 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1441 reply(550, "%s: not a plain file.", filename);
1447 while((c=getc(fin)) != EOF) {
1448 if (c == '\n') /* will get expanded to \r\n */
1454 reply(213, "%lu", (unsigned long)count);
1458 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);