BSD 4_3_Net_2 development
[unix-history] / usr / src / contrib / isode / ftp-ftam / ftpcmd.y
CommitLineData
53102063
C
1/* ftpcmd.y - grammar for FTP commands */
2
3/*
4 * $Header: /f/osi/ftp-ftam/RCS/ftpcmd.y,v 7.1 91/02/22 09:24:20 mrose Interim $
5 *
6 * $Log: ftpcmd.y,v $
7 * Revision 7.1 91/02/22 09:24:20 mrose
8 * Interim 6.8
9 *
10 * Revision 7.0 89/11/23 21:55:19 mrose
11 * Release 6.0
12 *
13 */
14
15/*
16 * NOTICE
17 *
18 * Acquisition, use, and distribution of this module and related
19 * materials are subject to the restrictions of a license agreement.
20 * Consult the Preface in the User's Manual for the full terms of
21 * this agreement.
22 *
23 */
24
25
26/*
27 * Shamelessly taken from UCB
28 */
29
30/*
31 * Grammar for FTP commands.
32 * See RFC 765.
33 */
34
35%{
36
37#ifndef lint
38static char *rcsid = "$Header: /f/osi/ftp-ftam/RCS/ftpcmd.y,v 7.1 91/02/22 09:24:20 mrose Interim $";
39#endif
40
41#include "config.h"
42#include "internet.h"
43
44#include <arpa/ftp.h>
45
46#include <stdio.h>
47#include <signal.h>
48#include <ctype.h>
49#include <pwd.h>
50#include <setjmp.h>
51#include "manifest.h"
52#include "general.h"
53void advise();
54time_t time ();
55
56extern struct sockaddr_in data_dest;
57extern int logged_in;
58extern int logging;
59extern int type;
60extern int form;
61extern int debug;
62extern int timeout;
63extern char hostname[];
64extern int usedefault;
65extern char *osi_host;
66extern char *ftp_user;
67extern char *ftp_passwd;
68extern char *ftp_account;
69extern int verbose;
70
71static int cmd_type;
72static int cmd_form;
73static int cmd_bytesz;
74
75char *index();
76char *savestr();
77%}
78
79%token
80 A B C E F I
81 L N P R S T
82
83 SP CRLF COMMA STRING NUMBER
84
85 USER PASS ACCT REIN QUIT PORT
86 PASV TYPE STRU MODE RETR STOR
87 APPE MLFL MAIL MSND MSOM MSAM
88 MRSQ MRCP ALLO REST RNFR RNTO
89 ABOR DELE CWD LIST NLST SITE
90 STAT HELP NOOP XMKD XRMD XPWD
91 XCUP
92
93 LEXERR
94
95%start cmd_list
96
97%%
98
99cmd_list: /* empty */
100 | cmd_list cmd
101 ;
102
103cmd: USER SP username CRLF
104 = {
105 /* remote host information may appear in user
106 * name as user@osihost. Save user and hostname until
107 * all neccessary information is gathered.
108 */
109 char *op;
110
111 ftp_user = savestr((char*)$3);
112 if (op = index (ftp_user, '@')) {
113 *op++ = NULL;
114 osi_host = op;
115 }
116
117 if (strcmp(ftp_user, "ftp") == 0 ||
118 strcmp(ftp_user, "anonymous") == 0) {
119 free(ftp_user);
120 ftp_user = "ANON";
121 reply(331,
122 "Guest login ok, send ident as password.");
123 } else if (checkuser(ftp_user)) {
124 reply(331, "Password required for \"%s\".", $3);
125 } else
126 reply(500,"User disallowed");
127 free((char*)$3);
128 }
129 | PASS SP password CRLF
130 = {
131 /* Try and login. dologin() checks if it has
132 * all the neccessary information to try and login.
133 * Appropriate response codes are generated.
134 */
135 ftp_passwd = savestr((char*)$3);
136 logged_in = dologin();
137 free((char*)$3);
138 }
139 | SITE SP osi_hostname CRLF
140 = {
141 osi_host = savestr((char*)$3);
142 logged_in = ftp_passwd ? dologin() : 0;
143 free((char*)$3);
144 }
145 | ACCT SP account CRLF
146 = {
147 ftp_account = savestr((char*)$3);
148 logged_in = dologin();
149 free((char*)$3);
150 }
151 | PORT SP host_port CRLF
152 = {
153 usedefault = 0;
154 ack((char*)$1);
155 }
156 | TYPE SP type_code CRLF
157 = {
158 /* The ISODE supports three file types:
159 * binary
160 * text
161 * directory
162 * Binary and Text are selected here.
163 * Directory file types are used for LIST and NLST
164 */
165 switch (cmd_type) {
166
167 case TYPE_A:
168 if (cmd_form == FORM_N &&
169 f_type(TYPE_A) != NOTOK) {
170 reply(200, "Type set to A.");
171 type = cmd_type;
172 form = cmd_form;
173 } else
174 reply(504, "TYPE set error.");
175 break;
176
177 case TYPE_E:
178 reply(504, "Type E not implemented.");
179 break;
180
181 case TYPE_I:
182 if (f_type(TYPE_I) == OK){
183 reply(200, "Type set to I.");
184 type = cmd_type;
185 } else
186 reply(504, "TYPE set error.");
187 break;
188
189 case TYPE_L:
190 if (cmd_bytesz == 8 && f_type(TYPE_L) == OK) {
191 reply(200,
192 "Type set to L (byte size 8).");
193 type = cmd_type;
194 } else
195 reply(504, "TYPE set error.");
196 }
197 }
198 | STRU SP struct_code CRLF
199 = {
200 switch ($3) {
201
202 case STRU_F:
203 reply(200, "STRU F ok.");
204 break;
205
206 default:
207 reply(502, "Unimplemented STRU type.");
208 }
209 }
210 | MODE SP mode_code CRLF
211 = {
212 switch ($3) {
213
214 case MODE_S:
215 reply(200, "MODE S ok.");
216 break;
217
218 default:
219 reply(502, "Unimplemented MODE type.");
220 }
221 }
222 | ALLO SP NUMBER CRLF
223 = {
224 ack((char*)$1);
225 }
226 | RETR check_login SP pathname CRLF
227 = {
228 if ($2 && $4 != NULL)
229 retrieve((char*)$4);
230 if ($4 != NULL)
231 free((char*)$4);
232 }
233 | STOR check_login SP pathname CRLF
234 = {
235 if ($2 && $4 != NULL)
236 ftp_store((char*)$4, "w");
237 if ($4 != NULL)
238 free((char*)$4);
239 }
240 | APPE check_login SP pathname CRLF
241 = {
242 if ($2 && $4 != NULL)
243 ftp_store((char *)$4, "a");
244 if ($4 != NULL)
245 free((char*)$4);
246 }
247 | NLST check_login CRLF
248 = {
249 if ($2)
250 directory("NLST",".");
251 }
252 | NLST check_login SP pathname CRLF
253 = {
254 if ($2 && $4 != NULL)
255 directory("NLST", (char*)$4);
256 if ($4 != NULL)
257 free((char*)$4);
258 }
259 | LIST check_login CRLF
260 = {
261 if ($2)
262 directory("LIST", ".");
263 }
264 | LIST check_login SP pathname CRLF
265 = {
266 if ($2 && $4 != NULL)
267 directory("LIST", (char*)$4);
268 if ($4 != NULL)
269 free((char*)$4);
270 }
271 | DELE check_login SP pathname CRLF
272 = {
273 if ($2 && $4 != NULL)
274 ftp_delete((char*)$4);
275 if ($4 != NULL)
276 free((char*)$4);
277 }
278 | rename_cmd
279 | HELP CRLF
280 = {
281 help((char*)0);
282 }
283 | HELP SP STRING CRLF
284 = {
285 help((char*)$3);
286 }
287 | NOOP CRLF
288 = {
289 ack((char*)$1);
290 }
291 | XMKD check_login SP pathname CRLF
292 = {
293 if ($2 && $4 != NULL)
294 (void)makedir((char*)$4);
295 if ($4 != NULL)
296 free((char*)$4);
297 }
298 | XRMD check_login SP pathname CRLF
299 = {
300 if ($2 && $4 != NULL)
301 removedir((char*)$4);
302 if ($4 != NULL)
303 free((char*)$4);
304 }
305 | QUIT CRLF
306 = {
307 dologout(0);
308 }
309 | error CRLF
310 = {
311 yyerrok;
312 }
313 ;
314
315username: STRING
316 ;
317
318password: STRING
319 ;
320
321osi_hostname: STRING
322 ;
323account: STRING
324 ;
325
326byte_size: NUMBER
327 ;
328
329host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
330 NUMBER COMMA NUMBER
331 = {
332 register char *a, *p;
333
334 a = (char *)&data_dest.sin_addr;
335 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
336 p = (char *)&data_dest.sin_port;
337 p[0] = $9; p[1] = $11;
338 data_dest.sin_family = AF_INET;
339 }
340 ;
341
342form_code: N
343 = {
344 $$ = FORM_N;
345 }
346 | T
347 = {
348 $$ = FORM_T;
349 }
350 | C
351 = {
352 $$ = FORM_C;
353 }
354 ;
355
356type_code: A
357 = {
358 cmd_type = TYPE_A;
359 cmd_form = FORM_N;
360 }
361 | A SP form_code
362 = {
363 cmd_type = TYPE_A;
364 cmd_form = $3;
365 }
366 | E
367 = {
368 cmd_type = TYPE_E;
369 cmd_form = FORM_N;
370 }
371 | E SP form_code
372 = {
373 cmd_type = TYPE_E;
374 cmd_form = $3;
375 }
376 | I
377 = {
378 cmd_type = TYPE_I;
379 }
380 | L
381 = {
382 cmd_type = TYPE_L;
383 cmd_bytesz = 8;
384 }
385 | L SP byte_size
386 = {
387 cmd_type = TYPE_L;
388 cmd_bytesz = $3;
389 }
390 /* this is for a bug in the BBN ftp */
391 | L byte_size
392 = {
393 cmd_type = TYPE_L;
394 cmd_bytesz = $2;
395 }
396 ;
397
398struct_code: F
399 = {
400 $$ = STRU_F;
401 }
402 | R
403 = {
404 $$ = STRU_R;
405 }
406 | P
407 = {
408 $$ = STRU_P;
409 }
410 ;
411
412mode_code: S
413 = {
414 $$ = MODE_S;
415 }
416 | B
417 = {
418 $$ = MODE_B;
419 }
420 | C
421 = {
422 $$ = MODE_C;
423 }
424 ;
425
426pathname: pathstring
427 = {
428 $$ = $1;
429 }
430 ;
431
432pathstring: STRING
433 ;
434
435rename_cmd: rename_from rename_to
436 = {
437 if ($1 && $2)
438 renamecmd((char*)$1, (char*)$2);
439 else
440 reply(503, "Bad sequence of commands.");
441 if ($1)
442 free((char*)$1);
443 if ($2)
444 free((char*)$2);
445 }
446 ;
447
448rename_from: RNFR check_login SP pathname CRLF
449 = {
450 char *from = 0, *renamefrom();
451
452 if ($2 && $4)
453 from = renamefrom((char*)$4);
454 if (from == 0 && $4)
455 free((char*)$4);
456 $$ = (int)from;
457 }
458 ;
459
460rename_to: RNTO SP pathname CRLF
461 = {
462 $$ = $3;
463 }
464 ;
465
466check_login: /* empty */
467 = {
468 if (logged_in)
469 $$ = 1;
470 else {
471 reply(530, "Please login with USER and PASS.");
472 $$ = 0;
473 }
474 }
475 ;
476
477%%
478
479extern jmp_buf errcatch;
480
481#define CMD 0 /* beginning of command */
482#define ARGS 1 /* expect miscellaneous arguments */
483#define STR1 2 /* expect SP followed by STRING */
484#define STR2 3 /* expect STRING */
485#define OSTR 4 /* optional STRING */
486
487struct tab {
488 char *name;
489 short token;
490 short state;
491 short implemented; /* 1 if command is implemented */
492 char *help;
493};
494
495struct tab cmdtab[] = { /* In order defined in RFC 765 */
496 { "USER", USER, STR1, 1, "<sp> username" },
497 { "PASS", PASS, STR1, 1, "<sp> password" },
498 { "ACCT", ACCT, STR1, 1, "(specify account)" },
499 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
500 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
501 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
502 { "PASV", PASV, ARGS, 0, "(set server in passive mode)" },
503 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
504 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
505 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
506 { "RETR", RETR, STR1, 1, "<sp> file-name" },
507 { "STOR", STOR, STR1, 1, "<sp> file-name" },
508 { "APPE", APPE, STR1, 1, "<sp> file-name" },
509 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
510 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
511 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
512 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
513 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
514 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
515 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
516 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
517 { "REST", REST, STR1, 0, "(restart command)" },
518 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
519 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
520 { "ABOR", ABOR, ARGS, 0, "(abort operation)" },
521 { "DELE", DELE, STR1, 1, "<sp> file-name" },
522 /* Most directory oriented commands (except XMKD and XRMD) are
523 * not supported. The ISODE FTAM requires more knowledge
524 * about the remote filesystem type than is available through FTP.
525 */
526 { "CWD", CWD, OSTR, 0, "[ <sp> directory-name]" },
527 { "XCWD", CWD, OSTR, 0, "[ <sp> directory-name ]" },
528 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
529 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
530 { "SITE", SITE, STR1, 1, "(get site parameters)" },
531 { "STAT", STAT, OSTR, 0, "(get server status)" },
532 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
533 { "NOOP", NOOP, ARGS, 1, "" },
534 { "XMKD", XMKD, STR1, 1, "<sp> path-name" },
535 { "XRMD", XRMD, STR1, 1, "<sp> path-name" },
536 { "XPWD", XPWD, ARGS, 0, "(return current directory)" },
537 { "XCUP", XCUP, ARGS, 0, "(change to parent directory)" },
538 { NULL, 0, 0, 0, 0 }
539};
540
541struct tab *
542lookup(cmd)
543 char *cmd;
544{
545 register struct tab *p;
546
547 for (p = cmdtab; p->name != NULL; p++)
548 if (strcmp(cmd, p->name) == 0)
549 return (p);
550 return (0);
551}
552
553#include <arpa/telnet.h>
554
555/*
556 * getline - a hacked up version of fgets to ignore TELNET escape codes.
557 */
558char *
559getline(s, n, iop)
560 char *s;
561 register FILE *iop;
562{
563 register c;
564 register char *cs;
565
566 cs = s;
567 while (--n > 0 && (c = getc(iop)) >= 0) {
568 while (c == IAC) {
569 c = getc(iop); /* skip command */
570 c = getc(iop); /* try next char */
571 }
572 *cs++ = c;
573 if (c=='\n')
574 break;
575 }
576 if (c < 0 && cs == s)
577 exit(1);
578 *cs++ = '\0';
579 if (verbose)
580 advise (NULLCP, "---> %s", s);
581 return (s);
582}
583
584static SFD
585toolong()
586{
587 long now;
588 extern char *ctime();
589
590 reply(421,
591 "Timeout (%d seconds): closing control connection.", timeout);
592 (void)time(&now);
593 if (logging)
594 advise(NULLCP,
595 "user %s timed out after %d seconds at %s",
596 ftp_user, timeout, ctime(&now));
597 dologout(1);
598}
599
600yylex()
601{
602 static char cbuf[512];
603 static int cpos, state;
604 register char *cp;
605 register struct tab *p;
606 int n;
607 char c;
608
609 for (;;) {
610 switch (state) {
611
612 case CMD:
613 (void)signal(SIGALRM, toolong);
614 (void)alarm((unsigned)timeout);
615 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
616 dologout(0);
617 }
618 (void)alarm(0);
619 if (index(cbuf, '\r')) {
620 cp = index(cbuf, '\r');
621 cp[0] = '\n'; cp[1] = 0;
622 }
623 if (index(cbuf, ' '))
624 cpos = index(cbuf, ' ') - cbuf;
625 else
626 cpos = 4;
627 c = cbuf[cpos];
628 cbuf[cpos] = '\0';
629 upper(cbuf);
630 p = lookup(cbuf);
631 cbuf[cpos] = c;
632 if (p != 0) {
633 if (p->implemented == 0) {
634 nack(p->name);
635 longjmp(errcatch,NOTOK);
636 /* NOTREACHED */
637 }
638 state = p->state;
639 yylval = (int) p->name;
640 return (p->token);
641 }
642 break;
643
644 case OSTR:
645 if (cbuf[cpos] == '\n') {
646 state = CMD;
647 return (CRLF);
648 }
649 /* FALL THRU */
650
651 case STR1:
652 if (cbuf[cpos] == ' ') {
653 /* trim leading blanks */
654 for(;cbuf[cpos] == ' ';cpos++);
655 state = STR2;
656 return (SP);
657 }
658 break;
659
660 case STR2:
661 cp = &cbuf[cpos];
662 n = strlen(cp);
663 cpos += n - 1;
664 /*
665 * Make sure the string is nonempty and \n terminated.
666 */
667 if (n > 1 && cbuf[cpos] == '\n') {
668 cbuf[cpos] = '\0';
669 yylval = copy(cp);
670 cbuf[cpos] = '\n';
671 state = ARGS;
672 return (STRING);
673 }
674 break;
675
676 case ARGS:
677 if (isdigit(cbuf[cpos])) {
678 cp = &cbuf[cpos];
679 while (isdigit(cbuf[++cpos]))
680 ;
681 c = cbuf[cpos];
682 cbuf[cpos] = '\0';
683 yylval = atoi(cp);
684 cbuf[cpos] = c;
685 return (NUMBER);
686 }
687 switch (cbuf[cpos++]) {
688
689 case '\n':
690 state = CMD;
691 return (CRLF);
692
693 case ' ':
694 return (SP);
695
696 case ',':
697 return (COMMA);
698
699 case 'A':
700 case 'a':
701 return (A);
702
703 case 'B':
704 case 'b':
705 return (B);
706
707 case 'C':
708 case 'c':
709 return (C);
710
711 case 'E':
712 case 'e':
713 return (E);
714
715 case 'F':
716 case 'f':
717 return (F);
718
719 case 'I':
720 case 'i':
721 return (I);
722
723 case 'L':
724 case 'l':
725 return (L);
726
727 case 'N':
728 case 'n':
729 return (N);
730
731 case 'P':
732 case 'p':
733 return (P);
734
735 case 'R':
736 case 'r':
737 return (R);
738
739 case 'S':
740 case 's':
741 return (S);
742
743 case 'T':
744 case 't':
745 return (T);
746
747 }
748 break;
749
750 default:
751 fatal("Unknown state in scanner.");
752 }
753 yyerror("");
754 state = CMD;
755 longjmp(errcatch,NOTOK);
756 }
757}
758
759upper(s)
760 char *s;
761{
762 while (*s != '\0') {
763 if (islower(*s))
764 *s = toupper(*s);
765 s++;
766 }
767}
768
769copy(s)
770 char *s;
771{
772 char *p;
773 extern char *malloc();
774
775 p = malloc((unsigned) (strlen(s) + 1));
776 if (p == NULL)
777 fatal("Ran out of memory.");
778 (void)strcpy(p, s);
779 return ((int)p);
780}
781
782help(s)
783 char *s;
784{
785 register struct tab *c;
786 register int width, NCMDS;
787
788 width = 0, NCMDS = 0;
789 for (c = cmdtab; c->name != NULL; c++) {
790 int len = strlen(c->name);
791
792 if (c->implemented == 0)
793 len++;
794 if (len > width)
795 width = len;
796 NCMDS++;
797 }
798 width = (width + 8) &~ 7;
799 if (s == 0) {
800 register int i, j, w;
801 int columns, lines;
802
803 lreply(214,
804 "The following commands are recognized (* =>'s unimplemented).");
805 columns = 76 / width;
806 if (columns == 0)
807 columns = 1;
808 lines = (NCMDS + columns - 1) / columns;
809 for (i = 0; i < lines; i++) {
810 printf(" ");
811 for (j = 0; j < columns; j++) {
812 c = cmdtab + j * lines + i;
813 printf("%s%c", c->name,
814 c->implemented ? ' ' : '*');
815 if (c + lines >= &cmdtab[NCMDS])
816 break;
817 w = strlen(c->name);
818 while (w < width) {
819 putchar(' ');
820 w++;
821 }
822 }
823 printf("\r\n");
824 }
825 (void)fflush(stdout);
826 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
827 return;
828 }
829 upper(s);
830 c = lookup(s);
831 if (c == (struct tab *)0) {
832 reply(504, "Unknown command %s.", s);
833 return;
834 }
835 if (c->implemented)
836 reply(214, "Syntax: %s %s", c->name, c->help);
837 else
838 reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
839}