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