xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/ftpcmd.y (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*	$NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, 1988, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
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.
22  *
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.
34  *
35  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
36  */
37 
38 /*
39  * Grammar for FTP commands.
40  * See RFC 959.
41  */
42 
43 %{
44 
45 #include "ftpd_locl.h"
46 RCSID("$Id: ftpcmd.y,v 1.60 2000/11/05 16:53:20 joda Exp $");
47 
48 off_t	restart_point;
49 
50 static	int cmd_type;
51 static	int cmd_form;
52 static	int cmd_bytesz;
53 char	cbuf[2048];
54 char	*fromname;
55 
56 struct tab {
57 	char	*name;
58 	short	token;
59 	short	state;
60 	short	implemented;	/* 1 if command is implemented */
61 	char	*help;
62 };
63 
64 extern struct tab cmdtab[];
65 extern struct tab sitetab[];
66 
67 static char		*copy (char *);
68 static void		 help (struct tab *, char *);
69 static struct tab *
70 			 lookup (struct tab *, char *);
71 static void		 sizecmd (char *);
72 static RETSIGTYPE	 toolong (int);
73 static int		 yylex (void);
74 
75 /* This is for bison */
76 
77 #if !defined(alloca) && !defined(HAVE_ALLOCA)
78 #define alloca(x) malloc(x)
79 #endif
80 
81 %}
82 
83 %union {
84 	int	i;
85 	char   *s;
86 }
87 
88 %token
89 	A	B	C	E	F	I
90 	L	N	P	R	S	T
91 
92 	SP	CRLF	COMMA
93 
94 	USER	PASS	ACCT	REIN	QUIT	PORT
95 	PASV	TYPE	STRU	MODE	RETR	STOR
96 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
97 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
98 	ABOR	DELE	CWD	LIST	NLST	SITE
99 	sTAT	HELP	NOOP	MKD	RMD	PWD
100 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
101 	EPRT	EPSV
102 
103 	UMASK	IDLE	CHMOD
104 
105 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
106 	CONF	ENC
107 
108 	KAUTH	KLIST	KDESTROY KRBTKFILE AFSLOG
109 	LOCATE	URL
110 
111 	FEAT	OPTS
112 
113 	LEXERR
114 
115 %token	<s> STRING
116 %token	<i> NUMBER
117 
118 %type	<i> check_login check_login_no_guest check_secure octal_number byte_size
119 %type	<i> struct_code mode_code type_code form_code
120 %type	<s> pathstring pathname password username
121 
122 %start	cmd_list
123 
124 %%
125 
126 cmd_list
127 	: /* empty */
128 	| cmd_list cmd
129 		{
130 			fromname = (char *) 0;
131 			restart_point = (off_t) 0;
132 		}
133 	| cmd_list rcmd
134 	;
135 
136 cmd
137 	: USER SP username CRLF
138 		{
139 			user($3);
140 			free($3);
141 		}
142 	| PASS SP password CRLF
143 		{
144 			pass($3);
145 			memset ($3, 0, strlen($3));
146 			free($3);
147 		}
148 	| PORT SP host_port CRLF
149 		{
150 			usedefault = 0;
151 			if (pdata >= 0) {
152 				close(pdata);
153 				pdata = -1;
154 			}
155 			reply(200, "PORT command successful.");
156 		}
157 	| EPRT SP STRING CRLF
158 		{
159 			eprt ($3);
160 			free ($3);
161 		}
162 	| PASV CRLF check_login
163 		{
164 		    if($3)
165 			pasv ();
166 		}
167 	| EPSV CRLF check_login
168 		{
169 		    if($3)
170 			epsv (NULL);
171 		}
172 	| EPSV SP STRING CRLF check_login
173 		{
174 		    if($5)
175 			epsv ($3);
176 		    free ($3);
177 		}
178 	| TYPE SP type_code CRLF
179 		{
180 			switch (cmd_type) {
181 
182 			case TYPE_A:
183 				if (cmd_form == FORM_N) {
184 					reply(200, "Type set to A.");
185 					type = cmd_type;
186 					form = cmd_form;
187 				} else
188 					reply(504, "Form must be N.");
189 				break;
190 
191 			case TYPE_E:
192 				reply(504, "Type E not implemented.");
193 				break;
194 
195 			case TYPE_I:
196 				reply(200, "Type set to I.");
197 				type = cmd_type;
198 				break;
199 
200 			case TYPE_L:
201 #if NBBY == 8
202 				if (cmd_bytesz == 8) {
203 					reply(200,
204 					    "Type set to L (byte size 8).");
205 					type = cmd_type;
206 				} else
207 					reply(504, "Byte size must be 8.");
208 #else /* NBBY == 8 */
209 				UNIMPLEMENTED for NBBY != 8
210 #endif /* NBBY == 8 */
211 			}
212 		}
213 	| STRU SP struct_code CRLF
214 		{
215 			switch ($3) {
216 
217 			case STRU_F:
218 				reply(200, "STRU F ok.");
219 				break;
220 
221 			default:
222 				reply(504, "Unimplemented STRU type.");
223 			}
224 		}
225 	| MODE SP mode_code CRLF
226 		{
227 			switch ($3) {
228 
229 			case MODE_S:
230 				reply(200, "MODE S ok.");
231 				break;
232 
233 			default:
234 				reply(502, "Unimplemented MODE type.");
235 			}
236 		}
237 	| ALLO SP NUMBER CRLF
238 		{
239 			reply(202, "ALLO command ignored.");
240 		}
241 	| ALLO SP NUMBER SP R SP NUMBER CRLF
242 		{
243 			reply(202, "ALLO command ignored.");
244 		}
245 	| RETR SP pathname CRLF check_login
246 		{
247 			char *name = $3;
248 
249 			if ($5 && name != NULL)
250 				retrieve(0, name);
251 			if (name != NULL)
252 				free(name);
253 		}
254 	| STOR SP pathname CRLF check_login
255 		{
256 			char *name = $3;
257 
258 			if ($5 && name != NULL)
259 				do_store(name, "w", 0);
260 			if (name != NULL)
261 				free(name);
262 		}
263 	| APPE SP pathname CRLF check_login
264 		{
265 			char *name = $3;
266 
267 			if ($5 && name != NULL)
268 				do_store(name, "a", 0);
269 			if (name != NULL)
270 				free(name);
271 		}
272 	| NLST CRLF check_login
273 		{
274 			if ($3)
275 				send_file_list(".");
276 		}
277 	| NLST SP STRING CRLF check_login
278 		{
279 			char *name = $3;
280 
281 			if ($5 && name != NULL)
282 				send_file_list(name);
283 			if (name != NULL)
284 				free(name);
285 		}
286 	| LIST CRLF check_login
287 		{
288 		    if($3)
289 			list_file(".");
290 		}
291 	| LIST SP pathname CRLF check_login
292 		{
293 		    if($5)
294 			list_file($3);
295 		    free($3);
296 		}
297 	| sTAT SP pathname CRLF check_login
298 		{
299 			if ($5 && $3 != NULL)
300 				statfilecmd($3);
301 			if ($3 != NULL)
302 				free($3);
303 		}
304 	| sTAT CRLF
305 		{
306 		    if(oobflag){
307 			if (file_size != (off_t) -1)
308 			    reply(213, "Status: %lu of %lu bytes transferred",
309 				  (unsigned long)byte_count,
310 				  (unsigned long)file_size);
311 			else
312 			    reply(213, "Status: %lu bytes transferred",
313 				  (unsigned long)byte_count);
314 		    }else
315 			statcmd();
316 	}
317 	| DELE SP pathname CRLF check_login_no_guest
318 		{
319 			if ($5 && $3 != NULL)
320 				do_delete($3);
321 			if ($3 != NULL)
322 				free($3);
323 		}
324 	| RNTO SP pathname CRLF check_login_no_guest
325 		{
326 			if($5){
327 				if (fromname) {
328 					renamecmd(fromname, $3);
329 					free(fromname);
330 					fromname = (char *) 0;
331 				} else {
332 					reply(503, "Bad sequence of commands.");
333 				}
334 			}
335 			if ($3 != NULL)
336 				free($3);
337 		}
338 	| ABOR CRLF
339 		{
340 			if(oobflag){
341 				reply(426, "Transfer aborted. Data connection closed.");
342 				reply(226, "Abort successful");
343 				oobflag = 0;
344 				longjmp(urgcatch, 1);
345 			}else
346 				reply(225, "ABOR command successful.");
347 		}
348 	| CWD CRLF check_login
349 		{
350 			if ($3)
351 				cwd(pw->pw_dir);
352 		}
353 	| CWD SP pathname CRLF check_login
354 		{
355 			if ($5 && $3 != NULL)
356 				cwd($3);
357 			if ($3 != NULL)
358 				free($3);
359 		}
360 	| HELP CRLF
361 		{
362 			help(cmdtab, (char *) 0);
363 		}
364 	| HELP SP STRING CRLF
365 		{
366 			char *cp = $3;
367 
368 			if (strncasecmp(cp, "SITE", 4) == 0) {
369 				cp = $3 + 4;
370 				if (*cp == ' ')
371 					cp++;
372 				if (*cp)
373 					help(sitetab, cp);
374 				else
375 					help(sitetab, (char *) 0);
376 			} else
377 				help(cmdtab, $3);
378 		}
379 	| NOOP CRLF
380 		{
381 			reply(200, "NOOP command successful.");
382 		}
383 	| MKD SP pathname CRLF check_login
384 		{
385 			if ($5 && $3 != NULL)
386 				makedir($3);
387 			if ($3 != NULL)
388 				free($3);
389 		}
390 	| RMD SP pathname CRLF check_login_no_guest
391 		{
392 			if ($5 && $3 != NULL)
393 				removedir($3);
394 			if ($3 != NULL)
395 				free($3);
396 		}
397 	| PWD CRLF check_login
398 		{
399 			if ($3)
400 				pwd();
401 		}
402 	| CDUP CRLF check_login
403 		{
404 			if ($3)
405 				cwd("..");
406 		}
407 	| FEAT CRLF
408 		{
409 			lreply(211, "Supported features:");
410 			lreply(0, " MDTM");
411 			lreply(0, " REST STREAM");
412 			lreply(0, " SIZE");
413 			reply(211, "End");
414 		}
415 	| OPTS SP STRING CRLF
416 		{
417 			free ($3);
418 			reply(501, "Bad options");
419 		}
420 
421 	| SITE SP HELP CRLF
422 		{
423 			help(sitetab, (char *) 0);
424 		}
425 	| SITE SP HELP SP STRING CRLF
426 		{
427 			help(sitetab, $5);
428 		}
429 	| SITE SP UMASK CRLF check_login
430 		{
431 			if ($5) {
432 				int oldmask = umask(0);
433 				umask(oldmask);
434 				reply(200, "Current UMASK is %03o", oldmask);
435 			}
436 		}
437 	| SITE SP UMASK SP octal_number CRLF check_login_no_guest
438 		{
439 			if ($7) {
440 				if (($5 == -1) || ($5 > 0777)) {
441 					reply(501, "Bad UMASK value");
442 				} else {
443 					int oldmask = umask($5);
444 					reply(200,
445 					      "UMASK set to %03o (was %03o)",
446 					      $5, oldmask);
447 				}
448 			}
449 		}
450 	| SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
451 		{
452 			if ($9 && $7 != NULL) {
453 				if ($5 > 0777)
454 					reply(501,
455 				"CHMOD: Mode value must be between 0 and 0777");
456 				else if (chmod($7, $5) < 0)
457 					perror_reply(550, $7);
458 				else
459 					reply(200, "CHMOD command successful.");
460 			}
461 			if ($7 != NULL)
462 				free($7);
463 		}
464 	| SITE SP IDLE CRLF
465 		{
466 			reply(200,
467 			    "Current IDLE time limit is %d seconds; max %d",
468 				ftpd_timeout, maxtimeout);
469 		}
470 	| SITE SP IDLE SP NUMBER CRLF
471 		{
472 			if ($5 < 30 || $5 > maxtimeout) {
473 				reply(501,
474 			"Maximum IDLE time must be between 30 and %d seconds",
475 				    maxtimeout);
476 			} else {
477 				ftpd_timeout = $5;
478 				alarm((unsigned) ftpd_timeout);
479 				reply(200,
480 				    "Maximum IDLE time set to %d seconds",
481 				    ftpd_timeout);
482 			}
483 		}
484 
485 	| SITE SP KAUTH SP STRING CRLF check_login
486 		{
487 #ifdef KRB4
488 			char *p;
489 
490 			if(guest)
491 				reply(500, "Can't be done as guest.");
492 			else{
493 				if($7 && $5 != NULL){
494 				    p = strpbrk($5, " \t");
495 				    if(p){
496 					*p++ = 0;
497 					kauth($5, p + strspn(p, " \t"));
498 				    }else
499 					kauth($5, NULL);
500 				}
501 			}
502 			if($5 != NULL)
503 			    free($5);
504 #else
505 			reply(500, "Command not implemented.");
506 #endif
507 		}
508 	| SITE SP KLIST CRLF check_login
509 		{
510 #ifdef KRB4
511 		    if($5)
512 			klist();
513 #else
514 		    reply(500, "Command not implemented.");
515 #endif
516 		}
517 	| SITE SP KDESTROY CRLF check_login
518 		{
519 #ifdef KRB4
520 		    if($5)
521 			kdestroy();
522 #else
523 		    reply(500, "Command not implemented.");
524 #endif
525 		}
526 	| SITE SP KRBTKFILE SP STRING CRLF check_login
527 		{
528 #ifdef KRB4
529 		    if(guest)
530 			reply(500, "Can't be done as guest.");
531 		    else if($7 && $5)
532 			krbtkfile($5);
533 		    if($5)
534 			free($5);
535 #else
536 		    reply(500, "Command not implemented.");
537 #endif
538 		}
539 	| SITE SP AFSLOG CRLF check_login
540 		{
541 #ifdef KRB4
542 		    if(guest)
543 			reply(500, "Can't be done as guest.");
544 		    else if($5)
545 			afslog(NULL);
546 #else
547 		    reply(500, "Command not implemented.");
548 #endif
549 		}
550 	| SITE SP AFSLOG SP STRING CRLF check_login
551 		{
552 #ifdef KRB4
553 		    if(guest)
554 			reply(500, "Can't be done as guest.");
555 		    else if($7)
556 			afslog($5);
557 		    if($5)
558 			free($5);
559 #else
560 		    reply(500, "Command not implemented.");
561 #endif
562 		}
563 	| SITE SP LOCATE SP STRING CRLF check_login
564 		{
565 		    if($7 && $5 != NULL)
566 			find($5);
567 		    if($5 != NULL)
568 			free($5);
569 		}
570 	| SITE SP URL CRLF
571 		{
572 			reply(200, "http://www.pdc.kth.se/kth-krb/");
573 		}
574 	| STOU SP pathname CRLF check_login
575 		{
576 			if ($5 && $3 != NULL)
577 				do_store($3, "w", 1);
578 			if ($3 != NULL)
579 				free($3);
580 		}
581 	| SYST CRLF
582 		{
583 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
584 		    reply(215, "UNIX Type: L%d", NBBY);
585 #else
586 		    reply(215, "UNKNOWN Type: L%d", NBBY);
587 #endif
588 		}
589 
590 		/*
591 		 * SIZE is not in RFC959, but Postel has blessed it and
592 		 * it will be in the updated RFC.
593 		 *
594 		 * Return size of file in a format suitable for
595 		 * using with RESTART (we just count bytes).
596 		 */
597 	| SIZE SP pathname CRLF check_login
598 		{
599 			if ($5 && $3 != NULL)
600 				sizecmd($3);
601 			if ($3 != NULL)
602 				free($3);
603 		}
604 
605 		/*
606 		 * MDTM is not in RFC959, but Postel has blessed it and
607 		 * it will be in the updated RFC.
608 		 *
609 		 * Return modification time of file as an ISO 3307
610 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
611 		 * where xxx is the fractional second (of any precision,
612 		 * not necessarily 3 digits)
613 		 */
614 	| MDTM SP pathname CRLF check_login
615 		{
616 			if ($5 && $3 != NULL) {
617 				struct stat stbuf;
618 				if (stat($3, &stbuf) < 0)
619 					reply(550, "%s: %s",
620 					    $3, strerror(errno));
621 				else if (!S_ISREG(stbuf.st_mode)) {
622 					reply(550,
623 					      "%s: not a plain file.", $3);
624 				} else {
625 					struct tm *t;
626 					time_t mtime = stbuf.st_mtime;
627 
628 					t = gmtime(&mtime);
629 					reply(213,
630 					      "%04d%02d%02d%02d%02d%02d",
631 					      t->tm_year + 1900,
632 					      t->tm_mon + 1,
633 					      t->tm_mday,
634 					      t->tm_hour,
635 					      t->tm_min,
636 					      t->tm_sec);
637 				}
638 			}
639 			if ($3 != NULL)
640 				free($3);
641 		}
642 	| QUIT CRLF
643 		{
644 			reply(221, "Goodbye.");
645 			dologout(0);
646 		}
647 	| error CRLF
648 		{
649 			yyerrok;
650 		}
651 	;
652 rcmd
653 	: RNFR SP pathname CRLF check_login_no_guest
654 		{
655 			restart_point = (off_t) 0;
656 			if ($5 && $3) {
657 				fromname = renamefrom($3);
658 				if (fromname == (char *) 0 && $3) {
659 					free($3);
660 				}
661 			}
662 		}
663 	| REST SP byte_size CRLF
664 		{
665 			fromname = (char *) 0;
666 			restart_point = $3;	/* XXX $3 is only "int" */
667 			reply(350, "Restarting at %ld. %s",
668 			      (long)restart_point,
669 			      "Send STORE or RETRIEVE to initiate transfer.");
670 		}
671 	| AUTH SP STRING CRLF
672 		{
673 			auth($3);
674 			free($3);
675 		}
676 	| ADAT SP STRING CRLF
677 		{
678 			adat($3);
679 			free($3);
680 		}
681 	| PBSZ SP NUMBER CRLF
682 		{
683 			pbsz($3);
684 		}
685 	| PROT SP STRING CRLF
686 		{
687 			prot($3);
688 		}
689 	| CCC CRLF
690 		{
691 			ccc();
692 		}
693 	| MIC SP STRING CRLF
694 		{
695 			mec($3, prot_safe);
696 			free($3);
697 		}
698 	| CONF SP STRING CRLF
699 		{
700 			mec($3, prot_confidential);
701 			free($3);
702 		}
703 	| ENC SP STRING CRLF
704 		{
705 			mec($3, prot_private);
706 			free($3);
707 		}
708 	;
709 
710 username
711 	: STRING
712 	;
713 
714 password
715 	: /* empty */
716 		{
717 			$$ = (char *)calloc(1, sizeof(char));
718 		}
719 	| STRING
720 	;
721 
722 byte_size
723 	: NUMBER
724 	;
725 
726 host_port
727 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
728 		NUMBER COMMA NUMBER
729 		{
730 			struct sockaddr_in *sin = (struct sockaddr_in *)data_dest;
731 
732 			sin->sin_family = AF_INET;
733 			sin->sin_port = htons($9 * 256 + $11);
734 			sin->sin_addr.s_addr =
735 			    htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
736 		}
737 	;
738 
739 form_code
740 	: N
741 		{
742 			$$ = FORM_N;
743 		}
744 	| T
745 		{
746 			$$ = FORM_T;
747 		}
748 	| C
749 		{
750 			$$ = FORM_C;
751 		}
752 	;
753 
754 type_code
755 	: A
756 		{
757 			cmd_type = TYPE_A;
758 			cmd_form = FORM_N;
759 		}
760 	| A SP form_code
761 		{
762 			cmd_type = TYPE_A;
763 			cmd_form = $3;
764 		}
765 	| E
766 		{
767 			cmd_type = TYPE_E;
768 			cmd_form = FORM_N;
769 		}
770 	| E SP form_code
771 		{
772 			cmd_type = TYPE_E;
773 			cmd_form = $3;
774 		}
775 	| I
776 		{
777 			cmd_type = TYPE_I;
778 		}
779 	| L
780 		{
781 			cmd_type = TYPE_L;
782 			cmd_bytesz = NBBY;
783 		}
784 	| L SP byte_size
785 		{
786 			cmd_type = TYPE_L;
787 			cmd_bytesz = $3;
788 		}
789 		/* this is for a bug in the BBN ftp */
790 	| L byte_size
791 		{
792 			cmd_type = TYPE_L;
793 			cmd_bytesz = $2;
794 		}
795 	;
796 
797 struct_code
798 	: F
799 		{
800 			$$ = STRU_F;
801 		}
802 	| R
803 		{
804 			$$ = STRU_R;
805 		}
806 	| P
807 		{
808 			$$ = STRU_P;
809 		}
810 	;
811 
812 mode_code
813 	: S
814 		{
815 			$$ = MODE_S;
816 		}
817 	| B
818 		{
819 			$$ = MODE_B;
820 		}
821 	| C
822 		{
823 			$$ = MODE_C;
824 		}
825 	;
826 
827 pathname
828 	: pathstring
829 		{
830 			/*
831 			 * Problem: this production is used for all pathname
832 			 * processing, but only gives a 550 error reply.
833 			 * This is a valid reply in some cases but not in others.
834 			 */
835 			if (logged_in && $1 && *$1 == '~') {
836 				glob_t gl;
837 				int flags =
838 				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
839 
840 				memset(&gl, 0, sizeof(gl));
841 				if (glob($1, flags, NULL, &gl) ||
842 				    gl.gl_pathc == 0) {
843 					reply(550, "not found");
844 					$$ = NULL;
845 				} else {
846 					$$ = strdup(gl.gl_pathv[0]);
847 				}
848 				globfree(&gl);
849 				free($1);
850 			} else
851 				$$ = $1;
852 		}
853 	;
854 
855 pathstring
856 	: STRING
857 	;
858 
859 octal_number
860 	: NUMBER
861 		{
862 			int ret, dec, multby, digit;
863 
864 			/*
865 			 * Convert a number that was read as decimal number
866 			 * to what it would be if it had been read as octal.
867 			 */
868 			dec = $1;
869 			multby = 1;
870 			ret = 0;
871 			while (dec) {
872 				digit = dec%10;
873 				if (digit > 7) {
874 					ret = -1;
875 					break;
876 				}
877 				ret += digit * multby;
878 				multby *= 8;
879 				dec /= 10;
880 			}
881 			$$ = ret;
882 		}
883 	;
884 
885 
886 check_login_no_guest : check_login
887 		{
888 			$$ = $1 && !guest;
889 			if($1 && !$$)
890 				reply(550, "Permission denied");
891 		}
892 	;
893 
894 check_login : check_secure
895 		{
896 		    if($1) {
897 			if(($$ = logged_in) == 0)
898 			    reply(530, "Please login with USER and PASS.");
899 		    } else
900 			$$ = 0;
901 		}
902 	;
903 
904 check_secure : /* empty */
905 		{
906 		    $$ = 1;
907 		    if(sec_complete && !secure_command()) {
908 			$$ = 0;
909 			reply(533, "Command protection level denied "
910 			      "for paranoid reasons.");
911 		    }
912 		}
913 	;
914 
915 %%
916 
917 extern jmp_buf errcatch;
918 
919 #define	CMD	0	/* beginning of command */
920 #define	ARGS	1	/* expect miscellaneous arguments */
921 #define	STR1	2	/* expect SP followed by STRING */
922 #define	STR2	3	/* expect STRING */
923 #define	OSTR	4	/* optional SP then STRING */
924 #define	ZSTR1	5	/* SP then optional STRING */
925 #define	ZSTR2	6	/* optional STRING after SP */
926 #define	SITECMD	7	/* SITE command */
927 #define	NSTR	8	/* Number followed by a string */
928 
929 struct tab cmdtab[] = {		/* In order defined in RFC 765 */
930 	{ "USER", USER, STR1, 1,	"<sp> username" },
931 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
932 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
933 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
934 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
935 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
936 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
937 	{ "EPRT", EPRT, STR1, 1,	"<sp> string" },
938 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
939 	{ "EPSV", EPSV, OSTR, 1,	"[<sp> foo]" },
940 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
941 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
942 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
943 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
944 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
945 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
946 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
947 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
948 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
949 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
950 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
951 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
952 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
953 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
954 	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
955 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
956 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
957 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
958 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
959 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
960 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
961 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
962 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
963 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
964 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
965 	{ "STAT", sTAT, OSTR, 1,	"[ <sp> path-name ]" },
966 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
967 	{ "NOOP", NOOP, ARGS, 1,	"" },
968 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
969 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
970 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
971 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
972 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
973 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
974 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
975 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
976 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
977 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
978 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
979 
980 	/* extensions from RFC2228 */
981 	{ "AUTH", AUTH,	STR1, 1,	"<sp> auth-type" },
982 	{ "ADAT", ADAT,	STR1, 1,	"<sp> auth-data" },
983 	{ "PBSZ", PBSZ,	ARGS, 1,	"<sp> buffer-size" },
984 	{ "PROT", PROT,	STR1, 1,	"<sp> prot-level" },
985 	{ "CCC",  CCC,	ARGS, 1,	"" },
986 	{ "MIC",  MIC,	STR1, 1,	"<sp> integrity command" },
987 	{ "CONF", CONF,	STR1, 1,	"<sp> confidentiality command" },
988 	{ "ENC",  ENC,	STR1, 1,	"<sp> privacy command" },
989 
990 	/* RFC2389 */
991 	{ "FEAT", FEAT, ARGS, 1,	"" },
992 	{ "OPTS", OPTS, ARGS, 1,	"<sp> command [<sp> options]" },
993 
994 	{ NULL,   0,    0,    0,	0 }
995 };
996 
997 struct tab sitetab[] = {
998 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
999 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1000 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1001 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1002 
1003 	{ "KAUTH", KAUTH, STR1, 1,	"<sp> principal [ <sp> ticket ]" },
1004 	{ "KLIST", KLIST, ARGS, 1,	"(show ticket file)" },
1005 	{ "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1006 	{ "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1007 	{ "AFSLOG", AFSLOG, OSTR, 1,	"[<sp> cell]" },
1008 
1009 	{ "LOCATE", LOCATE, STR1, 1,	"<sp> globexpr" },
1010 	{ "FIND", LOCATE, STR1, 1,	"<sp> globexpr" },
1011 
1012 	{ "URL",  URL,  ARGS, 1,	"?" },
1013 
1014 	{ NULL,   0,    0,    0,	0 }
1015 };
1016 
1017 static struct tab *
1018 lookup(struct tab *p, char *cmd)
1019 {
1020 
1021 	for (; p->name != NULL; p++)
1022 		if (strcmp(cmd, p->name) == 0)
1023 			return (p);
1024 	return (0);
1025 }
1026 
1027 /*
1028  * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1029  */
1030 char *
1031 ftpd_getline(char *s, int n)
1032 {
1033 	int c;
1034 	char *cs;
1035 
1036 	cs = s;
1037 /* tmpline may contain saved command from urgent mode interruption */
1038 	if(ftp_command){
1039 	  strlcpy(s, ftp_command, n);
1040 	  if (debug)
1041 	    syslog(LOG_DEBUG, "command: %s", s);
1042 #ifdef XXX
1043 	  fprintf(stderr, "%s\n", s);
1044 #endif
1045 	  return s;
1046 	}
1047 	while ((c = getc(stdin)) != EOF) {
1048 		c &= 0377;
1049 		if (c == IAC) {
1050 		    if ((c = getc(stdin)) != EOF) {
1051 			c &= 0377;
1052 			switch (c) {
1053 			case WILL:
1054 			case WONT:
1055 				c = getc(stdin);
1056 				printf("%c%c%c", IAC, DONT, 0377&c);
1057 				fflush(stdout);
1058 				continue;
1059 			case DO:
1060 			case DONT:
1061 				c = getc(stdin);
1062 				printf("%c%c%c", IAC, WONT, 0377&c);
1063 				fflush(stdout);
1064 				continue;
1065 			case IAC:
1066 				break;
1067 			default:
1068 				continue;	/* ignore command */
1069 			}
1070 		    }
1071 		}
1072 		*cs++ = c;
1073 		if (--n <= 0 || c == '\n')
1074 			break;
1075 	}
1076 	if (c == EOF && cs == s)
1077 		return (NULL);
1078 	*cs++ = '\0';
1079 	if (debug) {
1080 		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1081 			/* Don't syslog passwords */
1082 			syslog(LOG_DEBUG, "command: %.5s ???", s);
1083 		} else {
1084 			char *cp;
1085 			int len;
1086 
1087 			/* Don't syslog trailing CR-LF */
1088 			len = strlen(s);
1089 			cp = s + len - 1;
1090 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1091 				--cp;
1092 				--len;
1093 			}
1094 			syslog(LOG_DEBUG, "command: %.*s", len, s);
1095 		}
1096 	}
1097 #ifdef XXX
1098 	fprintf(stderr, "%s\n", s);
1099 #endif
1100 	return (s);
1101 }
1102 
1103 static RETSIGTYPE
1104 toolong(int signo)
1105 {
1106 
1107 	reply(421,
1108 	    "Timeout (%d seconds): closing control connection.",
1109 	      ftpd_timeout);
1110 	if (logging)
1111 		syslog(LOG_INFO, "User %s timed out after %d seconds",
1112 		    (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1113 	dologout(1);
1114 	SIGRETURN(0);
1115 }
1116 
1117 static int
1118 yylex(void)
1119 {
1120 	static int cpos, state;
1121 	char *cp, *cp2;
1122 	struct tab *p;
1123 	int n;
1124 	char c;
1125 
1126 	for (;;) {
1127 		switch (state) {
1128 
1129 		case CMD:
1130 			signal(SIGALRM, toolong);
1131 			alarm((unsigned) ftpd_timeout);
1132 			if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1133 				reply(221, "You could at least say goodbye.");
1134 				dologout(0);
1135 			}
1136 			alarm(0);
1137 #ifdef HAVE_SETPROCTITLE
1138 			if (strncasecmp(cbuf, "PASS", 4) != NULL)
1139 				setproctitle("%s: %s", proctitle, cbuf);
1140 #endif /* HAVE_SETPROCTITLE */
1141 			if ((cp = strchr(cbuf, '\r'))) {
1142 				*cp++ = '\n';
1143 				*cp = '\0';
1144 			}
1145 			if ((cp = strpbrk(cbuf, " \n")))
1146 				cpos = cp - cbuf;
1147 			if (cpos == 0)
1148 				cpos = 4;
1149 			c = cbuf[cpos];
1150 			cbuf[cpos] = '\0';
1151 			strupr(cbuf);
1152 			p = lookup(cmdtab, cbuf);
1153 			cbuf[cpos] = c;
1154 			if (p != 0) {
1155 				if (p->implemented == 0) {
1156 					nack(p->name);
1157 					longjmp(errcatch,0);
1158 					/* NOTREACHED */
1159 				}
1160 				state = p->state;
1161 				yylval.s = p->name;
1162 				return (p->token);
1163 			}
1164 			break;
1165 
1166 		case SITECMD:
1167 			if (cbuf[cpos] == ' ') {
1168 				cpos++;
1169 				return (SP);
1170 			}
1171 			cp = &cbuf[cpos];
1172 			if ((cp2 = strpbrk(cp, " \n")))
1173 				cpos = cp2 - cbuf;
1174 			c = cbuf[cpos];
1175 			cbuf[cpos] = '\0';
1176 			strupr(cp);
1177 			p = lookup(sitetab, cp);
1178 			cbuf[cpos] = c;
1179 			if (p != 0) {
1180 				if (p->implemented == 0) {
1181 					state = CMD;
1182 					nack(p->name);
1183 					longjmp(errcatch,0);
1184 					/* NOTREACHED */
1185 				}
1186 				state = p->state;
1187 				yylval.s = p->name;
1188 				return (p->token);
1189 			}
1190 			state = CMD;
1191 			break;
1192 
1193 		case OSTR:
1194 			if (cbuf[cpos] == '\n') {
1195 				state = CMD;
1196 				return (CRLF);
1197 			}
1198 			/* FALLTHROUGH */
1199 
1200 		case STR1:
1201 		case ZSTR1:
1202 		dostr1:
1203 			if (cbuf[cpos] == ' ') {
1204 				cpos++;
1205 				if(state == OSTR)
1206 				    state = STR2;
1207 				else
1208 				    state++;
1209 				return (SP);
1210 			}
1211 			break;
1212 
1213 		case ZSTR2:
1214 			if (cbuf[cpos] == '\n') {
1215 				state = CMD;
1216 				return (CRLF);
1217 			}
1218 			/* FALLTHROUGH */
1219 
1220 		case STR2:
1221 			cp = &cbuf[cpos];
1222 			n = strlen(cp);
1223 			cpos += n - 1;
1224 			/*
1225 			 * Make sure the string is nonempty and \n terminated.
1226 			 */
1227 			if (n > 1 && cbuf[cpos] == '\n') {
1228 				cbuf[cpos] = '\0';
1229 				yylval.s = copy(cp);
1230 				cbuf[cpos] = '\n';
1231 				state = ARGS;
1232 				return (STRING);
1233 			}
1234 			break;
1235 
1236 		case NSTR:
1237 			if (cbuf[cpos] == ' ') {
1238 				cpos++;
1239 				return (SP);
1240 			}
1241 			if (isdigit(cbuf[cpos])) {
1242 				cp = &cbuf[cpos];
1243 				while (isdigit(cbuf[++cpos]))
1244 					;
1245 				c = cbuf[cpos];
1246 				cbuf[cpos] = '\0';
1247 				yylval.i = atoi(cp);
1248 				cbuf[cpos] = c;
1249 				state = STR1;
1250 				return (NUMBER);
1251 			}
1252 			state = STR1;
1253 			goto dostr1;
1254 
1255 		case ARGS:
1256 			if (isdigit(cbuf[cpos])) {
1257 				cp = &cbuf[cpos];
1258 				while (isdigit(cbuf[++cpos]))
1259 					;
1260 				c = cbuf[cpos];
1261 				cbuf[cpos] = '\0';
1262 				yylval.i = atoi(cp);
1263 				cbuf[cpos] = c;
1264 				return (NUMBER);
1265 			}
1266 			switch (cbuf[cpos++]) {
1267 
1268 			case '\n':
1269 				state = CMD;
1270 				return (CRLF);
1271 
1272 			case ' ':
1273 				return (SP);
1274 
1275 			case ',':
1276 				return (COMMA);
1277 
1278 			case 'A':
1279 			case 'a':
1280 				return (A);
1281 
1282 			case 'B':
1283 			case 'b':
1284 				return (B);
1285 
1286 			case 'C':
1287 			case 'c':
1288 				return (C);
1289 
1290 			case 'E':
1291 			case 'e':
1292 				return (E);
1293 
1294 			case 'F':
1295 			case 'f':
1296 				return (F);
1297 
1298 			case 'I':
1299 			case 'i':
1300 				return (I);
1301 
1302 			case 'L':
1303 			case 'l':
1304 				return (L);
1305 
1306 			case 'N':
1307 			case 'n':
1308 				return (N);
1309 
1310 			case 'P':
1311 			case 'p':
1312 				return (P);
1313 
1314 			case 'R':
1315 			case 'r':
1316 				return (R);
1317 
1318 			case 'S':
1319 			case 's':
1320 				return (S);
1321 
1322 			case 'T':
1323 			case 't':
1324 				return (T);
1325 
1326 			}
1327 			break;
1328 
1329 		default:
1330 			fatal("Unknown state in scanner.");
1331 		}
1332 		yyerror((char *) 0);
1333 		state = CMD;
1334 		longjmp(errcatch,0);
1335 	}
1336 }
1337 
1338 static char *
1339 copy(char *s)
1340 {
1341 	char *p;
1342 
1343 	p = strdup(s);
1344 	if (p == NULL)
1345 		fatal("Ran out of memory.");
1346 	return p;
1347 }
1348 
1349 static void
1350 help(struct tab *ctab, char *s)
1351 {
1352 	struct tab *c;
1353 	int width, NCMDS;
1354 	char *type;
1355 	char buf[1024];
1356 
1357 	if (ctab == sitetab)
1358 		type = "SITE ";
1359 	else
1360 		type = "";
1361 	width = 0, NCMDS = 0;
1362 	for (c = ctab; c->name != NULL; c++) {
1363 		int len = strlen(c->name);
1364 
1365 		if (len > width)
1366 			width = len;
1367 		NCMDS++;
1368 	}
1369 	width = (width + 8) &~ 7;
1370 	if (s == 0) {
1371 		int i, j, w;
1372 		int columns, lines;
1373 
1374 		lreply(214, "The following %scommands are recognized %s.",
1375 		    type, "(* =>'s unimplemented)");
1376 		columns = 76 / width;
1377 		if (columns == 0)
1378 			columns = 1;
1379 		lines = (NCMDS + columns - 1) / columns;
1380 		for (i = 0; i < lines; i++) {
1381 		    strlcpy (buf, "   ", sizeof(buf));
1382 		    for (j = 0; j < columns; j++) {
1383 			c = ctab + j * lines + i;
1384 			snprintf (buf + strlen(buf),
1385 				  sizeof(buf) - strlen(buf),
1386 				  "%s%c",
1387 				  c->name,
1388 				  c->implemented ? ' ' : '*');
1389 			if (c + lines >= &ctab[NCMDS])
1390 			    break;
1391 			w = strlen(c->name) + 1;
1392 			while (w < width) {
1393 			    strlcat (buf,
1394 					     " ",
1395 					     sizeof(buf));
1396 			    w++;
1397 			}
1398 		    }
1399 		    lreply(214, "%s", buf);
1400 		}
1401 		reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1402 		return;
1403 	}
1404 	strupr(s);
1405 	c = lookup(ctab, s);
1406 	if (c == (struct tab *)0) {
1407 		reply(502, "Unknown command %s.", s);
1408 		return;
1409 	}
1410 	if (c->implemented)
1411 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1412 	else
1413 		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1414 		    c->name, c->help);
1415 }
1416 
1417 static void
1418 sizecmd(char *filename)
1419 {
1420 	switch (type) {
1421 	case TYPE_L:
1422 	case TYPE_I: {
1423 		struct stat stbuf;
1424 		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1425 			reply(550, "%s: not a plain file.", filename);
1426 		else
1427 			reply(213, "%lu", (unsigned long)stbuf.st_size);
1428 		break;
1429 	}
1430 	case TYPE_A: {
1431 		FILE *fin;
1432 		int c;
1433 		size_t count;
1434 		struct stat stbuf;
1435 		fin = fopen(filename, "r");
1436 		if (fin == NULL) {
1437 			perror_reply(550, filename);
1438 			return;
1439 		}
1440 		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1441 			reply(550, "%s: not a plain file.", filename);
1442 			fclose(fin);
1443 			return;
1444 		}
1445 
1446 		count = 0;
1447 		while((c=getc(fin)) != EOF) {
1448 			if (c == '\n')	/* will get expanded to \r\n */
1449 				count++;
1450 			count++;
1451 		}
1452 		fclose(fin);
1453 
1454 		reply(213, "%lu", (unsigned long)count);
1455 		break;
1456 	}
1457 	default:
1458 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1459 	}
1460 }
1461