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