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