grab wtmp path from utmp.h
[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 *
5 * Redistribution and use in source and binary forms are permitted
b8c620d6
KB
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
fdb56acd 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
b8c620d6 16 *
8ba00015 17 * @(#)ftpcmd.y 5.20.1.1 (Berkeley) %G%
f644bb55
DF
18 */
19
25d264e2
SL
20/*
21 * Grammar for FTP commands.
fdb56acd 22 * See RFC 959.
25d264e2
SL
23 */
24
25%{
26
27#ifndef lint
8ba00015 28static char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) %G%";
e2071415 29#endif /* not lint */
25d264e2 30
4d340163 31#include <sys/param.h>
25d264e2
SL
32#include <sys/socket.h>
33
34#include <netinet/in.h>
35
23eeaca6
SL
36#include <arpa/ftp.h>
37
25d264e2 38#include <stdio.h>
5ac6fc46 39#include <signal.h>
25d264e2
SL
40#include <ctype.h>
41#include <pwd.h>
42#include <setjmp.h>
df2f99b4 43#include <syslog.h>
fdb56acd
MK
44#include <sys/stat.h>
45#include <time.h>
25d264e2
SL
46
47extern struct sockaddr_in data_dest;
48extern int logged_in;
49extern struct passwd *pw;
50extern int guest;
51extern int logging;
52extern int type;
53extern int form;
54extern int debug;
5ac6fc46 55extern int timeout;
fdb56acd 56extern int maxtimeout;
07fffe50 57extern int pdata;
843d1a1c 58extern char hostname[], remotehost[];
fdb56acd 59extern char proctitle[];
25d264e2 60extern char *globerr;
1d92d63d 61extern int usedefault;
07fffe50
GM
62extern int transflag;
63extern char tmpline[];
25d264e2
SL
64char **glob();
65
66static int cmd_type;
67static int cmd_form;
68static int cmd_bytesz;
882508af
MK
69char cbuf[512];
70char *fromname;
25d264e2 71
25d264e2
SL
72char *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
843d1a1c 86 STAT HELP NOOP MKD RMD PWD
fdb56acd
MK
87 CDUP STOU SMNT SYST SIZE MDTM
88
89 UMASK IDLE CHMOD
25d264e2
SL
90
91 LEXERR
92
93%start cmd_list
94
95%%
96
97cmd_list: /* empty */
98 | cmd_list cmd
ec67a549
SJ
99 = {
100 fromname = (char *) 0;
101 }
102 | cmd_list rcmd
25d264e2
SL
103 ;
104
105cmd: USER SP username CRLF
106 = {
882508af 107 user((char *) $3);
df2f99b4 108 free((char *) $3);
25d264e2
SL
109 }
110 | PASS SP password CRLF
111 = {
df2f99b4
GM
112 pass((char *) $3);
113 free((char *) $3);
25d264e2
SL
114 }
115 | PORT SP host_port CRLF
116 = {
1d92d63d 117 usedefault = 0;
882508af 118 if (pdata >= 0) {
07fffe50 119 (void) close(pdata);
882508af 120 pdata = -1;
07fffe50 121 }
7ccaf1e3 122 reply(200, "PORT command successful.");
25d264e2 123 }
07fffe50
GM
124 | PASV CRLF
125 = {
126 passive();
127 }
25d264e2
SL
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:
fdb56acd
MK
151#if NBBY == 8
152 if (cmd_bytesz == 8) {
25d264e2 153 reply(200,
fdb56acd 154 "Type set to L (byte size 8).");
25d264e2
SL
155 type = cmd_type;
156 } else
fdb56acd
MK
157 reply(504, "Byte size must be 8.");
158#else /* NBBY == 8 */
159 UNIMPLEMENTED for NBBY != 8
160#endif /* NBBY == 8 */
25d264e2
SL
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:
7ccaf1e3 172 reply(504, "Unimplemented STRU type.");
25d264e2
SL
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 = {
7ccaf1e3 189 reply(202, "ALLO command ignored.");
25d264e2 190 }
fdb56acd
MK
191 | ALLO SP NUMBER SP R SP NUMBER CRLF
192 = {
193 reply(202, "ALLO command ignored.");
194 }
25d264e2
SL
195 | RETR check_login SP pathname CRLF
196 = {
8365e2f7 197 if ($2 && $4 != NULL)
fdb56acd 198 retrieve((char *) 0, (char *) $4);
8365e2f7 199 if ($4 != NULL)
df2f99b4 200 free((char *) $4);
25d264e2
SL
201 }
202 | STOR check_login SP pathname CRLF
203 = {
8365e2f7 204 if ($2 && $4 != NULL)
882508af 205 store((char *) $4, "w", 0);
8365e2f7 206 if ($4 != NULL)
df2f99b4 207 free((char *) $4);
25d264e2
SL
208 }
209 | APPE check_login SP pathname CRLF
210 = {
8365e2f7 211 if ($2 && $4 != NULL)
882508af 212 store((char *) $4, "a", 0);
8365e2f7 213 if ($4 != NULL)
df2f99b4 214 free((char *) $4);
25d264e2
SL
215 }
216 | NLST check_login CRLF
217 = {
218 if ($2)
843d1a1c 219 send_file_list(".");
25d264e2 220 }
843d1a1c 221 | NLST check_login SP STRING CRLF
25d264e2 222 = {
843d1a1c
RA
223 if ($2 && $4 != NULL)
224 send_file_list((char *) $4);
8365e2f7 225 if ($4 != NULL)
df2f99b4 226 free((char *) $4);
25d264e2
SL
227 }
228 | LIST check_login CRLF
229 = {
230 if ($2)
843d1a1c 231 retrieve("/bin/ls -lgA", "");
25d264e2
SL
232 }
233 | LIST check_login SP pathname CRLF
234 = {
8365e2f7 235 if ($2 && $4 != NULL)
843d1a1c 236 retrieve("/bin/ls -lgA %s", (char *) $4);
8365e2f7 237 if ($4 != NULL)
df2f99b4 238 free((char *) $4);
25d264e2 239 }
fdb56acd
MK
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 }
25d264e2
SL
251 | DELE check_login SP pathname CRLF
252 = {
8365e2f7 253 if ($2 && $4 != NULL)
df2f99b4 254 delete((char *) $4);
8365e2f7 255 if ($4 != NULL)
df2f99b4 256 free((char *) $4);
25d264e2 257 }
ec67a549
SJ
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 }
07fffe50
GM
269 | ABOR CRLF
270 = {
7ccaf1e3 271 reply(225, "ABOR command successful.");
07fffe50 272 }
25d264e2
SL
273 | CWD check_login CRLF
274 = {
275 if ($2)
276 cwd(pw->pw_dir);
277 }
278 | CWD check_login SP pathname CRLF
279 = {
8365e2f7 280 if ($2 && $4 != NULL)
df2f99b4 281 cwd((char *) $4);
8365e2f7 282 if ($4 != NULL)
df2f99b4 283 free((char *) $4);
25d264e2 284 }
25d264e2
SL
285 | HELP CRLF
286 = {
fdb56acd 287 help(cmdtab, (char *) 0);
25d264e2
SL
288 }
289 | HELP SP STRING CRLF
290 = {
fdb56acd
MK
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);
25d264e2
SL
303 }
304 | NOOP CRLF
305 = {
7ccaf1e3 306 reply(200, "NOOP command successful.");
25d264e2 307 }
843d1a1c 308 | MKD check_login SP pathname CRLF
25d264e2 309 = {
8365e2f7 310 if ($2 && $4 != NULL)
df2f99b4 311 makedir((char *) $4);
8365e2f7 312 if ($4 != NULL)
df2f99b4 313 free((char *) $4);
25d264e2 314 }
843d1a1c 315 | RMD check_login SP pathname CRLF
25d264e2 316 = {
8365e2f7 317 if ($2 && $4 != NULL)
df2f99b4 318 removedir((char *) $4);
8365e2f7 319 if ($4 != NULL)
df2f99b4 320 free((char *) $4);
25d264e2 321 }
843d1a1c 322 | PWD check_login CRLF
25d264e2
SL
323 = {
324 if ($2)
8365e2f7 325 pwd();
25d264e2 326 }
843d1a1c 327 | CDUP check_login CRLF
25d264e2
SL
328 = {
329 if ($2)
330 cwd("..");
331 }
fdb56acd
MK
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 }
07fffe50
GM
399 | STOU check_login SP pathname CRLF
400 = {
882508af
MK
401 if ($2 && $4 != NULL)
402 store((char *) $4, "w", 1);
07fffe50 403 if ($4 != NULL)
df2f99b4 404 free((char *) $4);
07fffe50 405 }
4d340163
KB
406 | SYST CRLF
407 = {
ff8adb76 408#ifdef unix
fdb56acd 409#ifdef BSD
4d340163
KB
410 reply(215, "UNIX Type: L%d Version: BSD-%d",
411 NBBY, BSD);
fdb56acd
MK
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);
4d340163 465 }
25d264e2
SL
466 | QUIT CRLF
467 = {
468 reply(221, "Goodbye.");
bb16805b 469 dologout(0);
25d264e2
SL
470 }
471 | error CRLF
472 = {
473 yyerrok;
474 }
475 ;
ec67a549
SJ
476rcmd: 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
25d264e2
SL
489username: STRING
490 ;
491
882508af
MK
492password: /* empty */
493 = {
fdb56acd 494 *(char **)&($$) = "";
882508af
MK
495 }
496 | STRING
25d264e2
SL
497 ;
498
499byte_size: NUMBER
500 ;
501
502host_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;
5914191e 511 data_dest.sin_family = AF_INET;
25d264e2
SL
512 }
513 ;
514
515form_code: N
516 = {
517 $$ = FORM_N;
518 }
519 | T
520 = {
521 $$ = FORM_T;
522 }
523 | C
524 = {
525 $$ = FORM_C;
526 }
527 ;
528
529type_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;
4d340163 556 cmd_bytesz = NBBY;
25d264e2
SL
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
571struct_code: F
572 = {
573 $$ = STRU_F;
574 }
575 | R
576 = {
577 $$ = STRU_R;
578 }
579 | P
580 = {
581 $$ = STRU_P;
582 }
583 ;
584
585mode_code: S
586 = {
587 $$ = MODE_S;
588 }
589 | B
590 = {
591 $$ = MODE_B;
592 }
593 | C
594 = {
595 $$ = MODE_C;
596 }
597 ;
598
599pathname: pathstring
600 = {
7ccaf1e3
KM
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 */
882508af 606 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
fdb56acd 607 *(char **)&($$) = *glob((char *) $1);
8365e2f7 608 if (globerr != NULL) {
25d264e2 609 reply(550, globerr);
8365e2f7
SL
610 $$ = NULL;
611 }
df2f99b4 612 free((char *) $1);
25d264e2
SL
613 } else
614 $$ = $1;
615 }
616 ;
617
618pathstring: STRING
619 ;
620
fdb56acd
MK
621octal_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
25d264e2
SL
646check_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
659extern 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 */
882508af
MK
665#define OSTR 4 /* optional SP then STRING */
666#define ZSTR1 5 /* SP then optional STRING */
667#define ZSTR2 6 /* optional STRING after SP */
fdb56acd
MK
668#define SITECMD 7 /* SITE command */
669#define NSTR 8 /* Number followed by a string */
25d264e2
SL
670
671struct tab {
672 char *name;
673 short token;
674 short state;
675 short implemented; /* 1 if command is implemented */
676 char *help;
677};
678
679struct tab cmdtab[] = { /* In order defined in RFC 765 */
680 { "USER", USER, STR1, 1, "<sp> username" },
882508af 681 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
25d264e2 682 { "ACCT", ACCT, STR1, 0, "(specify account)" },
fdb56acd 683 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
25d264e2
SL
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" },
07fffe50 687 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
25d264e2
SL
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)" },
8ba00015 702 { "REST", REST, ARGS, 0, "(restart command)" },
25d264e2
SL
703 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
704 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
07fffe50 705 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
25d264e2 706 { "DELE", DELE, STR1, 1, "<sp> file-name" },
882508af 707 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
25d264e2
SL
708 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
709 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
710 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
fdb56acd 711 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
4d340163 712 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
fdb56acd 713 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
25d264e2
SL
714 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
715 { "NOOP", NOOP, ARGS, 1, "" },
843d1a1c
RA
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)" },
07fffe50 724 { "STOU", STOU, STR1, 1, "<sp> file-name" },
fdb56acd
MK
725 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
726 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
727 { NULL, 0, 0, 0, 0 }
728};
729
730struct 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> ]" },
25d264e2
SL
735 { NULL, 0, 0, 0, 0 }
736};
737
738struct 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;
758 register FILE *iop;
759{
760 register c;
df2f99b4 761 register char *cs;
25d264e2
SL
762
763 cs = s;
b7cc6a20 764/* tmpline may contain saved command from urgent mode interruption */
07fffe50
GM
765 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
766 *cs++ = tmpline[c];
767 if (tmpline[c] == '\n') {
768 *cs++ = '\0';
882508af
MK
769 if (debug)
770 syslog(LOG_DEBUG, "command: %s", s);
07fffe50
GM
771 tmpline[0] = '\0';
772 return(s);
773 }
882508af 774 if (c == 0)
07fffe50 775 tmpline[0] = '\0';
07fffe50 776 }
882508af
MK
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) {
b7cc6a20
GM
783 case WILL:
784 case WONT:
5256bbac 785 c = getc(iop);
882508af 786 printf("%c%c%c", IAC, DONT, 0377&c);
b7cc6a20 787 (void) fflush(stdout);
882508af 788 continue;
b7cc6a20
GM
789 case DO:
790 case DONT:
5256bbac 791 c = getc(iop);
882508af 792 printf("%c%c%c", IAC, WONT, 0377&c);
b7cc6a20 793 (void) fflush(stdout);
882508af
MK
794 continue;
795 case IAC:
b7cc6a20
GM
796 break;
797 default:
882508af 798 continue; /* ignore command */
b7cc6a20 799 }
882508af 800 }
25d264e2 801 }
882508af
MK
802 *cs++ = c;
803 if (--n <= 0 || c == '\n')
25d264e2
SL
804 break;
805 }
b7cc6a20 806 if (c == EOF && cs == s)
418345d1 807 return (NULL);
25d264e2 808 *cs++ = '\0';
882508af
MK
809 if (debug)
810 syslog(LOG_DEBUG, "command: %s", s);
25d264e2
SL
811 return (s);
812}
813
5ac6fc46
SL
814static int
815toolong()
816{
df2f99b4 817 time_t now;
5ac6fc46 818 extern char *ctime();
df2f99b4 819 extern time_t time();
5ac6fc46
SL
820
821 reply(421,
822 "Timeout (%d seconds): closing control connection.", timeout);
df2f99b4 823 (void) time(&now);
5ac6fc46 824 if (logging) {
df2f99b4 825 syslog(LOG_INFO,
882508af 826 "User %s timed out after %d seconds at %s",
5ac6fc46 827 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
5ac6fc46 828 }
bb16805b 829 dologout(1);
5ac6fc46
SL
830}
831
25d264e2
SL
832yylex()
833{
25d264e2 834 static int cpos, state;
fdb56acd 835 register char *cp, *cp2;
25d264e2
SL
836 register struct tab *p;
837 int n;
5256bbac 838 char c, *strpbrk();
fdb56acd 839 char *copy();
25d264e2
SL
840
841 for (;;) {
842 switch (state) {
843
844 case CMD:
df2f99b4
GM
845 (void) signal(SIGALRM, toolong);
846 (void) alarm((unsigned) timeout);
25d264e2
SL
847 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
848 reply(221, "You could at least say goodbye.");
bb16805b 849 dologout(0);
25d264e2 850 }
df2f99b4 851 (void) alarm(0);
843d1a1c 852#ifdef SETPROCTITLE
fdb56acd
MK
853 if (strncasecmp(cbuf, "PASS", 4) != NULL)
854 setproctitle("%s: %s", proctitle, cbuf);
843d1a1c 855#endif /* SETPROCTITLE */
02b12178
KB
856 if ((cp = index(cbuf, '\r'))) {
857 *cp++ = '\n';
858 *cp = '\0';
859 }
5256bbac
KB
860 if ((cp = strpbrk(cbuf, " \n")))
861 cpos = cp - cbuf;
882508af 862 if (cpos == 0)
25d264e2
SL
863 cpos = 4;
864 c = cbuf[cpos];
865 cbuf[cpos] = '\0';
866 upper(cbuf);
fdb56acd 867 p = lookup(cmdtab, cbuf);
25d264e2
SL
868 cbuf[cpos] = c;
869 if (p != 0) {
870 if (p->implemented == 0) {
871 nack(p->name);
df2f99b4 872 longjmp(errcatch,0);
25d264e2
SL
873 /* NOTREACHED */
874 }
875 state = p->state;
fdb56acd 876 *(char **)&yylval = p->name;
25d264e2
SL
877 return (p->token);
878 }
879 break;
880
fdb56acd
MK
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
25d264e2
SL
908 case OSTR:
909 if (cbuf[cpos] == '\n') {
910 state = CMD;
911 return (CRLF);
912 }
9103ec12 913 /* FALLTHROUGH */
25d264e2
SL
914
915 case STR1:
882508af 916 case ZSTR1:
fdb56acd 917 dostr1:
25d264e2
SL
918 if (cbuf[cpos] == ' ') {
919 cpos++;
9103ec12 920 state = state == OSTR ? STR2 : ++state;
25d264e2
SL
921 return (SP);
922 }
923 break;
924
882508af
MK
925 case ZSTR2:
926 if (cbuf[cpos] == '\n') {
927 state = CMD;
928 return (CRLF);
929 }
fdb56acd 930 /* FALLTHROUGH */
882508af 931
25d264e2
SL
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';
fdb56acd 941 *(char **)&yylval = copy(cp);
25d264e2
SL
942 cbuf[cpos] = '\n';
943 state = ARGS;
944 return (STRING);
945 }
946 break;
947
fdb56acd
MK
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
25d264e2
SL
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 }
df2f99b4 1044 yyerror((char *) 0);
25d264e2 1045 state = CMD;
df2f99b4 1046 longjmp(errcatch,0);
25d264e2
SL
1047 }
1048}
1049
1050upper(s)
5256bbac 1051 register char *s;
25d264e2
SL
1052{
1053 while (*s != '\0') {
1054 if (islower(*s))
1055 *s = toupper(*s);
1056 s++;
1057 }
1058}
1059
fdb56acd 1060char *
25d264e2
SL
1061copy(s)
1062 char *s;
1063{
1064 char *p;
df2f99b4 1065 extern char *malloc(), *strcpy();
25d264e2 1066
df2f99b4 1067 p = malloc((unsigned) strlen(s) + 1);
25d264e2
SL
1068 if (p == NULL)
1069 fatal("Ran out of memory.");
df2f99b4 1070 (void) strcpy(p, s);
fdb56acd 1071 return (p);
25d264e2
SL
1072}
1073
fdb56acd
MK
1074help(ctab, s)
1075 struct tab *ctab;
25d264e2
SL
1076 char *s;
1077{
1078 register struct tab *c;
1079 register int width, NCMDS;
fdb56acd 1080 char *type;
25d264e2 1081
fdb56acd
MK
1082 if (ctab == sitetab)
1083 type = "SITE ";
1084 else
1085 type = "";
25d264e2 1086 width = 0, NCMDS = 0;
fdb56acd
MK
1087 for (c = ctab; c->name != NULL; c++) {
1088 int len = strlen(c->name);
25d264e2 1089
25d264e2
SL
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
fdb56acd
MK
1099 lreply(214, "The following %scommands are recognized %s.",
1100 type, "(* =>'s unimplemented)");
25d264e2
SL
1101 columns = 76 / width;
1102 if (columns == 0)
1103 columns = 1;
1104 lines = (NCMDS + columns - 1) / columns;
1105 for (i = 0; i < lines; i++) {
7ccaf1e3 1106 printf(" ");
25d264e2 1107 for (j = 0; j < columns; j++) {
fdb56acd 1108 c = ctab + j * lines + i;
25d264e2
SL
1109 printf("%s%c", c->name,
1110 c->implemented ? ' ' : '*');
fdb56acd 1111 if (c + lines >= &ctab[NCMDS])
25d264e2 1112 break;
30ed6738 1113 w = strlen(c->name) + 1;
25d264e2
SL
1114 while (w < width) {
1115 putchar(' ');
1116 w++;
1117 }
1118 }
1119 printf("\r\n");
1120 }
df2f99b4 1121 (void) fflush(stdout);
25d264e2
SL
1122 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1123 return;
1124 }
1125 upper(s);
fdb56acd 1126 c = lookup(ctab, s);
25d264e2 1127 if (c == (struct tab *)0) {
7ccaf1e3 1128 reply(502, "Unknown command %s.", s);
25d264e2
SL
1129 return;
1130 }
1131 if (c->implemented)
fdb56acd 1132 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
25d264e2 1133 else
fdb56acd
MK
1134 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1135 c->name, c->help);
1136}
1137
1138sizecmd(filename)
1139char *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 }
25d264e2 1180}