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