]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - crypto/heimdal/appl/ftp/ftpd/ftpcmd.y
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / crypto / heimdal / appl / ftp / ftpd / ftpcmd.y
1 /*      $NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $       */
2
3 /*
4  * Copyright (c) 1985, 1988, 1993, 1994
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by the University of
18  *      California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *      @(#)ftpcmd.y    8.3 (Berkeley) 4/6/94
36  */
37
38 /*
39  * Grammar for FTP commands.
40  * See RFC 959.
41  */
42
43 %{
44
45 #include "ftpd_locl.h"
46 RCSID("$Id: ftpcmd.y,v 1.61.10.2 2004/08/20 15:15:46 lha Exp $");
47
48 off_t   restart_point;
49
50 static  int hasyyerrored;
51
52
53 static  int cmd_type;
54 static  int cmd_form;
55 static  int cmd_bytesz;
56 char    cbuf[64*1024];
57 char    *fromname;
58
59 struct tab {
60         char    *name;
61         short   token;
62         short   state;
63         short   implemented;    /* 1 if command is implemented */
64         char    *help;
65 };
66
67 extern struct tab cmdtab[];
68 extern struct tab sitetab[];
69
70 static char             *copy (char *);
71 static void              help (struct tab *, char *);
72 static struct tab *
73                          lookup (struct tab *, char *);
74 static void              sizecmd (char *);
75 static RETSIGTYPE        toolong (int);
76 static int               yylex (void);
77
78 /* This is for bison */
79
80 #if !defined(alloca) && !defined(HAVE_ALLOCA)
81 #define alloca(x) malloc(x)
82 #endif
83
84 %}
85
86 %union {
87         int     i;
88         char   *s;
89 }
90
91 %token
92         A       B       C       E       F       I
93         L       N       P       R       S       T
94
95         SP      CRLF    COMMA
96
97         USER    PASS    ACCT    REIN    QUIT    PORT
98         PASV    TYPE    STRU    MODE    RETR    STOR
99         APPE    MLFL    MAIL    MSND    MSOM    MSAM
100         MRSQ    MRCP    ALLO    REST    RNFR    RNTO
101         ABOR    DELE    CWD     LIST    NLST    SITE
102         sTAT    HELP    NOOP    MKD     RMD     PWD
103         CDUP    STOU    SMNT    SYST    SIZE    MDTM
104         EPRT    EPSV
105
106         UMASK   IDLE    CHMOD
107
108         AUTH    ADAT    PROT    PBSZ    CCC     MIC
109         CONF    ENC
110
111         KAUTH   KLIST   KDESTROY KRBTKFILE AFSLOG
112         LOCATE  URL
113
114         FEAT    OPTS
115
116         LEXERR
117
118 %token  <s> STRING
119 %token  <i> NUMBER
120
121 %type   <i> check_login check_login_no_guest check_secure octal_number byte_size
122 %type   <i> struct_code mode_code type_code form_code
123 %type   <s> pathstring pathname password username
124
125 %start  cmd_list
126
127 %%
128
129 cmd_list
130         : /* empty */
131         | cmd_list cmd
132                 {
133                         fromname = (char *) 0;
134                         restart_point = (off_t) 0;
135                 }
136         | cmd_list rcmd
137         ;
138
139 cmd
140         : USER SP username CRLF
141                 {
142                         user($3);
143                         free($3);
144                 }
145         | PASS SP password CRLF
146                 {
147                         pass($3);
148                         memset ($3, 0, strlen($3));
149                         free($3);
150                 }
151         | PORT SP host_port CRLF
152                 {
153                         usedefault = 0;
154                         if (pdata >= 0) {
155                                 close(pdata);
156                                 pdata = -1;
157                         }
158                         reply(200, "PORT command successful.");
159                 }
160         | EPRT SP STRING CRLF
161                 {
162                         eprt ($3);
163                         free ($3);
164                 }
165         | PASV CRLF check_login
166                 {
167                     if($3)
168                         pasv ();
169                 }
170         | EPSV CRLF check_login
171                 {
172                     if($3)
173                         epsv (NULL);
174                 }
175         | EPSV SP STRING CRLF check_login
176                 {
177                     if($5)
178                         epsv ($3);
179                     free ($3);
180                 }
181         | TYPE SP type_code CRLF
182                 {
183                         switch (cmd_type) {
184
185                         case TYPE_A:
186                                 if (cmd_form == FORM_N) {
187                                         reply(200, "Type set to A.");
188                                         type = cmd_type;
189                                         form = cmd_form;
190                                 } else
191                                         reply(504, "Form must be N.");
192                                 break;
193
194                         case TYPE_E:
195                                 reply(504, "Type E not implemented.");
196                                 break;
197
198                         case TYPE_I:
199                                 reply(200, "Type set to I.");
200                                 type = cmd_type;
201                                 break;
202
203                         case TYPE_L:
204 #if NBBY == 8
205                                 if (cmd_bytesz == 8) {
206                                         reply(200,
207                                             "Type set to L (byte size 8).");
208                                         type = cmd_type;
209                                 } else
210                                         reply(504, "Byte size must be 8.");
211 #else /* NBBY == 8 */
212                                 UNIMPLEMENTED for NBBY != 8
213 #endif /* NBBY == 8 */
214                         }
215                 }
216         | STRU SP struct_code CRLF
217                 {
218                         switch ($3) {
219
220                         case STRU_F:
221                                 reply(200, "STRU F ok.");
222                                 break;
223
224                         default:
225                                 reply(504, "Unimplemented STRU type.");
226                         }
227                 }
228         | MODE SP mode_code CRLF
229                 {
230                         switch ($3) {
231
232                         case MODE_S:
233                                 reply(200, "MODE S ok.");
234                                 break;
235
236                         default:
237                                 reply(502, "Unimplemented MODE type.");
238                         }
239                 }
240         | ALLO SP NUMBER CRLF
241                 {
242                         reply(202, "ALLO command ignored.");
243                 }
244         | ALLO SP NUMBER SP R SP NUMBER CRLF
245                 {
246                         reply(202, "ALLO command ignored.");
247                 }
248         | RETR SP pathname CRLF check_login
249                 {
250                         char *name = $3;
251
252                         if ($5 && name != NULL)
253                                 retrieve(0, name);
254                         if (name != NULL)
255                                 free(name);
256                 }
257         | STOR SP pathname CRLF check_login
258                 {
259                         char *name = $3;
260
261                         if ($5 && name != NULL)
262                                 do_store(name, "w", 0);
263                         if (name != NULL)
264                                 free(name);
265                 }
266         | APPE SP pathname CRLF check_login
267                 {
268                         char *name = $3;
269
270                         if ($5 && name != NULL)
271                                 do_store(name, "a", 0);
272                         if (name != NULL)
273                                 free(name);
274                 }
275         | NLST CRLF check_login
276                 {
277                         if ($3)
278                                 send_file_list(".");
279                 }
280         | NLST SP STRING CRLF check_login
281                 {
282                         char *name = $3;
283
284                         if ($5 && name != NULL)
285                                 send_file_list(name);
286                         if (name != NULL)
287                                 free(name);
288                 }
289         | LIST CRLF check_login
290                 {
291                     if($3)
292                         list_file(".");
293                 }
294         | LIST SP pathname CRLF check_login
295                 {
296                     if($5)
297                         list_file($3);
298                     free($3);
299                 }
300         | sTAT SP pathname CRLF check_login
301                 {
302                         if ($5 && $3 != NULL)
303                                 statfilecmd($3);
304                         if ($3 != NULL)
305                                 free($3);
306                 }
307         | sTAT CRLF
308                 {
309                         statcmd();
310         }
311         | DELE SP pathname CRLF check_login_no_guest
312                 {
313                         if ($5 && $3 != NULL)
314                                 do_delete($3);
315                         if ($3 != NULL)
316                                 free($3);
317                 }
318         | RNTO SP pathname CRLF check_login_no_guest
319                 {
320                         if($5){
321                                 if (fromname) {
322                                         renamecmd(fromname, $3);
323                                         free(fromname);
324                                         fromname = (char *) 0;
325                                 } else {
326                                         reply(503, "Bad sequence of commands.");
327                                 }
328                         }
329                         if ($3 != NULL)
330                                 free($3);
331                 }
332         | ABOR CRLF
333                 {
334                         reply(225, "ABOR command successful.");
335                 }
336         | CWD CRLF check_login
337                 {
338                         if ($3)
339                                 cwd(pw->pw_dir);
340                 }
341         | CWD SP pathname CRLF check_login
342                 {
343                         if ($5 && $3 != NULL)
344                                 cwd($3);
345                         if ($3 != NULL)
346                                 free($3);
347                 }
348         | HELP CRLF
349                 {
350                         help(cmdtab, (char *) 0);
351                 }
352         | HELP SP STRING CRLF
353                 {
354                         char *cp = $3;
355
356                         if (strncasecmp(cp, "SITE", 4) == 0) {
357                                 cp = $3 + 4;
358                                 if (*cp == ' ')
359                                         cp++;
360                                 if (*cp)
361                                         help(sitetab, cp);
362                                 else
363                                         help(sitetab, (char *) 0);
364                         } else
365                                 help(cmdtab, $3);
366                 }
367         | NOOP CRLF
368                 {
369                         reply(200, "NOOP command successful.");
370                 }
371         | MKD SP pathname CRLF check_login
372                 {
373                         if ($5 && $3 != NULL)
374                                 makedir($3);
375                         if ($3 != NULL)
376                                 free($3);
377                 }
378         | RMD SP pathname CRLF check_login_no_guest
379                 {
380                         if ($5 && $3 != NULL)
381                                 removedir($3);
382                         if ($3 != NULL)
383                                 free($3);
384                 }
385         | PWD CRLF check_login
386                 {
387                         if ($3)
388                                 pwd();
389                 }
390         | CDUP CRLF check_login
391                 {
392                         if ($3)
393                                 cwd("..");
394                 }
395         | FEAT CRLF
396                 {
397                         lreply(211, "Supported features:");
398                         lreply(0, " MDTM");
399                         lreply(0, " REST STREAM");
400                         lreply(0, " SIZE");
401                         reply(211, "End");
402                 }
403         | OPTS SP STRING CRLF
404                 {
405                         free ($3);
406                         reply(501, "Bad options");
407                 }
408
409         | SITE SP HELP CRLF
410                 {
411                         help(sitetab, (char *) 0);
412                 }
413         | SITE SP HELP SP STRING CRLF
414                 {
415                         help(sitetab, $5);
416                 }
417         | SITE SP UMASK CRLF check_login
418                 {
419                         if ($5) {
420                                 int oldmask = umask(0);
421                                 umask(oldmask);
422                                 reply(200, "Current UMASK is %03o", oldmask);
423                         }
424                 }
425         | SITE SP UMASK SP octal_number CRLF check_login_no_guest
426                 {
427                         if ($7) {
428                                 if (($5 == -1) || ($5 > 0777)) {
429                                         reply(501, "Bad UMASK value");
430                                 } else {
431                                         int oldmask = umask($5);
432                                         reply(200,
433                                               "UMASK set to %03o (was %03o)",
434                                               $5, oldmask);
435                                 }
436                         }
437                 }
438         | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
439                 {
440                         if ($9 && $7 != NULL) {
441                                 if ($5 > 0777)
442                                         reply(501,
443                                 "CHMOD: Mode value must be between 0 and 0777");
444                                 else if (chmod($7, $5) < 0)
445                                         perror_reply(550, $7);
446                                 else
447                                         reply(200, "CHMOD command successful.");
448                         }
449                         if ($7 != NULL)
450                                 free($7);
451                 }
452         | SITE SP IDLE CRLF
453                 {
454                         reply(200,
455                             "Current IDLE time limit is %d seconds; max %d",
456                                 ftpd_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                                 ftpd_timeout = $5;
466                                 alarm((unsigned) ftpd_timeout);
467                                 reply(200,
468                                     "Maximum IDLE time set to %d seconds",
469                                     ftpd_timeout);
470                         }
471                 }
472
473         | SITE SP KAUTH SP STRING CRLF check_login
474                 {
475 #ifdef KRB4
476                         char *p;
477                         
478                         if(guest)
479                                 reply(500, "Can't be done as guest.");
480                         else{
481                                 if($7 && $5 != NULL){
482                                     p = strpbrk($5, " \t");
483                                     if(p){
484                                         *p++ = 0;
485                                         kauth($5, p + strspn(p, " \t"));
486                                     }else
487                                         kauth($5, NULL);
488                                 }
489                         }
490                         if($5 != NULL)
491                             free($5);
492 #else
493                         reply(500, "Command not implemented.");
494 #endif
495                 }
496         | SITE SP KLIST CRLF check_login
497                 {
498 #ifdef KRB4
499                     if($5)
500                         klist();
501 #else
502                     reply(500, "Command not implemented.");
503 #endif
504                 }
505         | SITE SP KDESTROY CRLF check_login
506                 {
507 #ifdef KRB4
508                     if($5)
509                         kdestroy();
510 #else
511                     reply(500, "Command not implemented.");
512 #endif
513                 }
514         | SITE SP KRBTKFILE SP STRING CRLF check_login
515                 {
516 #ifdef KRB4
517                     if(guest)
518                         reply(500, "Can't be done as guest.");
519                     else if($7 && $5)
520                         krbtkfile($5);
521                     if($5)
522                         free($5);
523 #else
524                     reply(500, "Command not implemented.");
525 #endif
526                 }
527         | SITE SP AFSLOG CRLF check_login
528                 {
529 #ifdef KRB4
530                     if(guest)
531                         reply(500, "Can't be done as guest.");
532                     else if($5)
533                         afslog(NULL);
534 #else
535                     reply(500, "Command not implemented.");
536 #endif
537                 }
538         | SITE SP AFSLOG SP STRING CRLF check_login
539                 {
540 #ifdef KRB4
541                     if(guest)
542                         reply(500, "Can't be done as guest.");
543                     else if($7)
544                         afslog($5);
545                     if($5)
546                         free($5);
547 #else
548                     reply(500, "Command not implemented.");
549 #endif
550                 }
551         | SITE SP LOCATE SP STRING CRLF check_login
552                 {
553                     if($7 && $5 != NULL)
554                         find($5);
555                     if($5 != NULL)
556                         free($5);
557                 }
558         | SITE SP URL CRLF
559                 {
560                         reply(200, "http://www.pdc.kth.se/kth-krb/");
561                 }
562         | STOU SP pathname CRLF check_login
563                 {
564                         if ($5 && $3 != NULL)
565                                 do_store($3, "w", 1);
566                         if ($3 != NULL)
567                                 free($3);
568                 }
569         | SYST CRLF
570                 {
571 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
572                     reply(215, "UNIX Type: L%d", NBBY);
573 #else
574                     reply(215, "UNKNOWN Type: L%d", NBBY);
575 #endif
576                 }
577
578                 /*
579                  * SIZE is not in RFC959, but Postel has blessed it and
580                  * it will be in the updated RFC.
581                  *
582                  * Return size of file in a format suitable for
583                  * using with RESTART (we just count bytes).
584                  */
585         | SIZE SP pathname CRLF check_login
586                 {
587                         if ($5 && $3 != NULL)
588                                 sizecmd($3);
589                         if ($3 != NULL)
590                                 free($3);
591                 }
592
593                 /*
594                  * MDTM is not in RFC959, but Postel has blessed it and
595                  * it will be in the updated RFC.
596                  *
597                  * Return modification time of file as an ISO 3307
598                  * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
599                  * where xxx is the fractional second (of any precision,
600                  * not necessarily 3 digits)
601                  */
602         | MDTM SP pathname CRLF check_login
603                 {
604                         if ($5 && $3 != NULL) {
605                                 struct stat stbuf;
606                                 if (stat($3, &stbuf) < 0)
607                                         reply(550, "%s: %s",
608                                             $3, strerror(errno));
609                                 else if (!S_ISREG(stbuf.st_mode)) {
610                                         reply(550,
611                                               "%s: not a plain file.", $3);
612                                 } else {
613                                         struct tm *t;
614                                         time_t mtime = stbuf.st_mtime;
615
616                                         t = gmtime(&mtime);
617                                         reply(213,
618                                               "%04d%02d%02d%02d%02d%02d",
619                                               t->tm_year + 1900,
620                                               t->tm_mon + 1,
621                                               t->tm_mday,
622                                               t->tm_hour,
623                                               t->tm_min,
624                                               t->tm_sec);
625                                 }
626                         }
627                         if ($3 != NULL)
628                                 free($3);
629                 }
630         | QUIT CRLF
631                 {
632                         reply(221, "Goodbye.");
633                         dologout(0);
634                 }
635         | error CRLF
636                 {
637                         yyerrok;
638                 }
639         ;
640 rcmd
641         : RNFR SP pathname CRLF check_login_no_guest
642                 {
643                         restart_point = (off_t) 0;
644                         if ($5 && $3) {
645                                 fromname = renamefrom($3);
646                                 if (fromname == (char *) 0 && $3) {
647                                         free($3);
648                                 }
649                         }
650                 }
651         | REST SP byte_size CRLF
652                 {
653                         fromname = (char *) 0;
654                         restart_point = $3;     /* XXX $3 is only "int" */
655                         reply(350, "Restarting at %ld. %s",
656                               (long)restart_point,
657                               "Send STORE or RETRIEVE to initiate transfer.");
658                 }
659         | AUTH SP STRING CRLF
660                 {
661                         auth($3);
662                         free($3);
663                 }
664         | ADAT SP STRING CRLF
665                 {
666                         adat($3);
667                         free($3);
668                 }
669         | PBSZ SP NUMBER CRLF
670                 {
671                         pbsz($3);
672                 }
673         | PROT SP STRING CRLF
674                 {
675                         prot($3);
676                 }
677         | CCC CRLF
678                 {
679                         ccc();
680                 }
681         | MIC SP STRING CRLF
682                 {
683                         mec($3, prot_safe);
684                         free($3);
685                 }
686         | CONF SP STRING CRLF
687                 {
688                         mec($3, prot_confidential);
689                         free($3);
690                 }
691         | ENC SP STRING CRLF
692                 {
693                         mec($3, prot_private);
694                         free($3);
695                 }
696         ;
697
698 username
699         : STRING
700         ;
701
702 password
703         : /* empty */
704                 {
705                         $$ = (char *)calloc(1, sizeof(char));
706                 }
707         | STRING
708         ;
709
710 byte_size
711         : NUMBER
712         ;
713
714 host_port
715         : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
716                 NUMBER COMMA NUMBER
717                 {
718                         struct sockaddr_in *sin = (struct sockaddr_in *)data_dest;
719
720                         sin->sin_family = AF_INET;
721                         sin->sin_port = htons($9 * 256 + $11);
722                         sin->sin_addr.s_addr = 
723                             htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
724                 }
725         ;
726
727 form_code
728         : N
729                 {
730                         $$ = FORM_N;
731                 }
732         | T
733                 {
734                         $$ = FORM_T;
735                 }
736         | C
737                 {
738                         $$ = FORM_C;
739                 }
740         ;
741
742 type_code
743         : A
744                 {
745                         cmd_type = TYPE_A;
746                         cmd_form = FORM_N;
747                 }
748         | A SP form_code
749                 {
750                         cmd_type = TYPE_A;
751                         cmd_form = $3;
752                 }
753         | E
754                 {
755                         cmd_type = TYPE_E;
756                         cmd_form = FORM_N;
757                 }
758         | E SP form_code
759                 {
760                         cmd_type = TYPE_E;
761                         cmd_form = $3;
762                 }
763         | I
764                 {
765                         cmd_type = TYPE_I;
766                 }
767         | L
768                 {
769                         cmd_type = TYPE_L;
770                         cmd_bytesz = NBBY;
771                 }
772         | L SP byte_size
773                 {
774                         cmd_type = TYPE_L;
775                         cmd_bytesz = $3;
776                 }
777                 /* this is for a bug in the BBN ftp */
778         | L byte_size
779                 {
780                         cmd_type = TYPE_L;
781                         cmd_bytesz = $2;
782                 }
783         ;
784
785 struct_code
786         : F
787                 {
788                         $$ = STRU_F;
789                 }
790         | R
791                 {
792                         $$ = STRU_R;
793                 }
794         | P
795                 {
796                         $$ = STRU_P;
797                 }
798         ;
799
800 mode_code
801         : S
802                 {
803                         $$ = MODE_S;
804                 }
805         | B
806                 {
807                         $$ = MODE_B;
808                 }
809         | C
810                 {
811                         $$ = MODE_C;
812                 }
813         ;
814
815 pathname
816         : pathstring
817                 {
818                         /*
819                          * Problem: this production is used for all pathname
820                          * processing, but only gives a 550 error reply.
821                          * This is a valid reply in some cases but not in others.
822                          */
823                         if (logged_in && $1 && *$1 == '~') {
824                                 glob_t gl;
825                                 int flags =
826                                  GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
827
828                                 memset(&gl, 0, sizeof(gl));
829                                 if (glob($1, flags, NULL, &gl) ||
830                                     gl.gl_pathc == 0) {
831                                         reply(550, "not found");
832                                         $$ = NULL;
833                                 } else {
834                                         $$ = strdup(gl.gl_pathv[0]);
835                                 }
836                                 globfree(&gl);
837                                 free($1);
838                         } else
839                                 $$ = $1;
840                 }
841         ;
842
843 pathstring
844         : STRING
845         ;
846
847 octal_number
848         : NUMBER
849                 {
850                         int ret, dec, multby, digit;
851
852                         /*
853                          * Convert a number that was read as decimal number
854                          * to what it would be if it had been read as octal.
855                          */
856                         dec = $1;
857                         multby = 1;
858                         ret = 0;
859                         while (dec) {
860                                 digit = dec%10;
861                                 if (digit > 7) {
862                                         ret = -1;
863                                         break;
864                                 }
865                                 ret += digit * multby;
866                                 multby *= 8;
867                                 dec /= 10;
868                         }
869                         $$ = ret;
870                 }
871         ;
872
873
874 check_login_no_guest : check_login
875                 {
876                         $$ = $1 && !guest;
877                         if($1 && !$$)
878                                 reply(550, "Permission denied");
879                 }
880         ;
881
882 check_login : check_secure
883                 {
884                     if($1) {
885                         if(($$ = logged_in) == 0)
886                             reply(530, "Please login with USER and PASS.");
887                     } else
888                         $$ = 0;
889                 }
890         ;
891
892 check_secure : /* empty */
893                 {
894                     $$ = 1;
895                     if(sec_complete && !secure_command()) {
896                         $$ = 0;
897                         reply(533, "Command protection level denied "
898                               "for paranoid reasons.");
899                     }
900                 }
901         ;
902
903 %%
904
905 #define CMD     0       /* beginning of command */
906 #define ARGS    1       /* expect miscellaneous arguments */
907 #define STR1    2       /* expect SP followed by STRING */
908 #define STR2    3       /* expect STRING */
909 #define OSTR    4       /* optional SP then STRING */
910 #define ZSTR1   5       /* SP then optional STRING */
911 #define ZSTR2   6       /* optional STRING after SP */
912 #define SITECMD 7       /* SITE command */
913 #define NSTR    8       /* Number followed by a string */
914
915 struct tab cmdtab[] = {         /* In order defined in RFC 765 */
916         { "USER", USER, STR1, 1,        "<sp> username" },
917         { "PASS", PASS, ZSTR1, 1,       "<sp> password" },
918         { "ACCT", ACCT, STR1, 0,        "(specify account)" },
919         { "SMNT", SMNT, ARGS, 0,        "(structure mount)" },
920         { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
921         { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
922         { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4" },
923         { "EPRT", EPRT, STR1, 1,        "<sp> string" },
924         { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
925         { "EPSV", EPSV, OSTR, 1,        "[<sp> foo]" },
926         { "TYPE", TYPE, ARGS, 1,        "<sp> [ A | E | I | L ]" },
927         { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
928         { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
929         { "RETR", RETR, STR1, 1,        "<sp> file-name" },
930         { "STOR", STOR, STR1, 1,        "<sp> file-name" },
931         { "APPE", APPE, STR1, 1,        "<sp> file-name" },
932         { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
933         { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
934         { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
935         { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
936         { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
937         { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
938         { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
939         { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
940         { "REST", REST, ARGS, 1,        "<sp> offset (restart command)" },
941         { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
942         { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
943         { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
944         { "DELE", DELE, STR1, 1,        "<sp> file-name" },
945         { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
946         { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
947         { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
948         { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
949         { "SITE", SITE, SITECMD, 1,     "site-cmd [ <sp> arguments ]" },
950         { "SYST", SYST, ARGS, 1,        "(get type of operating system)" },
951         { "STAT", sTAT, OSTR, 1,        "[ <sp> path-name ]" },
952         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
953         { "NOOP", NOOP, ARGS, 1,        "" },
954         { "MKD",  MKD,  STR1, 1,        "<sp> path-name" },
955         { "XMKD", MKD,  STR1, 1,        "<sp> path-name" },
956         { "RMD",  RMD,  STR1, 1,        "<sp> path-name" },
957         { "XRMD", RMD,  STR1, 1,        "<sp> path-name" },
958         { "PWD",  PWD,  ARGS, 1,        "(return current directory)" },
959         { "XPWD", PWD,  ARGS, 1,        "(return current directory)" },
960         { "CDUP", CDUP, ARGS, 1,        "(change to parent directory)" },
961         { "XCUP", CDUP, ARGS, 1,        "(change to parent directory)" },
962         { "STOU", STOU, STR1, 1,        "<sp> file-name" },
963         { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
964         { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
965
966         /* extensions from RFC2228 */
967         { "AUTH", AUTH, STR1, 1,        "<sp> auth-type" },
968         { "ADAT", ADAT, STR1, 1,        "<sp> auth-data" },
969         { "PBSZ", PBSZ, ARGS, 1,        "<sp> buffer-size" },
970         { "PROT", PROT, STR1, 1,        "<sp> prot-level" },
971         { "CCC",  CCC,  ARGS, 1,        "" },
972         { "MIC",  MIC,  STR1, 1,        "<sp> integrity command" },
973         { "CONF", CONF, STR1, 1,        "<sp> confidentiality command" },
974         { "ENC",  ENC,  STR1, 1,        "<sp> privacy command" },
975
976         /* RFC2389 */
977         { "FEAT", FEAT, ARGS, 1,        "" },
978         { "OPTS", OPTS, ARGS, 1,        "<sp> command [<sp> options]" },
979
980         { NULL,   0,    0,    0,        0 }
981 };
982
983 struct tab sitetab[] = {
984         { "UMASK", UMASK, ARGS, 1,      "[ <sp> umask ]" },
985         { "IDLE", IDLE, ARGS, 1,        "[ <sp> maximum-idle-time ]" },
986         { "CHMOD", CHMOD, NSTR, 1,      "<sp> mode <sp> file-name" },
987         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
988
989         { "KAUTH", KAUTH, STR1, 1,      "<sp> principal [ <sp> ticket ]" },
990         { "KLIST", KLIST, ARGS, 1,      "(show ticket file)" },
991         { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
992         { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
993         { "AFSLOG", AFSLOG, OSTR, 1,    "[<sp> cell]" },
994
995         { "LOCATE", LOCATE, STR1, 1,    "<sp> globexpr" },
996         { "FIND", LOCATE, STR1, 1,      "<sp> globexpr" },
997
998         { "URL",  URL,  ARGS, 1,        "?" },
999         
1000         { NULL,   0,    0,    0,        0 }
1001 };
1002
1003 static struct tab *
1004 lookup(struct tab *p, char *cmd)
1005 {
1006
1007         for (; p->name != NULL; p++)
1008                 if (strcmp(cmd, p->name) == 0)
1009                         return (p);
1010         return (0);
1011 }
1012
1013 /*
1014  * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1015  */
1016 char *
1017 ftpd_getline(char *s, int n)
1018 {
1019         int c;
1020         char *cs;
1021
1022         cs = s;
1023
1024         /* might still be data within the security MIC/CONF/ENC */
1025         if(ftp_command){
1026             strlcpy(s, ftp_command, n);
1027             if (debug)
1028                 syslog(LOG_DEBUG, "command: %s", s);
1029             return s;
1030         }
1031         while ((c = getc(stdin)) != EOF) {
1032                 c &= 0377;
1033                 if (c == IAC) {
1034                     if ((c = getc(stdin)) != EOF) {
1035                         c &= 0377;
1036                         switch (c) {
1037                         case WILL:
1038                         case WONT:
1039                                 c = getc(stdin);
1040                                 printf("%c%c%c", IAC, DONT, 0377&c);
1041                                 fflush(stdout);
1042                                 continue;
1043                         case DO:
1044                         case DONT:
1045                                 c = getc(stdin);
1046                                 printf("%c%c%c", IAC, WONT, 0377&c);
1047                                 fflush(stdout);
1048                                 continue;
1049                         case IAC:
1050                                 break;
1051                         default:
1052                                 continue;       /* ignore command */
1053                         }
1054                     }
1055                 }
1056                 *cs++ = c;
1057                 if (--n <= 0 || c == '\n')
1058                         break;
1059         }
1060         if (c == EOF && cs == s)
1061                 return (NULL);
1062         *cs++ = '\0';
1063         if (debug) {
1064                 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1065                         /* Don't syslog passwords */
1066                         syslog(LOG_DEBUG, "command: %.5s ???", s);
1067                 } else {
1068                         char *cp;
1069                         int len;
1070
1071                         /* Don't syslog trailing CR-LF */
1072                         len = strlen(s);
1073                         cp = s + len - 1;
1074                         while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1075                                 --cp;
1076                                 --len;
1077                         }
1078                         syslog(LOG_DEBUG, "command: %.*s", len, s);
1079                 }
1080         }
1081 #ifdef XXX
1082         fprintf(stderr, "%s\n", s);
1083 #endif
1084         return (s);
1085 }
1086
1087 static RETSIGTYPE
1088 toolong(int signo)
1089 {
1090
1091         reply(421,
1092             "Timeout (%d seconds): closing control connection.",
1093               ftpd_timeout);
1094         if (logging)
1095                 syslog(LOG_INFO, "User %s timed out after %d seconds",
1096                     (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1097         dologout(1);
1098         SIGRETURN(0);
1099 }
1100
1101 static int
1102 yylex(void)
1103 {
1104         static int cpos, state;
1105         char *cp, *cp2;
1106         struct tab *p;
1107         int n;
1108         char c;
1109
1110         for (;;) {
1111                 switch (state) {
1112
1113                 case CMD:
1114                         hasyyerrored = 0;
1115
1116                         signal(SIGALRM, toolong);
1117                         alarm((unsigned) ftpd_timeout);
1118                         if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1119                                 reply(221, "You could at least say goodbye.");
1120                                 dologout(0);
1121                         }
1122                         alarm(0);
1123 #ifdef HAVE_SETPROCTITLE
1124                         if (strncasecmp(cbuf, "PASS", 4) != 0)
1125                                 setproctitle("%s: %s", proctitle, cbuf);
1126 #endif /* HAVE_SETPROCTITLE */
1127                         if ((cp = strchr(cbuf, '\r'))) {
1128                                 *cp++ = '\n';
1129                                 *cp = '\0';
1130                         }
1131                         if ((cp = strpbrk(cbuf, " \n")))
1132                                 cpos = cp - cbuf;
1133                         if (cpos == 0)
1134                                 cpos = 4;
1135                         c = cbuf[cpos];
1136                         cbuf[cpos] = '\0';
1137                         strupr(cbuf);
1138                         p = lookup(cmdtab, cbuf);
1139                         cbuf[cpos] = c;
1140                         if (p != 0) {
1141                                 if (p->implemented == 0) {
1142                                         nack(p->name);
1143                                         hasyyerrored = 1;
1144                                         break;
1145                                 }
1146                                 state = p->state;
1147                                 yylval.s = p->name;
1148                                 return (p->token);
1149                         }
1150                         break;
1151
1152                 case SITECMD:
1153                         if (cbuf[cpos] == ' ') {
1154                                 cpos++;
1155                                 return (SP);
1156                         }
1157                         cp = &cbuf[cpos];
1158                         if ((cp2 = strpbrk(cp, " \n")))
1159                                 cpos = cp2 - cbuf;
1160                         c = cbuf[cpos];
1161                         cbuf[cpos] = '\0';
1162                         strupr(cp);
1163                         p = lookup(sitetab, cp);
1164                         cbuf[cpos] = c;
1165                         if (p != 0) {
1166                                 if (p->implemented == 0) {
1167                                         state = CMD;
1168                                         nack(p->name);
1169                                         hasyyerrored = 1;
1170                                         break;
1171                                 }
1172                                 state = p->state;
1173                                 yylval.s = p->name;
1174                                 return (p->token);
1175                         }
1176                         state = CMD;
1177                         break;
1178
1179                 case OSTR:
1180                         if (cbuf[cpos] == '\n') {
1181                                 state = CMD;
1182                                 return (CRLF);
1183                         }
1184                         /* FALLTHROUGH */
1185
1186                 case STR1:
1187                 case ZSTR1:
1188                 dostr1:
1189                         if (cbuf[cpos] == ' ') {
1190                                 cpos++;
1191                                 if(state == OSTR)
1192                                     state = STR2;
1193                                 else
1194                                     state++;
1195                                 return (SP);
1196                         }
1197                         break;
1198
1199                 case ZSTR2:
1200                         if (cbuf[cpos] == '\n') {
1201                                 state = CMD;
1202                                 return (CRLF);
1203                         }
1204                         /* FALLTHROUGH */
1205
1206                 case STR2:
1207                         cp = &cbuf[cpos];
1208                         n = strlen(cp);
1209                         cpos += n - 1;
1210                         /*
1211                          * Make sure the string is nonempty and \n terminated.
1212                          */
1213                         if (n > 1 && cbuf[cpos] == '\n') {
1214                                 cbuf[cpos] = '\0';
1215                                 yylval.s = copy(cp);
1216                                 cbuf[cpos] = '\n';
1217                                 state = ARGS;
1218                                 return (STRING);
1219                         }
1220                         break;
1221
1222                 case NSTR:
1223                         if (cbuf[cpos] == ' ') {
1224                                 cpos++;
1225                                 return (SP);
1226                         }
1227                         if (isdigit((unsigned char)cbuf[cpos])) {
1228                                 cp = &cbuf[cpos];
1229                                 while (isdigit((unsigned char)cbuf[++cpos]))
1230                                         ;
1231                                 c = cbuf[cpos];
1232                                 cbuf[cpos] = '\0';
1233                                 yylval.i = atoi(cp);
1234                                 cbuf[cpos] = c;
1235                                 state = STR1;
1236                                 return (NUMBER);
1237                         }
1238                         state = STR1;
1239                         goto dostr1;
1240
1241                 case ARGS:
1242                         if (isdigit((unsigned char)cbuf[cpos])) {
1243                                 cp = &cbuf[cpos];
1244                                 while (isdigit((unsigned char)cbuf[++cpos]))
1245                                         ;
1246                                 c = cbuf[cpos];
1247                                 cbuf[cpos] = '\0';
1248                                 yylval.i = atoi(cp);
1249                                 cbuf[cpos] = c;
1250                                 return (NUMBER);
1251                         }
1252                         switch (cbuf[cpos++]) {
1253
1254                         case '\n':
1255                                 state = CMD;
1256                                 return (CRLF);
1257
1258                         case ' ':
1259                                 return (SP);
1260
1261                         case ',':
1262                                 return (COMMA);
1263
1264                         case 'A':
1265                         case 'a':
1266                                 return (A);
1267
1268                         case 'B':
1269                         case 'b':
1270                                 return (B);
1271
1272                         case 'C':
1273                         case 'c':
1274                                 return (C);
1275
1276                         case 'E':
1277                         case 'e':
1278                                 return (E);
1279
1280                         case 'F':
1281                         case 'f':
1282                                 return (F);
1283
1284                         case 'I':
1285                         case 'i':
1286                                 return (I);
1287
1288                         case 'L':
1289                         case 'l':
1290                                 return (L);
1291
1292                         case 'N':
1293                         case 'n':
1294                                 return (N);
1295
1296                         case 'P':
1297                         case 'p':
1298                                 return (P);
1299
1300                         case 'R':
1301                         case 'r':
1302                                 return (R);
1303
1304                         case 'S':
1305                         case 's':
1306                                 return (S);
1307
1308                         case 'T':
1309                         case 't':
1310                                 return (T);
1311
1312                         }
1313                         break;
1314
1315                 default:
1316                         fatal("Unknown state in scanner.");
1317                 }
1318                 yyerror(NULL);
1319                 state = CMD;
1320                 return (0);
1321         }
1322 }
1323
1324 /* ARGSUSED */
1325 void
1326 yyerror(char *s)
1327 {
1328         char *cp;
1329
1330         if (hasyyerrored)
1331             return;
1332
1333         if ((cp = strchr(cbuf,'\n')))
1334                 *cp = '\0';
1335         reply(500, "'%s': command not understood.", cbuf);
1336         hasyyerrored = 1;
1337 }
1338
1339 static char *
1340 copy(char *s)
1341 {
1342         char *p;
1343
1344         p = strdup(s);
1345         if (p == NULL)
1346                 fatal("Ran out of memory.");
1347         return p;
1348 }
1349
1350 static void
1351 help(struct tab *ctab, char *s)
1352 {
1353         struct tab *c;
1354         int width, NCMDS;
1355         char *type;
1356         char buf[1024];
1357
1358         if (ctab == sitetab)
1359                 type = "SITE ";
1360         else
1361                 type = "";
1362         width = 0, NCMDS = 0;
1363         for (c = ctab; c->name != NULL; c++) {
1364                 int len = strlen(c->name);
1365
1366                 if (len > width)
1367                         width = len;
1368                 NCMDS++;
1369         }
1370         width = (width + 8) &~ 7;
1371         if (s == 0) {
1372                 int i, j, w;
1373                 int columns, lines;
1374
1375                 lreply(214, "The following %scommands are recognized %s.",
1376                     type, "(* =>'s unimplemented)");
1377                 columns = 76 / width;
1378                 if (columns == 0)
1379                         columns = 1;
1380                 lines = (NCMDS + columns - 1) / columns;
1381                 for (i = 0; i < lines; i++) {
1382                     strlcpy (buf, "   ", sizeof(buf));
1383                     for (j = 0; j < columns; j++) {
1384                         c = ctab + j * lines + i;
1385                         snprintf (buf + strlen(buf),
1386                                   sizeof(buf) - strlen(buf),
1387                                   "%s%c",
1388                                   c->name,
1389                                   c->implemented ? ' ' : '*');
1390                         if (c + lines >= &ctab[NCMDS])
1391                             break;
1392                         w = strlen(c->name) + 1;
1393                         while (w < width) {
1394                             strlcat (buf,
1395                                              " ",
1396                                              sizeof(buf));
1397                             w++;
1398                         }
1399                     }
1400                     lreply(214, "%s", buf);
1401                 }
1402                 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1403                 return;
1404         }
1405         strupr(s);
1406         c = lookup(ctab, s);
1407         if (c == (struct tab *)0) {
1408                 reply(502, "Unknown command %s.", s);
1409                 return;
1410         }
1411         if (c->implemented)
1412                 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1413         else
1414                 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1415                     c->name, c->help);
1416 }
1417
1418 static void
1419 sizecmd(char *filename)
1420 {
1421         switch (type) {
1422         case TYPE_L:
1423         case TYPE_I: {
1424                 struct stat stbuf;
1425                 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1426                         reply(550, "%s: not a plain file.", filename);
1427                 else
1428                         reply(213, "%lu", (unsigned long)stbuf.st_size);
1429                 break;
1430         }
1431         case TYPE_A: {
1432                 FILE *fin;
1433                 int c;
1434                 size_t count;
1435                 struct stat stbuf;
1436                 fin = fopen(filename, "r");
1437                 if (fin == NULL) {
1438                         perror_reply(550, filename);
1439                         return;
1440                 }
1441                 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1442                         reply(550, "%s: not a plain file.", filename);
1443                         fclose(fin);
1444                         return;
1445                 }
1446
1447                 count = 0;
1448                 while((c=getc(fin)) != EOF) {
1449                         if (c == '\n')  /* will get expanded to \r\n */
1450                                 count++;
1451                         count++;
1452                 }
1453                 fclose(fin);
1454
1455                 reply(213, "%lu", (unsigned long)count);
1456                 break;
1457         }
1458         default:
1459                 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1460         }
1461 }