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