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