More doxygen.
[heimdal.git] / appl / ftp / ftpd / ftpcmd.y
blob5fb8e8e2e9c2cf8e26b30b41e482eac91042cb3c
1 /* $NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $ */
3 /*
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
9 * are met:
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
33 * SUCH DAMAGE.
35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
39 * Grammar for FTP commands.
40 * See RFC 959.
45 #include "ftpd_locl.h"
46 RCSID("$Id$");
48 off_t restart_point;
50 static int hasyyerrored;
53 static int cmd_type;
54 static int cmd_form;
55 static int cmd_bytesz;
56 char cbuf[64*1024];
57 char *fromname;
59 struct tab {
60 char *name;
61 short token;
62 short state;
63 short implemented; /* 1 if command is implemented */
64 char *help;
67 extern struct tab cmdtab[];
68 extern struct tab sitetab[];
70 static char *copy (char *);
71 static void help (struct tab *, char *);
72 static struct tab *
73 lookup (struct tab *, char *);
74 static void sizecmd (char *);
75 static RETSIGTYPE toolong (int);
76 static int yylex (void);
78 /* This is for bison */
80 #if !defined(alloca) && !defined(HAVE_ALLOCA)
81 #define alloca(x) malloc(x)
82 #endif
86 %union {
87 int i;
88 char *s;
91 %token
92 A B C E F I
93 L N P R S T
95 SP CRLF COMMA
97 USER PASS ACCT REIN QUIT PORT
98 PASV TYPE STRU MODE RETR STOR
99 APPE MLFL MAIL MSND MSOM MSAM
100 MRSQ MRCP ALLO REST RNFR RNTO
101 ABOR DELE CWD LIST NLST SITE
102 sTAT HELP NOOP MKD RMD PWD
103 CDUP STOU SMNT SYST SIZE MDTM
104 EPRT EPSV
106 UMASK IDLE CHMOD
108 AUTH ADAT PROT PBSZ CCC MIC
109 CONF ENC
111 KAUTH KLIST KDESTROY KRBTKFILE AFSLOG
112 LOCATE URL
114 FEAT OPTS
116 LEXERR
118 %token <s> STRING
119 %token <i> NUMBER
121 %type <i> check_login check_login_no_guest check_secure octal_number byte_size
122 %type <i> struct_code mode_code type_code form_code
123 %type <s> pathstring pathname password username
125 %start cmd_list
129 cmd_list
130 : /* empty */
131 | cmd_list cmd
133 fromname = (char *) 0;
134 restart_point = (off_t) 0;
136 | cmd_list rcmd
140 : USER SP username CRLF check_secure
142 if ($5)
143 user($3);
144 free($3);
146 | PASS SP password CRLF check_secure
148 if ($5)
149 pass($3);
150 memset ($3, 0, strlen($3));
151 free($3);
154 | PORT SP host_port CRLF check_secure
156 if ($5) {
157 if (paranoid &&
158 (data_dest->sa_family != his_addr->sa_family ||
159 (socket_get_port(data_dest) < IPPORT_RESERVED) ||
160 memcmp(socket_get_address(data_dest),
161 socket_get_address(his_addr),
162 socket_addr_size(his_addr)) != 0)) {
163 usedefault = 1;
164 reply(500, "Illegal PORT range rejected.");
165 } else {
166 usedefault = 0;
167 if (pdata >= 0) {
168 close(pdata);
169 pdata = -1;
171 reply(200, "PORT command successful.");
175 | EPRT SP STRING CRLF check_secure
177 if ($5)
178 eprt ($3);
179 free ($3);
181 | PASV CRLF check_login
183 if($3)
184 pasv ();
186 | EPSV CRLF check_login
188 if($3)
189 epsv (NULL);
191 | EPSV SP STRING CRLF check_login
193 if($5)
194 epsv ($3);
195 free ($3);
197 | TYPE SP type_code CRLF check_secure
199 if ($5) {
200 switch (cmd_type) {
202 case TYPE_A:
203 if (cmd_form == FORM_N) {
204 reply(200, "Type set to A.");
205 type = cmd_type;
206 form = cmd_form;
207 } else
208 reply(504, "Form must be N.");
209 break;
211 case TYPE_E:
212 reply(504, "Type E not implemented.");
213 break;
215 case TYPE_I:
216 reply(200, "Type set to I.");
217 type = cmd_type;
218 break;
220 case TYPE_L:
221 #if NBBY == 8
222 if (cmd_bytesz == 8) {
223 reply(200,
224 "Type set to L (byte size 8).");
225 type = cmd_type;
226 } else
227 reply(504, "Byte size must be 8.");
228 #else /* NBBY == 8 */
229 UNIMPLEMENTED for NBBY != 8
230 #endif /* NBBY == 8 */
234 | STRU SP struct_code CRLF check_secure
236 if ($5) {
237 switch ($3) {
239 case STRU_F:
240 reply(200, "STRU F ok.");
241 break;
243 default:
244 reply(504, "Unimplemented STRU type.");
248 | MODE SP mode_code CRLF check_secure
250 if ($5) {
251 switch ($3) {
253 case MODE_S:
254 reply(200, "MODE S ok.");
255 break;
257 default:
258 reply(502, "Unimplemented MODE type.");
262 | ALLO SP NUMBER CRLF check_secure
264 if ($5) {
265 reply(202, "ALLO command ignored.");
268 | ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
270 if ($9) {
271 reply(202, "ALLO command ignored.");
274 | RETR SP pathname CRLF check_login
276 char *name = $3;
278 if ($5 && name != NULL)
279 retrieve(0, name);
280 if (name != NULL)
281 free(name);
283 | STOR SP pathname CRLF check_login
285 char *name = $3;
287 if ($5 && name != NULL)
288 do_store(name, "w", 0);
289 if (name != NULL)
290 free(name);
292 | APPE SP pathname CRLF check_login
294 char *name = $3;
296 if ($5 && name != NULL)
297 do_store(name, "a", 0);
298 if (name != NULL)
299 free(name);
301 | NLST CRLF check_login
303 if ($3)
304 send_file_list(".");
306 | NLST SP STRING CRLF check_login
308 char *name = $3;
310 if ($5 && name != NULL)
311 send_file_list(name);
312 if (name != NULL)
313 free(name);
315 | LIST CRLF check_login
317 if($3)
318 list_file(".");
320 | LIST SP pathname CRLF check_login
322 if($5)
323 list_file($3);
324 free($3);
326 | sTAT SP pathname CRLF check_login
328 if ($5 && $3 != NULL)
329 statfilecmd($3);
330 if ($3 != NULL)
331 free($3);
333 | sTAT CRLF check_secure
335 if ($3)
336 statcmd();
338 | DELE SP pathname CRLF check_login_no_guest
340 if ($5 && $3 != NULL)
341 do_delete($3);
342 if ($3 != NULL)
343 free($3);
345 | RNTO SP pathname CRLF check_login_no_guest
347 if($5){
348 if (fromname) {
349 renamecmd(fromname, $3);
350 free(fromname);
351 fromname = (char *) 0;
352 } else {
353 reply(503, "Bad sequence of commands.");
356 if ($3 != NULL)
357 free($3);
359 | ABOR CRLF check_secure
361 if ($3)
362 reply(225, "ABOR command successful.");
364 | CWD CRLF check_login
366 if ($3) {
367 const char *path = pw->pw_dir;
368 if (dochroot || guest)
369 path = "/";
370 cwd(path);
373 | CWD SP pathname CRLF check_login
375 if ($5 && $3 != NULL)
376 cwd($3);
377 if ($3 != NULL)
378 free($3);
380 | HELP CRLF check_secure
382 if ($3)
383 help(cmdtab, (char *) 0);
385 | HELP SP STRING CRLF check_secure
387 if ($5) {
388 char *cp = $3;
390 if (strncasecmp(cp, "SITE", 4) == 0) {
391 cp = $3 + 4;
392 if (*cp == ' ')
393 cp++;
394 if (*cp)
395 help(sitetab, cp);
396 else
397 help(sitetab, (char *) 0);
398 } else
399 help(cmdtab, $3);
402 | NOOP CRLF check_secure
404 if ($3)
405 reply(200, "NOOP command successful.");
407 | MKD SP pathname CRLF check_login
409 if ($5 && $3 != NULL)
410 makedir($3);
411 if ($3 != NULL)
412 free($3);
414 | RMD SP pathname CRLF check_login_no_guest
416 if ($5 && $3 != NULL)
417 removedir($3);
418 if ($3 != NULL)
419 free($3);
421 | PWD CRLF check_login
423 if ($3)
424 pwd();
426 | CDUP CRLF check_login
428 if ($3)
429 cwd("..");
431 | FEAT CRLF check_secure
433 if ($3) {
434 lreply(211, "Supported features:");
435 lreply(0, " MDTM");
436 lreply(0, " REST STREAM");
437 lreply(0, " SIZE");
438 reply(211, "End");
441 | OPTS SP STRING CRLF check_secure
443 if ($5)
444 reply(501, "Bad options");
445 free ($3);
448 | SITE SP HELP CRLF check_secure
450 if ($5)
451 help(sitetab, (char *) 0);
453 | SITE SP HELP SP STRING CRLF check_secure
455 if ($7)
456 help(sitetab, $5);
458 | SITE SP UMASK CRLF check_login
460 if ($5) {
461 int oldmask = umask(0);
462 umask(oldmask);
463 reply(200, "Current UMASK is %03o", oldmask);
466 | SITE SP UMASK SP octal_number CRLF check_login_no_guest
468 if ($7) {
469 if (($5 == -1) || ($5 > 0777)) {
470 reply(501, "Bad UMASK value");
471 } else {
472 int oldmask = umask($5);
473 reply(200,
474 "UMASK set to %03o (was %03o)",
475 $5, oldmask);
479 | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
481 if ($9 && $7 != NULL) {
482 if ($5 > 0777)
483 reply(501,
484 "CHMOD: Mode value must be between 0 and 0777");
485 else if (chmod($7, $5) < 0)
486 perror_reply(550, $7);
487 else
488 reply(200, "CHMOD command successful.");
490 if ($7 != NULL)
491 free($7);
493 | SITE SP IDLE CRLF check_secure
495 if ($5)
496 reply(200,
497 "Current IDLE time limit is %d seconds; max %d",
498 ftpd_timeout, maxtimeout);
500 | SITE SP IDLE SP NUMBER CRLF check_secure
502 if ($7) {
503 if ($5 < 30 || $5 > maxtimeout) {
504 reply(501,
505 "Maximum IDLE time must be between 30 and %d seconds",
506 maxtimeout);
507 } else {
508 ftpd_timeout = $5;
509 alarm((unsigned) ftpd_timeout);
510 reply(200,
511 "Maximum IDLE time set to %d seconds",
512 ftpd_timeout);
517 | SITE SP KAUTH SP STRING CRLF check_login
519 reply(500, "Command not implemented.");
521 | SITE SP KLIST CRLF check_login
523 if($5)
524 klist();
526 | SITE SP KDESTROY CRLF check_login
528 reply(500, "Command not implemented.");
530 | SITE SP KRBTKFILE SP STRING CRLF check_login
532 reply(500, "Command not implemented.");
534 | SITE SP AFSLOG CRLF check_login
536 #if defined(KRB5)
537 if(guest)
538 reply(500, "Can't be done as guest.");
539 else if($5)
540 afslog(NULL, 0);
541 #else
542 reply(500, "Command not implemented.");
543 #endif
545 | SITE SP AFSLOG SP STRING CRLF check_login
547 #if defined(KRB5)
548 if(guest)
549 reply(500, "Can't be done as guest.");
550 else if($7)
551 afslog($5, 0);
552 if($5)
553 free($5);
554 #else
555 reply(500, "Command not implemented.");
556 #endif
558 | SITE SP LOCATE SP STRING CRLF check_login
560 if($7 && $5 != NULL)
561 find($5);
562 if($5 != NULL)
563 free($5);
565 | SITE SP URL CRLF check_secure
567 if ($5)
568 reply(200, "http://www.pdc.kth.se/heimdal/");
570 | STOU SP pathname CRLF check_login
572 if ($5 && $3 != NULL)
573 do_store($3, "w", 1);
574 if ($3 != NULL)
575 free($3);
577 | SYST CRLF check_secure
579 if ($3) {
580 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
581 reply(215, "UNIX Type: L%d", NBBY);
582 #else
583 reply(215, "UNKNOWN Type: L%d", NBBY);
584 #endif
589 * SIZE is not in RFC959, but Postel has blessed it and
590 * it will be in the updated RFC.
592 * Return size of file in a format suitable for
593 * using with RESTART (we just count bytes).
595 | SIZE SP pathname CRLF check_login
597 if ($5 && $3 != NULL)
598 sizecmd($3);
599 if ($3 != NULL)
600 free($3);
604 * MDTM is not in RFC959, but Postel has blessed it and
605 * it will be in the updated RFC.
607 * Return modification time of file as an ISO 3307
608 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
609 * where xxx is the fractional second (of any precision,
610 * not necessarily 3 digits)
612 | MDTM SP pathname CRLF check_login
614 if ($5 && $3 != NULL) {
615 struct stat stbuf;
616 if (stat($3, &stbuf) < 0)
617 reply(550, "%s: %s",
618 $3, strerror(errno));
619 else if (!S_ISREG(stbuf.st_mode)) {
620 reply(550,
621 "%s: not a plain file.", $3);
622 } else {
623 struct tm *t;
624 time_t mtime = stbuf.st_mtime;
626 t = gmtime(&mtime);
627 reply(213,
628 "%04d%02d%02d%02d%02d%02d",
629 t->tm_year + 1900,
630 t->tm_mon + 1,
631 t->tm_mday,
632 t->tm_hour,
633 t->tm_min,
634 t->tm_sec);
637 if ($3 != NULL)
638 free($3);
640 | QUIT CRLF check_secure
642 if ($3) {
643 reply(221, "Goodbye.");
644 dologout(0);
647 | error CRLF
649 yyerrok;
652 rcmd
653 : RNFR SP pathname CRLF check_login_no_guest
655 restart_point = (off_t) 0;
656 if ($5 && $3) {
657 fromname = renamefrom($3);
658 if (fromname == (char *) 0 && $3) {
659 free($3);
663 | REST SP byte_size CRLF check_secure
665 if ($5) {
666 fromname = (char *) 0;
667 restart_point = $3; /* XXX $3 is only "int" */
668 reply(350, "Restarting at %ld. %s",
669 (long)restart_point,
670 "Send STORE or RETRIEVE to initiate transfer.");
673 | AUTH SP STRING CRLF
675 auth($3);
676 free($3);
678 | ADAT SP STRING CRLF
680 adat($3);
681 free($3);
683 | PBSZ SP NUMBER CRLF check_secure
685 if ($5)
686 pbsz($3);
688 | PROT SP STRING CRLF check_secure
690 if ($5)
691 prot($3);
693 | CCC CRLF check_secure
695 if ($3)
696 ccc();
698 | MIC SP STRING CRLF
700 mec($3, prot_safe);
701 free($3);
703 | CONF SP STRING CRLF
705 mec($3, prot_confidential);
706 free($3);
708 | ENC SP STRING CRLF
710 mec($3, prot_private);
711 free($3);
715 username
716 : STRING
719 password
720 : /* empty */
722 $$ = (char *)calloc(1, sizeof(char));
724 | STRING
727 byte_size
728 : NUMBER
731 host_port
732 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
733 NUMBER COMMA NUMBER
735 struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest;
737 sin4->sin_family = AF_INET;
738 sin4->sin_port = htons($9 * 256 + $11);
739 sin4->sin_addr.s_addr =
740 htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
744 form_code
747 $$ = FORM_N;
751 $$ = FORM_T;
755 $$ = FORM_C;
759 type_code
762 cmd_type = TYPE_A;
763 cmd_form = FORM_N;
765 | A SP form_code
767 cmd_type = TYPE_A;
768 cmd_form = $3;
772 cmd_type = TYPE_E;
773 cmd_form = FORM_N;
775 | E SP form_code
777 cmd_type = TYPE_E;
778 cmd_form = $3;
782 cmd_type = TYPE_I;
786 cmd_type = TYPE_L;
787 cmd_bytesz = NBBY;
789 | L SP byte_size
791 cmd_type = TYPE_L;
792 cmd_bytesz = $3;
794 /* this is for a bug in the BBN ftp */
795 | L byte_size
797 cmd_type = TYPE_L;
798 cmd_bytesz = $2;
802 struct_code
805 $$ = STRU_F;
809 $$ = STRU_R;
813 $$ = STRU_P;
817 mode_code
820 $$ = MODE_S;
824 $$ = MODE_B;
828 $$ = MODE_C;
832 pathname
833 : pathstring
836 * Problem: this production is used for all pathname
837 * processing, but only gives a 550 error reply.
838 * This is a valid reply in some cases but not in others.
840 if (logged_in && $1 && *$1 == '~') {
841 glob_t gl;
842 int flags =
843 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
845 memset(&gl, 0, sizeof(gl));
846 if (glob($1, flags, NULL, &gl) ||
847 gl.gl_pathc == 0) {
848 reply(550, "not found");
849 $$ = NULL;
850 } else {
851 $$ = strdup(gl.gl_pathv[0]);
853 globfree(&gl);
854 free($1);
855 } else
856 $$ = $1;
860 pathstring
861 : STRING
864 octal_number
865 : NUMBER
867 int ret, dec, multby, digit;
870 * Convert a number that was read as decimal number
871 * to what it would be if it had been read as octal.
873 dec = $1;
874 multby = 1;
875 ret = 0;
876 while (dec) {
877 digit = dec%10;
878 if (digit > 7) {
879 ret = -1;
880 break;
882 ret += digit * multby;
883 multby *= 8;
884 dec /= 10;
886 $$ = ret;
891 check_login_no_guest : check_login
893 $$ = $1 && !guest;
894 if($1 && !$$)
895 reply(550, "Permission denied");
899 check_login : check_secure
901 if($1) {
902 if(($$ = logged_in) == 0)
903 reply(530, "Please login with USER and PASS.");
904 } else
905 $$ = 0;
909 check_secure : /* empty */
911 $$ = 1;
912 if(sec_complete && !ccc_passed && !secure_command()) {
913 $$ = 0;
914 reply(533, "Command protection level denied "
915 "for paranoid reasons.");
922 #define CMD 0 /* beginning of command */
923 #define ARGS 1 /* expect miscellaneous arguments */
924 #define STR1 2 /* expect SP followed by STRING */
925 #define STR2 3 /* expect STRING */
926 #define OSTR 4 /* optional SP then STRING */
927 #define ZSTR1 5 /* SP then optional STRING */
928 #define ZSTR2 6 /* optional STRING after SP */
929 #define SITECMD 7 /* SITE command */
930 #define NSTR 8 /* Number followed by a string */
932 struct tab cmdtab[] = { /* In order defined in RFC 765 */
933 { "USER", USER, STR1, 1, "<sp> username" },
934 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
935 { "ACCT", ACCT, STR1, 0, "(specify account)" },
936 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
937 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
938 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
939 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
940 { "EPRT", EPRT, STR1, 1, "<sp> string" },
941 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
942 { "EPSV", EPSV, OSTR, 1, "[<sp> foo]" },
943 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
944 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
945 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
946 { "RETR", RETR, STR1, 1, "<sp> file-name" },
947 { "STOR", STOR, STR1, 1, "<sp> file-name" },
948 { "APPE", APPE, STR1, 1, "<sp> file-name" },
949 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
950 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
951 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
952 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
953 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
954 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
955 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
956 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
957 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
958 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
959 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
960 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
961 { "DELE", DELE, STR1, 1, "<sp> file-name" },
962 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
963 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
964 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
965 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
966 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
967 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
968 { "STAT", sTAT, OSTR, 1, "[ <sp> path-name ]" },
969 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
970 { "NOOP", NOOP, ARGS, 1, "" },
971 { "MKD", MKD, STR1, 1, "<sp> path-name" },
972 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
973 { "RMD", RMD, STR1, 1, "<sp> path-name" },
974 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
975 { "PWD", PWD, ARGS, 1, "(return current directory)" },
976 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
977 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
978 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
979 { "STOU", STOU, STR1, 1, "<sp> file-name" },
980 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
981 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
983 /* extensions from RFC2228 */
984 { "AUTH", AUTH, STR1, 1, "<sp> auth-type" },
985 { "ADAT", ADAT, STR1, 1, "<sp> auth-data" },
986 { "PBSZ", PBSZ, ARGS, 1, "<sp> buffer-size" },
987 { "PROT", PROT, STR1, 1, "<sp> prot-level" },
988 { "CCC", CCC, ARGS, 1, "" },
989 { "MIC", MIC, STR1, 1, "<sp> integrity command" },
990 { "CONF", CONF, STR1, 1, "<sp> confidentiality command" },
991 { "ENC", ENC, STR1, 1, "<sp> privacy command" },
993 /* RFC2389 */
994 { "FEAT", FEAT, ARGS, 1, "" },
995 { "OPTS", OPTS, ARGS, 1, "<sp> command [<sp> options]" },
997 { NULL, 0, 0, 0, 0 }
1000 struct tab sitetab[] = {
1001 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1002 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1003 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1004 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1006 { "KAUTH", KAUTH, STR1, 1, "<sp> principal [ <sp> ticket ]" },
1007 { "KLIST", KLIST, ARGS, 1, "(show ticket file)" },
1008 { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1009 { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1010 { "AFSLOG", AFSLOG, OSTR, 1, "[<sp> cell]" },
1012 { "LOCATE", LOCATE, STR1, 1, "<sp> globexpr" },
1013 { "FIND", LOCATE, STR1, 1, "<sp> globexpr" },
1015 { "URL", URL, ARGS, 1, "?" },
1017 { NULL, 0, 0, 0, 0 }
1020 static struct tab *
1021 lookup(struct tab *p, char *cmd)
1024 for (; p->name != NULL; p++)
1025 if (strcmp(cmd, p->name) == 0)
1026 return (p);
1027 return (0);
1031 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1033 char *
1034 ftpd_getline(char *s, int n)
1036 int c;
1037 char *cs;
1039 cs = s;
1041 /* might still be data within the security MIC/CONF/ENC */
1042 if(ftp_command){
1043 strlcpy(s, ftp_command, n);
1044 if (debug)
1045 syslog(LOG_DEBUG, "command: %s", s);
1046 return s;
1048 while ((c = getc(stdin)) != EOF) {
1049 c &= 0377;
1050 if (c == IAC) {
1051 if ((c = getc(stdin)) != EOF) {
1052 c &= 0377;
1053 switch (c) {
1054 case WILL:
1055 case WONT:
1056 c = getc(stdin);
1057 printf("%c%c%c", IAC, DONT, 0377&c);
1058 fflush(stdout);
1059 continue;
1060 case DO:
1061 case DONT:
1062 c = getc(stdin);
1063 printf("%c%c%c", IAC, WONT, 0377&c);
1064 fflush(stdout);
1065 continue;
1066 case IAC:
1067 break;
1068 default:
1069 continue; /* ignore command */
1073 *cs++ = c;
1074 if (--n <= 0 || c == '\n')
1075 break;
1077 if (c == EOF && cs == s)
1078 return (NULL);
1079 *cs++ = '\0';
1080 if (debug) {
1081 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1082 /* Don't syslog passwords */
1083 syslog(LOG_DEBUG, "command: %.5s ???", s);
1084 } else {
1085 char *cp;
1086 int len;
1088 /* Don't syslog trailing CR-LF */
1089 len = strlen(s);
1090 cp = s + len - 1;
1091 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1092 --cp;
1093 --len;
1095 syslog(LOG_DEBUG, "command: %.*s", len, s);
1098 #ifdef XXX
1099 fprintf(stderr, "%s\n", s);
1100 #endif
1101 return (s);
1104 static RETSIGTYPE
1105 toolong(int signo)
1108 reply(421,
1109 "Timeout (%d seconds): closing control connection.",
1110 ftpd_timeout);
1111 if (logging)
1112 syslog(LOG_INFO, "User %s timed out after %d seconds",
1113 (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1114 dologout(1);
1115 SIGRETURN(0);
1118 static int
1119 yylex(void)
1121 static int cpos, state;
1122 char *cp, *cp2;
1123 struct tab *p;
1124 int n;
1125 char c;
1127 for (;;) {
1128 switch (state) {
1130 case CMD:
1131 hasyyerrored = 0;
1133 signal(SIGALRM, toolong);
1134 alarm((unsigned) ftpd_timeout);
1135 if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1136 reply(221, "You could at least say goodbye.");
1137 dologout(0);
1139 alarm(0);
1140 #ifdef HAVE_SETPROCTITLE
1141 if (strncasecmp(cbuf, "PASS", 4) != 0)
1142 setproctitle("%s: %s", proctitle, cbuf);
1143 #endif /* HAVE_SETPROCTITLE */
1144 if ((cp = strchr(cbuf, '\r'))) {
1145 *cp++ = '\n';
1146 *cp = '\0';
1148 if ((cp = strpbrk(cbuf, " \n")))
1149 cpos = cp - cbuf;
1150 if (cpos == 0)
1151 cpos = 4;
1152 c = cbuf[cpos];
1153 cbuf[cpos] = '\0';
1154 strupr(cbuf);
1155 p = lookup(cmdtab, cbuf);
1156 cbuf[cpos] = c;
1157 if (p != 0) {
1158 if (p->implemented == 0) {
1159 nack(p->name);
1160 hasyyerrored = 1;
1161 break;
1163 state = p->state;
1164 yylval.s = p->name;
1165 return (p->token);
1167 break;
1169 case SITECMD:
1170 if (cbuf[cpos] == ' ') {
1171 cpos++;
1172 return (SP);
1174 cp = &cbuf[cpos];
1175 if ((cp2 = strpbrk(cp, " \n")))
1176 cpos = cp2 - cbuf;
1177 c = cbuf[cpos];
1178 cbuf[cpos] = '\0';
1179 strupr(cp);
1180 p = lookup(sitetab, cp);
1181 cbuf[cpos] = c;
1182 if (p != 0) {
1183 if (p->implemented == 0) {
1184 state = CMD;
1185 nack(p->name);
1186 hasyyerrored = 1;
1187 break;
1189 state = p->state;
1190 yylval.s = p->name;
1191 return (p->token);
1193 state = CMD;
1194 break;
1196 case OSTR:
1197 if (cbuf[cpos] == '\n') {
1198 state = CMD;
1199 return (CRLF);
1201 /* FALLTHROUGH */
1203 case STR1:
1204 case ZSTR1:
1205 dostr1:
1206 if (cbuf[cpos] == ' ') {
1207 cpos++;
1208 if(state == OSTR)
1209 state = STR2;
1210 else
1211 state++;
1212 return (SP);
1214 break;
1216 case ZSTR2:
1217 if (cbuf[cpos] == '\n') {
1218 state = CMD;
1219 return (CRLF);
1221 /* FALLTHROUGH */
1223 case STR2:
1224 cp = &cbuf[cpos];
1225 n = strlen(cp);
1226 cpos += n - 1;
1228 * Make sure the string is nonempty and \n terminated.
1230 if (n > 1 && cbuf[cpos] == '\n') {
1231 cbuf[cpos] = '\0';
1232 yylval.s = copy(cp);
1233 cbuf[cpos] = '\n';
1234 state = ARGS;
1235 return (STRING);
1237 break;
1239 case NSTR:
1240 if (cbuf[cpos] == ' ') {
1241 cpos++;
1242 return (SP);
1244 if (isdigit((unsigned char)cbuf[cpos])) {
1245 cp = &cbuf[cpos];
1246 while (isdigit((unsigned char)cbuf[++cpos]))
1248 c = cbuf[cpos];
1249 cbuf[cpos] = '\0';
1250 yylval.i = atoi(cp);
1251 cbuf[cpos] = c;
1252 state = STR1;
1253 return (NUMBER);
1255 state = STR1;
1256 goto dostr1;
1258 case ARGS:
1259 if (isdigit((unsigned char)cbuf[cpos])) {
1260 cp = &cbuf[cpos];
1261 while (isdigit((unsigned char)cbuf[++cpos]))
1263 c = cbuf[cpos];
1264 cbuf[cpos] = '\0';
1265 yylval.i = atoi(cp);
1266 cbuf[cpos] = c;
1267 return (NUMBER);
1269 switch (cbuf[cpos++]) {
1271 case '\n':
1272 state = CMD;
1273 return (CRLF);
1275 case ' ':
1276 return (SP);
1278 case ',':
1279 return (COMMA);
1281 case 'A':
1282 case 'a':
1283 return (A);
1285 case 'B':
1286 case 'b':
1287 return (B);
1289 case 'C':
1290 case 'c':
1291 return (C);
1293 case 'E':
1294 case 'e':
1295 return (E);
1297 case 'F':
1298 case 'f':
1299 return (F);
1301 case 'I':
1302 case 'i':
1303 return (I);
1305 case 'L':
1306 case 'l':
1307 return (L);
1309 case 'N':
1310 case 'n':
1311 return (N);
1313 case 'P':
1314 case 'p':
1315 return (P);
1317 case 'R':
1318 case 'r':
1319 return (R);
1321 case 'S':
1322 case 's':
1323 return (S);
1325 case 'T':
1326 case 't':
1327 return (T);
1330 break;
1332 default:
1333 fatal("Unknown state in scanner.");
1335 yyerror(NULL);
1336 state = CMD;
1337 return (0);
1341 /* ARGSUSED */
1342 void
1343 yyerror(char *s)
1345 char *cp;
1347 if (hasyyerrored)
1348 return;
1350 if ((cp = strchr(cbuf,'\n')))
1351 *cp = '\0';
1352 reply(500, "'%s': command not understood.", cbuf);
1353 hasyyerrored = 1;
1356 static char *
1357 copy(char *s)
1359 char *p;
1361 p = strdup(s);
1362 if (p == NULL)
1363 fatal("Ran out of memory.");
1364 return p;
1367 static void
1368 help(struct tab *ctab, char *s)
1370 struct tab *c;
1371 int width, NCMDS;
1372 char *t;
1373 char buf[1024];
1375 if (ctab == sitetab)
1376 t = "SITE ";
1377 else
1378 t = "";
1379 width = 0, NCMDS = 0;
1380 for (c = ctab; c->name != NULL; c++) {
1381 int len = strlen(c->name);
1383 if (len > width)
1384 width = len;
1385 NCMDS++;
1387 width = (width + 8) &~ 7;
1388 if (s == 0) {
1389 int i, j, w;
1390 int columns, lines;
1392 lreply(214, "The following %scommands are recognized %s.",
1393 t, "(* =>'s unimplemented)");
1394 columns = 76 / width;
1395 if (columns == 0)
1396 columns = 1;
1397 lines = (NCMDS + columns - 1) / columns;
1398 for (i = 0; i < lines; i++) {
1399 strlcpy (buf, " ", sizeof(buf));
1400 for (j = 0; j < columns; j++) {
1401 c = ctab + j * lines + i;
1402 snprintf (buf + strlen(buf),
1403 sizeof(buf) - strlen(buf),
1404 "%s%c",
1405 c->name,
1406 c->implemented ? ' ' : '*');
1407 if (c + lines >= &ctab[NCMDS])
1408 break;
1409 w = strlen(c->name) + 1;
1410 while (w < width) {
1411 strlcat (buf,
1412 " ",
1413 sizeof(buf));
1414 w++;
1417 lreply(214, "%s", buf);
1419 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1420 return;
1422 strupr(s);
1423 c = lookup(ctab, s);
1424 if (c == (struct tab *)0) {
1425 reply(502, "Unknown command %s.", s);
1426 return;
1428 if (c->implemented)
1429 reply(214, "Syntax: %s%s %s", t, c->name, c->help);
1430 else
1431 reply(214, "%s%-*s\t%s; unimplemented.", t, width,
1432 c->name, c->help);
1435 static void
1436 sizecmd(char *filename)
1438 switch (type) {
1439 case TYPE_L:
1440 case TYPE_I: {
1441 struct stat stbuf;
1442 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1443 reply(550, "%s: not a plain file.", filename);
1444 else
1445 reply(213, "%lu", (unsigned long)stbuf.st_size);
1446 break;
1448 case TYPE_A: {
1449 FILE *fin;
1450 int c;
1451 size_t count;
1452 struct stat stbuf;
1453 fin = fopen(filename, "r");
1454 if (fin == NULL) {
1455 perror_reply(550, filename);
1456 return;
1458 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1459 reply(550, "%s: not a plain file.", filename);
1460 fclose(fin);
1461 return;
1464 count = 0;
1465 while((c=getc(fin)) != EOF) {
1466 if (c == '\n') /* will get expanded to \r\n */
1467 count++;
1468 count++;
1470 fclose(fin);
1472 reply(213, "%lu", (unsigned long)count);
1473 break;
1475 default:
1476 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);