1 /* $NetBSD: ftp.y,v 1.5 1997/01/09 20:23:33 tls Exp $ */
4 * Copyright (c) 1985, 1988 Regents of the University of California.
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by the University of California, Berkeley. The name of the
13 * University may not be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 * from: @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89
20 * $NetBSD: ftp.y,v 1.5 1997/01/09 20:23:33 tls Exp $
24 * Grammar for FTP commands.
31 /*static char sccsid[] = "from: @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89";*/
32 static char rcsid
[] = "$NetBSD: ftp.y,v 1.5 1997/01/09 20:23:33 tls Exp $";
35 #include <sys/param.h>
36 #include <sys/socket.h>
38 #include <netinet/in.h>
51 extern
struct sockaddr_in data_dest
;
53 extern
struct passwd
*pw
;
60 extern
int maxtimeout
;
62 extern
char hostname
[], remotehost
[];
63 extern
char proctitle
[];
65 extern
int usedefault
;
67 extern
char tmpline
[];
72 static int cmd_bytesz
;
83 SP CRLF COMMA STRING NUMBER
85 USER PASS ACCT REIN QUIT PORT
86 PASV TYPE STRU MODE RETR STOR
87 APPE MLFL MAIL MSND MSOM MSAM
88 MRSQ MRCP ALLO REST RNFR RNTO
89 ABOR DELE CWD LIST NLST SITE
90 STAT HELP NOOP MKD RMD PWD
91 CDUP STOU SMNT SYST SIZE MDTM
101 cmd_list: /* empty */
104 fromname
= (char *) 0;
109 cmd: USER SP username CRLF
114 | PASS SP password CRLF
119 | PORT SP host_port CRLF
126 reply
(200, "PORT command successful.");
132 | TYPE SP type_code CRLF
137 if
(cmd_form
== FORM_N
) {
138 reply
(200, "Type set to A.");
142 reply
(504, "Form must be N.");
146 reply
(504, "Type E not implemented.");
150 reply
(200, "Type set to I.");
156 if
(cmd_bytesz
== 8) {
158 "Type set to L (byte size 8).");
161 reply
(504, "Byte size must be 8.");
162 #else /* NBBY == 8 */
163 UNIMPLEMENTED for NBBY
!= 8
164 #endif /* NBBY == 8 */
167 | STRU SP struct_code CRLF
172 reply
(200, "STRU F ok.");
176 reply
(504, "Unimplemented STRU type.");
179 | MODE SP mode_code CRLF
184 reply
(200, "MODE S ok.");
188 reply
(502, "Unimplemented MODE type.");
191 | ALLO SP NUMBER CRLF
193 reply
(202, "ALLO command ignored.");
195 | ALLO SP NUMBER SP R SP NUMBER CRLF
197 reply
(202, "ALLO command ignored.");
199 | RETR check_login SP pathname CRLF
201 if
($2 && $4 != NULL
)
202 retrieve
((char *) 0, (char *) $4);
206 | STOR check_login SP pathname CRLF
208 if
($2 && $4 != NULL
)
209 store
((char *) $4, "w", 0);
213 | APPE check_login SP pathname CRLF
215 if
($2 && $4 != NULL
)
216 store
((char *) $4, "a", 0);
220 | NLST check_login CRLF
225 | NLST check_login SP STRING CRLF
227 if
($2 && $4 != NULL
)
228 send_file_list
((char *) $4);
232 | LIST check_login CRLF
235 retrieve
("/bin/ls -lgA", "");
237 | LIST check_login SP pathname CRLF
239 if
($2 && $4 != NULL
)
240 retrieve
("/bin/ls -lgA %s", (char *) $4);
244 | STAT check_login SP pathname CRLF
246 if
($2 && $4 != NULL
)
247 statfilecmd
((char *) $4);
255 | DELE check_login SP pathname CRLF
257 if
($2 && $4 != NULL
)
262 | RNTO SP pathname CRLF
265 renamecmd
(fromname
, (char *) $3);
267 fromname
= (char *) 0;
269 reply
(503, "Bad sequence of commands.");
275 reply
(225, "ABOR command successful.");
277 | CWD check_login CRLF
282 | CWD check_login SP pathname CRLF
284 if
($2 && $4 != NULL
)
291 help
(cmdtab
, (char *) 0);
293 | HELP SP STRING CRLF
295 register
char *cp
= (char *)$3;
297 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
304 help
(sitetab
, (char *) 0);
306 help
(cmdtab
, (char *) $3);
310 reply
(200, "NOOP command successful.");
312 | MKD check_login SP pathname CRLF
314 if
($2 && $4 != NULL
)
315 makedir
((char *) $4);
319 | RMD check_login SP pathname CRLF
321 if
($2 && $4 != NULL
)
322 removedir
((char *) $4);
326 | PWD check_login CRLF
331 | CDUP check_login CRLF
338 help
(sitetab
, (char *) 0);
340 | SITE SP HELP SP STRING CRLF
342 help
(sitetab
, (char *) $5);
344 | SITE SP UMASK check_login CRLF
350 (void) umask
(oldmask
);
351 reply
(200, "Current UMASK is %03o", oldmask
);
354 | SITE SP UMASK check_login SP octal_number CRLF
359 if
(($6 == -1) ||
($6 > 0777)) {
360 reply
(501, "Bad UMASK value");
364 "UMASK set to %03o (was %03o)",
369 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
371 if
($4 && ($8 != NULL
)) {
374 "CHMOD: Mode value must be between 0 and 0777");
375 else if
(chmod
((char *) $8, $6) < 0)
376 perror_reply
(550, (char *) $8);
378 reply
(200, "CHMOD command successful.");
386 "Current IDLE time limit is %d seconds; max %d",
387 timeout
, maxtimeout
);
389 | SITE SP IDLE SP NUMBER CRLF
391 if
($5 < 30 ||
$5 > maxtimeout
) {
393 "Maximum IDLE time must be between 30 and %d seconds",
397 (void) alarm
((unsigned) timeout
);
399 "Maximum IDLE time set to %d seconds",
403 | STOU check_login SP pathname CRLF
405 if
($2 && $4 != NULL
)
406 store
((char *) $4, "w", 1);
414 reply
(215, "UNIX Type: L%d Version: BSD-%d",
417 reply
(215, "UNIX Type: L%d", NBBY
);
420 reply
(215, "UNKNOWN Type: L%d", NBBY
);
425 * SIZE is not in RFC959, but Postel has blessed it and
426 * it will be in the updated RFC.
428 * Return size of file in a format suitable for
429 * using with RESTART (we just count bytes).
431 | SIZE check_login SP pathname CRLF
433 if
($2 && $4 != NULL
)
434 sizecmd
((char *) $4);
440 * MDTM is not in RFC959, but Postel has blessed it and
441 * it will be in the updated RFC.
443 * Return modification time of file as an ISO 3307
444 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
445 * where xxx is the fractional second (of any precision,
446 * not necessarily 3 digits)
448 | MDTM check_login SP pathname CRLF
450 if
($2 && $4 != NULL
) {
452 if
(stat
((char *) $4, &stbuf
) < 0)
453 perror_reply
(550, "%s", (char *) $4);
454 else if
((stbuf.st_mode
&S_IFMT
) != S_IFREG
) {
455 reply
(550, "%s: not a plain file.",
458 register
struct tm
*t
;
460 t
= gmtime
(&stbuf.st_mtime
);
462 "%04d%02d%02d%02d%02d%02d",
464 t
->tm_mon
+1, t
->tm_mday
,
465 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
473 reply
(221, "Goodbye.");
481 rcmd: RNFR check_login SP pathname CRLF
486 fromname
= renamefrom
((char *) $4);
487 if
(fromname
== (char *) 0 && $4) {
497 password: /* empty */
499 *(char **)&($$
) = "";
507 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
510 register
char *a
, *p
;
512 a
= (char *)&data_dest.sin_addr
;
513 a
[0] = $1; a
[1] = $3; a
[2] = $5; a
[3] = $7;
514 p
= (char *)&data_dest.sin_port
;
515 p
[0] = $9; p
[1] = $11;
516 data_dest.sin_family
= AF_INET
;
568 /* this is for a bug in the BBN ftp */
607 * Problem: this production is used for all pathname
608 * processing, but only gives a 550 error reply.
609 * This is a valid reply in some cases but not in others.
611 if
(logged_in
&& $1 && strncmp
((char *) $1, "~", 1) == 0) {
612 *(char **)&($$
) = *glob
((char *) $1);
613 if
(globerr
!= NULL
) {
628 register
int ret
, dec
, multby
, digit
;
631 * Convert a number that was read as decimal number
632 * to what it would be if it had been read as octal.
643 ret
+= digit
* multby
;
651 check_login: /* empty */
656 reply
(530, "Please login with USER and PASS.");
664 extern
jmp_buf errcatch
;
666 #define CMD 0 /* beginning of command */
667 #define ARGS 1 /* expect miscellaneous arguments */
668 #define STR1 2 /* expect SP followed by STRING */
669 #define STR2 3 /* expect STRING */
670 #define OSTR 4 /* optional SP then STRING */
671 #define ZSTR1 5 /* SP then optional STRING */
672 #define ZSTR2 6 /* optional STRING after SP */
673 #define SITECMD 7 /* SITE command */
674 #define NSTR 8 /* Number followed by a string */
680 short implemented
; /* 1 if command is implemented */
684 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
685 { "USER", USER
, STR1
, 1, "<sp> username" },
686 { "PASS", PASS
, ZSTR1
, 1, "<sp> password" },
687 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
688 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
689 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
690 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
691 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4" },
692 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
693 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]" },
694 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
695 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
696 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
697 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
698 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
699 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
700 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
701 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
702 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
703 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
704 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
705 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
706 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
707 { "REST", REST
, ARGS
, 0, "(restart command)" },
708 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
709 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
710 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
711 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
712 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
713 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
714 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
715 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
716 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
717 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
718 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
719 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
720 { "NOOP", NOOP
, ARGS
, 1, "" },
721 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
722 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
723 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
724 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
725 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
726 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
727 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
728 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
729 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
730 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
731 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
735 struct tab sitetab
[] = {
736 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
737 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
738 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
739 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
745 register
struct tab
*p
;
749 for
(; p
->name
!= NULL
; p
++)
750 if
(strcmp
(cmd
, p
->name
) == 0)
755 #include <arpa/telnet.h>
758 * getline - a hacked up version of fgets to ignore TELNET escape codes.
769 /* tmpline may contain saved command from urgent mode interruption */
770 for
(c
= 0; tmpline
[c
] != '\0' && --n
> 0; ++c
) {
772 if
(tmpline
[c
] == '\n') {
775 syslog
(LOG_DEBUG
, "command: %s", s
);
782 while
((c
= getc
(iop
)) != EOF
) {
785 if
((c
= getc
(iop
)) != EOF
) {
791 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
792 (void) fflush
(stdout
);
797 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
798 (void) fflush
(stdout
);
803 continue
; /* ignore command */
808 if
(--n
<= 0 || c
== '\n')
811 if
(c
== EOF
&& cs
== s
)
815 syslog
(LOG_DEBUG
, "command: %s", s
);
823 extern
char *ctime
();
824 extern
time_t time
();
827 "Timeout (%d seconds): closing control connection.", timeout
);
831 "User %s timed out after %d seconds at %s",
832 (pw ? pw
-> pw_name
: "unknown"), timeout
, ctime
(&now
));
839 static int cpos
, state
;
840 register
char *cp
, *cp2
;
841 register
struct tab
*p
;
850 (void) signal
(SIGALRM
, toolong
);
851 (void) alarm
((unsigned) timeout
);
852 if
(getline
(cbuf
, sizeof
(cbuf
)-1, stdin
) == NULL
) {
853 reply
(221, "You could at least say goodbye.");
858 if
(strncasecmp
(cbuf
, "PASS", 4) != NULL
)
859 setproctitle
("%s: %s", proctitle
, cbuf
);
860 #endif /* SETPROCTITLE */
861 if
((cp
= index
(cbuf
, '\r'))) {
865 if
((cp
= strpbrk
(cbuf
, " \n")))
872 p
= lookup
(cmdtab
, cbuf
);
875 if
(p
->implemented
== 0) {
881 *(char **)&yylval = p
->name
;
887 if
(cbuf
[cpos
] == ' ') {
892 if
((cp2
= strpbrk
(cp
, " \n")))
897 p
= lookup
(sitetab
, cp
);
900 if
(p
->implemented
== 0) {
907 *(char **)&yylval = p
->name
;
914 if
(cbuf
[cpos
] == '\n') {
923 if
(cbuf
[cpos
] == ' ') {
925 state
= state
== OSTR ? STR2
: ++state
;
931 if
(cbuf
[cpos
] == '\n') {
942 * Make sure the string is nonempty and \n terminated.
944 if
(n
> 1 && cbuf
[cpos
] == '\n') {
946 *(char **)&yylval = copy
(cp
);
954 if
(cbuf
[cpos
] == ' ') {
958 if
(isdigit
(cbuf
[cpos
])) {
960 while
(isdigit
(cbuf
[++cpos
]))
973 if
(isdigit
(cbuf
[cpos
])) {
975 while
(isdigit
(cbuf
[++cpos
]))
983 switch
(cbuf
[cpos
++]) {
1047 fatal
("Unknown state in scanner.");
1049 yyerror((char *) 0);
1051 longjmp
(errcatch
,0);
1058 while
(*s
!= '\0') {
1070 extern
char *malloc
(), *strcpy
();
1072 p
= malloc
((unsigned) strlen
(s
) + 1);
1074 fatal
("Ran out of memory.");
1075 (void) strcpy
(p
, s
);
1083 register
struct tab
*c
;
1084 register
int width
, NCMDS
;
1087 if
(ctab
== sitetab
)
1091 width
= 0, NCMDS
= 0;
1092 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1093 int len
= strlen
(c
->name
);
1099 width
= (width
+ 8) &~
7;
1101 register
int i
, j
, w
;
1104 lreply
(214, "The following %scommands are recognized %s.",
1105 type
, "(* =>'s unimplemented)");
1106 columns
= 76 / width
;
1109 lines
= (NCMDS
+ columns
- 1) / columns
;
1110 for
(i
= 0; i
< lines
; i
++) {
1112 for
(j
= 0; j
< columns
; j
++) {
1113 c
= ctab
+ j
* lines
+ i
;
1114 printf
("%s%c", c
->name
,
1115 c
->implemented ?
' ' : '*');
1116 if
(c
+ lines
>= &ctab
[NCMDS
])
1118 w
= strlen
(c
->name
) + 1;
1126 (void) fflush
(stdout
);
1127 reply
(214, "Direct comments to ftp-bugs@%s.", hostname
);
1131 c
= lookup
(ctab
, s
);
1132 if
(c
== (struct tab
*)0) {
1133 reply
(502, "Unknown command %s.", s
);
1137 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1139 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1150 if
(stat
(filename
, &stbuf
) < 0 ||
1151 (stbuf.st_mode
&S_IFMT
) != S_IFREG
)
1152 reply
(550, "%s: not a plain file.", filename
);
1154 reply
(213, "%llu", (long long)stbuf.st_size
);
1158 register
int c
, count
;
1160 fin
= fopen
(filename
, "r");
1162 perror_reply
(550, filename
);
1165 if
(fstat
(fileno
(fin
), &stbuf
) < 0 ||
1166 (stbuf.st_mode
&S_IFMT
) != S_IFREG
) {
1167 reply
(550, "%s: not a plain file.", filename
);
1173 while
((c
=getc
(fin
)) != EOF
) {
1174 if
(c
== '\n') /* will get expanded to \r\n */
1180 reply
(213, "%ld", count
);
1183 reply
(504, "SIZE not implemented for Type %c.", "?AEIL"[type
]);