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