Small mdoc cleanup.
[dragonfly.git] / contrib / opie / ftpcmd.y
blobb09e2fa2124eb49274e20d56b79c2375e6d2ded1
1 /* ftpcmd.y: yacc parser for the FTP daemon.
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
10 History:
12 Modified by cmetz for OPIE 2.4. Use DOTITLE rather than SETPROCTITLE.
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.
26 $FreeBSD: src/contrib/opie/ftpcmd.y,v 1.2.6.4 2002/07/15 14:48:43 des Exp $
27 $DragonFly: src/contrib/opie/ftpcmd.y,v 1.2 2003/06/17 04:24:04 dillon Exp $
30 * Copyright (c) 1985, 1988 Regents of the University of California.
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
61 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
65 * Grammar for FTP commands.
66 * See RFC 959.
70 #include "opie_cfg.h"
72 #include <sys/param.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
75 #include <sys/stat.h>
76 #include <netinet/in.h>
77 #include <arpa/ftp.h>
78 #include <signal.h>
79 #include <setjmp.h>
80 #include <syslog.h>
81 #if TM_IN_SYS_TIME
82 #include <sys/time.h>
83 #else /* TM_IN_SYS_TIME */
84 #include <time.h>
85 #endif /* TM_IN_SYS_TIME */
86 #include <pwd.h>
87 #include <unistd.h>
88 #include <stdio.h>
89 #include <ctype.h>
90 #include <stdlib.h>
91 #include <string.h>
93 #include "opie.h"
95 #if HAVE_LS_G_FLAG
96 #define LS_COMMAND "/bin/ls -lgA"
97 #else /* HAVE_LS_G_FLAG */
98 #define LS_COMMAND "/bin/ls -lA"
99 #endif /* HAVE_LS_G_FLAG */
101 extern struct sockaddr_in data_dest;
102 extern struct sockaddr_in his_addr;
103 extern int logged_in;
104 extern struct passwd *pw;
105 extern int guest;
106 extern int type;
107 extern int form;
108 extern int debug;
109 extern int timeout;
110 extern int maxtimeout;
111 extern int pdata;
112 extern char *remotehost;
113 extern char *proctitle;
114 extern char *globerr;
115 extern int usedefault;
116 extern int transflag;
117 extern char tmpline[];
118 char **ftpglob();
120 VOIDRET dologout __P((int));
121 VOIDRET upper __P((char *));
122 VOIDRET nack __P((char *));
123 VOIDRET opiefatal __P((char *));
125 VOIDRET pass __P((char *));
126 int user __P((char *));
127 VOIDRET passive __P((void));
128 VOIDRET retrieve __P((char *, char *));
129 VOIDRET store __P((char *, char *, int));
130 VOIDRET send_file_list __P((char *));
131 VOIDRET statfilecmd __P((char *));
132 VOIDRET statcmd __P((void));
133 VOIDRET delete __P((char *));
134 VOIDRET renamecmd __P((char *, char *));
135 VOIDRET cwd __P((char *));
136 VOIDRET makedir __P((char *));
137 VOIDRET removedir __P((char *));
138 VOIDRET pwd __P((void));
140 VOIDRET sizecmd __P((char *));
142 off_t restart_point;
144 static int cmd_type;
145 static int cmd_form;
146 static int cmd_bytesz;
147 static unsigned short cliport = 0;
148 char cbuf[512];
149 char *fromname;
151 struct tab {
152 char *name;
153 short token;
154 short state;
155 short implemented; /* 1 if command is implemented */
156 char *help;
159 VOIDRET help __P((struct tab *, char *));
161 struct tab cmdtab[], sitetab[];
165 %token
166 A B C E F I
167 L N P R S T
169 SP CRLF COMMA STRING NUMBER
171 USER PASS ACCT REIN QUIT PORT
172 PASV TYPE STRU MODE RETR STOR
173 APPE MLFL MAIL MSND MSOM MSAM
174 MRSQ MRCP ALLO REST RNFR RNTO
175 ABOR DELE CWD LIST NLST SITE
176 STAT HELP NOOP MKD RMD PWD
177 CDUP STOU SMNT SYST SIZE MDTM
179 UMASK IDLE CHMOD
181 LEXERR
183 %start cmd_list
187 cmd_list: /* empty */
188 | cmd_list cmd
190 fromname = (char *) 0;
191 restart_point = (off_t) 0;
193 | cmd_list rcmd
196 cmd: USER SP username CRLF
198 user((char *) $3);
199 free((char *) $3);
201 | PASS SP password CRLF
203 pass((char *) $3);
204 free((char *) $3);
206 | PORT check_login SP host_port CRLF
207 = {
208 usedefault = 0;
209 if (pdata >= 0) {
210 (void) close(pdata);
211 pdata = -1;
213 /* H* port fix, part B: admonish the twit.
214 Also require login before PORT works */
215 if ($2) {
216 if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) {
217 reply(200, "PORT command successful.");
218 } else {
219 syslog (LOG_WARNING, "refused %s from %s",
220 cbuf, remotehost);
221 reply(500, "You've GOT to be joking.");
225 /* | PASV CRLF
227 passive();
228 } */
229 | PASV check_login CRLF
231 /* Require login for PASV, too. This actually fixes a bug -- telnet to an
232 unfixed wu-ftpd and type PASV first off, and it crashes! */
233 if ($2) {
234 passive();
237 | TYPE SP type_code CRLF
239 switch (cmd_type) {
241 case TYPE_A:
242 if (cmd_form == FORM_N) {
243 reply(200, "Type set to A.");
244 type = cmd_type;
245 form = cmd_form;
246 } else
247 reply(504, "Form must be N.");
248 break;
250 case TYPE_E:
251 reply(504, "Type E not implemented.");
252 break;
254 case TYPE_I:
255 reply(200, "Type set to I.");
256 type = cmd_type;
257 break;
259 case TYPE_L:
260 #if NBBY == 8
261 if (cmd_bytesz == 8) {
262 reply(200,
263 "Type set to L (byte size 8).");
264 type = cmd_type;
265 } else
266 reply(504, "Byte size must be 8.");
267 #else /* NBBY == 8 */
268 UNIMPLEMENTED for NBBY != 8
269 #endif /* NBBY == 8 */
272 | STRU SP struct_code CRLF
274 switch ($3) {
276 case STRU_F:
277 reply(200, "STRU F ok.");
278 break;
280 default:
281 reply(504, "Unimplemented STRU type.");
284 | MODE SP mode_code CRLF
286 switch ($3) {
288 case MODE_S:
289 reply(200, "MODE S ok.");
290 break;
292 default:
293 reply(502, "Unimplemented MODE type.");
296 | ALLO SP NUMBER CRLF
298 reply(202, "ALLO command ignored.");
300 | ALLO SP NUMBER SP R SP NUMBER CRLF
302 reply(202, "ALLO command ignored.");
304 | RETR check_login SP pathname CRLF
306 if ($2 && $4)
307 retrieve((char *) 0, (char *) $4);
308 if ($4)
309 free((char *) $4);
311 | STOR check_login SP pathname CRLF
313 if ($2 && $4)
314 store((char *) $4, "w", 0);
315 if ($4)
316 free((char *) $4);
318 | APPE check_login SP pathname CRLF
320 if ($2 && $4)
321 store((char *) $4, "a", 0);
322 if ($4)
323 free((char *) $4);
325 | NLST check_login CRLF
327 if ($2)
328 send_file_list(".");
330 | NLST check_login SP STRING CRLF
332 if ($2 && $4)
333 send_file_list((char *) $4);
334 if ($4)
335 free((char *) $4);
337 | LIST check_login CRLF
339 if ($2)
340 retrieve(LS_COMMAND, "");
342 | LIST check_login SP pathname CRLF
344 if ($2 && $4)
346 char buffer[sizeof(LS_COMMAND)+3];
347 strcpy(buffer, LS_COMMAND);
348 strcat(buffer, " %s");
349 retrieve(buffer, (char *) $4);
351 if ($4)
352 free((char *) $4);
354 | STAT check_login SP pathname CRLF
356 if ($2 && $4)
357 statfilecmd((char *) $4);
358 if ($4)
359 free((char *) $4);
361 | STAT CRLF
363 statcmd();
365 | DELE check_login SP pathname CRLF
367 if ($2 && $4)
368 delete((char *) $4);
369 if ($4)
370 free((char *) $4);
372 | RNTO SP pathname CRLF
374 if (fromname) {
375 renamecmd(fromname, (char *) $3);
376 free(fromname);
377 fromname = (char *) 0;
378 } else {
379 reply(503, "Bad sequence of commands.");
381 free((char *) $3);
383 | ABOR CRLF
385 reply(225, "ABOR command successful.");
387 | CWD check_login CRLF
389 if ($2)
390 cwd(pw->pw_dir);
392 | CWD check_login SP pathname CRLF
394 if ($2 && $4)
395 cwd((char *) $4);
396 if ($4)
397 free((char *) $4);
399 | HELP CRLF
401 help(cmdtab, (char *) 0);
403 | HELP SP STRING CRLF
405 register char *cp = (char *)$3;
407 if (strncasecmp(cp, "SITE", 4) == 0) {
408 cp = (char *)$3 + 4;
409 if (*cp == ' ')
410 cp++;
411 if (*cp)
412 help(sitetab, cp);
413 else
414 help(sitetab, (char *) 0);
415 } else
416 help(cmdtab, (char *) $3);
418 | NOOP CRLF
420 reply(200, "NOOP command successful.");
422 | MKD check_login SP pathname CRLF
424 if ($2 && $4)
425 makedir((char *) $4);
426 if ($4)
427 free((char *) $4);
429 | RMD check_login SP pathname CRLF
431 if ($2 && $4)
432 removedir((char *) $4);
433 if ($4)
434 free((char *) $4);
436 | PWD check_login CRLF
438 if ($2)
439 pwd();
441 | CDUP check_login CRLF
443 if ($2)
444 cwd("..");
446 | SITE SP HELP CRLF
448 help(sitetab, (char *) 0);
450 | SITE SP HELP SP STRING CRLF
452 help(sitetab, (char *) $5);
454 | SITE SP UMASK check_login CRLF
456 int oldmask;
458 if ($4) {
459 oldmask = umask(0);
460 (void) umask(oldmask);
461 reply(200, "Current UMASK is %03o", oldmask);
464 | SITE SP UMASK check_login SP octal_number CRLF
466 int oldmask;
468 if ($4) {
469 if (($6 == -1) || ($6 > 0777)) {
470 reply(501, "Bad UMASK value");
471 } else {
472 oldmask = umask($6);
473 reply(200,
474 "UMASK set to %03o (was %03o)",
475 $6, oldmask);
479 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
481 if ($4 && $8) {
482 if ($6 > 0777)
483 reply(501,
484 "CHMOD: Mode value must be between 0 and 0777");
485 else if (chmod((char *) $8, $6) < 0)
486 perror_reply(550, (char *) $8);
487 else
488 reply(200, "CHMOD command successful.");
490 if ($8)
491 free((char *) $8);
493 | SITE SP IDLE CRLF
495 reply(200,
496 "Current IDLE time limit is %d seconds; max %d",
497 timeout, maxtimeout);
499 | SITE SP IDLE SP NUMBER CRLF
501 if ($5 < 30 || $5 > maxtimeout) {
502 reply(501,
503 "Maximum IDLE time must be between 30 and %d seconds",
504 maxtimeout);
505 } else {
506 timeout = $5;
507 (void) alarm((unsigned) timeout);
508 reply(200,
509 "Maximum IDLE time set to %d seconds",
510 timeout);
513 | STOU check_login SP pathname CRLF
515 if ($2 && $4)
516 store((char *) $4, "w", 1);
517 if ($4)
518 free((char *) $4);
520 | SYST CRLF
522 #ifdef unix
523 #ifdef BSD
524 reply(215, "UNIX Type: L%d Version: BSD-%d",
525 NBBY, BSD);
526 #else /* BSD */
527 reply(215, "UNIX Type: L%d", NBBY);
528 #endif /* BSD */
529 #else /* unix */
530 reply(215, "UNKNOWN Type: L%d", NBBY);
531 #endif /* unix */
535 * SIZE is not in RFC959, but Postel has blessed it and
536 * it will be in the updated RFC.
538 * Return size of file in a format suitable for
539 * using with RESTART (we just count bytes).
541 | SIZE check_login SP pathname CRLF
543 if ($2 && $4)
544 sizecmd((char *) $4);
545 if ($4)
546 free((char *) $4);
550 * MDTM is not in RFC959, but Postel has blessed it and
551 * it will be in the updated RFC.
553 * Return modification time of file as an ISO 3307
554 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
555 * where xxx is the fractional second (of any precision,
556 * not necessarily 3 digits)
558 | MDTM check_login SP pathname CRLF
560 if ($2 && $4) {
561 struct stat stbuf;
562 if (stat((char *) $4, &stbuf) < 0)
563 perror_reply(550, (char *) $4);
564 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
565 reply(550, "%s: not a plain file.",
566 (char *) $4);
567 } else {
568 register struct tm *t;
569 struct tm *gmtime();
570 t = gmtime(&stbuf.st_mtime);
571 reply(213,
572 "%d%02d%02d%02d%02d%02d",
573 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
574 t->tm_hour, t->tm_min, t->tm_sec);
577 if ($4)
578 free((char *) $4);
580 | QUIT CRLF
582 reply(221, "Goodbye.");
583 dologout(0);
585 | error CRLF
587 yyerrok;
590 rcmd: RNFR check_login SP pathname CRLF
592 char *renamefrom();
594 restart_point = (off_t) 0;
595 if ($2 && $4) {
596 fromname = renamefrom((char *) $4);
597 if (fromname == (char *) 0 && $4) {
598 free((char *) $4);
602 | REST SP byte_size CRLF
604 long atol();
606 fromname = (char *) 0;
607 restart_point = $3;
608 reply(350, "Restarting at %ld. %s", restart_point,
609 "Send STORE or RETRIEVE to initiate transfer.");
613 username: STRING
616 password: /* empty */
618 *(char **)&($$) = (char *)calloc(1, sizeof(char));
620 | STRING
623 byte_size: NUMBER
626 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
627 NUMBER COMMA NUMBER
629 register char *a, *p;
631 a = (char *)&data_dest.sin_addr;
632 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
634 /* H* port fix, part A-1: Check the args against the client addr */
635 p = (char *)&his_addr.sin_addr;
636 if (memcmp (a, p, sizeof (data_dest.sin_addr)))
637 memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */
639 p = (char *)&data_dest.sin_port;
641 /* H* port fix, part A-2: only allow client ports in "user space" */
642 p[0] = 0; p[1] = 0;
643 cliport = ($9 << 8) + $11;
644 if (cliport > 1023) {
645 p[0] = $9; p[1] = $11;
648 p[0] = $9; p[1] = $11;
649 data_dest.sin_family = AF_INET;
653 form_code: N
655 $$ = FORM_N;
659 $$ = FORM_T;
663 $$ = FORM_C;
667 type_code: A
669 cmd_type = TYPE_A;
670 cmd_form = FORM_N;
672 | A SP form_code
674 cmd_type = TYPE_A;
675 cmd_form = $3;
679 cmd_type = TYPE_E;
680 cmd_form = FORM_N;
682 | E SP form_code
684 cmd_type = TYPE_E;
685 cmd_form = $3;
689 cmd_type = TYPE_I;
693 cmd_type = TYPE_L;
694 cmd_bytesz = NBBY;
696 | L SP byte_size
698 cmd_type = TYPE_L;
699 cmd_bytesz = $3;
701 /* this is for a bug in the BBN ftp */
702 | L byte_size
704 cmd_type = TYPE_L;
705 cmd_bytesz = $2;
709 struct_code: F
711 $$ = STRU_F;
715 $$ = STRU_R;
719 $$ = STRU_P;
723 mode_code: S
725 $$ = MODE_S;
729 $$ = MODE_B;
733 $$ = MODE_C;
737 pathname: pathstring
740 * Problem: this production is used for all pathname
741 * processing, but only gives a 550 error reply.
742 * This is a valid reply in some cases but not in others.
744 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
745 *(char **)&($$) = *ftpglob((char *) $1);
746 if (globerr != NULL) {
747 reply(550, globerr);
748 /* $$ = NULL; */
749 $$ = 0;
751 free((char *) $1);
752 } else
753 $$ = $1;
757 pathstring: STRING
760 octal_number: NUMBER
762 register int ret, dec, multby, digit;
765 * Convert a number that was read as decimal number
766 * to what it would be if it had been read as octal.
768 dec = $1;
769 multby = 1;
770 ret = 0;
771 while (dec) {
772 digit = dec%10;
773 if (digit > 7) {
774 ret = -1;
775 break;
777 ret += digit * multby;
778 multby *= 8;
779 dec /= 10;
781 $$ = ret;
785 check_login: /* empty */
787 if (logged_in)
788 $$ = 1;
789 else {
790 reply(530, "Please login with USER and PASS.");
791 $$ = 0;
798 extern jmp_buf errcatch;
800 #define CMD 0 /* beginning of command */
801 #define ARGS 1 /* expect miscellaneous arguments */
802 #define STR1 2 /* expect SP followed by STRING */
803 #define STR2 3 /* expect STRING */
804 #define OSTR 4 /* optional SP then STRING */
805 #define ZSTR1 5 /* SP then optional STRING */
806 #define ZSTR2 6 /* optional STRING after SP */
807 #define SITECMD 7 /* SITE command */
808 #define NSTR 8 /* Number followed by a string */
810 struct tab cmdtab[] = { /* In order defined in RFC 765 */
811 { "USER", USER, STR1, 1, "<sp> username" },
812 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
813 { "ACCT", ACCT, STR1, 0, "(specify account)" },
814 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
815 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
816 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
817 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
818 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
819 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
820 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
821 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
822 { "RETR", RETR, STR1, 1, "<sp> file-name" },
823 { "STOR", STOR, STR1, 1, "<sp> file-name" },
824 { "APPE", APPE, STR1, 1, "<sp> file-name" },
825 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
826 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
827 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
828 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
829 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
830 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
831 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
832 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
833 { "REST", REST, ARGS, 1, "(restart command)" },
834 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
835 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
836 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
837 { "DELE", DELE, STR1, 1, "<sp> file-name" },
838 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
839 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
840 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
841 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
842 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
843 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
844 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
845 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
846 { "NOOP", NOOP, ARGS, 1, "" },
847 { "MKD", MKD, STR1, 1, "<sp> path-name" },
848 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
849 { "RMD", RMD, STR1, 1, "<sp> path-name" },
850 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
851 { "PWD", PWD, ARGS, 1, "(return current directory)" },
852 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
853 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
854 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
855 { "STOU", STOU, STR1, 1, "<sp> file-name" },
856 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
857 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
858 { NULL, 0, 0, 0, 0 }
861 struct tab sitetab[] = {
862 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
863 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
864 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
865 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
866 { NULL, 0, 0, 0, 0 }
869 struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd)
872 for (; p->name != NULL; p++)
873 if (strcmp(cmd, p->name) == 0)
874 return (p);
875 return (0);
878 #include <arpa/telnet.h>
881 * getline - a hacked up version of fgets to ignore TELNET escape codes.
883 char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop)
885 register c;
886 register char *cs;
888 cs = s;
889 /* tmpline may contain saved command from urgent mode interruption */
890 for (c = 0; *(tmpline + c) && --n > 0; ++c) {
891 *cs++ = *(tmpline + c);
892 if (*(tmpline + c) == '\n') {
893 *cs++ = '\0';
894 if (debug)
895 syslog(LOG_DEBUG, "command: %s", s);
896 *tmpline = '\0';
897 return(s);
899 if (c == 0)
900 *tmpline = '\0';
902 while ((c = getc(iop)) != EOF) {
903 c &= 0377;
904 if (c == IAC) {
905 if ((c = getc(iop)) != EOF) {
906 c &= 0377;
907 switch (c) {
908 case WILL:
909 case WONT:
910 c = getc(iop);
911 printf("%c%c%c", IAC, DONT, 0377&c);
912 (void) fflush(stdout);
913 continue;
914 case DO:
915 case DONT:
916 c = getc(iop);
917 printf("%c%c%c", IAC, WONT, 0377&c);
918 (void) fflush(stdout);
919 continue;
920 case IAC:
921 break;
922 default:
923 continue; /* ignore command */
927 *cs++ = c;
928 if (--n <= 0 || c == '\n')
929 break;
931 if (c == EOF && cs == s)
932 return (NULL);
933 *cs++ = '\0';
934 if (debug)
935 syslog(LOG_DEBUG, "command: %s", s);
936 return (s);
939 static VOIDRET toolong FUNCTION((input), int input)
941 time_t now;
943 reply(421, "Timeout (%d seconds): closing control connection.", timeout);
944 (void) time(&now);
945 syslog(LOG_INFO, "User %s timed out after %d seconds at %s",
946 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
947 dologout(1);
950 int yylex FUNCTION_NOARGS
952 static int cpos, state;
953 register char *cp, *cp2;
954 register struct tab *p;
955 int n;
956 char c, *copy();
958 for (;;) {
959 switch (state) {
961 case CMD:
962 (void) signal(SIGALRM, toolong);
963 (void) alarm((unsigned) timeout);
964 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
965 reply(221, "You could at least say goodbye.");
966 dologout(0);
968 (void) alarm(0);
969 #if DOTITLE
970 if (strncasecmp(cbuf, "PASS", 4) != NULL)
971 setproctitle("%s: %s", proctitle, cbuf);
972 #endif /* DOTITLE */
973 if ((cp = strchr(cbuf, '\r'))) {
974 *cp++ = '\n';
975 *cp = '\0';
977 if ((cp = strpbrk(cbuf, " \n")))
978 cpos = cp - cbuf;
979 if (cpos == 0)
980 cpos = 4;
981 c = cbuf[cpos];
982 cbuf[cpos] = '\0';
983 upper(cbuf);
984 p = lookup(cmdtab, cbuf);
985 cbuf[cpos] = c;
986 if (p != 0) {
987 if (p->implemented == 0) {
988 nack(p->name);
989 longjmp(errcatch,0);
990 /* NOTREACHED */
992 state = p->state;
993 *(char **)&yylval = p->name;
994 return (p->token);
996 break;
998 case SITECMD:
999 if (cbuf[cpos] == ' ') {
1000 cpos++;
1001 return (SP);
1003 cp = &cbuf[cpos];
1004 if ((cp2 = strpbrk(cp, " \n")))
1005 cpos = cp2 - cbuf;
1006 c = cbuf[cpos];
1007 cbuf[cpos] = '\0';
1008 upper(cp);
1009 p = lookup(sitetab, cp);
1010 cbuf[cpos] = c;
1011 if (p != 0) {
1012 if (p->implemented == 0) {
1013 state = CMD;
1014 nack(p->name);
1015 longjmp(errcatch,0);
1016 /* NOTREACHED */
1018 state = p->state;
1019 *(char **)&yylval = p->name;
1020 return (p->token);
1022 state = CMD;
1023 break;
1025 case OSTR:
1026 if (cbuf[cpos] == '\n') {
1027 state = CMD;
1028 return (CRLF);
1030 /* FALLTHROUGH */
1032 case STR1:
1033 case ZSTR1:
1034 dostr1:
1035 if (cbuf[cpos] == ' ') {
1036 cpos++;
1037 state = state == OSTR ? STR2 : ++state;
1038 return (SP);
1040 break;
1042 case ZSTR2:
1043 if (cbuf[cpos] == '\n') {
1044 state = CMD;
1045 return (CRLF);
1047 /* FALLTHROUGH */
1049 case STR2:
1050 cp = &cbuf[cpos];
1051 n = strlen(cp);
1052 cpos += n - 1;
1054 * Make sure the string is nonempty and \n terminated.
1056 if (n > 1 && cbuf[cpos] == '\n') {
1057 cbuf[cpos] = '\0';
1058 *(char **)&yylval = copy(cp);
1059 cbuf[cpos] = '\n';
1060 state = ARGS;
1061 return (STRING);
1063 break;
1065 case NSTR:
1066 if (cbuf[cpos] == ' ') {
1067 cpos++;
1068 return (SP);
1070 if (isdigit(cbuf[cpos])) {
1071 cp = &cbuf[cpos];
1072 while (isdigit(cbuf[++cpos]))
1074 c = cbuf[cpos];
1075 cbuf[cpos] = '\0';
1076 yylval = atoi(cp);
1077 cbuf[cpos] = c;
1078 state = STR1;
1079 return (NUMBER);
1081 state = STR1;
1082 goto dostr1;
1084 case ARGS:
1085 if (isdigit(cbuf[cpos])) {
1086 cp = &cbuf[cpos];
1087 while (isdigit(cbuf[++cpos]))
1089 c = cbuf[cpos];
1090 cbuf[cpos] = '\0';
1091 yylval = atoi(cp);
1092 cbuf[cpos] = c;
1093 return (NUMBER);
1095 switch (cbuf[cpos++]) {
1097 case '\n':
1098 state = CMD;
1099 return (CRLF);
1101 case ' ':
1102 return (SP);
1104 case ',':
1105 return (COMMA);
1107 case 'A':
1108 case 'a':
1109 return (A);
1111 case 'B':
1112 case 'b':
1113 return (B);
1115 case 'C':
1116 case 'c':
1117 return (C);
1119 case 'E':
1120 case 'e':
1121 return (E);
1123 case 'F':
1124 case 'f':
1125 return (F);
1127 case 'I':
1128 case 'i':
1129 return (I);
1131 case 'L':
1132 case 'l':
1133 return (L);
1135 case 'N':
1136 case 'n':
1137 return (N);
1139 case 'P':
1140 case 'p':
1141 return (P);
1143 case 'R':
1144 case 'r':
1145 return (R);
1147 case 'S':
1148 case 's':
1149 return (S);
1151 case 'T':
1152 case 't':
1153 return (T);
1156 break;
1158 default:
1159 opiefatal("Unknown state in scanner.");
1161 yyerror((char *) 0);
1162 state = CMD;
1163 longjmp(errcatch,0);
1167 VOIDRET upper FUNCTION((s), char *s)
1169 while (*s != '\0') {
1170 if (islower(*s))
1171 *s = toupper(*s);
1172 s++;
1176 char *copy FUNCTION((s), char *s)
1178 char *p;
1180 p = malloc((unsigned) strlen(s) + 1);
1181 if (p == NULL)
1182 opiefatal("Ran out of memory.");
1183 (void) strcpy(p, s);
1184 return (p);
1187 VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s)
1189 register struct tab *c;
1190 register int width, NCMDS;
1191 char *type;
1193 if (ctab == sitetab)
1194 type = "SITE ";
1195 else
1196 type = "";
1197 width = 0, NCMDS = 0;
1198 for (c = ctab; c->name != NULL; c++) {
1199 int len = strlen(c->name);
1201 if (len > width)
1202 width = len;
1203 NCMDS++;
1205 width = (width + 8) &~ 7;
1206 if (s == 0) {
1207 register int i, j, w;
1208 int columns, lines;
1210 lreply(214, "The following %scommands are recognized %s.",
1211 type, "(* =>'s unimplemented)");
1212 columns = 76 / width;
1213 if (columns == 0)
1214 columns = 1;
1215 lines = (NCMDS + columns - 1) / columns;
1216 for (i = 0; i < lines; i++) {
1217 printf(" ");
1218 for (j = 0; j < columns; j++) {
1219 c = ctab + j * lines + i;
1220 printf("%s%c", c->name,
1221 c->implemented ? ' ' : '*');
1222 if (c + lines >= &ctab[NCMDS])
1223 break;
1224 w = strlen(c->name) + 1;
1225 while (w < width) {
1226 putchar(' ');
1227 w++;
1230 printf("\r\n");
1232 (void) fflush(stdout);
1233 reply(214, " ");
1234 return;
1236 upper(s);
1237 c = lookup(ctab, s);
1238 if (c == (struct tab *)0) {
1239 reply(502, "Unknown command %s.", s);
1240 return;
1242 if (c->implemented)
1243 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1244 else
1245 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1246 c->name, c->help);
1249 VOIDRET sizecmd FUNCTION((filename), char *filename)
1251 switch (type) {
1252 case TYPE_L:
1253 case TYPE_I: {
1254 struct stat stbuf;
1255 if (stat(filename, &stbuf) < 0 ||
1256 (stbuf.st_mode&S_IFMT) != S_IFREG)
1257 reply(550, "%s: not a plain file.", filename);
1258 else
1259 reply(213, "%lu", stbuf.st_size);
1260 break;}
1261 case TYPE_A: {
1262 FILE *fin;
1263 register int c;
1264 register long count;
1265 struct stat stbuf;
1266 fin = fopen(filename, "r");
1267 if (fin == NULL) {
1268 perror_reply(550, filename);
1269 return;
1271 if (fstat(fileno(fin), &stbuf) < 0 ||
1272 (stbuf.st_mode&S_IFMT) != S_IFREG) {
1273 reply(550, "%s: not a plain file.", filename);
1274 (void) fclose(fin);
1275 return;
1278 count = 0;
1279 while((c=getc(fin)) != EOF) {
1280 if (c == '\n') /* will get expanded to \r\n */
1281 count++;
1282 count++;
1284 (void) fclose(fin);
1286 reply(213, "%ld", count);
1287 break;}
1288 default:
1289 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);