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