]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/opie/ftpcmd.y
This commit was generated by cvs2svn to compensate for changes in r92686,
[FreeBSD/FreeBSD.git] / contrib / opie / ftpcmd.y
1 /* ftpcmd.y: yacc parser for the FTP daemon.
2
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1998 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9
10         History:
11
12         Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here.
13         Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings.
14                 Use FUNCTION declaration et al. Removed useless strings.
15                 Changed some char []s to char *s. Deleted comment address.
16                 Changed tmpline references to be more pure-pointer
17                 references. Changed tmpline declaration back to char [].
18         Modified at NRL for OPIE 2.1. Minor changes for autoconf.
19         Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[]
20                 -- fixes problems experienced by bison users. Merged in new 
21                 PORT attack fixes from Hobbit.
22         Modified at NRL for OPIE 2.0.
23         Originally from BSD.
24
25 $FreeBSD$
26 */
27 /*
28  * Copyright (c) 1985, 1988 Regents of the University of California.
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without
32  * modification, are permitted provided that the following conditions
33  * are met:
34  * 1. Redistributions of source code must retain the above copyright
35  *    notice, this list of conditions and the following disclaimer.
36  * 2. Redistributions in binary form must reproduce the above copyright
37  *    notice, this list of conditions and the following disclaimer in the
38  *    documentation and/or other materials provided with the distribution.
39  * 3. All advertising materials mentioning features or use of this software
40  *    must display the following acknowledgement:
41  *      This product includes software developed by the University of
42  *      California, Berkeley and its contributors.
43  * 4. Neither the name of the University nor the names of its contributors
44  *    may be used to endorse or promote products derived from this software
45  *    without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  *
59  *      @(#)ftpcmd.y    5.24 (Berkeley) 2/25/91
60  */
61
62 /*
63  * Grammar for FTP commands.
64  * See RFC 959.
65  */
66
67 %{
68 #include "opie_cfg.h"
69
70 #include <sys/param.h>
71 #include <sys/types.h>
72 #include <sys/socket.h>
73 #include <sys/stat.h>
74 #include <netinet/in.h>
75 #include <arpa/ftp.h>
76 #include <signal.h>
77 #include <setjmp.h>
78 #include <syslog.h>
79 #if TM_IN_SYS_TIME
80 #include <sys/time.h>
81 #else /* TM_IN_SYS_TIME */
82 #include <time.h>
83 #endif /* TM_IN_SYS_TIME */
84 #include <pwd.h>
85 #include <unistd.h>
86 #include <stdio.h>
87 #include <ctype.h>
88 #include <stdlib.h>
89 #include <string.h>
90
91 #include "opie.h"
92
93 #if HAVE_LS_G_FLAG
94 #define LS_COMMAND "/bin/ls -lgA"
95 #else /* HAVE_LS_G_FLAG */
96 #define LS_COMMAND "/bin/ls -lA"
97 #endif /* HAVE_LS_G_FLAG */
98
99 extern  struct sockaddr_in data_dest;
100 extern  struct sockaddr_in his_addr;
101 extern  int logged_in;
102 extern  struct passwd *pw;
103 extern  int guest;
104 extern  int type;
105 extern  int form;
106 extern  int debug;
107 extern  int timeout;
108 extern  int maxtimeout;
109 extern  int pdata;
110 extern  char *remotehost;
111 extern  char *proctitle;
112 extern  char *globerr;
113 extern  int usedefault;
114 extern  int transflag;
115 extern  char tmpline[];
116 char    **ftpglob();
117
118 VOIDRET dologout __P((int));
119 VOIDRET upper __P((char *));
120 VOIDRET nack __P((char *));
121 VOIDRET opiefatal __P((char *));
122
123 VOIDRET pass __P((char *));
124 int user __P((char *));
125 VOIDRET passive __P((void));
126 VOIDRET retrieve __P((char *, char *));
127 VOIDRET store __P((char *, char *, int));
128 VOIDRET send_file_list __P((char *));
129 VOIDRET statfilecmd __P((char *));
130 VOIDRET statcmd __P((void));
131 VOIDRET delete __P((char *));
132 VOIDRET renamecmd __P((char *, char *));
133 VOIDRET cwd __P((char *));
134 VOIDRET makedir __P((char *));
135 VOIDRET removedir __P((char *));
136 VOIDRET pwd __P((void));
137
138 VOIDRET sizecmd __P((char *));
139
140 off_t   restart_point;
141
142 static  int cmd_type;
143 static  int cmd_form;
144 static  int cmd_bytesz;
145 static  unsigned short cliport = 0;
146 char    cbuf[512];
147 char    *fromname;
148
149 struct tab {
150         char    *name;
151         short   token;
152         short   state;
153         short   implemented;    /* 1 if command is implemented */
154         char    *help;
155 };
156
157 VOIDRET help __P((struct tab *, char *));
158
159 struct tab cmdtab[], sitetab[];
160
161 %}
162
163 %token
164         A       B       C       E       F       I
165         L       N       P       R       S       T
166
167         SP      CRLF    COMMA   STRING  NUMBER
168
169         USER    PASS    ACCT    REIN    QUIT    PORT
170         PASV    TYPE    STRU    MODE    RETR    STOR
171         APPE    MLFL    MAIL    MSND    MSOM    MSAM
172         MRSQ    MRCP    ALLO    REST    RNFR    RNTO
173         ABOR    DELE    CWD     LIST    NLST    SITE
174         STAT    HELP    NOOP    MKD     RMD     PWD
175         CDUP    STOU    SMNT    SYST    SIZE    MDTM
176
177         UMASK   IDLE    CHMOD
178
179         LEXERR
180
181 %start  cmd_list
182
183 %%
184
185 cmd_list:       /* empty */
186         |       cmd_list cmd
187                 = {
188                         fromname = (char *) 0;
189                         restart_point = (off_t) 0;
190                 }
191         |       cmd_list rcmd
192         ;
193
194 cmd:            USER SP username CRLF
195                 = {
196                         user((char *) $3);
197                         free((char *) $3);
198                 }
199         |       PASS SP password CRLF
200                 = {
201                         pass((char *) $3);
202                         free((char *) $3);
203                 }
204         |   PORT check_login SP host_port CRLF
205                 = {   
206              usedefault = 0;  
207              if (pdata >= 0) {
208                  (void) close(pdata);
209                  pdata = -1;
210              }
211 /* H* port fix, part B: admonish the twit.
212    Also require login before PORT works */
213             if ($2) {
214               if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) {
215                 reply(200, "PORT command successful.");
216               } else {
217                 syslog (LOG_WARNING, "refused %s from %s",
218                        cbuf, remotehost);
219                 reply(500, "You've GOT to be joking.");
220               }
221             }
222                 }
223 /*      |       PASV CRLF
224                 = {
225                         passive();
226                 } */
227     |   PASV check_login CRLF
228         = {
229 /* Require login for PASV, too.  This actually fixes a bug -- telnet to an
230    unfixed wu-ftpd and type PASV first off, and it crashes! */
231             if ($2) {
232                 passive();
233             }
234         }     
235         |       TYPE SP type_code CRLF
236                 = {
237                         switch (cmd_type) {
238
239                         case TYPE_A:
240                                 if (cmd_form == FORM_N) {
241                                         reply(200, "Type set to A.");
242                                         type = cmd_type;
243                                         form = cmd_form;
244                                 } else
245                                         reply(504, "Form must be N.");
246                                 break;
247
248                         case TYPE_E:
249                                 reply(504, "Type E not implemented.");
250                                 break;
251
252                         case TYPE_I:
253                                 reply(200, "Type set to I.");
254                                 type = cmd_type;
255                                 break;
256
257                         case TYPE_L:
258 #if NBBY == 8
259                                 if (cmd_bytesz == 8) {
260                                         reply(200,
261                                             "Type set to L (byte size 8).");
262                                         type = cmd_type;
263                                 } else
264                                         reply(504, "Byte size must be 8.");
265 #else /* NBBY == 8 */
266                                 UNIMPLEMENTED for NBBY != 8
267 #endif /* NBBY == 8 */
268                         }
269                 }
270         |       STRU SP struct_code CRLF
271                 = {
272                         switch ($3) {
273
274                         case STRU_F:
275                                 reply(200, "STRU F ok.");
276                                 break;
277
278                         default:
279                                 reply(504, "Unimplemented STRU type.");
280                         }
281                 }
282         |       MODE SP mode_code CRLF
283                 = {
284                         switch ($3) {
285
286                         case MODE_S:
287                                 reply(200, "MODE S ok.");
288                                 break;
289
290                         default:
291                                 reply(502, "Unimplemented MODE type.");
292                         }
293                 }
294         |       ALLO SP NUMBER CRLF
295                 = {
296                         reply(202, "ALLO command ignored.");
297                 }
298         |       ALLO SP NUMBER SP R SP NUMBER CRLF
299                 = {
300                         reply(202, "ALLO command ignored.");
301                 }
302         |       RETR check_login SP pathname CRLF
303                 = {
304                         if ($2 && $4)
305                                 retrieve((char *) 0, (char *) $4);
306                         if ($4)
307                                 free((char *) $4);
308                 }
309         |       STOR check_login SP pathname CRLF
310                 = {
311                         if ($2 && $4)
312                                 store((char *) $4, "w", 0);
313                         if ($4)
314                                 free((char *) $4);
315                 }
316         |       APPE check_login SP pathname CRLF
317                 = {
318                         if ($2 && $4)
319                                 store((char *) $4, "a", 0);
320                         if ($4)
321                                 free((char *) $4);
322                 }
323         |       NLST check_login CRLF
324                 = {
325                         if ($2)
326                                 send_file_list(".");
327                 }
328         |       NLST check_login SP STRING CRLF
329                 = {
330                         if ($2 && $4) 
331                                 send_file_list((char *) $4);
332                         if ($4)
333                                 free((char *) $4);
334                 }
335         |       LIST check_login CRLF
336                 = {
337                         if ($2)
338                                 retrieve(LS_COMMAND, "");
339                 }
340         |       LIST check_login SP pathname CRLF
341                 = {
342                         if ($2 && $4)
343                                 {
344                                 char buffer[sizeof(LS_COMMAND)+3];
345                                 strcpy(buffer, LS_COMMAND);
346                                 strcat(buffer, " %s");
347                                 retrieve(buffer, (char *) $4);
348                                 }
349                         if ($4)
350                                 free((char *) $4);
351                 }
352         |       STAT check_login SP pathname CRLF
353                 = {
354                         if ($2 && $4)
355                                 statfilecmd((char *) $4);
356                         if ($4)
357                                 free((char *) $4);
358                 }
359         |       STAT CRLF
360                 = {
361                         statcmd();
362                 }
363         |       DELE check_login SP pathname CRLF
364                 = {
365                         if ($2 && $4)
366                                 delete((char *) $4);
367                         if ($4)
368                                 free((char *) $4);
369                 }
370         |       RNTO SP pathname CRLF
371                 = {
372                         if (fromname) {
373                                 renamecmd(fromname, (char *) $3);
374                                 free(fromname);
375                                 fromname = (char *) 0;
376                         } else {
377                                 reply(503, "Bad sequence of commands.");
378                         }
379                         free((char *) $3);
380                 }
381         |       ABOR CRLF
382                 = {
383                         reply(225, "ABOR command successful.");
384                 }
385         |       CWD check_login CRLF
386                 = {
387                         if ($2)
388                                 cwd(pw->pw_dir);
389                 }
390         |       CWD check_login SP pathname CRLF
391                 = {
392                         if ($2 && $4)
393                                 cwd((char *) $4);
394                         if ($4)
395                                 free((char *) $4);
396                 }
397         |       HELP CRLF
398                 = {
399                         help(cmdtab, (char *) 0);
400                 }
401         |       HELP SP STRING CRLF
402                 = {
403                         register char *cp = (char *)$3;
404
405                         if (strncasecmp(cp, "SITE", 4) == 0) {
406                                 cp = (char *)$3 + 4;
407                                 if (*cp == ' ')
408                                         cp++;
409                                 if (*cp)
410                                         help(sitetab, cp);
411                                 else
412                                         help(sitetab, (char *) 0);
413                         } else
414                                 help(cmdtab, (char *) $3);
415                 }
416         |       NOOP CRLF
417                 = {
418                         reply(200, "NOOP command successful.");
419                 }
420         |       MKD check_login SP pathname CRLF
421                 = {
422                         if ($2 && $4)
423                                 makedir((char *) $4);
424                         if ($4)
425                                 free((char *) $4);
426                 }
427         |       RMD check_login SP pathname CRLF
428                 = {
429                         if ($2 && $4)
430                                 removedir((char *) $4);
431                         if ($4)
432                                 free((char *) $4);
433                 }
434         |       PWD check_login CRLF
435                 = {
436                         if ($2)
437                                 pwd();
438                 }
439         |       CDUP check_login CRLF
440                 = {
441                         if ($2)
442                                 cwd("..");
443                 }
444         |       SITE SP HELP CRLF
445                 = {
446                         help(sitetab, (char *) 0);
447                 }
448         |       SITE SP HELP SP STRING CRLF
449                 = {
450                         help(sitetab, (char *) $5);
451                 }
452         |       SITE SP UMASK check_login CRLF
453                 = {
454                         int oldmask;
455
456                         if ($4) {
457                                 oldmask = umask(0);
458                                 (void) umask(oldmask);
459                                 reply(200, "Current UMASK is %03o", oldmask);
460                         }
461                 }
462         |       SITE SP UMASK check_login SP octal_number CRLF
463                 = {
464                         int oldmask;
465
466                         if ($4) {
467                                 if (($6 == -1) || ($6 > 0777)) {
468                                         reply(501, "Bad UMASK value");
469                                 } else {
470                                         oldmask = umask($6);
471                                         reply(200,
472                                             "UMASK set to %03o (was %03o)",
473                                             $6, oldmask);
474                                 }
475                         }
476                 }
477         |       SITE SP CHMOD check_login SP octal_number SP pathname CRLF
478                 = {
479                         if ($4 && $8) {
480                                 if ($6 > 0777)
481                                         reply(501,
482                                 "CHMOD: Mode value must be between 0 and 0777");
483                                 else if (chmod((char *) $8, $6) < 0)
484                                         perror_reply(550, (char *) $8);
485                                 else
486                                         reply(200, "CHMOD command successful.");
487                         }
488                         if ($8)
489                                 free((char *) $8);
490                 }
491         |       SITE SP IDLE CRLF
492                 = {
493                         reply(200,
494                             "Current IDLE time limit is %d seconds; max %d",
495                                 timeout, maxtimeout);
496                 }
497         |       SITE SP IDLE SP NUMBER CRLF
498                 = {
499                         if ($5 < 30 || $5 > maxtimeout) {
500                                 reply(501,
501                         "Maximum IDLE time must be between 30 and %d seconds",
502                                     maxtimeout);
503                         } else {
504                                 timeout = $5;
505                                 (void) alarm((unsigned) timeout);
506                                 reply(200,
507                                     "Maximum IDLE time set to %d seconds",
508                                     timeout);
509                         }
510                 }
511         |       STOU check_login SP pathname CRLF
512                 = {
513                         if ($2 && $4)
514                                 store((char *) $4, "w", 1);
515                         if ($4)
516                                 free((char *) $4);
517                 }
518         |       SYST CRLF
519                 = {
520 #ifdef unix
521 #ifdef BSD
522                         reply(215, "UNIX Type: L%d Version: BSD-%d",
523                                 NBBY, BSD);
524 #else /* BSD */
525                         reply(215, "UNIX Type: L%d", NBBY);
526 #endif /* BSD */
527 #else /* unix */
528                         reply(215, "UNKNOWN Type: L%d", NBBY);
529 #endif /* unix */
530                 }
531
532                 /*
533                  * SIZE is not in RFC959, but Postel has blessed it and
534                  * it will be in the updated RFC.
535                  *
536                  * Return size of file in a format suitable for
537                  * using with RESTART (we just count bytes).
538                  */
539         |       SIZE check_login SP pathname CRLF
540                 = {
541                         if ($2 && $4)
542                                 sizecmd((char *) $4);
543                         if ($4)
544                                 free((char *) $4);
545                 }
546
547                 /*
548                  * MDTM is not in RFC959, but Postel has blessed it and
549                  * it will be in the updated RFC.
550                  *
551                  * Return modification time of file as an ISO 3307
552                  * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
553                  * where xxx is the fractional second (of any precision,
554                  * not necessarily 3 digits)
555                  */
556         |       MDTM check_login SP pathname CRLF
557                 = {
558                         if ($2 && $4) {
559                                 struct stat stbuf;
560                                 if (stat((char *) $4, &stbuf) < 0)
561                                         perror_reply(550, (char *) $4);
562                                 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
563                                         reply(550, "%s: not a plain file.",
564                                                 (char *) $4);
565                                 } else {
566                                         register struct tm *t;
567                                         struct tm *gmtime();
568                                         t = gmtime(&stbuf.st_mtime);
569                                         reply(213,
570                                             "%d%02d%02d%02d%02d%02d",
571                                             t->tm_year+1900, t->tm_mon+1, t->tm_mday,
572                                             t->tm_hour, t->tm_min, t->tm_sec);
573                                 }
574                         }
575                         if ($4)
576                                 free((char *) $4);
577                 }
578         |       QUIT CRLF
579                 = {
580                         reply(221, "Goodbye.");
581                         dologout(0);
582                 }
583         |       error CRLF
584                 = {
585                         yyerrok;
586                 }
587         ;
588 rcmd:           RNFR check_login SP pathname CRLF
589                 = {
590                         char *renamefrom();
591
592                         restart_point = (off_t) 0;
593                         if ($2 && $4) {
594                                 fromname = renamefrom((char *) $4);
595                                 if (fromname == (char *) 0 && $4) {
596                                         free((char *) $4);
597                                 }
598                         }
599                 }
600         |       REST SP byte_size CRLF
601                 = {
602                         long atol();
603
604                         fromname = (char *) 0;
605                         restart_point = $3;
606                         reply(350, "Restarting at %ld. %s", restart_point,
607                             "Send STORE or RETRIEVE to initiate transfer.");
608                 }
609         ;
610                 
611 username:       STRING
612         ;
613
614 password:       /* empty */
615                 = {
616                         *(char **)&($$) = (char *)calloc(1, sizeof(char));
617                 }
618         |       STRING
619         ;
620
621 byte_size:      NUMBER
622         ;
623
624 host_port:      NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 
625                 NUMBER COMMA NUMBER
626                 = {
627                         register char *a, *p;
628
629                         a = (char *)&data_dest.sin_addr;
630                         a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
631
632 /* H* port fix, part A-1: Check the args against the client addr */
633             p = (char *)&his_addr.sin_addr;
634              if (memcmp (a, p, sizeof (data_dest.sin_addr))) 
635                  memset (a, 0, sizeof (data_dest.sin_addr));     /* XXX */
636
637                         p = (char *)&data_dest.sin_port;
638
639 /* H* port fix, part A-2: only allow client ports in "user space" */
640             p[0] = 0; p[1] = 0;
641             cliport = ($9 << 8) + $11;
642             if (cliport > 1023) {
643                  p[0] = $9; p[1] = $11;
644             } 
645
646                         p[0] = $9; p[1] = $11;
647                         data_dest.sin_family = AF_INET;
648                 }
649         ;
650
651 form_code:      N
652         = {
653                 $$ = FORM_N;
654         }
655         |       T
656         = {
657                 $$ = FORM_T;
658         }
659         |       C
660         = {
661                 $$ = FORM_C;
662         }
663         ;
664
665 type_code:      A
666         = {
667                 cmd_type = TYPE_A;
668                 cmd_form = FORM_N;
669         }
670         |       A SP form_code
671         = {
672                 cmd_type = TYPE_A;
673                 cmd_form = $3;
674         }
675         |       E
676         = {
677                 cmd_type = TYPE_E;
678                 cmd_form = FORM_N;
679         }
680         |       E SP form_code
681         = {
682                 cmd_type = TYPE_E;
683                 cmd_form = $3;
684         }
685         |       I
686         = {
687                 cmd_type = TYPE_I;
688         }
689         |       L
690         = {
691                 cmd_type = TYPE_L;
692                 cmd_bytesz = NBBY;
693         }
694         |       L SP byte_size
695         = {
696                 cmd_type = TYPE_L;
697                 cmd_bytesz = $3;
698         }
699         /* this is for a bug in the BBN ftp */
700         |       L byte_size
701         = {
702                 cmd_type = TYPE_L;
703                 cmd_bytesz = $2;
704         }
705         ;
706
707 struct_code:    F
708         = {
709                 $$ = STRU_F;
710         }
711         |       R
712         = {
713                 $$ = STRU_R;
714         }
715         |       P
716         = {
717                 $$ = STRU_P;
718         }
719         ;
720
721 mode_code:      S
722         = {
723                 $$ = MODE_S;
724         }
725         |       B
726         = {
727                 $$ = MODE_B;
728         }
729         |       C
730         = {
731                 $$ = MODE_C;
732         }
733         ;
734
735 pathname:       pathstring
736         = {
737                 /*
738                  * Problem: this production is used for all pathname
739                  * processing, but only gives a 550 error reply.
740                  * This is a valid reply in some cases but not in others.
741                  */
742                 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
743                         *(char **)&($$) = *ftpglob((char *) $1);
744                         if (globerr != NULL) {
745                                 reply(550, globerr);
746 /*                              $$ = NULL; */
747                                 $$ = 0;
748                         }
749                         free((char *) $1);
750                 } else
751                         $$ = $1;
752         }
753         ;
754
755 pathstring:     STRING
756         ;
757
758 octal_number:   NUMBER
759         = {
760                 register int ret, dec, multby, digit;
761
762                 /*
763                  * Convert a number that was read as decimal number
764                  * to what it would be if it had been read as octal.
765                  */
766                 dec = $1;
767                 multby = 1;
768                 ret = 0;
769                 while (dec) {
770                         digit = dec%10;
771                         if (digit > 7) {
772                                 ret = -1;
773                                 break;
774                         }
775                         ret += digit * multby;
776                         multby *= 8;
777                         dec /= 10;
778                 }
779                 $$ = ret;
780         }
781         ;
782
783 check_login:    /* empty */
784         = {
785                 if (logged_in)
786                         $$ = 1;
787                 else {
788                         reply(530, "Please login with USER and PASS.");
789                         $$ = 0;
790                 }
791         }
792         ;
793
794 %%
795
796 extern jmp_buf errcatch;
797
798 #define CMD     0       /* beginning of command */
799 #define ARGS    1       /* expect miscellaneous arguments */
800 #define STR1    2       /* expect SP followed by STRING */
801 #define STR2    3       /* expect STRING */
802 #define OSTR    4       /* optional SP then STRING */
803 #define ZSTR1   5       /* SP then optional STRING */
804 #define ZSTR2   6       /* optional STRING after SP */
805 #define SITECMD 7       /* SITE command */
806 #define NSTR    8       /* Number followed by a string */
807
808 struct tab cmdtab[] = {         /* In order defined in RFC 765 */
809         { "USER", USER, STR1, 1,        "<sp> username" },
810         { "PASS", PASS, ZSTR1, 1,       "<sp> password" },
811         { "ACCT", ACCT, STR1, 0,        "(specify account)" },
812         { "SMNT", SMNT, ARGS, 0,        "(structure mount)" },
813         { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
814         { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
815         { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4" },
816         { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
817         { "TYPE", TYPE, ARGS, 1,        "<sp> [ A | E | I | L ]" },
818         { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
819         { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
820         { "RETR", RETR, STR1, 1,        "<sp> file-name" },
821         { "STOR", STOR, STR1, 1,        "<sp> file-name" },
822         { "APPE", APPE, STR1, 1,        "<sp> file-name" },
823         { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
824         { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
825         { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
826         { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
827         { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
828         { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
829         { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
830         { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
831         { "REST", REST, ARGS, 1,        "(restart command)" },
832         { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
833         { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
834         { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
835         { "DELE", DELE, STR1, 1,        "<sp> file-name" },
836         { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
837         { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
838         { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
839         { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
840         { "SITE", SITE, SITECMD, 1,     "site-cmd [ <sp> arguments ]" },
841         { "SYST", SYST, ARGS, 1,        "(get type of operating system)" },
842         { "STAT", STAT, OSTR, 1,        "[ <sp> path-name ]" },
843         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
844         { "NOOP", NOOP, ARGS, 1,        "" },
845         { "MKD",  MKD,  STR1, 1,        "<sp> path-name" },
846         { "XMKD", MKD,  STR1, 1,        "<sp> path-name" },
847         { "RMD",  RMD,  STR1, 1,        "<sp> path-name" },
848         { "XRMD", RMD,  STR1, 1,        "<sp> path-name" },
849         { "PWD",  PWD,  ARGS, 1,        "(return current directory)" },
850         { "XPWD", PWD,  ARGS, 1,        "(return current directory)" },
851         { "CDUP", CDUP, ARGS, 1,        "(change to parent directory)" },
852         { "XCUP", CDUP, ARGS, 1,        "(change to parent directory)" },
853         { "STOU", STOU, STR1, 1,        "<sp> file-name" },
854         { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
855         { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
856         { NULL,   0,    0,    0,        0 }
857 };
858
859 struct tab sitetab[] = {
860         { "UMASK", UMASK, ARGS, 1,      "[ <sp> umask ]" },
861         { "IDLE", IDLE, ARGS, 1,        "[ <sp> maximum-idle-time ]" },
862         { "CHMOD", CHMOD, NSTR, 1,      "<sp> mode <sp> file-name" },
863         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
864         { NULL,   0,    0,    0,        0 }
865 };
866
867 struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd)
868 {
869
870         for (; p->name != NULL; p++)
871                 if (strcmp(cmd, p->name) == 0)
872                         return (p);
873         return (0);
874 }
875
876 #include <arpa/telnet.h>
877
878 /*
879  * getline - a hacked up version of fgets to ignore TELNET escape codes.
880  */
881 char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop)
882 {
883         register c;
884         register char *cs;
885
886         cs = s;
887 /* tmpline may contain saved command from urgent mode interruption */
888         for (c = 0; *(tmpline + c) && --n > 0; ++c) {
889                 *cs++ = *(tmpline + c);
890                 if (*(tmpline + c) == '\n') {
891                         *cs++ = '\0';
892                         if (debug)
893                                 syslog(LOG_DEBUG, "command: %s", s);
894                         *tmpline = '\0';
895                         return(s);
896                 }
897                 if (c == 0)
898                         *tmpline = '\0';
899         }
900         while ((c = getc(iop)) != EOF) {
901                 c &= 0377;
902                 if (c == IAC) {
903                     if ((c = getc(iop)) != EOF) {
904                         c &= 0377;
905                         switch (c) {
906                         case WILL:
907                         case WONT:
908                                 c = getc(iop);
909                                 printf("%c%c%c", IAC, DONT, 0377&c);
910                                 (void) fflush(stdout);
911                                 continue;
912                         case DO:
913                         case DONT:
914                                 c = getc(iop);
915                                 printf("%c%c%c", IAC, WONT, 0377&c);
916                                 (void) fflush(stdout);
917                                 continue;
918                         case IAC:
919                                 break;
920                         default:
921                                 continue;       /* ignore command */
922                         }
923                     }
924                 }
925                 *cs++ = c;
926                 if (--n <= 0 || c == '\n')
927                         break;
928         }
929         if (c == EOF && cs == s)
930                 return (NULL);
931         *cs++ = '\0';
932         if (debug)
933                 syslog(LOG_DEBUG, "command: %s", s);
934         return (s);
935 }
936
937 static VOIDRET toolong FUNCTION((input), int input)
938 {
939         time_t now;
940
941         reply(421, "Timeout (%d seconds): closing control connection.", timeout);
942         (void) time(&now);
943         syslog(LOG_INFO, "User %s timed out after %d seconds at %s",
944           (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
945         dologout(1);
946 }
947
948 int yylex FUNCTION_NOARGS
949 {
950         static int cpos, state;
951         register char *cp, *cp2;
952         register struct tab *p;
953         int n;
954         char c, *copy();
955
956         for (;;) {
957                 switch (state) {
958
959                 case CMD:
960                         (void) signal(SIGALRM, toolong);
961                         (void) alarm((unsigned) timeout);
962                         if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
963                                 reply(221, "You could at least say goodbye.");
964                                 dologout(0);
965                         }
966                         (void) alarm(0);
967 #ifdef SETPROCTITLE
968                         if (strncasecmp(cbuf, "PASS", 4) != NULL)
969                                 setproctitle("%s: %s", proctitle, cbuf);
970 #endif /* SETPROCTITLE */
971                         if ((cp = strchr(cbuf, '\r'))) {
972                                 *cp++ = '\n';
973                                 *cp = '\0';
974                         }
975                         if ((cp = strpbrk(cbuf, " \n")))
976                                 cpos = cp - cbuf;
977                         if (cpos == 0)
978                                 cpos = 4;
979                         c = cbuf[cpos];
980                         cbuf[cpos] = '\0';
981                         upper(cbuf);
982                         p = lookup(cmdtab, cbuf);
983                         cbuf[cpos] = c;
984                         if (p != 0) {
985                                 if (p->implemented == 0) {
986                                         nack(p->name);
987                                         longjmp(errcatch,0);
988                                         /* NOTREACHED */
989                                 }
990                                 state = p->state;
991                                 *(char **)&yylval = p->name;
992                                 return (p->token);
993                         }
994                         break;
995
996                 case SITECMD:
997                         if (cbuf[cpos] == ' ') {
998                                 cpos++;
999                                 return (SP);
1000                         }
1001                         cp = &cbuf[cpos];
1002                         if ((cp2 = strpbrk(cp, " \n")))
1003                                 cpos = cp2 - cbuf;
1004                         c = cbuf[cpos];
1005                         cbuf[cpos] = '\0';
1006                         upper(cp);
1007                         p = lookup(sitetab, cp);
1008                         cbuf[cpos] = c;
1009                         if (p != 0) {
1010                                 if (p->implemented == 0) {
1011                                         state = CMD;
1012                                         nack(p->name);
1013                                         longjmp(errcatch,0);
1014                                         /* NOTREACHED */
1015                                 }
1016                                 state = p->state;
1017                                 *(char **)&yylval = p->name;
1018                                 return (p->token);
1019                         }
1020                         state = CMD;
1021                         break;
1022
1023                 case OSTR:
1024                         if (cbuf[cpos] == '\n') {
1025                                 state = CMD;
1026                                 return (CRLF);
1027                         }
1028                         /* FALLTHROUGH */
1029
1030                 case STR1:
1031                 case ZSTR1:
1032                 dostr1:
1033                         if (cbuf[cpos] == ' ') {
1034                                 cpos++;
1035                                 state = state == OSTR ? STR2 : ++state;
1036                                 return (SP);
1037                         }
1038                         break;
1039
1040                 case ZSTR2:
1041                         if (cbuf[cpos] == '\n') {
1042                                 state = CMD;
1043                                 return (CRLF);
1044                         }
1045                         /* FALLTHROUGH */
1046
1047                 case STR2:
1048                         cp = &cbuf[cpos];
1049                         n = strlen(cp);
1050                         cpos += n - 1;
1051                         /*
1052                          * Make sure the string is nonempty and \n terminated.
1053                          */
1054                         if (n > 1 && cbuf[cpos] == '\n') {
1055                                 cbuf[cpos] = '\0';
1056                                 *(char **)&yylval = copy(cp);
1057                                 cbuf[cpos] = '\n';
1058                                 state = ARGS;
1059                                 return (STRING);
1060                         }
1061                         break;
1062
1063                 case NSTR:
1064                         if (cbuf[cpos] == ' ') {
1065                                 cpos++;
1066                                 return (SP);
1067                         }
1068                         if (isdigit(cbuf[cpos])) {
1069                                 cp = &cbuf[cpos];
1070                                 while (isdigit(cbuf[++cpos]))
1071                                         ;
1072                                 c = cbuf[cpos];
1073                                 cbuf[cpos] = '\0';
1074                                 yylval = atoi(cp);
1075                                 cbuf[cpos] = c;
1076                                 state = STR1;
1077                                 return (NUMBER);
1078                         }
1079                         state = STR1;
1080                         goto dostr1;
1081
1082                 case ARGS:
1083                         if (isdigit(cbuf[cpos])) {
1084                                 cp = &cbuf[cpos];
1085                                 while (isdigit(cbuf[++cpos]))
1086                                         ;
1087                                 c = cbuf[cpos];
1088                                 cbuf[cpos] = '\0';
1089                                 yylval = atoi(cp);
1090                                 cbuf[cpos] = c;
1091                                 return (NUMBER);
1092                         }
1093                         switch (cbuf[cpos++]) {
1094
1095                         case '\n':
1096                                 state = CMD;
1097                                 return (CRLF);
1098
1099                         case ' ':
1100                                 return (SP);
1101
1102                         case ',':
1103                                 return (COMMA);
1104
1105                         case 'A':
1106                         case 'a':
1107                                 return (A);
1108
1109                         case 'B':
1110                         case 'b':
1111                                 return (B);
1112
1113                         case 'C':
1114                         case 'c':
1115                                 return (C);
1116
1117                         case 'E':
1118                         case 'e':
1119                                 return (E);
1120
1121                         case 'F':
1122                         case 'f':
1123                                 return (F);
1124
1125                         case 'I':
1126                         case 'i':
1127                                 return (I);
1128
1129                         case 'L':
1130                         case 'l':
1131                                 return (L);
1132
1133                         case 'N':
1134                         case 'n':
1135                                 return (N);
1136
1137                         case 'P':
1138                         case 'p':
1139                                 return (P);
1140
1141                         case 'R':
1142                         case 'r':
1143                                 return (R);
1144
1145                         case 'S':
1146                         case 's':
1147                                 return (S);
1148
1149                         case 'T':
1150                         case 't':
1151                                 return (T);
1152
1153                         }
1154                         break;
1155
1156                 default:
1157                         opiefatal("Unknown state in scanner.");
1158                 }
1159                 yyerror((char *) 0);
1160                 state = CMD;
1161                 longjmp(errcatch,0);
1162         }
1163 }
1164
1165 VOIDRET upper FUNCTION((s), char *s)
1166 {
1167         while (*s != '\0') {
1168                 if (islower(*s))
1169                         *s = toupper(*s);
1170                 s++;
1171         }
1172 }
1173
1174 char *copy FUNCTION((s), char *s)
1175 {
1176         char *p;
1177
1178         p = malloc((unsigned) strlen(s) + 1);
1179         if (p == NULL)
1180                 opiefatal("Ran out of memory.");
1181         (void) strcpy(p, s);
1182         return (p);
1183 }
1184
1185 VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s)
1186 {
1187         register struct tab *c;
1188         register int width, NCMDS;
1189         char *type;
1190
1191         if (ctab == sitetab)
1192                 type = "SITE ";
1193         else
1194                 type = "";
1195         width = 0, NCMDS = 0;
1196         for (c = ctab; c->name != NULL; c++) {
1197                 int len = strlen(c->name);
1198
1199                 if (len > width)
1200                         width = len;
1201                 NCMDS++;
1202         }
1203         width = (width + 8) &~ 7;
1204         if (s == 0) {
1205                 register int i, j, w;
1206                 int columns, lines;
1207
1208                 lreply(214, "The following %scommands are recognized %s.",
1209                     type, "(* =>'s unimplemented)");
1210                 columns = 76 / width;
1211                 if (columns == 0)
1212                         columns = 1;
1213                 lines = (NCMDS + columns - 1) / columns;
1214                 for (i = 0; i < lines; i++) {
1215                         printf("   ");
1216                         for (j = 0; j < columns; j++) {
1217                                 c = ctab + j * lines + i;
1218                                 printf("%s%c", c->name,
1219                                         c->implemented ? ' ' : '*');
1220                                 if (c + lines >= &ctab[NCMDS])
1221                                         break;
1222                                 w = strlen(c->name) + 1;
1223                                 while (w < width) {
1224                                         putchar(' ');
1225                                         w++;
1226                                 }
1227                         }
1228                         printf("\r\n");
1229                 }
1230                 (void) fflush(stdout);
1231                 reply(214, " ");
1232                 return;
1233         }
1234         upper(s);
1235         c = lookup(ctab, s);
1236         if (c == (struct tab *)0) {
1237                 reply(502, "Unknown command %s.", s);
1238                 return;
1239         }
1240         if (c->implemented)
1241                 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1242         else
1243                 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1244                     c->name, c->help);
1245 }
1246
1247 VOIDRET sizecmd FUNCTION((filename), char *filename)
1248 {
1249         switch (type) {
1250         case TYPE_L:
1251         case TYPE_I: {
1252                 struct stat stbuf;
1253                 if (stat(filename, &stbuf) < 0 ||
1254                     (stbuf.st_mode&S_IFMT) != S_IFREG)
1255                         reply(550, "%s: not a plain file.", filename);
1256                 else
1257                         reply(213, "%lu", stbuf.st_size);
1258                 break;}
1259         case TYPE_A: {
1260                 FILE *fin;
1261                 register int c;
1262                 register long count;
1263                 struct stat stbuf;
1264                 fin = fopen(filename, "r");
1265                 if (fin == NULL) {
1266                         perror_reply(550, filename);
1267                         return;
1268                 }
1269                 if (fstat(fileno(fin), &stbuf) < 0 ||
1270                     (stbuf.st_mode&S_IFMT) != S_IFREG) {
1271                         reply(550, "%s: not a plain file.", filename);
1272                         (void) fclose(fin);
1273                         return;
1274                 }
1275
1276                 count = 0;
1277                 while((c=getc(fin)) != EOF) {
1278                         if (c == '\n')  /* will get expanded to \r\n */
1279                                 count++;
1280                         count++;
1281                 }
1282                 (void) fclose(fin);
1283
1284                 reply(213, "%ld", count);
1285                 break;}
1286         default:
1287                 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1288         }
1289 }