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