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