time: Use clock_gettime
[dragonfly.git] / contrib / opie / ftpcmd.y
blob34c9c5a21fed46d42cb1d0406e9cc9e6e3288c68
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: head/contrib/opie/ftpcmd.y 92914 2002-03-21 23:42:52Z markm $
29 * Copyright (c) 1985, 1988 Regents of the University of California.
30 * All rights reserved.
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.
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.
60 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
64 * Grammar for FTP commands.
65 * See RFC 959.
69 #include "opie_cfg.h"
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>
92 #include "opie.h"
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 */
100 extern struct sockaddr_in data_dest;
101 extern struct sockaddr_in his_addr;
102 extern int logged_in;
103 extern struct passwd *pw;
104 extern int guest;
105 extern int type;
106 extern int form;
107 extern int debug;
108 extern int timeout;
109 extern int maxtimeout;
110 extern int pdata;
111 extern char *remotehost;
112 extern char *proctitle;
113 extern char *globerr;
114 extern int usedefault;
115 extern int transflag;
116 extern char tmpline[];
117 char **ftpglob();
119 VOIDRET dologout __P((int));
120 VOIDRET upper __P((char *));
121 VOIDRET nack __P((char *));
122 VOIDRET opiefatal __P((char *));
124 VOIDRET pass __P((char *));
125 int user __P((char *));
126 VOIDRET passive __P((void));
127 VOIDRET retrieve __P((char *, char *));
128 VOIDRET store __P((char *, char *, int));
129 VOIDRET send_file_list __P((char *));
130 VOIDRET statfilecmd __P((char *));
131 VOIDRET statcmd __P((void));
132 VOIDRET delete __P((char *));
133 VOIDRET renamecmd __P((char *, char *));
134 VOIDRET cwd __P((char *));
135 VOIDRET makedir __P((char *));
136 VOIDRET removedir __P((char *));
137 VOIDRET pwd __P((void));
139 VOIDRET sizecmd __P((char *));
141 off_t restart_point;
143 static int cmd_type;
144 static int cmd_form;
145 static int cmd_bytesz;
146 static unsigned short cliport = 0;
147 char cbuf[512];
148 char *fromname;
150 struct tab {
151 char *name;
152 short token;
153 short state;
154 short implemented; /* 1 if command is implemented */
155 char *help;
158 VOIDRET help __P((struct tab *, char *));
160 struct tab cmdtab[], sitetab[];
164 %token
165 A B C E F I
166 L N P R S T
168 SP CRLF COMMA STRING NUMBER
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
178 UMASK IDLE CHMOD
180 LEXERR
182 %start cmd_list
186 cmd_list: /* empty */
187 | cmd_list cmd
189 fromname = (char *) 0;
190 restart_point = (off_t) 0;
192 | cmd_list rcmd
195 cmd: USER SP username CRLF
197 user((char *) $3);
198 free((char *) $3);
200 | PASS SP password CRLF
202 pass((char *) $3);
203 free((char *) $3);
205 | PORT check_login SP host_port CRLF
206 = {
207 usedefault = 0;
208 if (pdata >= 0) {
209 (void) close(pdata);
210 pdata = -1;
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.");
224 /* | PASV CRLF
226 passive();
227 } */
228 | PASV check_login CRLF
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();
236 | TYPE SP type_code CRLF
238 switch (cmd_type) {
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;
249 case TYPE_E:
250 reply(504, "Type E not implemented.");
251 break;
253 case TYPE_I:
254 reply(200, "Type set to I.");
255 type = cmd_type;
256 break;
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 */
271 | STRU SP struct_code CRLF
273 switch ($3) {
275 case STRU_F:
276 reply(200, "STRU F ok.");
277 break;
279 default:
280 reply(504, "Unimplemented STRU type.");
283 | MODE SP mode_code CRLF
285 switch ($3) {
287 case MODE_S:
288 reply(200, "MODE S ok.");
289 break;
291 default:
292 reply(502, "Unimplemented MODE type.");
295 | ALLO SP NUMBER CRLF
297 reply(202, "ALLO command ignored.");
299 | ALLO SP NUMBER SP R SP NUMBER CRLF
301 reply(202, "ALLO command ignored.");
303 | RETR check_login SP pathname CRLF
305 if ($2 && $4)
306 retrieve((char *) 0, (char *) $4);
307 if ($4)
308 free((char *) $4);
310 | STOR check_login SP pathname CRLF
312 if ($2 && $4)
313 store((char *) $4, "w", 0);
314 if ($4)
315 free((char *) $4);
317 | APPE check_login SP pathname CRLF
319 if ($2 && $4)
320 store((char *) $4, "a", 0);
321 if ($4)
322 free((char *) $4);
324 | NLST check_login CRLF
326 if ($2)
327 send_file_list(".");
329 | NLST check_login SP STRING CRLF
331 if ($2 && $4)
332 send_file_list((char *) $4);
333 if ($4)
334 free((char *) $4);
336 | LIST check_login CRLF
338 if ($2)
339 retrieve(LS_COMMAND, "");
341 | LIST check_login SP pathname CRLF
343 if ($2 && $4)
345 char buffer[sizeof(LS_COMMAND)+3];
346 strcpy(buffer, LS_COMMAND);
347 strcat(buffer, " %s");
348 retrieve(buffer, (char *) $4);
350 if ($4)
351 free((char *) $4);
353 | STAT check_login SP pathname CRLF
355 if ($2 && $4)
356 statfilecmd((char *) $4);
357 if ($4)
358 free((char *) $4);
360 | STAT CRLF
362 statcmd();
364 | DELE check_login SP pathname CRLF
366 if ($2 && $4)
367 delete((char *) $4);
368 if ($4)
369 free((char *) $4);
371 | RNTO SP pathname CRLF
373 if (fromname) {
374 renamecmd(fromname, (char *) $3);
375 free(fromname);
376 fromname = (char *) 0;
377 } else {
378 reply(503, "Bad sequence of commands.");
380 free((char *) $3);
382 | ABOR CRLF
384 reply(225, "ABOR command successful.");
386 | CWD check_login CRLF
388 if ($2)
389 cwd(pw->pw_dir);
391 | CWD check_login SP pathname CRLF
393 if ($2 && $4)
394 cwd((char *) $4);
395 if ($4)
396 free((char *) $4);
398 | HELP CRLF
400 help(cmdtab, (char *) 0);
402 | HELP SP STRING CRLF
404 register char *cp = (char *)$3;
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);
417 | NOOP CRLF
419 reply(200, "NOOP command successful.");
421 | MKD check_login SP pathname CRLF
423 if ($2 && $4)
424 makedir((char *) $4);
425 if ($4)
426 free((char *) $4);
428 | RMD check_login SP pathname CRLF
430 if ($2 && $4)
431 removedir((char *) $4);
432 if ($4)
433 free((char *) $4);
435 | PWD check_login CRLF
437 if ($2)
438 pwd();
440 | CDUP check_login CRLF
442 if ($2)
443 cwd("..");
445 | SITE SP HELP CRLF
447 help(sitetab, (char *) 0);
449 | SITE SP HELP SP STRING CRLF
451 help(sitetab, (char *) $5);
453 | SITE SP UMASK check_login CRLF
455 int oldmask;
457 if ($4) {
458 oldmask = umask(0);
459 (void) umask(oldmask);
460 reply(200, "Current UMASK is %03o", oldmask);
463 | SITE SP UMASK check_login SP octal_number CRLF
465 int oldmask;
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);
478 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
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.");
489 if ($8)
490 free((char *) $8);
492 | SITE SP IDLE CRLF
494 reply(200,
495 "Current IDLE time limit is %d seconds; max %d",
496 timeout, maxtimeout);
498 | SITE SP IDLE SP NUMBER CRLF
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);
512 | STOU check_login SP pathname CRLF
514 if ($2 && $4)
515 store((char *) $4, "w", 1);
516 if ($4)
517 free((char *) $4);
519 | SYST CRLF
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 */
534 * SIZE is not in RFC959, but Postel has blessed it and
535 * it will be in the updated RFC.
537 * Return size of file in a format suitable for
538 * using with RESTART (we just count bytes).
540 | SIZE check_login SP pathname CRLF
542 if ($2 && $4)
543 sizecmd((char *) $4);
544 if ($4)
545 free((char *) $4);
549 * MDTM is not in RFC959, but Postel has blessed it and
550 * it will be in the updated RFC.
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)
557 | MDTM check_login SP pathname CRLF
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);
576 if ($4)
577 free((char *) $4);
579 | QUIT CRLF
581 reply(221, "Goodbye.");
582 dologout(0);
584 | error CRLF
586 yyerrok;
589 rcmd: RNFR check_login SP pathname CRLF
591 char *renamefrom();
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);
601 | REST SP byte_size CRLF
603 long atol();
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.");
612 username: STRING
615 password: /* empty */
617 *(char **)&($$) = (char *)calloc(1, sizeof(char));
619 | STRING
622 byte_size: NUMBER
625 host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
626 NUMBER COMMA NUMBER
628 register char *a, *p;
630 a = (char *)&data_dest.sin_addr;
631 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
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 */
638 p = (char *)&data_dest.sin_port;
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;
647 p[0] = $9; p[1] = $11;
648 data_dest.sin_family = AF_INET;
652 form_code: N
654 $$ = FORM_N;
658 $$ = FORM_T;
662 $$ = FORM_C;
666 type_code: A
668 cmd_type = TYPE_A;
669 cmd_form = FORM_N;
671 | A SP form_code
673 cmd_type = TYPE_A;
674 cmd_form = $3;
678 cmd_type = TYPE_E;
679 cmd_form = FORM_N;
681 | E SP form_code
683 cmd_type = TYPE_E;
684 cmd_form = $3;
688 cmd_type = TYPE_I;
692 cmd_type = TYPE_L;
693 cmd_bytesz = NBBY;
695 | L SP byte_size
697 cmd_type = TYPE_L;
698 cmd_bytesz = $3;
700 /* this is for a bug in the BBN ftp */
701 | L byte_size
703 cmd_type = TYPE_L;
704 cmd_bytesz = $2;
708 struct_code: F
710 $$ = STRU_F;
714 $$ = STRU_R;
718 $$ = STRU_P;
722 mode_code: S
724 $$ = MODE_S;
728 $$ = MODE_B;
732 $$ = MODE_C;
736 pathname: pathstring
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.
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;
750 free((char *) $1);
751 } else
752 $$ = $1;
756 pathstring: STRING
759 octal_number: NUMBER
761 register int ret, dec, multby, digit;
764 * Convert a number that was read as decimal number
765 * to what it would be if it had been read as octal.
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;
776 ret += digit * multby;
777 multby *= 8;
778 dec /= 10;
780 $$ = ret;
784 check_login: /* empty */
786 if (logged_in)
787 $$ = 1;
788 else {
789 reply(530, "Please login with USER and PASS.");
790 $$ = 0;
797 extern jmp_buf errcatch;
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 */
809 struct 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 }
860 struct 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 }
868 struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd)
871 for (; p->name != NULL; p++)
872 if (strcmp(cmd, p->name) == 0)
873 return (p);
874 return (0);
877 #include <arpa/telnet.h>
880 * getline - a hacked up version of fgets to ignore TELNET escape codes.
882 char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop)
884 register c;
885 register char *cs;
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);
898 if (c == 0)
899 *tmpline = '\0';
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 */
926 *cs++ = c;
927 if (--n <= 0 || c == '\n')
928 break;
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);
938 static VOIDRET toolong FUNCTION((input), int input)
940 time_t now;
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);
949 int yylex FUNCTION_NOARGS
951 static int cpos, state;
952 register char *cp, *cp2;
953 register struct tab *p;
954 int n;
955 char c, *copy();
957 for (;;) {
958 switch (state) {
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);
967 (void) alarm(0);
968 #if DOTITLE
969 if (strncasecmp(cbuf, "PASS", 4) != NULL)
970 setproctitle("%s: %s", proctitle, cbuf);
971 #endif /* DOTITLE */
972 if ((cp = strchr(cbuf, '\r'))) {
973 *cp++ = '\n';
974 *cp = '\0';
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 */
991 state = p->state;
992 *(char **)&yylval = p->name;
993 return (p->token);
995 break;
997 case SITECMD:
998 if (cbuf[cpos] == ' ') {
999 cpos++;
1000 return (SP);
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 */
1017 state = p->state;
1018 *(char **)&yylval = p->name;
1019 return (p->token);
1021 state = CMD;
1022 break;
1024 case OSTR:
1025 if (cbuf[cpos] == '\n') {
1026 state = CMD;
1027 return (CRLF);
1029 /* FALLTHROUGH */
1031 case STR1:
1032 case ZSTR1:
1033 dostr1:
1034 if (cbuf[cpos] == ' ') {
1035 cpos++;
1036 state = state == OSTR ? STR2 : ++state;
1037 return (SP);
1039 break;
1041 case ZSTR2:
1042 if (cbuf[cpos] == '\n') {
1043 state = CMD;
1044 return (CRLF);
1046 /* FALLTHROUGH */
1048 case STR2:
1049 cp = &cbuf[cpos];
1050 n = strlen(cp);
1051 cpos += n - 1;
1053 * Make sure the string is nonempty and \n terminated.
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);
1062 break;
1064 case NSTR:
1065 if (cbuf[cpos] == ' ') {
1066 cpos++;
1067 return (SP);
1069 if (isdigit(cbuf[cpos])) {
1070 cp = &cbuf[cpos];
1071 while (isdigit(cbuf[++cpos]))
1073 c = cbuf[cpos];
1074 cbuf[cpos] = '\0';
1075 yylval = atoi(cp);
1076 cbuf[cpos] = c;
1077 state = STR1;
1078 return (NUMBER);
1080 state = STR1;
1081 goto dostr1;
1083 case ARGS:
1084 if (isdigit(cbuf[cpos])) {
1085 cp = &cbuf[cpos];
1086 while (isdigit(cbuf[++cpos]))
1088 c = cbuf[cpos];
1089 cbuf[cpos] = '\0';
1090 yylval = atoi(cp);
1091 cbuf[cpos] = c;
1092 return (NUMBER);
1094 switch (cbuf[cpos++]) {
1096 case '\n':
1097 state = CMD;
1098 return (CRLF);
1100 case ' ':
1101 return (SP);
1103 case ',':
1104 return (COMMA);
1106 case 'A':
1107 case 'a':
1108 return (A);
1110 case 'B':
1111 case 'b':
1112 return (B);
1114 case 'C':
1115 case 'c':
1116 return (C);
1118 case 'E':
1119 case 'e':
1120 return (E);
1122 case 'F':
1123 case 'f':
1124 return (F);
1126 case 'I':
1127 case 'i':
1128 return (I);
1130 case 'L':
1131 case 'l':
1132 return (L);
1134 case 'N':
1135 case 'n':
1136 return (N);
1138 case 'P':
1139 case 'p':
1140 return (P);
1142 case 'R':
1143 case 'r':
1144 return (R);
1146 case 'S':
1147 case 's':
1148 return (S);
1150 case 'T':
1151 case 't':
1152 return (T);
1155 break;
1157 default:
1158 opiefatal("Unknown state in scanner.");
1160 yyerror((char *) 0);
1161 state = CMD;
1162 longjmp(errcatch,0);
1166 VOIDRET upper FUNCTION((s), char *s)
1168 while (*s != '\0') {
1169 if (islower(*s))
1170 *s = toupper(*s);
1171 s++;
1175 char *copy FUNCTION((s), char *s)
1177 char *p;
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);
1186 VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s)
1188 register struct tab *c;
1189 register int width, NCMDS;
1190 char *type;
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);
1200 if (len > width)
1201 width = len;
1202 NCMDS++;
1204 width = (width + 8) &~ 7;
1205 if (s == 0) {
1206 register int i, j, w;
1207 int columns, lines;
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++;
1229 printf("\r\n");
1231 (void) fflush(stdout);
1232 reply(214, " ");
1233 return;
1235 upper(s);
1236 c = lookup(ctab, s);
1237 if (c == (struct tab *)0) {
1238 reply(502, "Unknown command %s.", s);
1239 return;
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);
1248 VOIDRET sizecmd FUNCTION((filename), char *filename)
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;
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;
1277 count = 0;
1278 while((c=getc(fin)) != EOF) {
1279 if (c == '\n') /* will get expanded to \r\n */
1280 count++;
1281 count++;
1283 (void) fclose(fin);
1285 reply(213, "%ld", count);
1286 break;}
1287 default:
1288 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);