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