Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / 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.2.2 2000/06/23 02:48:19 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(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
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                                         time_t mtime = stbuf.st_mtime;
624
625                                         t = gmtime(&mtime);
626                                         reply(213,
627                                               "%04d%02d%02d%02d%02d%02d",
628                                               t->tm_year + 1900,
629                                               t->tm_mon + 1,
630                                               t->tm_mday,
631                                               t->tm_hour,
632                                               t->tm_min,
633                                               t->tm_sec);
634                                 }
635                         }
636                         if ($3 != NULL)
637                                 free($3);
638                 }
639         | QUIT CRLF
640                 {
641                         reply(221, "Goodbye.");
642                         dologout(0);
643                 }
644         | error CRLF
645                 {
646                         yyerrok;
647                 }
648         ;
649 rcmd
650         : RNFR SP pathname CRLF check_login_no_guest
651                 {
652                         restart_point = (off_t) 0;
653                         if ($5 && $3) {
654                                 fromname = renamefrom($3);
655                                 if (fromname == (char *) 0 && $3) {
656                                         free($3);
657                                 }
658                         }
659                 }
660         | REST SP byte_size CRLF
661                 {
662                         fromname = (char *) 0;
663                         restart_point = $3;     /* XXX $3 is only "int" */
664                         reply(350, "Restarting at %ld. %s",
665                               (long)restart_point,
666                               "Send STORE or RETRIEVE to initiate transfer.");
667                 }
668         | AUTH SP STRING CRLF
669                 {
670                         auth($3);
671                         free($3);
672                 }
673         | ADAT SP STRING CRLF
674                 {
675                         adat($3);
676                         free($3);
677                 }
678         | PBSZ SP NUMBER CRLF
679                 {
680                         pbsz($3);
681                 }
682         | PROT SP STRING CRLF
683                 {
684                         prot($3);
685                 }
686         | CCC CRLF
687                 {
688                         ccc();
689                 }
690         | MIC SP STRING CRLF
691                 {
692                         mec($3, prot_safe);
693                         free($3);
694                 }
695         | CONF SP STRING CRLF
696                 {
697                         mec($3, prot_confidential);
698                         free($3);
699                 }
700         | ENC SP STRING CRLF
701                 {
702                         mec($3, prot_private);
703                         free($3);
704                 }
705         ;
706
707 username
708         : STRING
709         ;
710
711 password
712         : /* empty */
713                 {
714                         $$ = (char *)calloc(1, sizeof(char));
715                 }
716         | STRING
717         ;
718
719 byte_size
720         : NUMBER
721         ;
722
723 host_port
724         : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
725                 NUMBER COMMA NUMBER
726                 {
727                         struct sockaddr_in *sin = (struct sockaddr_in *)data_dest;
728
729                         sin->sin_family = AF_INET;
730                         sin->sin_port = htons($9 * 256 + $11);
731                         sin->sin_addr.s_addr = 
732                             htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
733                 }
734         ;
735
736 form_code
737         : N
738                 {
739                         $$ = FORM_N;
740                 }
741         | T
742                 {
743                         $$ = FORM_T;
744                 }
745         | C
746                 {
747                         $$ = FORM_C;
748                 }
749         ;
750
751 type_code
752         : A
753                 {
754                         cmd_type = TYPE_A;
755                         cmd_form = FORM_N;
756                 }
757         | A SP form_code
758                 {
759                         cmd_type = TYPE_A;
760                         cmd_form = $3;
761                 }
762         | E
763                 {
764                         cmd_type = TYPE_E;
765                         cmd_form = FORM_N;
766                 }
767         | E SP form_code
768                 {
769                         cmd_type = TYPE_E;
770                         cmd_form = $3;
771                 }
772         | I
773                 {
774                         cmd_type = TYPE_I;
775                 }
776         | L
777                 {
778                         cmd_type = TYPE_L;
779                         cmd_bytesz = NBBY;
780                 }
781         | L SP byte_size
782                 {
783                         cmd_type = TYPE_L;
784                         cmd_bytesz = $3;
785                 }
786                 /* this is for a bug in the BBN ftp */
787         | L byte_size
788                 {
789                         cmd_type = TYPE_L;
790                         cmd_bytesz = $2;
791                 }
792         ;
793
794 struct_code
795         : F
796                 {
797                         $$ = STRU_F;
798                 }
799         | R
800                 {
801                         $$ = STRU_R;
802                 }
803         | P
804                 {
805                         $$ = STRU_P;
806                 }
807         ;
808
809 mode_code
810         : S
811                 {
812                         $$ = MODE_S;
813                 }
814         | B
815                 {
816                         $$ = MODE_B;
817                 }
818         | C
819                 {
820                         $$ = MODE_C;
821                 }
822         ;
823
824 pathname
825         : pathstring
826                 {
827                         /*
828                          * Problem: this production is used for all pathname
829                          * processing, but only gives a 550 error reply.
830                          * This is a valid reply in some cases but not in others.
831                          */
832                         if (logged_in && $1 && *$1 == '~') {
833                                 glob_t gl;
834                                 int flags =
835                                  GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
836
837                                 memset(&gl, 0, sizeof(gl));
838                                 if (glob($1, flags, NULL, &gl) ||
839                                     gl.gl_pathc == 0) {
840                                         reply(550, "not found");
841                                         $$ = NULL;
842                                 } else {
843                                         $$ = strdup(gl.gl_pathv[0]);
844                                 }
845                                 globfree(&gl);
846                                 free($1);
847                         } else
848                                 $$ = $1;
849                 }
850         ;
851
852 pathstring
853         : STRING
854         ;
855
856 octal_number
857         : NUMBER
858                 {
859                         int ret, dec, multby, digit;
860
861                         /*
862                          * Convert a number that was read as decimal number
863                          * to what it would be if it had been read as octal.
864                          */
865                         dec = $1;
866                         multby = 1;
867                         ret = 0;
868                         while (dec) {
869                                 digit = dec%10;
870                                 if (digit > 7) {
871                                         ret = -1;
872                                         break;
873                                 }
874                                 ret += digit * multby;
875                                 multby *= 8;
876                                 dec /= 10;
877                         }
878                         $$ = ret;
879                 }
880         ;
881
882
883 check_login_no_guest : check_login
884                 {
885                         $$ = $1 && !guest;
886                         if($1 && !$$)
887                                 reply(550, "Permission denied");
888                 }
889         ;
890
891 check_login : check_secure
892                 {
893                     if($1) {
894                         if(($$ = logged_in) == 0)
895                             reply(530, "Please login with USER and PASS.");
896                     } else
897                         $$ = 0;
898                 }
899         ;
900
901 check_secure : /* empty */
902                 {
903                     $$ = 1;
904                     if(sec_complete && !secure_command()) {
905                         $$ = 0;
906                         reply(533, "Command protection level denied "
907                               "for paranoid reasons.");
908                     }
909                 }
910         ;
911
912 %%
913
914 extern jmp_buf errcatch;
915
916 #define CMD     0       /* beginning of command */
917 #define ARGS    1       /* expect miscellaneous arguments */
918 #define STR1    2       /* expect SP followed by STRING */
919 #define STR2    3       /* expect STRING */
920 #define OSTR    4       /* optional SP then STRING */
921 #define ZSTR1   5       /* SP then optional STRING */
922 #define ZSTR2   6       /* optional STRING after SP */
923 #define SITECMD 7       /* SITE command */
924 #define NSTR    8       /* Number followed by a string */
925
926 struct tab cmdtab[] = {         /* In order defined in RFC 765 */
927         { "USER", USER, STR1, 1,        "<sp> username" },
928         { "PASS", PASS, ZSTR1, 1,       "<sp> password" },
929         { "ACCT", ACCT, STR1, 0,        "(specify account)" },
930         { "SMNT", SMNT, ARGS, 0,        "(structure mount)" },
931         { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
932         { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
933         { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4" },
934         { "EPRT", EPRT, STR1, 1,        "<sp> string" },
935         { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
936         { "EPSV", EPSV, OSTR, 1,        "[<sp> foo]" },
937         { "TYPE", TYPE, ARGS, 1,        "<sp> [ A | E | I | L ]" },
938         { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
939         { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
940         { "RETR", RETR, STR1, 1,        "<sp> file-name" },
941         { "STOR", STOR, STR1, 1,        "<sp> file-name" },
942         { "APPE", APPE, STR1, 1,        "<sp> file-name" },
943         { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
944         { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
945         { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
946         { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
947         { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
948         { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
949         { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
950         { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
951         { "REST", REST, ARGS, 1,        "<sp> offset (restart command)" },
952         { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
953         { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
954         { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
955         { "DELE", DELE, STR1, 1,        "<sp> file-name" },
956         { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
957         { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
958         { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
959         { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
960         { "SITE", SITE, SITECMD, 1,     "site-cmd [ <sp> arguments ]" },
961         { "SYST", SYST, ARGS, 1,        "(get type of operating system)" },
962         { "STAT", sTAT, OSTR, 1,        "[ <sp> path-name ]" },
963         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
964         { "NOOP", NOOP, ARGS, 1,        "" },
965         { "MKD",  MKD,  STR1, 1,        "<sp> path-name" },
966         { "XMKD", MKD,  STR1, 1,        "<sp> path-name" },
967         { "RMD",  RMD,  STR1, 1,        "<sp> path-name" },
968         { "XRMD", RMD,  STR1, 1,        "<sp> path-name" },
969         { "PWD",  PWD,  ARGS, 1,        "(return current directory)" },
970         { "XPWD", PWD,  ARGS, 1,        "(return current directory)" },
971         { "CDUP", CDUP, ARGS, 1,        "(change to parent directory)" },
972         { "XCUP", CDUP, ARGS, 1,        "(change to parent directory)" },
973         { "STOU", STOU, STR1, 1,        "<sp> file-name" },
974         { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
975         { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
976
977         /* extensions from RFC2228 */
978         { "AUTH", AUTH, STR1, 1,        "<sp> auth-type" },
979         { "ADAT", ADAT, STR1, 1,        "<sp> auth-data" },
980         { "PBSZ", PBSZ, ARGS, 1,        "<sp> buffer-size" },
981         { "PROT", PROT, STR1, 1,        "<sp> prot-level" },
982         { "CCC",  CCC,  ARGS, 1,        "" },
983         { "MIC",  MIC,  STR1, 1,        "<sp> integrity command" },
984         { "CONF", CONF, STR1, 1,        "<sp> confidentiality command" },
985         { "ENC",  ENC,  STR1, 1,        "<sp> privacy command" },
986
987         /* RFC2389 */
988         { "FEAT", FEAT, ARGS, 1,        "" },
989         { "OPTS", OPTS, ARGS, 1,        "<sp> command [<sp> options]" },
990
991         { NULL,   0,    0,    0,        0 }
992 };
993
994 struct tab sitetab[] = {
995         { "UMASK", UMASK, ARGS, 1,      "[ <sp> umask ]" },
996         { "IDLE", IDLE, ARGS, 1,        "[ <sp> maximum-idle-time ]" },
997         { "CHMOD", CHMOD, NSTR, 1,      "<sp> mode <sp> file-name" },
998         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
999
1000         { "KAUTH", KAUTH, STR1, 1,      "<sp> principal [ <sp> ticket ]" },
1001         { "KLIST", KLIST, ARGS, 1,      "(show ticket file)" },
1002         { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1003         { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1004         { "AFSLOG", AFSLOG, OSTR, 1,    "[<sp> cell]" },
1005
1006         { "LOCATE", LOCATE, STR1, 1,    "<sp> globexpr" },
1007         { "FIND", LOCATE, STR1, 1,      "<sp> globexpr" },
1008
1009         { "URL",  URL,  ARGS, 1,        "?" },
1010         
1011         { NULL,   0,    0,    0,        0 }
1012 };
1013
1014 static struct tab *
1015 lookup(struct tab *p, char *cmd)
1016 {
1017
1018         for (; p->name != NULL; p++)
1019                 if (strcmp(cmd, p->name) == 0)
1020                         return (p);
1021         return (0);
1022 }
1023
1024 /*
1025  * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1026  */
1027 char *
1028 ftpd_getline(char *s, int n)
1029 {
1030         int c;
1031         char *cs;
1032
1033         cs = s;
1034 /* tmpline may contain saved command from urgent mode interruption */
1035         if(ftp_command){
1036           strlcpy(s, ftp_command, n);
1037           if (debug)
1038             syslog(LOG_DEBUG, "command: %s", s);
1039 #ifdef XXX
1040           fprintf(stderr, "%s\n", s);
1041 #endif
1042           return s;
1043         }
1044         while ((c = getc(stdin)) != EOF) {
1045                 c &= 0377;
1046                 if (c == IAC) {
1047                     if ((c = getc(stdin)) != EOF) {
1048                         c &= 0377;
1049                         switch (c) {
1050                         case WILL:
1051                         case WONT:
1052                                 c = getc(stdin);
1053                                 printf("%c%c%c", IAC, DONT, 0377&c);
1054                                 fflush(stdout);
1055                                 continue;
1056                         case DO:
1057                         case DONT:
1058                                 c = getc(stdin);
1059                                 printf("%c%c%c", IAC, WONT, 0377&c);
1060                                 fflush(stdout);
1061                                 continue;
1062                         case IAC:
1063                                 break;
1064                         default:
1065                                 continue;       /* ignore command */
1066                         }
1067                     }
1068                 }
1069                 *cs++ = c;
1070                 if (--n <= 0 || c == '\n')
1071                         break;
1072         }
1073         if (c == EOF && cs == s)
1074                 return (NULL);
1075         *cs++ = '\0';
1076         if (debug) {
1077                 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1078                         /* Don't syslog passwords */
1079                         syslog(LOG_DEBUG, "command: %.5s ???", s);
1080                 } else {
1081                         char *cp;
1082                         int len;
1083
1084                         /* Don't syslog trailing CR-LF */
1085                         len = strlen(s);
1086                         cp = s + len - 1;
1087                         while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1088                                 --cp;
1089                                 --len;
1090                         }
1091                         syslog(LOG_DEBUG, "command: %.*s", len, s);
1092                 }
1093         }
1094 #ifdef XXX
1095         fprintf(stderr, "%s\n", s);
1096 #endif
1097         return (s);
1098 }
1099
1100 static RETSIGTYPE
1101 toolong(int signo)
1102 {
1103
1104         reply(421,
1105             "Timeout (%d seconds): closing control connection.",
1106               ftpd_timeout);
1107         if (logging)
1108                 syslog(LOG_INFO, "User %s timed out after %d seconds",
1109                     (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1110         dologout(1);
1111         SIGRETURN(0);
1112 }
1113
1114 static int
1115 yylex(void)
1116 {
1117         static int cpos, state;
1118         char *cp, *cp2;
1119         struct tab *p;
1120         int n;
1121         char c;
1122
1123         for (;;) {
1124                 switch (state) {
1125
1126                 case CMD:
1127                         signal(SIGALRM, toolong);
1128                         alarm((unsigned) ftpd_timeout);
1129                         if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1130                                 reply(221, "You could at least say goodbye.");
1131                                 dologout(0);
1132                         }
1133                         alarm(0);
1134 #ifdef HAVE_SETPROCTITLE
1135                         if (strncasecmp(cbuf, "PASS", 4) != NULL)
1136                                 setproctitle("%s: %s", proctitle, cbuf);
1137 #endif /* HAVE_SETPROCTITLE */
1138                         if ((cp = strchr(cbuf, '\r'))) {
1139                                 *cp++ = '\n';
1140                                 *cp = '\0';
1141                         }
1142                         if ((cp = strpbrk(cbuf, " \n")))
1143                                 cpos = cp - cbuf;
1144                         if (cpos == 0)
1145                                 cpos = 4;
1146                         c = cbuf[cpos];
1147                         cbuf[cpos] = '\0';
1148                         strupr(cbuf);
1149                         p = lookup(cmdtab, cbuf);
1150                         cbuf[cpos] = c;
1151                         if (p != 0) {
1152                                 if (p->implemented == 0) {
1153                                         nack(p->name);
1154                                         longjmp(errcatch,0);
1155                                         /* NOTREACHED */
1156                                 }
1157                                 state = p->state;
1158                                 yylval.s = p->name;
1159                                 return (p->token);
1160                         }
1161                         break;
1162
1163                 case SITECMD:
1164                         if (cbuf[cpos] == ' ') {
1165                                 cpos++;
1166                                 return (SP);
1167                         }
1168                         cp = &cbuf[cpos];
1169                         if ((cp2 = strpbrk(cp, " \n")))
1170                                 cpos = cp2 - cbuf;
1171                         c = cbuf[cpos];
1172                         cbuf[cpos] = '\0';
1173                         strupr(cp);
1174                         p = lookup(sitetab, cp);
1175                         cbuf[cpos] = c;
1176                         if (p != 0) {
1177                                 if (p->implemented == 0) {
1178                                         state = CMD;
1179                                         nack(p->name);
1180                                         longjmp(errcatch,0);
1181                                         /* NOTREACHED */
1182                                 }
1183                                 state = p->state;
1184                                 yylval.s = p->name;
1185                                 return (p->token);
1186                         }
1187                         state = CMD;
1188                         break;
1189
1190                 case OSTR:
1191                         if (cbuf[cpos] == '\n') {
1192                                 state = CMD;
1193                                 return (CRLF);
1194                         }
1195                         /* FALLTHROUGH */
1196
1197                 case STR1:
1198                 case ZSTR1:
1199                 dostr1:
1200                         if (cbuf[cpos] == ' ') {
1201                                 cpos++;
1202                                 if(state == OSTR)
1203                                     state = STR2;
1204                                 else
1205                                     state++;
1206                                 return (SP);
1207                         }
1208                         break;
1209
1210                 case ZSTR2:
1211                         if (cbuf[cpos] == '\n') {
1212                                 state = CMD;
1213                                 return (CRLF);
1214                         }
1215                         /* FALLTHROUGH */
1216
1217                 case STR2:
1218                         cp = &cbuf[cpos];
1219                         n = strlen(cp);
1220                         cpos += n - 1;
1221                         /*
1222                          * Make sure the string is nonempty and \n terminated.
1223                          */
1224                         if (n > 1 && cbuf[cpos] == '\n') {
1225                                 cbuf[cpos] = '\0';
1226                                 yylval.s = copy(cp);
1227                                 cbuf[cpos] = '\n';
1228                                 state = ARGS;
1229                                 return (STRING);
1230                         }
1231                         break;
1232
1233                 case NSTR:
1234                         if (cbuf[cpos] == ' ') {
1235                                 cpos++;
1236                                 return (SP);
1237                         }
1238                         if (isdigit(cbuf[cpos])) {
1239                                 cp = &cbuf[cpos];
1240                                 while (isdigit(cbuf[++cpos]))
1241                                         ;
1242                                 c = cbuf[cpos];
1243                                 cbuf[cpos] = '\0';
1244                                 yylval.i = atoi(cp);
1245                                 cbuf[cpos] = c;
1246                                 state = STR1;
1247                                 return (NUMBER);
1248                         }
1249                         state = STR1;
1250                         goto dostr1;
1251
1252                 case ARGS:
1253                         if (isdigit(cbuf[cpos])) {
1254                                 cp = &cbuf[cpos];
1255                                 while (isdigit(cbuf[++cpos]))
1256                                         ;
1257                                 c = cbuf[cpos];
1258                                 cbuf[cpos] = '\0';
1259                                 yylval.i = atoi(cp);
1260                                 cbuf[cpos] = c;
1261                                 return (NUMBER);
1262                         }
1263                         switch (cbuf[cpos++]) {
1264
1265                         case '\n':
1266                                 state = CMD;
1267                                 return (CRLF);
1268
1269                         case ' ':
1270                                 return (SP);
1271
1272                         case ',':
1273                                 return (COMMA);
1274
1275                         case 'A':
1276                         case 'a':
1277                                 return (A);
1278
1279                         case 'B':
1280                         case 'b':
1281                                 return (B);
1282
1283                         case 'C':
1284                         case 'c':
1285                                 return (C);
1286
1287                         case 'E':
1288                         case 'e':
1289                                 return (E);
1290
1291                         case 'F':
1292                         case 'f':
1293                                 return (F);
1294
1295                         case 'I':
1296                         case 'i':
1297                                 return (I);
1298
1299                         case 'L':
1300                         case 'l':
1301                                 return (L);
1302
1303                         case 'N':
1304                         case 'n':
1305                                 return (N);
1306
1307                         case 'P':
1308                         case 'p':
1309                                 return (P);
1310
1311                         case 'R':
1312                         case 'r':
1313                                 return (R);
1314
1315                         case 'S':
1316                         case 's':
1317                                 return (S);
1318
1319                         case 'T':
1320                         case 't':
1321                                 return (T);
1322
1323                         }
1324                         break;
1325
1326                 default:
1327                         fatal("Unknown state in scanner.");
1328                 }
1329                 yyerror((char *) 0);
1330                 state = CMD;
1331                 longjmp(errcatch,0);
1332         }
1333 }
1334
1335 static char *
1336 copy(char *s)
1337 {
1338         char *p;
1339
1340         p = strdup(s);
1341         if (p == NULL)
1342                 fatal("Ran out of memory.");
1343         return p;
1344 }
1345
1346 static void
1347 help(struct tab *ctab, char *s)
1348 {
1349         struct tab *c;
1350         int width, NCMDS;
1351         char *type;
1352         char buf[1024];
1353
1354         if (ctab == sitetab)
1355                 type = "SITE ";
1356         else
1357                 type = "";
1358         width = 0, NCMDS = 0;
1359         for (c = ctab; c->name != NULL; c++) {
1360                 int len = strlen(c->name);
1361
1362                 if (len > width)
1363                         width = len;
1364                 NCMDS++;
1365         }
1366         width = (width + 8) &~ 7;
1367         if (s == 0) {
1368                 int i, j, w;
1369                 int columns, lines;
1370
1371                 lreply(214, "The following %scommands are recognized %s.",
1372                     type, "(* =>'s unimplemented)");
1373                 columns = 76 / width;
1374                 if (columns == 0)
1375                         columns = 1;
1376                 lines = (NCMDS + columns - 1) / columns;
1377                 for (i = 0; i < lines; i++) {
1378                     strlcpy (buf, "   ", sizeof(buf));
1379                     for (j = 0; j < columns; j++) {
1380                         c = ctab + j * lines + i;
1381                         snprintf (buf + strlen(buf),
1382                                   sizeof(buf) - strlen(buf),
1383                                   "%s%c",
1384                                   c->name,
1385                                   c->implemented ? ' ' : '*');
1386                         if (c + lines >= &ctab[NCMDS])
1387                             break;
1388                         w = strlen(c->name) + 1;
1389                         while (w < width) {
1390                             strlcat (buf,
1391                                              " ",
1392                                              sizeof(buf));
1393                             w++;
1394                         }
1395                     }
1396                     lreply(214, "%s", buf);
1397                 }
1398                 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1399                 return;
1400         }
1401         strupr(s);
1402         c = lookup(ctab, s);
1403         if (c == (struct tab *)0) {
1404                 reply(502, "Unknown command %s.", s);
1405                 return;
1406         }
1407         if (c->implemented)
1408                 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1409         else
1410                 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1411                     c->name, c->help);
1412 }
1413
1414 static void
1415 sizecmd(char *filename)
1416 {
1417         switch (type) {
1418         case TYPE_L:
1419         case TYPE_I: {
1420                 struct stat stbuf;
1421                 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1422                         reply(550, "%s: not a plain file.", filename);
1423                 else
1424                         reply(213, "%lu", (unsigned long)stbuf.st_size);
1425                 break;
1426         }
1427         case TYPE_A: {
1428                 FILE *fin;
1429                 int c;
1430                 size_t count;
1431                 struct stat stbuf;
1432                 fin = fopen(filename, "r");
1433                 if (fin == NULL) {
1434                         perror_reply(550, filename);
1435                         return;
1436                 }
1437                 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1438                         reply(550, "%s: not a plain file.", filename);
1439                         fclose(fin);
1440                         return;
1441                 }
1442
1443                 count = 0;
1444                 while((c=getc(fin)) != EOF) {
1445                         if (c == '\n')  /* will get expanded to \r\n */
1446                                 count++;
1447                         count++;
1448                 }
1449                 fclose(fin);
1450
1451                 reply(213, "%lu", (unsigned long)count);
1452                 break;
1453         }
1454         default:
1455                 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1456         }
1457 }