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