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