No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / appl / ftp / ftpd / ftpcmd.y
blobaed7e6edff36873ab8f4b98c23280435bcbff6e5
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("$Heimdal: ftpcmd.y 15677 2005-07-19 18:33:08Z lha $"
47 "$NetBSD$");
49 off_t restart_point;
51 static int hasyyerrored;
54 static int cmd_type;
55 static int cmd_form;
56 static int cmd_bytesz;
57 char cbuf[64*1024];
58 char *fromname;
60 struct tab {
61 char *name;
62 short token;
63 short state;
64 short implemented; /* 1 if command is implemented */
65 char *help;
68 extern struct tab cmdtab[];
69 extern struct tab sitetab[];
71 static char *copy (char *);
72 static void help (struct tab *, char *);
73 static struct tab *
74 lookup (struct tab *, char *);
75 static void sizecmd (char *);
76 static RETSIGTYPE toolong (int);
77 static int yylex (void);
79 /* This is for bison */
81 #if !defined(alloca) && !defined(HAVE_ALLOCA)
82 #define alloca(x) malloc(x)
83 #endif
87 %union {
88 int i;
89 char *s;
92 %token
93 A B C E F I
94 L N P R S T
96 SP CRLF COMMA
98 USER PASS ACCT REIN QUIT PORT
99 PASV TYPE STRU MODE RETR STOR
100 APPE MLFL MAIL MSND MSOM MSAM
101 MRSQ MRCP ALLO REST RNFR RNTO
102 ABOR DELE CWD LIST NLST SITE
103 sTAT HELP NOOP MKD RMD PWD
104 CDUP STOU SMNT SYST SIZE MDTM
105 EPRT EPSV
107 UMASK IDLE CHMOD
109 AUTH ADAT PROT PBSZ CCC MIC
110 CONF ENC
112 KAUTH KLIST KDESTROY KRBTKFILE AFSLOG
113 LOCATE URL
115 FEAT OPTS
117 LEXERR
119 %token <s> STRING
120 %token <i> NUMBER
122 %type <i> check_login check_login_no_guest check_secure octal_number byte_size
123 %type <i> struct_code mode_code type_code form_code
124 %type <s> pathstring pathname password username
126 %start cmd_list
130 cmd_list
131 : /* empty */
132 | cmd_list cmd
134 fromname = (char *) 0;
135 restart_point = (off_t) 0;
137 | cmd_list rcmd
141 : USER SP username CRLF check_secure
143 if ($5)
144 user($3);
145 free($3);
147 | PASS SP password CRLF check_secure
149 if ($5)
150 pass($3);
151 memset ($3, 0, strlen($3));
152 free($3);
154 | PORT SP host_port CRLF check_secure
156 if ($5) {
157 usedefault = 0;
158 if (pdata >= 0) {
159 close(pdata);
160 pdata = -1;
162 reply(200, "PORT command successful.");
165 | EPRT SP STRING CRLF check_secure
167 if ($5)
168 eprt ($3);
169 free ($3);
171 | PASV CRLF check_login
173 if($3)
174 pasv ();
176 | EPSV CRLF check_login
178 if($3)
179 epsv (NULL);
181 | EPSV SP STRING CRLF check_login
183 if($5)
184 epsv ($3);
185 free ($3);
187 | TYPE SP type_code CRLF check_secure
189 if ($5) {
190 switch (cmd_type) {
192 case TYPE_A:
193 if (cmd_form == FORM_N) {
194 reply(200, "Type set to A.");
195 type = cmd_type;
196 form = cmd_form;
197 } else
198 reply(504, "Form must be N.");
199 break;
201 case TYPE_E:
202 reply(504, "Type E not implemented.");
203 break;
205 case TYPE_I:
206 reply(200, "Type set to I.");
207 type = cmd_type;
208 break;
210 case TYPE_L:
211 #if NBBY == 8
212 if (cmd_bytesz == 8) {
213 reply(200,
214 "Type set to L (byte size 8).");
215 type = cmd_type;
216 } else
217 reply(504, "Byte size must be 8.");
218 #else /* NBBY == 8 */
219 UNIMPLEMENTED for NBBY != 8
220 #endif /* NBBY == 8 */
224 | STRU SP struct_code CRLF check_secure
226 if ($5) {
227 switch ($3) {
229 case STRU_F:
230 reply(200, "STRU F ok.");
231 break;
233 default:
234 reply(504, "Unimplemented STRU type.");
238 | MODE SP mode_code CRLF check_secure
240 if ($5) {
241 switch ($3) {
243 case MODE_S:
244 reply(200, "MODE S ok.");
245 break;
247 default:
248 reply(502, "Unimplemented MODE type.");
252 | ALLO SP NUMBER CRLF check_secure
254 if ($5) {
255 reply(202, "ALLO command ignored.");
258 | ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
260 if ($9) {
261 reply(202, "ALLO command ignored.");
264 | RETR SP pathname CRLF check_login
266 char *name = $3;
268 if ($5 && name != NULL)
269 retrieve(0, name);
270 if (name != NULL)
271 free(name);
273 | STOR SP pathname CRLF check_login
275 char *name = $3;
277 if ($5 && name != NULL)
278 do_store(name, "w", 0);
279 if (name != NULL)
280 free(name);
282 | APPE SP pathname CRLF check_login
284 char *name = $3;
286 if ($5 && name != NULL)
287 do_store(name, "a", 0);
288 if (name != NULL)
289 free(name);
291 | NLST CRLF check_login
293 if ($3)
294 send_file_list(".");
296 | NLST SP STRING CRLF check_login
298 char *name = $3;
300 if ($5 && name != NULL)
301 send_file_list(name);
302 if (name != NULL)
303 free(name);
305 | LIST CRLF check_login
307 if($3)
308 list_file(".");
310 | LIST SP pathname CRLF check_login
312 if($5)
313 list_file($3);
314 free($3);
316 | sTAT SP pathname CRLF check_login
318 if ($5 && $3 != NULL)
319 statfilecmd($3);
320 if ($3 != NULL)
321 free($3);
323 | sTAT CRLF check_secure
325 if ($3)
326 statcmd();
328 | DELE SP pathname CRLF check_login_no_guest
330 if ($5 && $3 != NULL)
331 do_delete($3);
332 if ($3 != NULL)
333 free($3);
335 | RNTO SP pathname CRLF check_login_no_guest
337 if($5){
338 if (fromname) {
339 renamecmd(fromname, $3);
340 free(fromname);
341 fromname = (char *) 0;
342 } else {
343 reply(503, "Bad sequence of commands.");
346 if ($3 != NULL)
347 free($3);
349 | ABOR CRLF check_secure
351 if ($3)
352 reply(225, "ABOR command successful.");
354 | CWD CRLF check_login
356 if ($3)
357 cwd(pw->pw_dir);
359 | CWD SP pathname CRLF check_login
361 if ($5 && $3 != NULL)
362 cwd($3);
363 if ($3 != NULL)
364 free($3);
366 | HELP CRLF check_secure
368 if ($3)
369 help(cmdtab, (char *) 0);
371 | HELP SP STRING CRLF check_secure
373 if ($5) {
374 char *cp = $3;
376 if (strncasecmp(cp, "SITE", 4) == 0) {
377 cp = $3 + 4;
378 if (*cp == ' ')
379 cp++;
380 if (*cp)
381 help(sitetab, cp);
382 else
383 help(sitetab, (char *) 0);
384 } else
385 help(cmdtab, $3);
388 | NOOP CRLF check_secure
390 if ($3)
391 reply(200, "NOOP command successful.");
393 | MKD SP pathname CRLF check_login
395 if ($5 && $3 != NULL)
396 makedir($3);
397 if ($3 != NULL)
398 free($3);
400 | RMD SP pathname CRLF check_login_no_guest
402 if ($5 && $3 != NULL)
403 removedir($3);
404 if ($3 != NULL)
405 free($3);
407 | PWD CRLF check_login
409 if ($3)
410 pwd();
412 | CDUP CRLF check_login
414 if ($3)
415 cwd("..");
417 | FEAT CRLF check_secure
419 if ($3) {
420 lreply(211, "Supported features:");
421 lreply(0, " MDTM");
422 lreply(0, " REST STREAM");
423 lreply(0, " SIZE");
424 reply(211, "End");
427 | OPTS SP STRING CRLF check_secure
429 if ($5)
430 reply(501, "Bad options");
431 free ($3);
434 | SITE SP HELP CRLF check_secure
436 if ($5)
437 help(sitetab, (char *) 0);
439 | SITE SP HELP SP STRING CRLF check_secure
441 if ($7)
442 help(sitetab, $5);
444 | SITE SP UMASK CRLF check_login
446 if ($5) {
447 int oldmask = umask(0);
448 umask(oldmask);
449 reply(200, "Current UMASK is %03o", oldmask);
452 | SITE SP UMASK SP octal_number CRLF check_login_no_guest
454 if ($7) {
455 if (($5 == -1) || ($5 > 0777)) {
456 reply(501, "Bad UMASK value");
457 } else {
458 int oldmask = umask($5);
459 reply(200,
460 "UMASK set to %03o (was %03o)",
461 $5, oldmask);
465 | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
467 if ($9 && $7 != NULL) {
468 if ($5 > 0777)
469 reply(501,
470 "CHMOD: Mode value must be between 0 and 0777");
471 else if (chmod($7, $5) < 0)
472 perror_reply(550, $7);
473 else
474 reply(200, "CHMOD command successful.");
476 if ($7 != NULL)
477 free($7);
479 | SITE SP IDLE CRLF check_secure
481 if ($5)
482 reply(200,
483 "Current IDLE time limit is %d seconds; max %d",
484 ftpd_timeout, maxtimeout);
486 | SITE SP IDLE SP NUMBER CRLF check_secure
488 if ($7) {
489 if ($5 < 30 || $5 > maxtimeout) {
490 reply(501,
491 "Maximum IDLE time must be between 30 and %d seconds",
492 maxtimeout);
493 } else {
494 ftpd_timeout = $5;
495 alarm((unsigned) ftpd_timeout);
496 reply(200,
497 "Maximum IDLE time set to %d seconds",
498 ftpd_timeout);
503 | SITE SP KAUTH SP STRING CRLF check_login
505 #ifdef KRB4
506 char *p;
508 if(guest)
509 reply(500, "Can't be done as guest.");
510 else{
511 if($7 && $5 != NULL){
512 p = strpbrk($5, " \t");
513 if(p){
514 *p++ = 0;
515 kauth($5, p + strspn(p, " \t"));
516 }else
517 kauth($5, NULL);
520 if($5 != NULL)
521 free($5);
522 #else
523 reply(500, "Command not implemented.");
524 #endif
526 | SITE SP KLIST CRLF check_login
528 if($5)
529 klist();
531 | SITE SP KDESTROY CRLF check_login
533 #ifdef KRB4
534 if($5)
535 kdestroy();
536 #else
537 reply(500, "Command not implemented.");
538 #endif
540 | SITE SP KRBTKFILE SP STRING CRLF check_login
542 #ifdef KRB4
543 if(guest)
544 reply(500, "Can't be done as guest.");
545 else if($7 && $5)
546 krbtkfile($5);
547 if($5)
548 free($5);
549 #else
550 reply(500, "Command not implemented.");
551 #endif
553 | SITE SP AFSLOG CRLF check_login
555 #if defined(KRB4) || defined(KRB5)
556 if(guest)
557 reply(500, "Can't be done as guest.");
558 else if($5)
559 afslog(NULL, 0);
560 #else
561 reply(500, "Command not implemented.");
562 #endif
564 | SITE SP AFSLOG SP STRING CRLF check_login
566 #if defined(KRB4) || defined(KRB5)
567 if(guest)
568 reply(500, "Can't be done as guest.");
569 else if($7)
570 afslog($5, 0);
571 if($5)
572 free($5);
573 #else
574 reply(500, "Command not implemented.");
575 #endif
577 | SITE SP LOCATE SP STRING CRLF check_login
579 if($7 && $5 != NULL)
580 find($5);
581 if($5 != NULL)
582 free($5);
584 | SITE SP URL CRLF check_secure
586 if ($5)
587 reply(200, "http://www.pdc.kth.se/heimdal/");
589 | STOU SP pathname CRLF check_login
591 if ($5 && $3 != NULL)
592 do_store($3, "w", 1);
593 if ($3 != NULL)
594 free($3);
596 | SYST CRLF check_secure
598 if ($3) {
599 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
600 reply(215, "UNIX Type: L%d", NBBY);
601 #else
602 reply(215, "UNKNOWN Type: L%d", NBBY);
603 #endif
608 * SIZE is not in RFC959, but Postel has blessed it and
609 * it will be in the updated RFC.
611 * Return size of file in a format suitable for
612 * using with RESTART (we just count bytes).
614 | SIZE SP pathname CRLF check_login
616 if ($5 && $3 != NULL)
617 sizecmd($3);
618 if ($3 != NULL)
619 free($3);
623 * MDTM is not in RFC959, but Postel has blessed it and
624 * it will be in the updated RFC.
626 * Return modification time of file as an ISO 3307
627 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
628 * where xxx is the fractional second (of any precision,
629 * not necessarily 3 digits)
631 | MDTM SP pathname CRLF check_login
633 if ($5 && $3 != NULL) {
634 struct stat stbuf;
635 if (stat($3, &stbuf) < 0)
636 reply(550, "%s: %s",
637 $3, strerror(errno));
638 else if (!S_ISREG(stbuf.st_mode)) {
639 reply(550,
640 "%s: not a plain file.", $3);
641 } else {
642 struct tm *t;
643 time_t mtime = stbuf.st_mtime;
645 t = gmtime(&mtime);
646 reply(213,
647 "%04d%02d%02d%02d%02d%02d",
648 t->tm_year + 1900,
649 t->tm_mon + 1,
650 t->tm_mday,
651 t->tm_hour,
652 t->tm_min,
653 t->tm_sec);
656 if ($3 != NULL)
657 free($3);
659 | QUIT CRLF check_secure
661 if ($3) {
662 reply(221, "Goodbye.");
663 dologout(0);
666 | error CRLF
668 yyerrok;
671 rcmd
672 : RNFR SP pathname CRLF check_login_no_guest
674 restart_point = (off_t) 0;
675 if ($5 && $3) {
676 fromname = renamefrom($3);
677 if (fromname == (char *) 0 && $3) {
678 free($3);
682 | REST SP byte_size CRLF check_secure
684 if ($5) {
685 fromname = (char *) 0;
686 restart_point = $3; /* XXX $3 is only "int" */
687 reply(350, "Restarting at %ld. %s",
688 (long)restart_point,
689 "Send STORE or RETRIEVE to initiate transfer.");
692 | AUTH SP STRING CRLF
694 auth($3);
695 free($3);
697 | ADAT SP STRING CRLF
699 adat($3);
700 free($3);
702 | PBSZ SP NUMBER CRLF check_secure
704 if ($5)
705 pbsz($3);
707 | PROT SP STRING CRLF check_secure
709 if ($5)
710 prot($3);
712 | CCC CRLF check_secure
714 if ($3)
715 ccc();
717 | MIC SP STRING CRLF
719 mec($3, prot_safe);
720 free($3);
722 | CONF SP STRING CRLF
724 mec($3, prot_confidential);
725 free($3);
727 | ENC SP STRING CRLF
729 mec($3, prot_private);
730 free($3);
734 username
735 : STRING
738 password
739 : /* empty */
741 $$ = (char *)calloc(1, sizeof(char));
743 | STRING
746 byte_size
747 : NUMBER
750 host_port
751 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
752 NUMBER COMMA NUMBER
754 struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest;
756 sin4->sin_family = AF_INET;
757 sin4->sin_port = htons($9 * 256 + $11);
758 sin4->sin_addr.s_addr =
759 htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
763 form_code
766 $$ = FORM_N;
770 $$ = FORM_T;
774 $$ = FORM_C;
778 type_code
781 cmd_type = TYPE_A;
782 cmd_form = FORM_N;
784 | A SP form_code
786 cmd_type = TYPE_A;
787 cmd_form = $3;
791 cmd_type = TYPE_E;
792 cmd_form = FORM_N;
794 | E SP form_code
796 cmd_type = TYPE_E;
797 cmd_form = $3;
801 cmd_type = TYPE_I;
805 cmd_type = TYPE_L;
806 cmd_bytesz = NBBY;
808 | L SP byte_size
810 cmd_type = TYPE_L;
811 cmd_bytesz = $3;
813 /* this is for a bug in the BBN ftp */
814 | L byte_size
816 cmd_type = TYPE_L;
817 cmd_bytesz = $2;
821 struct_code
824 $$ = STRU_F;
828 $$ = STRU_R;
832 $$ = STRU_P;
836 mode_code
839 $$ = MODE_S;
843 $$ = MODE_B;
847 $$ = MODE_C;
851 pathname
852 : pathstring
855 * Problem: this production is used for all pathname
856 * processing, but only gives a 550 error reply.
857 * This is a valid reply in some cases but not in others.
859 if (logged_in && $1 && *$1 == '~') {
860 glob_t gl;
861 int flags =
862 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
864 memset(&gl, 0, sizeof(gl));
865 if (glob($1, flags, NULL, &gl) ||
866 gl.gl_pathc == 0) {
867 reply(550, "not found");
868 $$ = NULL;
869 } else {
870 $$ = strdup(gl.gl_pathv[0]);
872 globfree(&gl);
873 free($1);
874 } else
875 $$ = $1;
879 pathstring
880 : STRING
883 octal_number
884 : NUMBER
886 int ret, dec, multby, digit;
889 * Convert a number that was read as decimal number
890 * to what it would be if it had been read as octal.
892 dec = $1;
893 multby = 1;
894 ret = 0;
895 while (dec) {
896 digit = dec%10;
897 if (digit > 7) {
898 ret = -1;
899 break;
901 ret += digit * multby;
902 multby *= 8;
903 dec /= 10;
905 $$ = ret;
910 check_login_no_guest : check_login
912 $$ = $1 && !guest;
913 if($1 && !$$)
914 reply(550, "Permission denied");
918 check_login : check_secure
920 if($1) {
921 if(($$ = logged_in) == 0)
922 reply(530, "Please login with USER and PASS.");
923 } else
924 $$ = 0;
928 check_secure : /* empty */
930 $$ = 1;
931 if(sec_complete && !ccc_passed && !secure_command()) {
932 $$ = 0;
933 reply(533, "Command protection level denied "
934 "for paranoid reasons.");
941 #define CMD 0 /* beginning of command */
942 #define ARGS 1 /* expect miscellaneous arguments */
943 #define STR1 2 /* expect SP followed by STRING */
944 #define STR2 3 /* expect STRING */
945 #define OSTR 4 /* optional SP then STRING */
946 #define ZSTR1 5 /* SP then optional STRING */
947 #define ZSTR2 6 /* optional STRING after SP */
948 #define SITECMD 7 /* SITE command */
949 #define NSTR 8 /* Number followed by a string */
951 struct tab cmdtab[] = { /* In order defined in RFC 765 */
952 { "USER", USER, STR1, 1, "<sp> username" },
953 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
954 { "ACCT", ACCT, STR1, 0, "(specify account)" },
955 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
956 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
957 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
958 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
959 { "EPRT", EPRT, STR1, 1, "<sp> string" },
960 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
961 { "EPSV", EPSV, OSTR, 1, "[<sp> foo]" },
962 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
963 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
964 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
965 { "RETR", RETR, STR1, 1, "<sp> file-name" },
966 { "STOR", STOR, STR1, 1, "<sp> file-name" },
967 { "APPE", APPE, STR1, 1, "<sp> file-name" },
968 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
969 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
970 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
971 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
972 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
973 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
974 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
975 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
976 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
977 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
978 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
979 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
980 { "DELE", DELE, STR1, 1, "<sp> file-name" },
981 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
982 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
983 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
984 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
985 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
986 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
987 { "STAT", sTAT, OSTR, 1, "[ <sp> path-name ]" },
988 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
989 { "NOOP", NOOP, ARGS, 1, "" },
990 { "MKD", MKD, STR1, 1, "<sp> path-name" },
991 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
992 { "RMD", RMD, STR1, 1, "<sp> path-name" },
993 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
994 { "PWD", PWD, ARGS, 1, "(return current directory)" },
995 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
996 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
997 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
998 { "STOU", STOU, STR1, 1, "<sp> file-name" },
999 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1000 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1002 /* extensions from RFC2228 */
1003 { "AUTH", AUTH, STR1, 1, "<sp> auth-type" },
1004 { "ADAT", ADAT, STR1, 1, "<sp> auth-data" },
1005 { "PBSZ", PBSZ, ARGS, 1, "<sp> buffer-size" },
1006 { "PROT", PROT, STR1, 1, "<sp> prot-level" },
1007 { "CCC", CCC, ARGS, 1, "" },
1008 { "MIC", MIC, STR1, 1, "<sp> integrity command" },
1009 { "CONF", CONF, STR1, 1, "<sp> confidentiality command" },
1010 { "ENC", ENC, STR1, 1, "<sp> privacy command" },
1012 /* RFC2389 */
1013 { "FEAT", FEAT, ARGS, 1, "" },
1014 { "OPTS", OPTS, ARGS, 1, "<sp> command [<sp> options]" },
1016 { NULL, 0, 0, 0, 0 }
1019 struct tab sitetab[] = {
1020 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1021 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1022 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1023 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1025 { "KAUTH", KAUTH, STR1, 1, "<sp> principal [ <sp> ticket ]" },
1026 { "KLIST", KLIST, ARGS, 1, "(show ticket file)" },
1027 { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1028 { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1029 { "AFSLOG", AFSLOG, OSTR, 1, "[<sp> cell]" },
1031 { "LOCATE", LOCATE, STR1, 1, "<sp> globexpr" },
1032 { "FIND", LOCATE, STR1, 1, "<sp> globexpr" },
1034 { "URL", URL, ARGS, 1, "?" },
1036 { NULL, 0, 0, 0, 0 }
1039 static struct tab *
1040 lookup(struct tab *p, char *cmd)
1043 for (; p->name != NULL; p++)
1044 if (strcmp(cmd, p->name) == 0)
1045 return (p);
1046 return (0);
1050 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1052 char *
1053 ftpd_getline(char *s, int n)
1055 int c;
1056 char *cs;
1058 cs = s;
1060 /* might still be data within the security MIC/CONF/ENC */
1061 if(ftp_command){
1062 strlcpy(s, ftp_command, n);
1063 if (debug)
1064 syslog(LOG_DEBUG, "command: %s", s);
1065 return s;
1067 while ((c = getc(stdin)) != EOF) {
1068 c &= 0377;
1069 if (c == IAC) {
1070 if ((c = getc(stdin)) != EOF) {
1071 c &= 0377;
1072 switch (c) {
1073 case WILL:
1074 case WONT:
1075 c = getc(stdin);
1076 printf("%c%c%c", IAC, DONT, 0377&c);
1077 fflush(stdout);
1078 continue;
1079 case DO:
1080 case DONT:
1081 c = getc(stdin);
1082 printf("%c%c%c", IAC, WONT, 0377&c);
1083 fflush(stdout);
1084 continue;
1085 case IAC:
1086 break;
1087 default:
1088 continue; /* ignore command */
1092 *cs++ = c;
1093 if (--n <= 0 || c == '\n')
1094 break;
1096 if (c == EOF && cs == s)
1097 return (NULL);
1098 *cs++ = '\0';
1099 if (debug) {
1100 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1101 /* Don't syslog passwords */
1102 syslog(LOG_DEBUG, "command: %.5s ???", s);
1103 } else {
1104 char *cp;
1105 int len;
1107 /* Don't syslog trailing CR-LF */
1108 len = strlen(s);
1109 cp = s + len - 1;
1110 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1111 --cp;
1112 --len;
1114 syslog(LOG_DEBUG, "command: %.*s", len, s);
1117 #ifdef XXX
1118 fprintf(stderr, "%s\n", s);
1119 #endif
1120 return (s);
1123 static RETSIGTYPE
1124 toolong(int signo)
1127 reply(421,
1128 "Timeout (%d seconds): closing control connection.",
1129 ftpd_timeout);
1130 if (logging)
1131 syslog(LOG_INFO, "User %s timed out after %d seconds",
1132 (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1133 dologout(1);
1134 SIGRETURN(0);
1137 static int
1138 yylex(void)
1140 static int cpos, state;
1141 char *cp, *cp2;
1142 struct tab *p;
1143 int n;
1144 char c;
1146 for (;;) {
1147 switch (state) {
1149 case CMD:
1150 hasyyerrored = 0;
1152 signal(SIGALRM, toolong);
1153 alarm((unsigned) ftpd_timeout);
1154 if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1155 reply(221, "You could at least say goodbye.");
1156 dologout(0);
1158 alarm(0);
1159 #ifdef HAVE_SETPROCTITLE
1160 if (strncasecmp(cbuf, "PASS", 4) != 0)
1161 setproctitle("%s: %s", proctitle, cbuf);
1162 #endif /* HAVE_SETPROCTITLE */
1163 if ((cp = strchr(cbuf, '\r'))) {
1164 *cp++ = '\n';
1165 *cp = '\0';
1167 if ((cp = strpbrk(cbuf, " \n")))
1168 cpos = cp - cbuf;
1169 if (cpos == 0)
1170 cpos = 4;
1171 c = cbuf[cpos];
1172 cbuf[cpos] = '\0';
1173 strupr(cbuf);
1174 p = lookup(cmdtab, cbuf);
1175 cbuf[cpos] = c;
1176 if (p != 0) {
1177 if (p->implemented == 0) {
1178 nack(p->name);
1179 hasyyerrored = 1;
1180 break;
1182 state = p->state;
1183 yylval.s = p->name;
1184 return (p->token);
1186 break;
1188 case SITECMD:
1189 if (cbuf[cpos] == ' ') {
1190 cpos++;
1191 return (SP);
1193 cp = &cbuf[cpos];
1194 if ((cp2 = strpbrk(cp, " \n")))
1195 cpos = cp2 - cbuf;
1196 c = cbuf[cpos];
1197 cbuf[cpos] = '\0';
1198 strupr(cp);
1199 p = lookup(sitetab, cp);
1200 cbuf[cpos] = c;
1201 if (p != 0) {
1202 if (p->implemented == 0) {
1203 state = CMD;
1204 nack(p->name);
1205 hasyyerrored = 1;
1206 break;
1208 state = p->state;
1209 yylval.s = p->name;
1210 return (p->token);
1212 state = CMD;
1213 break;
1215 case OSTR:
1216 if (cbuf[cpos] == '\n') {
1217 state = CMD;
1218 return (CRLF);
1220 /* FALLTHROUGH */
1222 case STR1:
1223 case ZSTR1:
1224 dostr1:
1225 if (cbuf[cpos] == ' ') {
1226 cpos++;
1227 if(state == OSTR)
1228 state = STR2;
1229 else
1230 state++;
1231 return (SP);
1233 break;
1235 case ZSTR2:
1236 if (cbuf[cpos] == '\n') {
1237 state = CMD;
1238 return (CRLF);
1240 /* FALLTHROUGH */
1242 case STR2:
1243 cp = &cbuf[cpos];
1244 n = strlen(cp);
1245 cpos += n - 1;
1247 * Make sure the string is nonempty and \n terminated.
1249 if (n > 1 && cbuf[cpos] == '\n') {
1250 cbuf[cpos] = '\0';
1251 yylval.s = copy(cp);
1252 cbuf[cpos] = '\n';
1253 state = ARGS;
1254 return (STRING);
1256 break;
1258 case NSTR:
1259 if (cbuf[cpos] == ' ') {
1260 cpos++;
1261 return (SP);
1263 if (isdigit((unsigned char)cbuf[cpos])) {
1264 cp = &cbuf[cpos];
1265 while (isdigit((unsigned char)cbuf[++cpos]))
1267 c = cbuf[cpos];
1268 cbuf[cpos] = '\0';
1269 yylval.i = atoi(cp);
1270 cbuf[cpos] = c;
1271 state = STR1;
1272 return (NUMBER);
1274 state = STR1;
1275 goto dostr1;
1277 case ARGS:
1278 if (isdigit((unsigned char)cbuf[cpos])) {
1279 cp = &cbuf[cpos];
1280 while (isdigit((unsigned char)cbuf[++cpos]))
1282 c = cbuf[cpos];
1283 cbuf[cpos] = '\0';
1284 yylval.i = atoi(cp);
1285 cbuf[cpos] = c;
1286 return (NUMBER);
1288 switch (cbuf[cpos++]) {
1290 case '\n':
1291 state = CMD;
1292 return (CRLF);
1294 case ' ':
1295 return (SP);
1297 case ',':
1298 return (COMMA);
1300 case 'A':
1301 case 'a':
1302 return (A);
1304 case 'B':
1305 case 'b':
1306 return (B);
1308 case 'C':
1309 case 'c':
1310 return (C);
1312 case 'E':
1313 case 'e':
1314 return (E);
1316 case 'F':
1317 case 'f':
1318 return (F);
1320 case 'I':
1321 case 'i':
1322 return (I);
1324 case 'L':
1325 case 'l':
1326 return (L);
1328 case 'N':
1329 case 'n':
1330 return (N);
1332 case 'P':
1333 case 'p':
1334 return (P);
1336 case 'R':
1337 case 'r':
1338 return (R);
1340 case 'S':
1341 case 's':
1342 return (S);
1344 case 'T':
1345 case 't':
1346 return (T);
1349 break;
1351 default:
1352 fatal("Unknown state in scanner.");
1354 yyerror(NULL);
1355 state = CMD;
1356 return (0);
1360 /* ARGSUSED */
1361 void
1362 yyerror(char *s)
1364 char *cp;
1366 if (hasyyerrored)
1367 return;
1369 if ((cp = strchr(cbuf,'\n')))
1370 *cp = '\0';
1371 reply(500, "'%s': command not understood.", cbuf);
1372 hasyyerrored = 1;
1375 static char *
1376 copy(char *s)
1378 char *p;
1380 p = strdup(s);
1381 if (p == NULL)
1382 fatal("Ran out of memory.");
1383 return p;
1386 static void
1387 help(struct tab *ctab, char *s)
1389 struct tab *c;
1390 int width, NCMDS;
1391 char *t;
1392 char buf[1024];
1394 if (ctab == sitetab)
1395 t = "SITE ";
1396 else
1397 t = "";
1398 width = 0, NCMDS = 0;
1399 for (c = ctab; c->name != NULL; c++) {
1400 int len = strlen(c->name);
1402 if (len > width)
1403 width = len;
1404 NCMDS++;
1406 width = (width + 8) &~ 7;
1407 if (s == 0) {
1408 int i, j, w;
1409 int columns, lines;
1411 lreply(214, "The following %scommands are recognized %s.",
1412 t, "(* =>'s unimplemented)");
1413 columns = 76 / width;
1414 if (columns == 0)
1415 columns = 1;
1416 lines = (NCMDS + columns - 1) / columns;
1417 for (i = 0; i < lines; i++) {
1418 strlcpy (buf, " ", sizeof(buf));
1419 for (j = 0; j < columns; j++) {
1420 c = ctab + j * lines + i;
1421 snprintf (buf + strlen(buf),
1422 sizeof(buf) - strlen(buf),
1423 "%s%c",
1424 c->name,
1425 c->implemented ? ' ' : '*');
1426 if (c + lines >= &ctab[NCMDS])
1427 break;
1428 w = strlen(c->name) + 1;
1429 while (w < width) {
1430 strlcat (buf,
1431 " ",
1432 sizeof(buf));
1433 w++;
1436 lreply(214, "%s", buf);
1438 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1439 return;
1441 strupr(s);
1442 c = lookup(ctab, s);
1443 if (c == (struct tab *)0) {
1444 reply(502, "Unknown command %s.", s);
1445 return;
1447 if (c->implemented)
1448 reply(214, "Syntax: %s%s %s", t, c->name, c->help);
1449 else
1450 reply(214, "%s%-*s\t%s; unimplemented.", t, width,
1451 c->name, c->help);
1454 static void
1455 sizecmd(char *filename)
1457 switch (type) {
1458 case TYPE_L:
1459 case TYPE_I: {
1460 struct stat stbuf;
1461 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1462 reply(550, "%s: not a plain file.", filename);
1463 else
1464 reply(213, "%lu", (unsigned long)stbuf.st_size);
1465 break;
1467 case TYPE_A: {
1468 FILE *fin;
1469 int c;
1470 size_t count;
1471 struct stat stbuf;
1472 fin = fopen(filename, "r");
1473 if (fin == NULL) {
1474 perror_reply(550, filename);
1475 return;
1477 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1478 reply(550, "%s: not a plain file.", filename);
1479 fclose(fin);
1480 return;
1483 count = 0;
1484 while((c=getc(fin)) != EOF) {
1485 if (c == '\n') /* will get expanded to \r\n */
1486 count++;
1487 count++;
1489 fclose(fin);
1491 reply(213, "%lu", (unsigned long)count);
1492 break;
1494 default:
1495 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);