Restore unconditional use of getpwent vs. non-POSIX getpwent_r
[heimdal.git] / appl / ftp / ftpd / ftpcmd.y
blobb8f3480075c4c91ff054f3ff108eec3374ae5c86
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 #ifndef NO_AFS
541 afslog(NULL, 0);
542 #endif
544 #else
545 reply(500, "Command not implemented.");
546 #endif
548 | SITE SP AFSLOG SP STRING CRLF check_login
550 #if defined(KRB5)
551 if(guest)
552 reply(500, "Can't be done as guest.");
553 else if($7) {
554 #ifndef NO_AFS
555 afslog($5, 0);
556 #endif
558 if($5)
559 free($5);
560 #else
561 reply(500, "Command not implemented.");
562 #endif
564 | SITE SP LOCATE SP STRING CRLF check_login
566 if($7 && $5 != NULL)
567 find($5);
568 if($5 != NULL)
569 free($5);
571 | SITE SP URL CRLF check_secure
573 if ($5)
574 reply(200, "http://www.pdc.kth.se/heimdal/");
576 | STOU SP pathname CRLF check_login
578 if ($5 && $3 != NULL)
579 do_store($3, "w", 1);
580 if ($3 != NULL)
581 free($3);
583 | SYST CRLF check_secure
585 if ($3) {
586 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
587 reply(215, "UNIX Type: L%d", NBBY);
588 #else
589 reply(215, "UNKNOWN Type: L%d", NBBY);
590 #endif
595 * SIZE is not in RFC959, but Postel has blessed it and
596 * it will be in the updated RFC.
598 * Return size of file in a format suitable for
599 * using with RESTART (we just count bytes).
601 | SIZE SP pathname CRLF check_login
603 if ($5 && $3 != NULL)
604 sizecmd($3);
605 if ($3 != NULL)
606 free($3);
610 * MDTM is not in RFC959, but Postel has blessed it and
611 * it will be in the updated RFC.
613 * Return modification time of file as an ISO 3307
614 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
615 * where xxx is the fractional second (of any precision,
616 * not necessarily 3 digits)
618 | MDTM SP pathname CRLF check_login
620 if ($5 && $3 != NULL) {
621 struct stat stbuf;
622 if (stat($3, &stbuf) < 0)
623 reply(550, "%s: %s",
624 $3, strerror(errno));
625 else if (!S_ISREG(stbuf.st_mode)) {
626 reply(550,
627 "%s: not a plain file.", $3);
628 } else {
629 struct tm *t;
630 time_t mtime = stbuf.st_mtime;
632 t = gmtime(&mtime);
633 reply(213,
634 "%04d%02d%02d%02d%02d%02d",
635 t->tm_year + 1900,
636 t->tm_mon + 1,
637 t->tm_mday,
638 t->tm_hour,
639 t->tm_min,
640 t->tm_sec);
643 if ($3 != NULL)
644 free($3);
646 | QUIT CRLF check_secure
648 if ($3) {
649 reply(221, "Goodbye.");
650 dologout(0);
653 | error CRLF
655 yyerrok;
658 rcmd
659 : RNFR SP pathname CRLF check_login_no_guest
661 restart_point = (off_t) 0;
662 if ($5 && $3) {
663 fromname = renamefrom($3);
664 if (fromname == (char *) 0 && $3) {
665 free($3);
669 | REST SP byte_size CRLF check_secure
671 if ($5) {
672 fromname = (char *) 0;
673 restart_point = $3; /* XXX $3 is only "int" */
674 reply(350, "Restarting at %ld. %s",
675 (long)restart_point,
676 "Send STORE or RETRIEVE to initiate transfer.");
679 | AUTH SP STRING CRLF
681 auth($3);
682 free($3);
684 | ADAT SP STRING CRLF
686 adat($3);
687 free($3);
689 | PBSZ SP NUMBER CRLF check_secure
691 if ($5)
692 pbsz($3);
694 | PROT SP STRING CRLF check_secure
696 if ($5)
697 prot($3);
699 | CCC CRLF check_secure
701 if ($3)
702 ccc();
704 | MIC SP STRING CRLF
706 mec($3, prot_safe);
707 free($3);
709 | CONF SP STRING CRLF
711 mec($3, prot_confidential);
712 free($3);
714 | ENC SP STRING CRLF
716 mec($3, prot_private);
717 free($3);
721 username
722 : STRING
725 password
726 : /* empty */
728 $$ = (char *)calloc(1, sizeof(char));
730 | STRING
733 byte_size
734 : NUMBER
737 host_port
738 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
739 NUMBER COMMA NUMBER
741 struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest;
743 sin4->sin_family = AF_INET;
744 sin4->sin_port = htons($9 * 256 + $11);
745 sin4->sin_addr.s_addr =
746 htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
750 form_code
753 $$ = FORM_N;
757 $$ = FORM_T;
761 $$ = FORM_C;
765 type_code
768 cmd_type = TYPE_A;
769 cmd_form = FORM_N;
771 | A SP form_code
773 cmd_type = TYPE_A;
774 cmd_form = $3;
778 cmd_type = TYPE_E;
779 cmd_form = FORM_N;
781 | E SP form_code
783 cmd_type = TYPE_E;
784 cmd_form = $3;
788 cmd_type = TYPE_I;
792 cmd_type = TYPE_L;
793 cmd_bytesz = NBBY;
795 | L SP byte_size
797 cmd_type = TYPE_L;
798 cmd_bytesz = $3;
800 /* this is for a bug in the BBN ftp */
801 | L byte_size
803 cmd_type = TYPE_L;
804 cmd_bytesz = $2;
808 struct_code
811 $$ = STRU_F;
815 $$ = STRU_R;
819 $$ = STRU_P;
823 mode_code
826 $$ = MODE_S;
830 $$ = MODE_B;
834 $$ = MODE_C;
838 pathname
839 : pathstring
842 * Problem: this production is used for all pathname
843 * processing, but only gives a 550 error reply.
844 * This is a valid reply in some cases but not in others.
846 if (logged_in && $1 && *$1 == '~') {
847 glob_t gl;
848 int flags =
849 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
851 memset(&gl, 0, sizeof(gl));
852 if (glob($1, flags, NULL, &gl) ||
853 gl.gl_pathc == 0) {
854 reply(550, "not found");
855 $$ = NULL;
856 } else {
857 $$ = strdup(gl.gl_pathv[0]);
859 globfree(&gl);
860 free($1);
861 } else
862 $$ = $1;
866 pathstring
867 : STRING
870 octal_number
871 : NUMBER
873 int ret, dec, multby, digit;
876 * Convert a number that was read as decimal number
877 * to what it would be if it had been read as octal.
879 dec = $1;
880 multby = 1;
881 ret = 0;
882 while (dec) {
883 digit = dec%10;
884 if (digit > 7) {
885 ret = -1;
886 break;
888 ret += digit * multby;
889 multby *= 8;
890 dec /= 10;
892 $$ = ret;
897 check_login_no_guest : check_login
899 $$ = $1 && !guest;
900 if($1 && !$$)
901 reply(550, "Permission denied");
905 check_login : check_secure
907 if($1) {
908 if(($$ = logged_in) == 0)
909 reply(530, "Please login with USER and PASS.");
910 } else
911 $$ = 0;
915 check_secure : /* empty */
917 $$ = 1;
918 if(sec_complete && !ccc_passed && !secure_command()) {
919 $$ = 0;
920 reply(533, "Command protection level denied "
921 "for paranoid reasons.");
928 #define CMD 0 /* beginning of command */
929 #define ARGS 1 /* expect miscellaneous arguments */
930 #define STR1 2 /* expect SP followed by STRING */
931 #define STR2 3 /* expect STRING */
932 #define OSTR 4 /* optional SP then STRING */
933 #define ZSTR1 5 /* SP then optional STRING */
934 #define ZSTR2 6 /* optional STRING after SP */
935 #define SITECMD 7 /* SITE command */
936 #define NSTR 8 /* Number followed by a string */
938 struct tab cmdtab[] = { /* In order defined in RFC 765 */
939 { "USER", USER, STR1, 1, "<sp> username" },
940 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
941 { "ACCT", ACCT, STR1, 0, "(specify account)" },
942 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
943 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
944 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
945 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
946 { "EPRT", EPRT, STR1, 1, "<sp> string" },
947 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
948 { "EPSV", EPSV, OSTR, 1, "[<sp> foo]" },
949 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
950 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
951 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
952 { "RETR", RETR, STR1, 1, "<sp> file-name" },
953 { "STOR", STOR, STR1, 1, "<sp> file-name" },
954 { "APPE", APPE, STR1, 1, "<sp> file-name" },
955 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
956 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
957 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
958 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
959 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
960 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
961 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
962 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
963 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
964 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
965 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
966 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
967 { "DELE", DELE, STR1, 1, "<sp> file-name" },
968 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
969 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
970 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
971 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
972 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
973 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
974 { "STAT", sTAT, OSTR, 1, "[ <sp> path-name ]" },
975 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
976 { "NOOP", NOOP, ARGS, 1, "" },
977 { "MKD", MKD, STR1, 1, "<sp> path-name" },
978 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
979 { "RMD", RMD, STR1, 1, "<sp> path-name" },
980 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
981 { "PWD", PWD, ARGS, 1, "(return current directory)" },
982 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
983 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
984 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
985 { "STOU", STOU, STR1, 1, "<sp> file-name" },
986 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
987 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
989 /* extensions from RFC2228 */
990 { "AUTH", AUTH, STR1, 1, "<sp> auth-type" },
991 { "ADAT", ADAT, STR1, 1, "<sp> auth-data" },
992 { "PBSZ", PBSZ, ARGS, 1, "<sp> buffer-size" },
993 { "PROT", PROT, STR1, 1, "<sp> prot-level" },
994 { "CCC", CCC, ARGS, 1, "" },
995 { "MIC", MIC, STR1, 1, "<sp> integrity command" },
996 { "CONF", CONF, STR1, 1, "<sp> confidentiality command" },
997 { "ENC", ENC, STR1, 1, "<sp> privacy command" },
999 /* RFC2389 */
1000 { "FEAT", FEAT, ARGS, 1, "" },
1001 { "OPTS", OPTS, ARGS, 1, "<sp> command [<sp> options]" },
1003 { NULL, 0, 0, 0, 0 }
1006 struct tab sitetab[] = {
1007 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1008 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1009 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1010 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1012 { "KAUTH", KAUTH, STR1, 1, "<sp> principal [ <sp> ticket ]" },
1013 { "KLIST", KLIST, ARGS, 1, "(show ticket file)" },
1014 { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1015 { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1016 { "AFSLOG", AFSLOG, OSTR, 1, "[<sp> cell]" },
1018 { "LOCATE", LOCATE, STR1, 1, "<sp> globexpr" },
1019 { "FIND", LOCATE, STR1, 1, "<sp> globexpr" },
1021 { "URL", URL, ARGS, 1, "?" },
1023 { NULL, 0, 0, 0, 0 }
1026 static struct tab *
1027 lookup(struct tab *p, char *cmd)
1030 for (; p->name != NULL; p++)
1031 if (strcmp(cmd, p->name) == 0)
1032 return (p);
1033 return (0);
1037 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1039 char *
1040 ftpd_getline(char *s, int n)
1042 int c;
1043 char *cs;
1045 cs = s;
1047 /* might still be data within the security MIC/CONF/ENC */
1048 if(ftp_command){
1049 strlcpy(s, ftp_command, n);
1050 if (debug)
1051 syslog(LOG_DEBUG, "command: %s", s);
1052 return s;
1054 while ((c = getc(stdin)) != EOF) {
1055 c &= 0377;
1056 if (c == IAC) {
1057 if ((c = getc(stdin)) != EOF) {
1058 c &= 0377;
1059 switch (c) {
1060 case WILL:
1061 case WONT:
1062 c = getc(stdin);
1063 printf("%c%c%c", IAC, DONT, 0377&c);
1064 fflush(stdout);
1065 continue;
1066 case DO:
1067 case DONT:
1068 c = getc(stdin);
1069 printf("%c%c%c", IAC, WONT, 0377&c);
1070 fflush(stdout);
1071 continue;
1072 case IAC:
1073 break;
1074 default:
1075 continue; /* ignore command */
1079 *cs++ = c;
1080 if (--n <= 0 || c == '\n')
1081 break;
1083 if (c == EOF && cs == s)
1084 return (NULL);
1085 *cs++ = '\0';
1086 if (debug) {
1087 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1088 /* Don't syslog passwords */
1089 syslog(LOG_DEBUG, "command: %.5s ???", s);
1090 } else {
1091 char *cp;
1092 int len;
1094 /* Don't syslog trailing CR-LF */
1095 len = strlen(s);
1096 cp = s + len - 1;
1097 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1098 --cp;
1099 --len;
1101 syslog(LOG_DEBUG, "command: %.*s", len, s);
1104 #ifdef XXX
1105 fprintf(stderr, "%s\n", s);
1106 #endif
1107 return (s);
1110 static RETSIGTYPE
1111 toolong(int signo)
1114 reply(421,
1115 "Timeout (%d seconds): closing control connection.",
1116 ftpd_timeout);
1117 if (logging)
1118 syslog(LOG_INFO, "User %s timed out after %d seconds",
1119 (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1120 dologout(1);
1121 SIGRETURN(0);
1124 static int
1125 yylex(void)
1127 static int cpos, state;
1128 char *cp, *cp2;
1129 struct tab *p;
1130 int n;
1131 char c;
1133 for (;;) {
1134 switch (state) {
1136 case CMD:
1137 hasyyerrored = 0;
1139 signal(SIGALRM, toolong);
1140 alarm((unsigned) ftpd_timeout);
1141 if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1142 reply(221, "You could at least say goodbye.");
1143 dologout(0);
1145 alarm(0);
1146 #ifdef HAVE_SETPROCTITLE
1147 if (strncasecmp(cbuf, "PASS", 4) != 0)
1148 setproctitle("%s: %s", proctitle, cbuf);
1149 #endif /* HAVE_SETPROCTITLE */
1150 if ((cp = strchr(cbuf, '\r'))) {
1151 *cp++ = '\n';
1152 *cp = '\0';
1154 if ((cp = strpbrk(cbuf, " \n")))
1155 cpos = cp - cbuf;
1156 if (cpos == 0)
1157 cpos = 4;
1158 c = cbuf[cpos];
1159 cbuf[cpos] = '\0';
1160 strupr(cbuf);
1161 p = lookup(cmdtab, cbuf);
1162 cbuf[cpos] = c;
1163 if (p != 0) {
1164 if (p->implemented == 0) {
1165 nack(p->name);
1166 hasyyerrored = 1;
1167 break;
1169 state = p->state;
1170 yylval.s = p->name;
1171 return (p->token);
1173 break;
1175 case SITECMD:
1176 if (cbuf[cpos] == ' ') {
1177 cpos++;
1178 return (SP);
1180 cp = &cbuf[cpos];
1181 if ((cp2 = strpbrk(cp, " \n")))
1182 cpos = cp2 - cbuf;
1183 c = cbuf[cpos];
1184 cbuf[cpos] = '\0';
1185 strupr(cp);
1186 p = lookup(sitetab, cp);
1187 cbuf[cpos] = c;
1188 if (p != 0) {
1189 if (p->implemented == 0) {
1190 state = CMD;
1191 nack(p->name);
1192 hasyyerrored = 1;
1193 break;
1195 state = p->state;
1196 yylval.s = p->name;
1197 return (p->token);
1199 state = CMD;
1200 break;
1202 case OSTR:
1203 if (cbuf[cpos] == '\n') {
1204 state = CMD;
1205 return (CRLF);
1207 /* FALLTHROUGH */
1209 case STR1:
1210 case ZSTR1:
1211 dostr1:
1212 if (cbuf[cpos] == ' ') {
1213 cpos++;
1214 if(state == OSTR)
1215 state = STR2;
1216 else
1217 state++;
1218 return (SP);
1220 break;
1222 case ZSTR2:
1223 if (cbuf[cpos] == '\n') {
1224 state = CMD;
1225 return (CRLF);
1227 /* FALLTHROUGH */
1229 case STR2:
1230 cp = &cbuf[cpos];
1231 n = strlen(cp);
1232 cpos += n - 1;
1234 * Make sure the string is nonempty and \n terminated.
1236 if (n > 1 && cbuf[cpos] == '\n') {
1237 cbuf[cpos] = '\0';
1238 yylval.s = copy(cp);
1239 cbuf[cpos] = '\n';
1240 state = ARGS;
1241 return (STRING);
1243 break;
1245 case NSTR:
1246 if (cbuf[cpos] == ' ') {
1247 cpos++;
1248 return (SP);
1250 if (isdigit((unsigned char)cbuf[cpos])) {
1251 cp = &cbuf[cpos];
1252 while (isdigit((unsigned char)cbuf[++cpos]))
1254 c = cbuf[cpos];
1255 cbuf[cpos] = '\0';
1256 yylval.i = atoi(cp);
1257 cbuf[cpos] = c;
1258 state = STR1;
1259 return (NUMBER);
1261 state = STR1;
1262 goto dostr1;
1264 case ARGS:
1265 if (isdigit((unsigned char)cbuf[cpos])) {
1266 cp = &cbuf[cpos];
1267 while (isdigit((unsigned char)cbuf[++cpos]))
1269 c = cbuf[cpos];
1270 cbuf[cpos] = '\0';
1271 yylval.i = atoi(cp);
1272 cbuf[cpos] = c;
1273 return (NUMBER);
1275 switch (cbuf[cpos++]) {
1277 case '\n':
1278 state = CMD;
1279 return (CRLF);
1281 case ' ':
1282 return (SP);
1284 case ',':
1285 return (COMMA);
1287 case 'A':
1288 case 'a':
1289 return (A);
1291 case 'B':
1292 case 'b':
1293 return (B);
1295 case 'C':
1296 case 'c':
1297 return (C);
1299 case 'E':
1300 case 'e':
1301 return (E);
1303 case 'F':
1304 case 'f':
1305 return (F);
1307 case 'I':
1308 case 'i':
1309 return (I);
1311 case 'L':
1312 case 'l':
1313 return (L);
1315 case 'N':
1316 case 'n':
1317 return (N);
1319 case 'P':
1320 case 'p':
1321 return (P);
1323 case 'R':
1324 case 'r':
1325 return (R);
1327 case 'S':
1328 case 's':
1329 return (S);
1331 case 'T':
1332 case 't':
1333 return (T);
1336 break;
1338 default:
1339 fatal("Unknown state in scanner.");
1341 yyerror(NULL);
1342 state = CMD;
1343 return (0);
1347 /* ARGSUSED */
1348 void
1349 yyerror(char *s)
1351 char *cp;
1353 if (hasyyerrored)
1354 return;
1356 if ((cp = strchr(cbuf,'\n')))
1357 *cp = '\0';
1358 reply(500, "'%s': command not understood.", cbuf);
1359 hasyyerrored = 1;
1362 static char *
1363 copy(char *s)
1365 char *p;
1367 p = strdup(s);
1368 if (p == NULL)
1369 fatal("Ran out of memory.");
1370 return p;
1373 static void
1374 help(struct tab *ctab, char *s)
1376 struct tab *c;
1377 int width, NCMDS;
1378 char *t;
1379 char buf[1024];
1381 if (ctab == sitetab)
1382 t = "SITE ";
1383 else
1384 t = "";
1385 width = 0, NCMDS = 0;
1386 for (c = ctab; c->name != NULL; c++) {
1387 int len = strlen(c->name);
1389 if (len > width)
1390 width = len;
1391 NCMDS++;
1393 width = (width + 8) &~ 7;
1394 if (s == 0) {
1395 int i, j, w;
1396 int columns, lines;
1398 lreply(214, "The following %scommands are recognized %s.",
1399 t, "(* =>'s unimplemented)");
1400 columns = 76 / width;
1401 if (columns == 0)
1402 columns = 1;
1403 lines = (NCMDS + columns - 1) / columns;
1404 for (i = 0; i < lines; i++) {
1405 strlcpy (buf, " ", sizeof(buf));
1406 for (j = 0; j < columns; j++) {
1407 c = ctab + j * lines + i;
1408 snprintf (buf + strlen(buf),
1409 sizeof(buf) - strlen(buf),
1410 "%s%c",
1411 c->name,
1412 c->implemented ? ' ' : '*');
1413 if (c + lines >= &ctab[NCMDS])
1414 break;
1415 w = strlen(c->name) + 1;
1416 while (w < width) {
1417 strlcat (buf,
1418 " ",
1419 sizeof(buf));
1420 w++;
1423 lreply(214, "%s", buf);
1425 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1426 return;
1428 strupr(s);
1429 c = lookup(ctab, s);
1430 if (c == (struct tab *)0) {
1431 reply(502, "Unknown command %s.", s);
1432 return;
1434 if (c->implemented)
1435 reply(214, "Syntax: %s%s %s", t, c->name, c->help);
1436 else
1437 reply(214, "%s%-*s\t%s; unimplemented.", t, width,
1438 c->name, c->help);
1441 static void
1442 sizecmd(char *filename)
1444 switch (type) {
1445 case TYPE_L:
1446 case TYPE_I: {
1447 struct stat stbuf;
1448 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1449 reply(550, "%s: not a plain file.", filename);
1450 else
1451 reply(213, "%lu", (unsigned long)stbuf.st_size);
1452 break;
1454 case TYPE_A: {
1455 FILE *fin;
1456 int c;
1457 size_t count;
1458 struct stat stbuf;
1459 fin = fopen(filename, "r");
1460 if (fin == NULL) {
1461 perror_reply(550, filename);
1462 return;
1464 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1465 reply(550, "%s: not a plain file.", filename);
1466 fclose(fin);
1467 return;
1470 count = 0;
1471 while((c=getc(fin)) != EOF) {
1472 if (c == '\n') /* will get expanded to \r\n */
1473 count++;
1474 count++;
1476 fclose(fin);
1478 reply(213, "%lu", (unsigned long)count);
1479 break;
1481 default:
1482 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);