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