Deleted Added
full compact
ftpcmd.y (59121) ftpcmd.y (92914)
1/* ftpcmd.y: yacc parser for the FTP daemon.
2
3%%% portions-copyright-cmetz-96
1/* ftpcmd.y: yacc parser for the FTP daemon.
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1998 by Craig Metz, All Rights
4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10 History:
11
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10 History:
11
12 Modified by cmetz for OPIE 2.4. Use DOTITLE rather than SETPROCTITLE.
12 Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here.
13 Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings.
14 Use FUNCTION declaration et al. Removed useless strings.
15 Changed some char []s to char *s. Deleted comment address.
16 Changed tmpline references to be more pure-pointer
17 references. Changed tmpline declaration back to char [].
18 Modified at NRL for OPIE 2.1. Minor changes for autoconf.
19 Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[]
20 -- fixes problems experienced by bison users. Merged in new
21 PORT attack fixes from Hobbit.
22 Modified at NRL for OPIE 2.0.
23 Originally from BSD.
24
13 Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here.
14 Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings.
15 Use FUNCTION declaration et al. Removed useless strings.
16 Changed some char []s to char *s. Deleted comment address.
17 Changed tmpline references to be more pure-pointer
18 references. Changed tmpline declaration back to char [].
19 Modified at NRL for OPIE 2.1. Minor changes for autoconf.
20 Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[]
21 -- fixes problems experienced by bison users. Merged in new
22 PORT attack fixes from Hobbit.
23 Modified at NRL for OPIE 2.0.
24 Originally from BSD.
25
25$FreeBSD: head/contrib/opie/ftpcmd.y 59121 2000-04-10 11:18:54Z kris $
26$FreeBSD: head/contrib/opie/ftpcmd.y 92914 2002-03-21 23:42:52Z markm $
26*/
27/*
28 * Copyright (c) 1985, 1988 Regents of the University of California.
29 * All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 *
59 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
60 */
61
62/*
63 * Grammar for FTP commands.
64 * See RFC 959.
65 */
66
67%{
68#include "opie_cfg.h"
69
70#include <sys/param.h>
71#include <sys/types.h>
72#include <sys/socket.h>
73#include <sys/stat.h>
74#include <netinet/in.h>
75#include <arpa/ftp.h>
76#include <signal.h>
77#include <setjmp.h>
78#include <syslog.h>
79#if TM_IN_SYS_TIME
80#include <sys/time.h>
81#else /* TM_IN_SYS_TIME */
82#include <time.h>
83#endif /* TM_IN_SYS_TIME */
84#include <pwd.h>
85#include <unistd.h>
86#include <stdio.h>
87#include <ctype.h>
88#include <stdlib.h>
89#include <string.h>
90
91#include "opie.h"
92
93#if HAVE_LS_G_FLAG
94#define LS_COMMAND "/bin/ls -lgA"
95#else /* HAVE_LS_G_FLAG */
96#define LS_COMMAND "/bin/ls -lA"
97#endif /* HAVE_LS_G_FLAG */
98
99extern struct sockaddr_in data_dest;
100extern struct sockaddr_in his_addr;
101extern int logged_in;
102extern struct passwd *pw;
103extern int guest;
104extern int type;
105extern int form;
106extern int debug;
107extern int timeout;
108extern int maxtimeout;
109extern int pdata;
110extern char *remotehost;
111extern char *proctitle;
112extern char *globerr;
113extern int usedefault;
114extern int transflag;
115extern char tmpline[];
116char **ftpglob();
117
118VOIDRET dologout __P((int));
119VOIDRET upper __P((char *));
120VOIDRET nack __P((char *));
121VOIDRET opiefatal __P((char *));
122
123VOIDRET pass __P((char *));
124int user __P((char *));
125VOIDRET passive __P((void));
126VOIDRET retrieve __P((char *, char *));
127VOIDRET store __P((char *, char *, int));
128VOIDRET send_file_list __P((char *));
129VOIDRET statfilecmd __P((char *));
130VOIDRET statcmd __P((void));
131VOIDRET delete __P((char *));
132VOIDRET renamecmd __P((char *, char *));
133VOIDRET cwd __P((char *));
134VOIDRET makedir __P((char *));
135VOIDRET removedir __P((char *));
136VOIDRET pwd __P((void));
137
138VOIDRET sizecmd __P((char *));
139
140off_t restart_point;
141
142static int cmd_type;
143static int cmd_form;
144static int cmd_bytesz;
145static unsigned short cliport = 0;
146char cbuf[512];
147char *fromname;
148
149struct tab {
150 char *name;
151 short token;
152 short state;
153 short implemented; /* 1 if command is implemented */
154 char *help;
155};
156
157VOIDRET help __P((struct tab *, char *));
158
159struct tab cmdtab[], sitetab[];
160
161%}
162
163%token
164 A B C E F I
165 L N P R S T
166
167 SP CRLF COMMA STRING NUMBER
168
169 USER PASS ACCT REIN QUIT PORT
170 PASV TYPE STRU MODE RETR STOR
171 APPE MLFL MAIL MSND MSOM MSAM
172 MRSQ MRCP ALLO REST RNFR RNTO
173 ABOR DELE CWD LIST NLST SITE
174 STAT HELP NOOP MKD RMD PWD
175 CDUP STOU SMNT SYST SIZE MDTM
176
177 UMASK IDLE CHMOD
178
179 LEXERR
180
181%start cmd_list
182
183%%
184
185cmd_list: /* empty */
186 | cmd_list cmd
187 = {
188 fromname = (char *) 0;
189 restart_point = (off_t) 0;
190 }
191 | cmd_list rcmd
192 ;
193
194cmd: USER SP username CRLF
195 = {
196 user((char *) $3);
197 free((char *) $3);
198 }
199 | PASS SP password CRLF
200 = {
201 pass((char *) $3);
202 free((char *) $3);
203 }
204 | PORT check_login SP host_port CRLF
205 = {
206 usedefault = 0;
207 if (pdata >= 0) {
208 (void) close(pdata);
209 pdata = -1;
210 }
211/* H* port fix, part B: admonish the twit.
212 Also require login before PORT works */
213 if ($2) {
214 if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) {
215 reply(200, "PORT command successful.");
216 } else {
217 syslog (LOG_WARNING, "refused %s from %s",
218 cbuf, remotehost);
219 reply(500, "You've GOT to be joking.");
220 }
221 }
222 }
223/* | PASV CRLF
224 = {
225 passive();
226 } */
227 | PASV check_login CRLF
228 = {
229/* Require login for PASV, too. This actually fixes a bug -- telnet to an
230 unfixed wu-ftpd and type PASV first off, and it crashes! */
231 if ($2) {
232 passive();
233 }
234 }
235 | TYPE SP type_code CRLF
236 = {
237 switch (cmd_type) {
238
239 case TYPE_A:
240 if (cmd_form == FORM_N) {
241 reply(200, "Type set to A.");
242 type = cmd_type;
243 form = cmd_form;
244 } else
245 reply(504, "Form must be N.");
246 break;
247
248 case TYPE_E:
249 reply(504, "Type E not implemented.");
250 break;
251
252 case TYPE_I:
253 reply(200, "Type set to I.");
254 type = cmd_type;
255 break;
256
257 case TYPE_L:
258#if NBBY == 8
259 if (cmd_bytesz == 8) {
260 reply(200,
261 "Type set to L (byte size 8).");
262 type = cmd_type;
263 } else
264 reply(504, "Byte size must be 8.");
265#else /* NBBY == 8 */
266 UNIMPLEMENTED for NBBY != 8
267#endif /* NBBY == 8 */
268 }
269 }
270 | STRU SP struct_code CRLF
271 = {
272 switch ($3) {
273
274 case STRU_F:
275 reply(200, "STRU F ok.");
276 break;
277
278 default:
279 reply(504, "Unimplemented STRU type.");
280 }
281 }
282 | MODE SP mode_code CRLF
283 = {
284 switch ($3) {
285
286 case MODE_S:
287 reply(200, "MODE S ok.");
288 break;
289
290 default:
291 reply(502, "Unimplemented MODE type.");
292 }
293 }
294 | ALLO SP NUMBER CRLF
295 = {
296 reply(202, "ALLO command ignored.");
297 }
298 | ALLO SP NUMBER SP R SP NUMBER CRLF
299 = {
300 reply(202, "ALLO command ignored.");
301 }
302 | RETR check_login SP pathname CRLF
303 = {
304 if ($2 && $4)
305 retrieve((char *) 0, (char *) $4);
306 if ($4)
307 free((char *) $4);
308 }
309 | STOR check_login SP pathname CRLF
310 = {
311 if ($2 && $4)
312 store((char *) $4, "w", 0);
313 if ($4)
314 free((char *) $4);
315 }
316 | APPE check_login SP pathname CRLF
317 = {
318 if ($2 && $4)
319 store((char *) $4, "a", 0);
320 if ($4)
321 free((char *) $4);
322 }
323 | NLST check_login CRLF
324 = {
325 if ($2)
326 send_file_list(".");
327 }
328 | NLST check_login SP STRING CRLF
329 = {
330 if ($2 && $4)
331 send_file_list((char *) $4);
332 if ($4)
333 free((char *) $4);
334 }
335 | LIST check_login CRLF
336 = {
337 if ($2)
338 retrieve(LS_COMMAND, "");
339 }
340 | LIST check_login SP pathname CRLF
341 = {
342 if ($2 && $4)
343 {
344 char buffer[sizeof(LS_COMMAND)+3];
345 strcpy(buffer, LS_COMMAND);
346 strcat(buffer, " %s");
347 retrieve(buffer, (char *) $4);
348 }
349 if ($4)
350 free((char *) $4);
351 }
352 | STAT check_login SP pathname CRLF
353 = {
354 if ($2 && $4)
355 statfilecmd((char *) $4);
356 if ($4)
357 free((char *) $4);
358 }
359 | STAT CRLF
360 = {
361 statcmd();
362 }
363 | DELE check_login SP pathname CRLF
364 = {
365 if ($2 && $4)
366 delete((char *) $4);
367 if ($4)
368 free((char *) $4);
369 }
370 | RNTO SP pathname CRLF
371 = {
372 if (fromname) {
373 renamecmd(fromname, (char *) $3);
374 free(fromname);
375 fromname = (char *) 0;
376 } else {
377 reply(503, "Bad sequence of commands.");
378 }
379 free((char *) $3);
380 }
381 | ABOR CRLF
382 = {
383 reply(225, "ABOR command successful.");
384 }
385 | CWD check_login CRLF
386 = {
387 if ($2)
388 cwd(pw->pw_dir);
389 }
390 | CWD check_login SP pathname CRLF
391 = {
392 if ($2 && $4)
393 cwd((char *) $4);
394 if ($4)
395 free((char *) $4);
396 }
397 | HELP CRLF
398 = {
399 help(cmdtab, (char *) 0);
400 }
401 | HELP SP STRING CRLF
402 = {
403 register char *cp = (char *)$3;
404
405 if (strncasecmp(cp, "SITE", 4) == 0) {
406 cp = (char *)$3 + 4;
407 if (*cp == ' ')
408 cp++;
409 if (*cp)
410 help(sitetab, cp);
411 else
412 help(sitetab, (char *) 0);
413 } else
414 help(cmdtab, (char *) $3);
415 }
416 | NOOP CRLF
417 = {
418 reply(200, "NOOP command successful.");
419 }
420 | MKD check_login SP pathname CRLF
421 = {
422 if ($2 && $4)
423 makedir((char *) $4);
424 if ($4)
425 free((char *) $4);
426 }
427 | RMD check_login SP pathname CRLF
428 = {
429 if ($2 && $4)
430 removedir((char *) $4);
431 if ($4)
432 free((char *) $4);
433 }
434 | PWD check_login CRLF
435 = {
436 if ($2)
437 pwd();
438 }
439 | CDUP check_login CRLF
440 = {
441 if ($2)
442 cwd("..");
443 }
444 | SITE SP HELP CRLF
445 = {
446 help(sitetab, (char *) 0);
447 }
448 | SITE SP HELP SP STRING CRLF
449 = {
450 help(sitetab, (char *) $5);
451 }
452 | SITE SP UMASK check_login CRLF
453 = {
454 int oldmask;
455
456 if ($4) {
457 oldmask = umask(0);
458 (void) umask(oldmask);
459 reply(200, "Current UMASK is %03o", oldmask);
460 }
461 }
462 | SITE SP UMASK check_login SP octal_number CRLF
463 = {
464 int oldmask;
465
466 if ($4) {
467 if (($6 == -1) || ($6 > 0777)) {
468 reply(501, "Bad UMASK value");
469 } else {
470 oldmask = umask($6);
471 reply(200,
472 "UMASK set to %03o (was %03o)",
473 $6, oldmask);
474 }
475 }
476 }
477 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
478 = {
479 if ($4 && $8) {
480 if ($6 > 0777)
481 reply(501,
482 "CHMOD: Mode value must be between 0 and 0777");
483 else if (chmod((char *) $8, $6) < 0)
484 perror_reply(550, (char *) $8);
485 else
486 reply(200, "CHMOD command successful.");
487 }
488 if ($8)
489 free((char *) $8);
490 }
491 | SITE SP IDLE CRLF
492 = {
493 reply(200,
494 "Current IDLE time limit is %d seconds; max %d",
495 timeout, maxtimeout);
496 }
497 | SITE SP IDLE SP NUMBER CRLF
498 = {
499 if ($5 < 30 || $5 > maxtimeout) {
500 reply(501,
501 "Maximum IDLE time must be between 30 and %d seconds",
502 maxtimeout);
503 } else {
504 timeout = $5;
505 (void) alarm((unsigned) timeout);
506 reply(200,
507 "Maximum IDLE time set to %d seconds",
508 timeout);
509 }
510 }
511 | STOU check_login SP pathname CRLF
512 = {
513 if ($2 && $4)
514 store((char *) $4, "w", 1);
515 if ($4)
516 free((char *) $4);
517 }
518 | SYST CRLF
519 = {
520#ifdef unix
521#ifdef BSD
522 reply(215, "UNIX Type: L%d Version: BSD-%d",
523 NBBY, BSD);
524#else /* BSD */
525 reply(215, "UNIX Type: L%d", NBBY);
526#endif /* BSD */
527#else /* unix */
528 reply(215, "UNKNOWN Type: L%d", NBBY);
529#endif /* unix */
530 }
531
532 /*
533 * SIZE is not in RFC959, but Postel has blessed it and
534 * it will be in the updated RFC.
535 *
536 * Return size of file in a format suitable for
537 * using with RESTART (we just count bytes).
538 */
539 | SIZE check_login SP pathname CRLF
540 = {
541 if ($2 && $4)
542 sizecmd((char *) $4);
543 if ($4)
544 free((char *) $4);
545 }
546
547 /*
548 * MDTM is not in RFC959, but Postel has blessed it and
549 * it will be in the updated RFC.
550 *
551 * Return modification time of file as an ISO 3307
552 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
553 * where xxx is the fractional second (of any precision,
554 * not necessarily 3 digits)
555 */
556 | MDTM check_login SP pathname CRLF
557 = {
558 if ($2 && $4) {
559 struct stat stbuf;
560 if (stat((char *) $4, &stbuf) < 0)
561 perror_reply(550, (char *) $4);
562 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
563 reply(550, "%s: not a plain file.",
564 (char *) $4);
565 } else {
566 register struct tm *t;
567 struct tm *gmtime();
568 t = gmtime(&stbuf.st_mtime);
569 reply(213,
570 "%d%02d%02d%02d%02d%02d",
571 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
572 t->tm_hour, t->tm_min, t->tm_sec);
573 }
574 }
575 if ($4)
576 free((char *) $4);
577 }
578 | QUIT CRLF
579 = {
580 reply(221, "Goodbye.");
581 dologout(0);
582 }
583 | error CRLF
584 = {
585 yyerrok;
586 }
587 ;
588rcmd: RNFR check_login SP pathname CRLF
589 = {
590 char *renamefrom();
591
592 restart_point = (off_t) 0;
593 if ($2 && $4) {
594 fromname = renamefrom((char *) $4);
595 if (fromname == (char *) 0 && $4) {
596 free((char *) $4);
597 }
598 }
599 }
600 | REST SP byte_size CRLF
601 = {
602 long atol();
603
604 fromname = (char *) 0;
605 restart_point = $3;
606 reply(350, "Restarting at %ld. %s", restart_point,
607 "Send STORE or RETRIEVE to initiate transfer.");
608 }
609 ;
610
611username: STRING
612 ;
613
614password: /* empty */
615 = {
616 *(char **)&($$) = (char *)calloc(1, sizeof(char));
617 }
618 | STRING
619 ;
620
621byte_size: NUMBER
622 ;
623
624host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
625 NUMBER COMMA NUMBER
626 = {
627 register char *a, *p;
628
629 a = (char *)&data_dest.sin_addr;
630 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
631
632/* H* port fix, part A-1: Check the args against the client addr */
633 p = (char *)&his_addr.sin_addr;
634 if (memcmp (a, p, sizeof (data_dest.sin_addr)))
635 memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */
636
637 p = (char *)&data_dest.sin_port;
638
639/* H* port fix, part A-2: only allow client ports in "user space" */
640 p[0] = 0; p[1] = 0;
641 cliport = ($9 << 8) + $11;
642 if (cliport > 1023) {
643 p[0] = $9; p[1] = $11;
644 }
645
646 p[0] = $9; p[1] = $11;
647 data_dest.sin_family = AF_INET;
648 }
649 ;
650
651form_code: N
652 = {
653 $$ = FORM_N;
654 }
655 | T
656 = {
657 $$ = FORM_T;
658 }
659 | C
660 = {
661 $$ = FORM_C;
662 }
663 ;
664
665type_code: A
666 = {
667 cmd_type = TYPE_A;
668 cmd_form = FORM_N;
669 }
670 | A SP form_code
671 = {
672 cmd_type = TYPE_A;
673 cmd_form = $3;
674 }
675 | E
676 = {
677 cmd_type = TYPE_E;
678 cmd_form = FORM_N;
679 }
680 | E SP form_code
681 = {
682 cmd_type = TYPE_E;
683 cmd_form = $3;
684 }
685 | I
686 = {
687 cmd_type = TYPE_I;
688 }
689 | L
690 = {
691 cmd_type = TYPE_L;
692 cmd_bytesz = NBBY;
693 }
694 | L SP byte_size
695 = {
696 cmd_type = TYPE_L;
697 cmd_bytesz = $3;
698 }
699 /* this is for a bug in the BBN ftp */
700 | L byte_size
701 = {
702 cmd_type = TYPE_L;
703 cmd_bytesz = $2;
704 }
705 ;
706
707struct_code: F
708 = {
709 $$ = STRU_F;
710 }
711 | R
712 = {
713 $$ = STRU_R;
714 }
715 | P
716 = {
717 $$ = STRU_P;
718 }
719 ;
720
721mode_code: S
722 = {
723 $$ = MODE_S;
724 }
725 | B
726 = {
727 $$ = MODE_B;
728 }
729 | C
730 = {
731 $$ = MODE_C;
732 }
733 ;
734
735pathname: pathstring
736 = {
737 /*
738 * Problem: this production is used for all pathname
739 * processing, but only gives a 550 error reply.
740 * This is a valid reply in some cases but not in others.
741 */
742 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
743 *(char **)&($$) = *ftpglob((char *) $1);
744 if (globerr != NULL) {
745 reply(550, globerr);
746/* $$ = NULL; */
747 $$ = 0;
748 }
749 free((char *) $1);
750 } else
751 $$ = $1;
752 }
753 ;
754
755pathstring: STRING
756 ;
757
758octal_number: NUMBER
759 = {
760 register int ret, dec, multby, digit;
761
762 /*
763 * Convert a number that was read as decimal number
764 * to what it would be if it had been read as octal.
765 */
766 dec = $1;
767 multby = 1;
768 ret = 0;
769 while (dec) {
770 digit = dec%10;
771 if (digit > 7) {
772 ret = -1;
773 break;
774 }
775 ret += digit * multby;
776 multby *= 8;
777 dec /= 10;
778 }
779 $$ = ret;
780 }
781 ;
782
783check_login: /* empty */
784 = {
785 if (logged_in)
786 $$ = 1;
787 else {
788 reply(530, "Please login with USER and PASS.");
789 $$ = 0;
790 }
791 }
792 ;
793
794%%
795
796extern jmp_buf errcatch;
797
798#define CMD 0 /* beginning of command */
799#define ARGS 1 /* expect miscellaneous arguments */
800#define STR1 2 /* expect SP followed by STRING */
801#define STR2 3 /* expect STRING */
802#define OSTR 4 /* optional SP then STRING */
803#define ZSTR1 5 /* SP then optional STRING */
804#define ZSTR2 6 /* optional STRING after SP */
805#define SITECMD 7 /* SITE command */
806#define NSTR 8 /* Number followed by a string */
807
808struct tab cmdtab[] = { /* In order defined in RFC 765 */
809 { "USER", USER, STR1, 1, "<sp> username" },
810 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
811 { "ACCT", ACCT, STR1, 0, "(specify account)" },
812 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
813 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
814 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
815 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
816 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
817 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
818 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
819 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
820 { "RETR", RETR, STR1, 1, "<sp> file-name" },
821 { "STOR", STOR, STR1, 1, "<sp> file-name" },
822 { "APPE", APPE, STR1, 1, "<sp> file-name" },
823 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
824 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
825 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
826 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
827 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
828 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
829 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
830 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
831 { "REST", REST, ARGS, 1, "(restart command)" },
832 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
833 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
834 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
835 { "DELE", DELE, STR1, 1, "<sp> file-name" },
836 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
837 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
838 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
839 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
840 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
841 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
842 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
843 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
844 { "NOOP", NOOP, ARGS, 1, "" },
845 { "MKD", MKD, STR1, 1, "<sp> path-name" },
846 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
847 { "RMD", RMD, STR1, 1, "<sp> path-name" },
848 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
849 { "PWD", PWD, ARGS, 1, "(return current directory)" },
850 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
851 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
852 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
853 { "STOU", STOU, STR1, 1, "<sp> file-name" },
854 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
855 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
856 { NULL, 0, 0, 0, 0 }
857};
858
859struct tab sitetab[] = {
860 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
861 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
862 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
863 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
864 { NULL, 0, 0, 0, 0 }
865};
866
867struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd)
868{
869
870 for (; p->name != NULL; p++)
871 if (strcmp(cmd, p->name) == 0)
872 return (p);
873 return (0);
874}
875
876#include <arpa/telnet.h>
877
878/*
879 * getline - a hacked up version of fgets to ignore TELNET escape codes.
880 */
881char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop)
882{
883 register c;
884 register char *cs;
885
886 cs = s;
887/* tmpline may contain saved command from urgent mode interruption */
888 for (c = 0; *(tmpline + c) && --n > 0; ++c) {
889 *cs++ = *(tmpline + c);
890 if (*(tmpline + c) == '\n') {
891 *cs++ = '\0';
892 if (debug)
893 syslog(LOG_DEBUG, "command: %s", s);
894 *tmpline = '\0';
895 return(s);
896 }
897 if (c == 0)
898 *tmpline = '\0';
899 }
900 while ((c = getc(iop)) != EOF) {
901 c &= 0377;
902 if (c == IAC) {
903 if ((c = getc(iop)) != EOF) {
904 c &= 0377;
905 switch (c) {
906 case WILL:
907 case WONT:
908 c = getc(iop);
909 printf("%c%c%c", IAC, DONT, 0377&c);
910 (void) fflush(stdout);
911 continue;
912 case DO:
913 case DONT:
914 c = getc(iop);
915 printf("%c%c%c", IAC, WONT, 0377&c);
916 (void) fflush(stdout);
917 continue;
918 case IAC:
919 break;
920 default:
921 continue; /* ignore command */
922 }
923 }
924 }
925 *cs++ = c;
926 if (--n <= 0 || c == '\n')
927 break;
928 }
929 if (c == EOF && cs == s)
930 return (NULL);
931 *cs++ = '\0';
932 if (debug)
933 syslog(LOG_DEBUG, "command: %s", s);
934 return (s);
935}
936
937static VOIDRET toolong FUNCTION((input), int input)
938{
939 time_t now;
940
941 reply(421, "Timeout (%d seconds): closing control connection.", timeout);
942 (void) time(&now);
943 syslog(LOG_INFO, "User %s timed out after %d seconds at %s",
944 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
945 dologout(1);
946}
947
948int yylex FUNCTION_NOARGS
949{
950 static int cpos, state;
951 register char *cp, *cp2;
952 register struct tab *p;
953 int n;
954 char c, *copy();
955
956 for (;;) {
957 switch (state) {
958
959 case CMD:
960 (void) signal(SIGALRM, toolong);
961 (void) alarm((unsigned) timeout);
962 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
963 reply(221, "You could at least say goodbye.");
964 dologout(0);
965 }
966 (void) alarm(0);
27*/
28/*
29 * Copyright (c) 1985, 1988 Regents of the University of California.
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 *
60 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
61 */
62
63/*
64 * Grammar for FTP commands.
65 * See RFC 959.
66 */
67
68%{
69#include "opie_cfg.h"
70
71#include <sys/param.h>
72#include <sys/types.h>
73#include <sys/socket.h>
74#include <sys/stat.h>
75#include <netinet/in.h>
76#include <arpa/ftp.h>
77#include <signal.h>
78#include <setjmp.h>
79#include <syslog.h>
80#if TM_IN_SYS_TIME
81#include <sys/time.h>
82#else /* TM_IN_SYS_TIME */
83#include <time.h>
84#endif /* TM_IN_SYS_TIME */
85#include <pwd.h>
86#include <unistd.h>
87#include <stdio.h>
88#include <ctype.h>
89#include <stdlib.h>
90#include <string.h>
91
92#include "opie.h"
93
94#if HAVE_LS_G_FLAG
95#define LS_COMMAND "/bin/ls -lgA"
96#else /* HAVE_LS_G_FLAG */
97#define LS_COMMAND "/bin/ls -lA"
98#endif /* HAVE_LS_G_FLAG */
99
100extern struct sockaddr_in data_dest;
101extern struct sockaddr_in his_addr;
102extern int logged_in;
103extern struct passwd *pw;
104extern int guest;
105extern int type;
106extern int form;
107extern int debug;
108extern int timeout;
109extern int maxtimeout;
110extern int pdata;
111extern char *remotehost;
112extern char *proctitle;
113extern char *globerr;
114extern int usedefault;
115extern int transflag;
116extern char tmpline[];
117char **ftpglob();
118
119VOIDRET dologout __P((int));
120VOIDRET upper __P((char *));
121VOIDRET nack __P((char *));
122VOIDRET opiefatal __P((char *));
123
124VOIDRET pass __P((char *));
125int user __P((char *));
126VOIDRET passive __P((void));
127VOIDRET retrieve __P((char *, char *));
128VOIDRET store __P((char *, char *, int));
129VOIDRET send_file_list __P((char *));
130VOIDRET statfilecmd __P((char *));
131VOIDRET statcmd __P((void));
132VOIDRET delete __P((char *));
133VOIDRET renamecmd __P((char *, char *));
134VOIDRET cwd __P((char *));
135VOIDRET makedir __P((char *));
136VOIDRET removedir __P((char *));
137VOIDRET pwd __P((void));
138
139VOIDRET sizecmd __P((char *));
140
141off_t restart_point;
142
143static int cmd_type;
144static int cmd_form;
145static int cmd_bytesz;
146static unsigned short cliport = 0;
147char cbuf[512];
148char *fromname;
149
150struct tab {
151 char *name;
152 short token;
153 short state;
154 short implemented; /* 1 if command is implemented */
155 char *help;
156};
157
158VOIDRET help __P((struct tab *, char *));
159
160struct tab cmdtab[], sitetab[];
161
162%}
163
164%token
165 A B C E F I
166 L N P R S T
167
168 SP CRLF COMMA STRING NUMBER
169
170 USER PASS ACCT REIN QUIT PORT
171 PASV TYPE STRU MODE RETR STOR
172 APPE MLFL MAIL MSND MSOM MSAM
173 MRSQ MRCP ALLO REST RNFR RNTO
174 ABOR DELE CWD LIST NLST SITE
175 STAT HELP NOOP MKD RMD PWD
176 CDUP STOU SMNT SYST SIZE MDTM
177
178 UMASK IDLE CHMOD
179
180 LEXERR
181
182%start cmd_list
183
184%%
185
186cmd_list: /* empty */
187 | cmd_list cmd
188 = {
189 fromname = (char *) 0;
190 restart_point = (off_t) 0;
191 }
192 | cmd_list rcmd
193 ;
194
195cmd: USER SP username CRLF
196 = {
197 user((char *) $3);
198 free((char *) $3);
199 }
200 | PASS SP password CRLF
201 = {
202 pass((char *) $3);
203 free((char *) $3);
204 }
205 | PORT check_login SP host_port CRLF
206 = {
207 usedefault = 0;
208 if (pdata >= 0) {
209 (void) close(pdata);
210 pdata = -1;
211 }
212/* H* port fix, part B: admonish the twit.
213 Also require login before PORT works */
214 if ($2) {
215 if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) {
216 reply(200, "PORT command successful.");
217 } else {
218 syslog (LOG_WARNING, "refused %s from %s",
219 cbuf, remotehost);
220 reply(500, "You've GOT to be joking.");
221 }
222 }
223 }
224/* | PASV CRLF
225 = {
226 passive();
227 } */
228 | PASV check_login CRLF
229 = {
230/* Require login for PASV, too. This actually fixes a bug -- telnet to an
231 unfixed wu-ftpd and type PASV first off, and it crashes! */
232 if ($2) {
233 passive();
234 }
235 }
236 | TYPE SP type_code CRLF
237 = {
238 switch (cmd_type) {
239
240 case TYPE_A:
241 if (cmd_form == FORM_N) {
242 reply(200, "Type set to A.");
243 type = cmd_type;
244 form = cmd_form;
245 } else
246 reply(504, "Form must be N.");
247 break;
248
249 case TYPE_E:
250 reply(504, "Type E not implemented.");
251 break;
252
253 case TYPE_I:
254 reply(200, "Type set to I.");
255 type = cmd_type;
256 break;
257
258 case TYPE_L:
259#if NBBY == 8
260 if (cmd_bytesz == 8) {
261 reply(200,
262 "Type set to L (byte size 8).");
263 type = cmd_type;
264 } else
265 reply(504, "Byte size must be 8.");
266#else /* NBBY == 8 */
267 UNIMPLEMENTED for NBBY != 8
268#endif /* NBBY == 8 */
269 }
270 }
271 | STRU SP struct_code CRLF
272 = {
273 switch ($3) {
274
275 case STRU_F:
276 reply(200, "STRU F ok.");
277 break;
278
279 default:
280 reply(504, "Unimplemented STRU type.");
281 }
282 }
283 | MODE SP mode_code CRLF
284 = {
285 switch ($3) {
286
287 case MODE_S:
288 reply(200, "MODE S ok.");
289 break;
290
291 default:
292 reply(502, "Unimplemented MODE type.");
293 }
294 }
295 | ALLO SP NUMBER CRLF
296 = {
297 reply(202, "ALLO command ignored.");
298 }
299 | ALLO SP NUMBER SP R SP NUMBER CRLF
300 = {
301 reply(202, "ALLO command ignored.");
302 }
303 | RETR check_login SP pathname CRLF
304 = {
305 if ($2 && $4)
306 retrieve((char *) 0, (char *) $4);
307 if ($4)
308 free((char *) $4);
309 }
310 | STOR check_login SP pathname CRLF
311 = {
312 if ($2 && $4)
313 store((char *) $4, "w", 0);
314 if ($4)
315 free((char *) $4);
316 }
317 | APPE check_login SP pathname CRLF
318 = {
319 if ($2 && $4)
320 store((char *) $4, "a", 0);
321 if ($4)
322 free((char *) $4);
323 }
324 | NLST check_login CRLF
325 = {
326 if ($2)
327 send_file_list(".");
328 }
329 | NLST check_login SP STRING CRLF
330 = {
331 if ($2 && $4)
332 send_file_list((char *) $4);
333 if ($4)
334 free((char *) $4);
335 }
336 | LIST check_login CRLF
337 = {
338 if ($2)
339 retrieve(LS_COMMAND, "");
340 }
341 | LIST check_login SP pathname CRLF
342 = {
343 if ($2 && $4)
344 {
345 char buffer[sizeof(LS_COMMAND)+3];
346 strcpy(buffer, LS_COMMAND);
347 strcat(buffer, " %s");
348 retrieve(buffer, (char *) $4);
349 }
350 if ($4)
351 free((char *) $4);
352 }
353 | STAT check_login SP pathname CRLF
354 = {
355 if ($2 && $4)
356 statfilecmd((char *) $4);
357 if ($4)
358 free((char *) $4);
359 }
360 | STAT CRLF
361 = {
362 statcmd();
363 }
364 | DELE check_login SP pathname CRLF
365 = {
366 if ($2 && $4)
367 delete((char *) $4);
368 if ($4)
369 free((char *) $4);
370 }
371 | RNTO SP pathname CRLF
372 = {
373 if (fromname) {
374 renamecmd(fromname, (char *) $3);
375 free(fromname);
376 fromname = (char *) 0;
377 } else {
378 reply(503, "Bad sequence of commands.");
379 }
380 free((char *) $3);
381 }
382 | ABOR CRLF
383 = {
384 reply(225, "ABOR command successful.");
385 }
386 | CWD check_login CRLF
387 = {
388 if ($2)
389 cwd(pw->pw_dir);
390 }
391 | CWD check_login SP pathname CRLF
392 = {
393 if ($2 && $4)
394 cwd((char *) $4);
395 if ($4)
396 free((char *) $4);
397 }
398 | HELP CRLF
399 = {
400 help(cmdtab, (char *) 0);
401 }
402 | HELP SP STRING CRLF
403 = {
404 register char *cp = (char *)$3;
405
406 if (strncasecmp(cp, "SITE", 4) == 0) {
407 cp = (char *)$3 + 4;
408 if (*cp == ' ')
409 cp++;
410 if (*cp)
411 help(sitetab, cp);
412 else
413 help(sitetab, (char *) 0);
414 } else
415 help(cmdtab, (char *) $3);
416 }
417 | NOOP CRLF
418 = {
419 reply(200, "NOOP command successful.");
420 }
421 | MKD check_login SP pathname CRLF
422 = {
423 if ($2 && $4)
424 makedir((char *) $4);
425 if ($4)
426 free((char *) $4);
427 }
428 | RMD check_login SP pathname CRLF
429 = {
430 if ($2 && $4)
431 removedir((char *) $4);
432 if ($4)
433 free((char *) $4);
434 }
435 | PWD check_login CRLF
436 = {
437 if ($2)
438 pwd();
439 }
440 | CDUP check_login CRLF
441 = {
442 if ($2)
443 cwd("..");
444 }
445 | SITE SP HELP CRLF
446 = {
447 help(sitetab, (char *) 0);
448 }
449 | SITE SP HELP SP STRING CRLF
450 = {
451 help(sitetab, (char *) $5);
452 }
453 | SITE SP UMASK check_login CRLF
454 = {
455 int oldmask;
456
457 if ($4) {
458 oldmask = umask(0);
459 (void) umask(oldmask);
460 reply(200, "Current UMASK is %03o", oldmask);
461 }
462 }
463 | SITE SP UMASK check_login SP octal_number CRLF
464 = {
465 int oldmask;
466
467 if ($4) {
468 if (($6 == -1) || ($6 > 0777)) {
469 reply(501, "Bad UMASK value");
470 } else {
471 oldmask = umask($6);
472 reply(200,
473 "UMASK set to %03o (was %03o)",
474 $6, oldmask);
475 }
476 }
477 }
478 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
479 = {
480 if ($4 && $8) {
481 if ($6 > 0777)
482 reply(501,
483 "CHMOD: Mode value must be between 0 and 0777");
484 else if (chmod((char *) $8, $6) < 0)
485 perror_reply(550, (char *) $8);
486 else
487 reply(200, "CHMOD command successful.");
488 }
489 if ($8)
490 free((char *) $8);
491 }
492 | SITE SP IDLE CRLF
493 = {
494 reply(200,
495 "Current IDLE time limit is %d seconds; max %d",
496 timeout, maxtimeout);
497 }
498 | SITE SP IDLE SP NUMBER CRLF
499 = {
500 if ($5 < 30 || $5 > maxtimeout) {
501 reply(501,
502 "Maximum IDLE time must be between 30 and %d seconds",
503 maxtimeout);
504 } else {
505 timeout = $5;
506 (void) alarm((unsigned) timeout);
507 reply(200,
508 "Maximum IDLE time set to %d seconds",
509 timeout);
510 }
511 }
512 | STOU check_login SP pathname CRLF
513 = {
514 if ($2 && $4)
515 store((char *) $4, "w", 1);
516 if ($4)
517 free((char *) $4);
518 }
519 | SYST CRLF
520 = {
521#ifdef unix
522#ifdef BSD
523 reply(215, "UNIX Type: L%d Version: BSD-%d",
524 NBBY, BSD);
525#else /* BSD */
526 reply(215, "UNIX Type: L%d", NBBY);
527#endif /* BSD */
528#else /* unix */
529 reply(215, "UNKNOWN Type: L%d", NBBY);
530#endif /* unix */
531 }
532
533 /*
534 * SIZE is not in RFC959, but Postel has blessed it and
535 * it will be in the updated RFC.
536 *
537 * Return size of file in a format suitable for
538 * using with RESTART (we just count bytes).
539 */
540 | SIZE check_login SP pathname CRLF
541 = {
542 if ($2 && $4)
543 sizecmd((char *) $4);
544 if ($4)
545 free((char *) $4);
546 }
547
548 /*
549 * MDTM is not in RFC959, but Postel has blessed it and
550 * it will be in the updated RFC.
551 *
552 * Return modification time of file as an ISO 3307
553 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
554 * where xxx is the fractional second (of any precision,
555 * not necessarily 3 digits)
556 */
557 | MDTM check_login SP pathname CRLF
558 = {
559 if ($2 && $4) {
560 struct stat stbuf;
561 if (stat((char *) $4, &stbuf) < 0)
562 perror_reply(550, (char *) $4);
563 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
564 reply(550, "%s: not a plain file.",
565 (char *) $4);
566 } else {
567 register struct tm *t;
568 struct tm *gmtime();
569 t = gmtime(&stbuf.st_mtime);
570 reply(213,
571 "%d%02d%02d%02d%02d%02d",
572 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
573 t->tm_hour, t->tm_min, t->tm_sec);
574 }
575 }
576 if ($4)
577 free((char *) $4);
578 }
579 | QUIT CRLF
580 = {
581 reply(221, "Goodbye.");
582 dologout(0);
583 }
584 | error CRLF
585 = {
586 yyerrok;
587 }
588 ;
589rcmd: RNFR check_login SP pathname CRLF
590 = {
591 char *renamefrom();
592
593 restart_point = (off_t) 0;
594 if ($2 && $4) {
595 fromname = renamefrom((char *) $4);
596 if (fromname == (char *) 0 && $4) {
597 free((char *) $4);
598 }
599 }
600 }
601 | REST SP byte_size CRLF
602 = {
603 long atol();
604
605 fromname = (char *) 0;
606 restart_point = $3;
607 reply(350, "Restarting at %ld. %s", restart_point,
608 "Send STORE or RETRIEVE to initiate transfer.");
609 }
610 ;
611
612username: STRING
613 ;
614
615password: /* empty */
616 = {
617 *(char **)&($$) = (char *)calloc(1, sizeof(char));
618 }
619 | STRING
620 ;
621
622byte_size: NUMBER
623 ;
624
625host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
626 NUMBER COMMA NUMBER
627 = {
628 register char *a, *p;
629
630 a = (char *)&data_dest.sin_addr;
631 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
632
633/* H* port fix, part A-1: Check the args against the client addr */
634 p = (char *)&his_addr.sin_addr;
635 if (memcmp (a, p, sizeof (data_dest.sin_addr)))
636 memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */
637
638 p = (char *)&data_dest.sin_port;
639
640/* H* port fix, part A-2: only allow client ports in "user space" */
641 p[0] = 0; p[1] = 0;
642 cliport = ($9 << 8) + $11;
643 if (cliport > 1023) {
644 p[0] = $9; p[1] = $11;
645 }
646
647 p[0] = $9; p[1] = $11;
648 data_dest.sin_family = AF_INET;
649 }
650 ;
651
652form_code: N
653 = {
654 $$ = FORM_N;
655 }
656 | T
657 = {
658 $$ = FORM_T;
659 }
660 | C
661 = {
662 $$ = FORM_C;
663 }
664 ;
665
666type_code: A
667 = {
668 cmd_type = TYPE_A;
669 cmd_form = FORM_N;
670 }
671 | A SP form_code
672 = {
673 cmd_type = TYPE_A;
674 cmd_form = $3;
675 }
676 | E
677 = {
678 cmd_type = TYPE_E;
679 cmd_form = FORM_N;
680 }
681 | E SP form_code
682 = {
683 cmd_type = TYPE_E;
684 cmd_form = $3;
685 }
686 | I
687 = {
688 cmd_type = TYPE_I;
689 }
690 | L
691 = {
692 cmd_type = TYPE_L;
693 cmd_bytesz = NBBY;
694 }
695 | L SP byte_size
696 = {
697 cmd_type = TYPE_L;
698 cmd_bytesz = $3;
699 }
700 /* this is for a bug in the BBN ftp */
701 | L byte_size
702 = {
703 cmd_type = TYPE_L;
704 cmd_bytesz = $2;
705 }
706 ;
707
708struct_code: F
709 = {
710 $$ = STRU_F;
711 }
712 | R
713 = {
714 $$ = STRU_R;
715 }
716 | P
717 = {
718 $$ = STRU_P;
719 }
720 ;
721
722mode_code: S
723 = {
724 $$ = MODE_S;
725 }
726 | B
727 = {
728 $$ = MODE_B;
729 }
730 | C
731 = {
732 $$ = MODE_C;
733 }
734 ;
735
736pathname: pathstring
737 = {
738 /*
739 * Problem: this production is used for all pathname
740 * processing, but only gives a 550 error reply.
741 * This is a valid reply in some cases but not in others.
742 */
743 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
744 *(char **)&($$) = *ftpglob((char *) $1);
745 if (globerr != NULL) {
746 reply(550, globerr);
747/* $$ = NULL; */
748 $$ = 0;
749 }
750 free((char *) $1);
751 } else
752 $$ = $1;
753 }
754 ;
755
756pathstring: STRING
757 ;
758
759octal_number: NUMBER
760 = {
761 register int ret, dec, multby, digit;
762
763 /*
764 * Convert a number that was read as decimal number
765 * to what it would be if it had been read as octal.
766 */
767 dec = $1;
768 multby = 1;
769 ret = 0;
770 while (dec) {
771 digit = dec%10;
772 if (digit > 7) {
773 ret = -1;
774 break;
775 }
776 ret += digit * multby;
777 multby *= 8;
778 dec /= 10;
779 }
780 $$ = ret;
781 }
782 ;
783
784check_login: /* empty */
785 = {
786 if (logged_in)
787 $$ = 1;
788 else {
789 reply(530, "Please login with USER and PASS.");
790 $$ = 0;
791 }
792 }
793 ;
794
795%%
796
797extern jmp_buf errcatch;
798
799#define CMD 0 /* beginning of command */
800#define ARGS 1 /* expect miscellaneous arguments */
801#define STR1 2 /* expect SP followed by STRING */
802#define STR2 3 /* expect STRING */
803#define OSTR 4 /* optional SP then STRING */
804#define ZSTR1 5 /* SP then optional STRING */
805#define ZSTR2 6 /* optional STRING after SP */
806#define SITECMD 7 /* SITE command */
807#define NSTR 8 /* Number followed by a string */
808
809struct tab cmdtab[] = { /* In order defined in RFC 765 */
810 { "USER", USER, STR1, 1, "<sp> username" },
811 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
812 { "ACCT", ACCT, STR1, 0, "(specify account)" },
813 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
814 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
815 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
816 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
817 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
818 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
819 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
820 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
821 { "RETR", RETR, STR1, 1, "<sp> file-name" },
822 { "STOR", STOR, STR1, 1, "<sp> file-name" },
823 { "APPE", APPE, STR1, 1, "<sp> file-name" },
824 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
825 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
826 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
827 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
828 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
829 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
830 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
831 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
832 { "REST", REST, ARGS, 1, "(restart command)" },
833 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
834 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
835 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
836 { "DELE", DELE, STR1, 1, "<sp> file-name" },
837 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
838 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
839 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
840 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
841 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
842 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
843 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
844 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
845 { "NOOP", NOOP, ARGS, 1, "" },
846 { "MKD", MKD, STR1, 1, "<sp> path-name" },
847 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
848 { "RMD", RMD, STR1, 1, "<sp> path-name" },
849 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
850 { "PWD", PWD, ARGS, 1, "(return current directory)" },
851 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
852 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
853 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
854 { "STOU", STOU, STR1, 1, "<sp> file-name" },
855 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
856 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
857 { NULL, 0, 0, 0, 0 }
858};
859
860struct tab sitetab[] = {
861 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
862 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
863 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
864 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
865 { NULL, 0, 0, 0, 0 }
866};
867
868struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd)
869{
870
871 for (; p->name != NULL; p++)
872 if (strcmp(cmd, p->name) == 0)
873 return (p);
874 return (0);
875}
876
877#include <arpa/telnet.h>
878
879/*
880 * getline - a hacked up version of fgets to ignore TELNET escape codes.
881 */
882char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop)
883{
884 register c;
885 register char *cs;
886
887 cs = s;
888/* tmpline may contain saved command from urgent mode interruption */
889 for (c = 0; *(tmpline + c) && --n > 0; ++c) {
890 *cs++ = *(tmpline + c);
891 if (*(tmpline + c) == '\n') {
892 *cs++ = '\0';
893 if (debug)
894 syslog(LOG_DEBUG, "command: %s", s);
895 *tmpline = '\0';
896 return(s);
897 }
898 if (c == 0)
899 *tmpline = '\0';
900 }
901 while ((c = getc(iop)) != EOF) {
902 c &= 0377;
903 if (c == IAC) {
904 if ((c = getc(iop)) != EOF) {
905 c &= 0377;
906 switch (c) {
907 case WILL:
908 case WONT:
909 c = getc(iop);
910 printf("%c%c%c", IAC, DONT, 0377&c);
911 (void) fflush(stdout);
912 continue;
913 case DO:
914 case DONT:
915 c = getc(iop);
916 printf("%c%c%c", IAC, WONT, 0377&c);
917 (void) fflush(stdout);
918 continue;
919 case IAC:
920 break;
921 default:
922 continue; /* ignore command */
923 }
924 }
925 }
926 *cs++ = c;
927 if (--n <= 0 || c == '\n')
928 break;
929 }
930 if (c == EOF && cs == s)
931 return (NULL);
932 *cs++ = '\0';
933 if (debug)
934 syslog(LOG_DEBUG, "command: %s", s);
935 return (s);
936}
937
938static VOIDRET toolong FUNCTION((input), int input)
939{
940 time_t now;
941
942 reply(421, "Timeout (%d seconds): closing control connection.", timeout);
943 (void) time(&now);
944 syslog(LOG_INFO, "User %s timed out after %d seconds at %s",
945 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
946 dologout(1);
947}
948
949int yylex FUNCTION_NOARGS
950{
951 static int cpos, state;
952 register char *cp, *cp2;
953 register struct tab *p;
954 int n;
955 char c, *copy();
956
957 for (;;) {
958 switch (state) {
959
960 case CMD:
961 (void) signal(SIGALRM, toolong);
962 (void) alarm((unsigned) timeout);
963 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
964 reply(221, "You could at least say goodbye.");
965 dologout(0);
966 }
967 (void) alarm(0);
967#ifdef SETPROCTITLE
968#if DOTITLE
968 if (strncasecmp(cbuf, "PASS", 4) != NULL)
969 setproctitle("%s: %s", proctitle, cbuf);
969 if (strncasecmp(cbuf, "PASS", 4) != NULL)
970 setproctitle("%s: %s", proctitle, cbuf);
970#endif /* SETPROCTITLE */
971#endif /* DOTITLE */
971 if ((cp = strchr(cbuf, '\r'))) {
972 *cp++ = '\n';
973 *cp = '\0';
974 }
975 if ((cp = strpbrk(cbuf, " \n")))
976 cpos = cp - cbuf;
977 if (cpos == 0)
978 cpos = 4;
979 c = cbuf[cpos];
980 cbuf[cpos] = '\0';
981 upper(cbuf);
982 p = lookup(cmdtab, cbuf);
983 cbuf[cpos] = c;
984 if (p != 0) {
985 if (p->implemented == 0) {
986 nack(p->name);
987 longjmp(errcatch,0);
988 /* NOTREACHED */
989 }
990 state = p->state;
991 *(char **)&yylval = p->name;
992 return (p->token);
993 }
994 break;
995
996 case SITECMD:
997 if (cbuf[cpos] == ' ') {
998 cpos++;
999 return (SP);
1000 }
1001 cp = &cbuf[cpos];
1002 if ((cp2 = strpbrk(cp, " \n")))
1003 cpos = cp2 - cbuf;
1004 c = cbuf[cpos];
1005 cbuf[cpos] = '\0';
1006 upper(cp);
1007 p = lookup(sitetab, cp);
1008 cbuf[cpos] = c;
1009 if (p != 0) {
1010 if (p->implemented == 0) {
1011 state = CMD;
1012 nack(p->name);
1013 longjmp(errcatch,0);
1014 /* NOTREACHED */
1015 }
1016 state = p->state;
1017 *(char **)&yylval = p->name;
1018 return (p->token);
1019 }
1020 state = CMD;
1021 break;
1022
1023 case OSTR:
1024 if (cbuf[cpos] == '\n') {
1025 state = CMD;
1026 return (CRLF);
1027 }
1028 /* FALLTHROUGH */
1029
1030 case STR1:
1031 case ZSTR1:
1032 dostr1:
1033 if (cbuf[cpos] == ' ') {
1034 cpos++;
1035 state = state == OSTR ? STR2 : ++state;
1036 return (SP);
1037 }
1038 break;
1039
1040 case ZSTR2:
1041 if (cbuf[cpos] == '\n') {
1042 state = CMD;
1043 return (CRLF);
1044 }
1045 /* FALLTHROUGH */
1046
1047 case STR2:
1048 cp = &cbuf[cpos];
1049 n = strlen(cp);
1050 cpos += n - 1;
1051 /*
1052 * Make sure the string is nonempty and \n terminated.
1053 */
1054 if (n > 1 && cbuf[cpos] == '\n') {
1055 cbuf[cpos] = '\0';
1056 *(char **)&yylval = copy(cp);
1057 cbuf[cpos] = '\n';
1058 state = ARGS;
1059 return (STRING);
1060 }
1061 break;
1062
1063 case NSTR:
1064 if (cbuf[cpos] == ' ') {
1065 cpos++;
1066 return (SP);
1067 }
1068 if (isdigit(cbuf[cpos])) {
1069 cp = &cbuf[cpos];
1070 while (isdigit(cbuf[++cpos]))
1071 ;
1072 c = cbuf[cpos];
1073 cbuf[cpos] = '\0';
1074 yylval = atoi(cp);
1075 cbuf[cpos] = c;
1076 state = STR1;
1077 return (NUMBER);
1078 }
1079 state = STR1;
1080 goto dostr1;
1081
1082 case ARGS:
1083 if (isdigit(cbuf[cpos])) {
1084 cp = &cbuf[cpos];
1085 while (isdigit(cbuf[++cpos]))
1086 ;
1087 c = cbuf[cpos];
1088 cbuf[cpos] = '\0';
1089 yylval = atoi(cp);
1090 cbuf[cpos] = c;
1091 return (NUMBER);
1092 }
1093 switch (cbuf[cpos++]) {
1094
1095 case '\n':
1096 state = CMD;
1097 return (CRLF);
1098
1099 case ' ':
1100 return (SP);
1101
1102 case ',':
1103 return (COMMA);
1104
1105 case 'A':
1106 case 'a':
1107 return (A);
1108
1109 case 'B':
1110 case 'b':
1111 return (B);
1112
1113 case 'C':
1114 case 'c':
1115 return (C);
1116
1117 case 'E':
1118 case 'e':
1119 return (E);
1120
1121 case 'F':
1122 case 'f':
1123 return (F);
1124
1125 case 'I':
1126 case 'i':
1127 return (I);
1128
1129 case 'L':
1130 case 'l':
1131 return (L);
1132
1133 case 'N':
1134 case 'n':
1135 return (N);
1136
1137 case 'P':
1138 case 'p':
1139 return (P);
1140
1141 case 'R':
1142 case 'r':
1143 return (R);
1144
1145 case 'S':
1146 case 's':
1147 return (S);
1148
1149 case 'T':
1150 case 't':
1151 return (T);
1152
1153 }
1154 break;
1155
1156 default:
1157 opiefatal("Unknown state in scanner.");
1158 }
1159 yyerror((char *) 0);
1160 state = CMD;
1161 longjmp(errcatch,0);
1162 }
1163}
1164
1165VOIDRET upper FUNCTION((s), char *s)
1166{
1167 while (*s != '\0') {
1168 if (islower(*s))
1169 *s = toupper(*s);
1170 s++;
1171 }
1172}
1173
1174char *copy FUNCTION((s), char *s)
1175{
1176 char *p;
1177
1178 p = malloc((unsigned) strlen(s) + 1);
1179 if (p == NULL)
1180 opiefatal("Ran out of memory.");
1181 (void) strcpy(p, s);
1182 return (p);
1183}
1184
1185VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s)
1186{
1187 register struct tab *c;
1188 register int width, NCMDS;
1189 char *type;
1190
1191 if (ctab == sitetab)
1192 type = "SITE ";
1193 else
1194 type = "";
1195 width = 0, NCMDS = 0;
1196 for (c = ctab; c->name != NULL; c++) {
1197 int len = strlen(c->name);
1198
1199 if (len > width)
1200 width = len;
1201 NCMDS++;
1202 }
1203 width = (width + 8) &~ 7;
1204 if (s == 0) {
1205 register int i, j, w;
1206 int columns, lines;
1207
1208 lreply(214, "The following %scommands are recognized %s.",
1209 type, "(* =>'s unimplemented)");
1210 columns = 76 / width;
1211 if (columns == 0)
1212 columns = 1;
1213 lines = (NCMDS + columns - 1) / columns;
1214 for (i = 0; i < lines; i++) {
1215 printf(" ");
1216 for (j = 0; j < columns; j++) {
1217 c = ctab + j * lines + i;
1218 printf("%s%c", c->name,
1219 c->implemented ? ' ' : '*');
1220 if (c + lines >= &ctab[NCMDS])
1221 break;
1222 w = strlen(c->name) + 1;
1223 while (w < width) {
1224 putchar(' ');
1225 w++;
1226 }
1227 }
1228 printf("\r\n");
1229 }
1230 (void) fflush(stdout);
1231 reply(214, " ");
1232 return;
1233 }
1234 upper(s);
1235 c = lookup(ctab, s);
1236 if (c == (struct tab *)0) {
1237 reply(502, "Unknown command %s.", s);
1238 return;
1239 }
1240 if (c->implemented)
1241 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1242 else
1243 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1244 c->name, c->help);
1245}
1246
1247VOIDRET sizecmd FUNCTION((filename), char *filename)
1248{
1249 switch (type) {
1250 case TYPE_L:
1251 case TYPE_I: {
1252 struct stat stbuf;
1253 if (stat(filename, &stbuf) < 0 ||
1254 (stbuf.st_mode&S_IFMT) != S_IFREG)
1255 reply(550, "%s: not a plain file.", filename);
1256 else
1257 reply(213, "%lu", stbuf.st_size);
1258 break;}
1259 case TYPE_A: {
1260 FILE *fin;
1261 register int c;
1262 register long count;
1263 struct stat stbuf;
1264 fin = fopen(filename, "r");
1265 if (fin == NULL) {
1266 perror_reply(550, filename);
1267 return;
1268 }
1269 if (fstat(fileno(fin), &stbuf) < 0 ||
1270 (stbuf.st_mode&S_IFMT) != S_IFREG) {
1271 reply(550, "%s: not a plain file.", filename);
1272 (void) fclose(fin);
1273 return;
1274 }
1275
1276 count = 0;
1277 while((c=getc(fin)) != EOF) {
1278 if (c == '\n') /* will get expanded to \r\n */
1279 count++;
1280 count++;
1281 }
1282 (void) fclose(fin);
1283
1284 reply(213, "%ld", count);
1285 break;}
1286 default:
1287 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1288 }
1289}
972 if ((cp = strchr(cbuf, '\r'))) {
973 *cp++ = '\n';
974 *cp = '\0';
975 }
976 if ((cp = strpbrk(cbuf, " \n")))
977 cpos = cp - cbuf;
978 if (cpos == 0)
979 cpos = 4;
980 c = cbuf[cpos];
981 cbuf[cpos] = '\0';
982 upper(cbuf);
983 p = lookup(cmdtab, cbuf);
984 cbuf[cpos] = c;
985 if (p != 0) {
986 if (p->implemented == 0) {
987 nack(p->name);
988 longjmp(errcatch,0);
989 /* NOTREACHED */
990 }
991 state = p->state;
992 *(char **)&yylval = p->name;
993 return (p->token);
994 }
995 break;
996
997 case SITECMD:
998 if (cbuf[cpos] == ' ') {
999 cpos++;
1000 return (SP);
1001 }
1002 cp = &cbuf[cpos];
1003 if ((cp2 = strpbrk(cp, " \n")))
1004 cpos = cp2 - cbuf;
1005 c = cbuf[cpos];
1006 cbuf[cpos] = '\0';
1007 upper(cp);
1008 p = lookup(sitetab, cp);
1009 cbuf[cpos] = c;
1010 if (p != 0) {
1011 if (p->implemented == 0) {
1012 state = CMD;
1013 nack(p->name);
1014 longjmp(errcatch,0);
1015 /* NOTREACHED */
1016 }
1017 state = p->state;
1018 *(char **)&yylval = p->name;
1019 return (p->token);
1020 }
1021 state = CMD;
1022 break;
1023
1024 case OSTR:
1025 if (cbuf[cpos] == '\n') {
1026 state = CMD;
1027 return (CRLF);
1028 }
1029 /* FALLTHROUGH */
1030
1031 case STR1:
1032 case ZSTR1:
1033 dostr1:
1034 if (cbuf[cpos] == ' ') {
1035 cpos++;
1036 state = state == OSTR ? STR2 : ++state;
1037 return (SP);
1038 }
1039 break;
1040
1041 case ZSTR2:
1042 if (cbuf[cpos] == '\n') {
1043 state = CMD;
1044 return (CRLF);
1045 }
1046 /* FALLTHROUGH */
1047
1048 case STR2:
1049 cp = &cbuf[cpos];
1050 n = strlen(cp);
1051 cpos += n - 1;
1052 /*
1053 * Make sure the string is nonempty and \n terminated.
1054 */
1055 if (n > 1 && cbuf[cpos] == '\n') {
1056 cbuf[cpos] = '\0';
1057 *(char **)&yylval = copy(cp);
1058 cbuf[cpos] = '\n';
1059 state = ARGS;
1060 return (STRING);
1061 }
1062 break;
1063
1064 case NSTR:
1065 if (cbuf[cpos] == ' ') {
1066 cpos++;
1067 return (SP);
1068 }
1069 if (isdigit(cbuf[cpos])) {
1070 cp = &cbuf[cpos];
1071 while (isdigit(cbuf[++cpos]))
1072 ;
1073 c = cbuf[cpos];
1074 cbuf[cpos] = '\0';
1075 yylval = atoi(cp);
1076 cbuf[cpos] = c;
1077 state = STR1;
1078 return (NUMBER);
1079 }
1080 state = STR1;
1081 goto dostr1;
1082
1083 case ARGS:
1084 if (isdigit(cbuf[cpos])) {
1085 cp = &cbuf[cpos];
1086 while (isdigit(cbuf[++cpos]))
1087 ;
1088 c = cbuf[cpos];
1089 cbuf[cpos] = '\0';
1090 yylval = atoi(cp);
1091 cbuf[cpos] = c;
1092 return (NUMBER);
1093 }
1094 switch (cbuf[cpos++]) {
1095
1096 case '\n':
1097 state = CMD;
1098 return (CRLF);
1099
1100 case ' ':
1101 return (SP);
1102
1103 case ',':
1104 return (COMMA);
1105
1106 case 'A':
1107 case 'a':
1108 return (A);
1109
1110 case 'B':
1111 case 'b':
1112 return (B);
1113
1114 case 'C':
1115 case 'c':
1116 return (C);
1117
1118 case 'E':
1119 case 'e':
1120 return (E);
1121
1122 case 'F':
1123 case 'f':
1124 return (F);
1125
1126 case 'I':
1127 case 'i':
1128 return (I);
1129
1130 case 'L':
1131 case 'l':
1132 return (L);
1133
1134 case 'N':
1135 case 'n':
1136 return (N);
1137
1138 case 'P':
1139 case 'p':
1140 return (P);
1141
1142 case 'R':
1143 case 'r':
1144 return (R);
1145
1146 case 'S':
1147 case 's':
1148 return (S);
1149
1150 case 'T':
1151 case 't':
1152 return (T);
1153
1154 }
1155 break;
1156
1157 default:
1158 opiefatal("Unknown state in scanner.");
1159 }
1160 yyerror((char *) 0);
1161 state = CMD;
1162 longjmp(errcatch,0);
1163 }
1164}
1165
1166VOIDRET upper FUNCTION((s), char *s)
1167{
1168 while (*s != '\0') {
1169 if (islower(*s))
1170 *s = toupper(*s);
1171 s++;
1172 }
1173}
1174
1175char *copy FUNCTION((s), char *s)
1176{
1177 char *p;
1178
1179 p = malloc((unsigned) strlen(s) + 1);
1180 if (p == NULL)
1181 opiefatal("Ran out of memory.");
1182 (void) strcpy(p, s);
1183 return (p);
1184}
1185
1186VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s)
1187{
1188 register struct tab *c;
1189 register int width, NCMDS;
1190 char *type;
1191
1192 if (ctab == sitetab)
1193 type = "SITE ";
1194 else
1195 type = "";
1196 width = 0, NCMDS = 0;
1197 for (c = ctab; c->name != NULL; c++) {
1198 int len = strlen(c->name);
1199
1200 if (len > width)
1201 width = len;
1202 NCMDS++;
1203 }
1204 width = (width + 8) &~ 7;
1205 if (s == 0) {
1206 register int i, j, w;
1207 int columns, lines;
1208
1209 lreply(214, "The following %scommands are recognized %s.",
1210 type, "(* =>'s unimplemented)");
1211 columns = 76 / width;
1212 if (columns == 0)
1213 columns = 1;
1214 lines = (NCMDS + columns - 1) / columns;
1215 for (i = 0; i < lines; i++) {
1216 printf(" ");
1217 for (j = 0; j < columns; j++) {
1218 c = ctab + j * lines + i;
1219 printf("%s%c", c->name,
1220 c->implemented ? ' ' : '*');
1221 if (c + lines >= &ctab[NCMDS])
1222 break;
1223 w = strlen(c->name) + 1;
1224 while (w < width) {
1225 putchar(' ');
1226 w++;
1227 }
1228 }
1229 printf("\r\n");
1230 }
1231 (void) fflush(stdout);
1232 reply(214, " ");
1233 return;
1234 }
1235 upper(s);
1236 c = lookup(ctab, s);
1237 if (c == (struct tab *)0) {
1238 reply(502, "Unknown command %s.", s);
1239 return;
1240 }
1241 if (c->implemented)
1242 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1243 else
1244 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1245 c->name, c->help);
1246}
1247
1248VOIDRET sizecmd FUNCTION((filename), char *filename)
1249{
1250 switch (type) {
1251 case TYPE_L:
1252 case TYPE_I: {
1253 struct stat stbuf;
1254 if (stat(filename, &stbuf) < 0 ||
1255 (stbuf.st_mode&S_IFMT) != S_IFREG)
1256 reply(550, "%s: not a plain file.", filename);
1257 else
1258 reply(213, "%lu", stbuf.st_size);
1259 break;}
1260 case TYPE_A: {
1261 FILE *fin;
1262 register int c;
1263 register long count;
1264 struct stat stbuf;
1265 fin = fopen(filename, "r");
1266 if (fin == NULL) {
1267 perror_reply(550, filename);
1268 return;
1269 }
1270 if (fstat(fileno(fin), &stbuf) < 0 ||
1271 (stbuf.st_mode&S_IFMT) != S_IFREG) {
1272 reply(550, "%s: not a plain file.", filename);
1273 (void) fclose(fin);
1274 return;
1275 }
1276
1277 count = 0;
1278 while((c=getc(fin)) != EOF) {
1279 if (c == '\n') /* will get expanded to \r\n */
1280 count++;
1281 count++;
1282 }
1283 (void) fclose(fin);
1284
1285 reply(213, "%ld", count);
1286 break;}
1287 default:
1288 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1289 }
1290}