ftpcmd.y revision 1.40 1 1.40 lukem /* $NetBSD: ftpcmd.y,v 1.40 1999/12/07 05:30:54 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.40 lukem __RCSID("$NetBSD: ftpcmd.y,v 1.40 1999/12/07 05:30:54 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.32 itojun #include <netdb.h>
76 1.26 explorer
77 1.26 explorer #ifdef KERBEROS5
78 1.36 christos #include <krb5/krb5.h>
79 1.26 explorer #endif
80 1.4 deraadt
81 1.4 deraadt #include "extern.h"
82 1.1 cgd
83 1.1 cgd off_t restart_point;
84 1.1 cgd
85 1.1 cgd static int cmd_type;
86 1.1 cgd static int cmd_form;
87 1.1 cgd static int cmd_bytesz;
88 1.1 cgd char cbuf[512];
89 1.1 cgd char *fromname;
90 1.22 lukem int hasyyerrored;
91 1.1 cgd
92 1.24 lukem extern jmp_buf errcatch;
93 1.24 lukem
94 1.1 cgd %}
95 1.1 cgd
96 1.4 deraadt %union {
97 1.4 deraadt int i;
98 1.4 deraadt char *s;
99 1.4 deraadt }
100 1.4 deraadt
101 1.1 cgd %token
102 1.1 cgd A B C E F I
103 1.1 cgd L N P R S T
104 1.32 itojun ALL
105 1.1 cgd
106 1.4 deraadt SP CRLF COMMA
107 1.1 cgd
108 1.23 lukem USER PASS ACCT CWD CDUP SMNT
109 1.23 lukem QUIT REIN PORT PASV TYPE STRU
110 1.23 lukem MODE RETR STOR STOU APPE ALLO
111 1.23 lukem REST RNFR RNTO ABOR DELE RMD
112 1.23 lukem MKD PWD LIST NLST SITE SYST
113 1.23 lukem STAT HELP NOOP
114 1.23 lukem
115 1.25 lukem AUTH ADAT PROT PBSZ CCC MIC
116 1.25 lukem CONF ENC
117 1.25 lukem
118 1.23 lukem FEAT OPTS
119 1.21 lukem
120 1.21 lukem SIZE MDTM
121 1.1 cgd
122 1.32 itojun LPRT LPSV EPRT EPSV
123 1.32 itojun
124 1.23 lukem MAIL MLFL MRCP MRSQ MSAM MSND
125 1.23 lukem MSOM
126 1.23 lukem
127 1.1 cgd UMASK IDLE CHMOD
128 1.1 cgd
129 1.1 cgd LEXERR
130 1.1 cgd
131 1.4 deraadt %token <s> STRING
132 1.32 itojun %token <s> ALL
133 1.4 deraadt %token <i> NUMBER
134 1.4 deraadt
135 1.12 lukem %type <i> check_login check_modify octal_number byte_size
136 1.25 lukem %type <i> struct_code mode_code type_code form_code decimal_integer
137 1.4 deraadt %type <s> pathstring pathname password username
138 1.25 lukem %type <s> mechanism_name base64data prot_code
139 1.4 deraadt
140 1.1 cgd %start cmd_list
141 1.1 cgd
142 1.1 cgd %%
143 1.1 cgd
144 1.4 deraadt cmd_list
145 1.4 deraadt : /* empty */
146 1.23 lukem
147 1.4 deraadt | cmd_list cmd
148 1.4 deraadt {
149 1.22 lukem fromname = NULL;
150 1.1 cgd restart_point = (off_t) 0;
151 1.1 cgd }
152 1.23 lukem
153 1.4 deraadt | cmd_list rcmd
154 1.23 lukem
155 1.1 cgd ;
156 1.1 cgd
157 1.4 deraadt cmd
158 1.23 lukem /* RFC 959 */
159 1.4 deraadt : USER SP username CRLF
160 1.4 deraadt {
161 1.4 deraadt user($3);
162 1.4 deraadt free($3);
163 1.4 deraadt }
164 1.23 lukem
165 1.4 deraadt | PASS SP password CRLF
166 1.4 deraadt {
167 1.4 deraadt pass($3);
168 1.4 deraadt free($3);
169 1.1 cgd }
170 1.23 lukem
171 1.23 lukem | CWD check_login CRLF
172 1.23 lukem {
173 1.23 lukem if ($2)
174 1.23 lukem cwd(pw->pw_dir);
175 1.23 lukem }
176 1.23 lukem
177 1.23 lukem | CWD check_login SP pathname CRLF
178 1.23 lukem {
179 1.23 lukem if ($2 && $4 != NULL)
180 1.23 lukem cwd($4);
181 1.23 lukem if ($4 != NULL)
182 1.23 lukem free($4);
183 1.23 lukem }
184 1.23 lukem
185 1.23 lukem | CDUP check_login CRLF
186 1.23 lukem {
187 1.23 lukem if ($2)
188 1.23 lukem cwd("..");
189 1.23 lukem }
190 1.23 lukem
191 1.23 lukem | QUIT CRLF
192 1.23 lukem {
193 1.27 lukem if (logged_in) {
194 1.28 lukem lreply(221, "");
195 1.28 lukem lreply(0,
196 1.27 lukem "Data traffic for this session was %qd byte%s in %qd file%s.",
197 1.30 ross (qdfmt_t)total_data, PLURAL(total_data),
198 1.30 ross (qdfmt_t)total_files, PLURAL(total_files));
199 1.28 lukem lreply(0,
200 1.27 lukem "Total traffic for this session was %qd byte%s in %qd transfer%s.",
201 1.30 ross (qdfmt_t)total_bytes, PLURAL(total_bytes),
202 1.30 ross (qdfmt_t)total_xfers, PLURAL(total_xfers));
203 1.31 lukem }
204 1.31 lukem reply(221,
205 1.31 lukem "Thank you for using the FTP service on %s.",
206 1.31 lukem hostname);
207 1.31 lukem if (logged_in) {
208 1.27 lukem syslog(LOG_INFO,
209 1.27 lukem "Data traffic: %qd byte%s in %qd file%s",
210 1.30 ross (qdfmt_t)total_data, PLURAL(total_data),
211 1.30 ross (qdfmt_t)total_files, PLURAL(total_files));
212 1.27 lukem syslog(LOG_INFO,
213 1.27 lukem "Total traffic: %qd byte%s in %qd transfer%s",
214 1.30 ross (qdfmt_t)total_bytes, PLURAL(total_bytes),
215 1.30 ross (qdfmt_t)total_xfers, PLURAL(total_xfers));
216 1.27 lukem }
217 1.31 lukem
218 1.23 lukem dologout(0);
219 1.23 lukem }
220 1.23 lukem
221 1.15 mrg | PORT check_login SP host_port CRLF
222 1.4 deraadt {
223 1.22 lukem if ($2) {
224 1.22 lukem /* be paranoid, if told so */
225 1.16 mrg if (curclass.checkportcmd &&
226 1.32 itojun ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
227 1.32 itojun memcmp(&data_dest.su_sin.sin_addr,
228 1.32 itojun &his_addr.su_sin.sin_addr,
229 1.32 itojun sizeof(data_dest.su_sin.sin_addr)) != 0)) {
230 1.22 lukem reply(500,
231 1.22 lukem "Illegal PORT command rejected");
232 1.32 itojun } else if (epsvall) {
233 1.32 itojun reply(501, "PORT disallowed after EPSV ALL");
234 1.22 lukem } else {
235 1.22 lukem usedefault = 0;
236 1.22 lukem if (pdata >= 0) {
237 1.22 lukem (void) close(pdata);
238 1.22 lukem pdata = -1;
239 1.22 lukem }
240 1.22 lukem reply(200, "PORT command successful.");
241 1.15 mrg }
242 1.32 itojun
243 1.32 itojun }
244 1.32 itojun }
245 1.32 itojun
246 1.34 itojun | LPRT check_login SP host_long_port4 CRLF
247 1.32 itojun {
248 1.35 itojun /* reject invalid host_long_port4 */
249 1.35 itojun if (data_dest.su_family != AF_INET) {
250 1.35 itojun reply(500, "Illegal LPRT command rejected");
251 1.35 itojun return (NULL);
252 1.35 itojun }
253 1.32 itojun /* be paranoid, if told so */
254 1.32 itojun if (curclass.checkportcmd &&
255 1.34 itojun ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
256 1.34 itojun memcmp(&data_dest.su_sin.sin_addr,
257 1.34 itojun &his_addr.su_sin.sin_addr,
258 1.34 itojun sizeof(data_dest.su_sin.sin_addr)) != 0)) {
259 1.34 itojun reply(500, "Illegal LPRT command rejected");
260 1.34 itojun return (NULL);
261 1.34 itojun }
262 1.34 itojun if (epsvall)
263 1.34 itojun reply(501, "LPRT disallowed after EPSV ALL");
264 1.34 itojun else {
265 1.34 itojun usedefault = 0;
266 1.34 itojun if (pdata >= 0) {
267 1.34 itojun (void) close(pdata);
268 1.34 itojun pdata = -1;
269 1.34 itojun }
270 1.34 itojun reply(200, "LPRT command successful.");
271 1.34 itojun }
272 1.34 itojun }
273 1.34 itojun
274 1.34 itojun | LPRT check_login SP host_long_port6 CRLF
275 1.34 itojun {
276 1.35 itojun /* reject invalid host_long_port6 */
277 1.35 itojun if (data_dest.su_family != AF_INET6) {
278 1.35 itojun reply(500, "Illegal LPRT command rejected");
279 1.35 itojun return (NULL);
280 1.35 itojun }
281 1.34 itojun /* be paranoid, if told so */
282 1.34 itojun if (curclass.checkportcmd &&
283 1.34 itojun ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
284 1.34 itojun memcmp(&data_dest.su_sin6.sin6_addr,
285 1.34 itojun &his_addr.su_sin6.sin6_addr,
286 1.34 itojun sizeof(data_dest.su_sin6.sin6_addr)) != 0)) {
287 1.32 itojun reply(500, "Illegal LPRT command rejected");
288 1.32 itojun return (NULL);
289 1.32 itojun }
290 1.32 itojun if (epsvall)
291 1.32 itojun reply(501, "LPRT disallowed after EPSV ALL");
292 1.32 itojun else {
293 1.32 itojun usedefault = 0;
294 1.32 itojun if (pdata >= 0) {
295 1.32 itojun (void) close(pdata);
296 1.32 itojun pdata = -1;
297 1.32 itojun }
298 1.32 itojun reply(200, "LPRT command successful.");
299 1.1 cgd }
300 1.1 cgd }
301 1.23 lukem
302 1.32 itojun | EPRT check_login SP STRING CRLF
303 1.32 itojun {
304 1.32 itojun char *tmp = NULL;
305 1.32 itojun char *result[3];
306 1.32 itojun char *p, *q;
307 1.32 itojun char delim;
308 1.32 itojun struct addrinfo hints;
309 1.32 itojun struct addrinfo *res;
310 1.32 itojun int i;
311 1.32 itojun
312 1.32 itojun if (epsvall) {
313 1.32 itojun reply(501, "EPRT disallowed after EPSV ALL");
314 1.32 itojun goto eprt_done;
315 1.32 itojun }
316 1.32 itojun usedefault = 0;
317 1.32 itojun if (pdata >= 0) {
318 1.32 itojun (void) close(pdata);
319 1.32 itojun pdata = -1;
320 1.32 itojun }
321 1.32 itojun
322 1.32 itojun /*XXX checks for login */
323 1.32 itojun
324 1.40 lukem tmp = xstrdup($4);
325 1.32 itojun p = tmp;
326 1.32 itojun delim = p[0];
327 1.32 itojun p++;
328 1.32 itojun memset(result, 0, sizeof(result));
329 1.32 itojun for (i = 0; i < 3; i++) {
330 1.32 itojun q = strchr(p, delim);
331 1.32 itojun if (!q || *q != delim) {
332 1.32 itojun parsefail:
333 1.32 itojun reply(500, "Invalid argument, rejected.");
334 1.32 itojun if (tmp)
335 1.32 itojun free(tmp);
336 1.32 itojun usedefault = 1;
337 1.32 itojun goto eprt_done;
338 1.32 itojun }
339 1.32 itojun *q++ = '\0';
340 1.32 itojun result[i] = p;
341 1.32 itojun p = q;
342 1.32 itojun }
343 1.32 itojun
344 1.32 itojun /* some more sanity check */
345 1.32 itojun p = result[0];
346 1.32 itojun while (*p) {
347 1.32 itojun if (!isdigit(*p))
348 1.32 itojun goto parsefail;
349 1.32 itojun p++;
350 1.32 itojun }
351 1.32 itojun p = result[2];
352 1.32 itojun while (*p) {
353 1.32 itojun if (!isdigit(*p))
354 1.32 itojun goto parsefail;
355 1.32 itojun p++;
356 1.32 itojun }
357 1.32 itojun
358 1.32 itojun memset(&hints, 0, sizeof(hints));
359 1.32 itojun if (atoi(result[0]) == 1)
360 1.32 itojun hints.ai_family = PF_INET;
361 1.32 itojun if (atoi(result[0]) == 2)
362 1.32 itojun hints.ai_family = PF_INET6;
363 1.32 itojun else
364 1.32 itojun hints.ai_family = PF_UNSPEC; /*XXX*/
365 1.32 itojun hints.ai_socktype = SOCK_STREAM;
366 1.32 itojun if (getaddrinfo(result[1], result[2], &hints, &res))
367 1.32 itojun goto parsefail;
368 1.32 itojun memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
369 1.37 itojun if (his_addr.su_family == AF_INET6
370 1.37 itojun && data_dest.su_family == AF_INET6) {
371 1.37 itojun /* XXX more sanity checks! */
372 1.37 itojun data_dest.su_sin6.sin6_scope_id =
373 1.37 itojun his_addr.su_sin6.sin6_scope_id;
374 1.37 itojun }
375 1.32 itojun /* be paranoid, if told so */
376 1.32 itojun if (curclass.checkportcmd) {
377 1.32 itojun int fail;
378 1.32 itojun fail = 0;
379 1.32 itojun if (ntohs(data_dest.su_port) < IPPORT_RESERVED)
380 1.32 itojun fail++;
381 1.32 itojun if (data_dest.su_family != his_addr.su_family)
382 1.32 itojun fail++;
383 1.32 itojun if (data_dest.su_len != his_addr.su_len)
384 1.32 itojun fail++;
385 1.32 itojun switch (data_dest.su_family) {
386 1.32 itojun case AF_INET:
387 1.32 itojun fail += memcmp(&data_dest.su_sin.sin_addr,
388 1.32 itojun &his_addr.su_sin.sin_addr,
389 1.32 itojun sizeof(data_dest.su_sin.sin_addr));
390 1.32 itojun break;
391 1.32 itojun case AF_INET6:
392 1.32 itojun fail += memcmp(&data_dest.su_sin6.sin6_addr,
393 1.32 itojun &his_addr.su_sin6.sin6_addr,
394 1.32 itojun sizeof(data_dest.su_sin6.sin6_addr));
395 1.32 itojun break;
396 1.32 itojun default:
397 1.32 itojun fail++;
398 1.32 itojun }
399 1.32 itojun if (fail) {
400 1.32 itojun reply(500,
401 1.32 itojun "Illegal EPRT command rejected");
402 1.32 itojun return (NULL);
403 1.32 itojun }
404 1.32 itojun }
405 1.32 itojun free(tmp);
406 1.32 itojun tmp = NULL;
407 1.33 itojun if (pdata >= 0) {
408 1.33 itojun (void) close(pdata);
409 1.33 itojun pdata = -1;
410 1.33 itojun }
411 1.32 itojun reply(200, "EPRT command successful.");
412 1.32 itojun eprt_done:;
413 1.32 itojun }
414 1.32 itojun
415 1.20 tv | PASV check_login CRLF
416 1.4 deraadt {
417 1.20 tv if (curclass.passive) {
418 1.20 tv passive();
419 1.20 tv } else {
420 1.20 tv reply(500, "PASV mode not available.");
421 1.20 tv }
422 1.1 cgd }
423 1.23 lukem
424 1.32 itojun | LPSV CRLF
425 1.32 itojun {
426 1.32 itojun if (epsvall)
427 1.32 itojun reply(501, "LPSV disallowed after EPSV ALL");
428 1.32 itojun else
429 1.34 itojun long_passive("LPSV", PF_UNSPEC);
430 1.32 itojun }
431 1.32 itojun
432 1.32 itojun | EPSV SP NUMBER CRLF
433 1.32 itojun {
434 1.32 itojun int pf;
435 1.32 itojun switch ($3) {
436 1.32 itojun case 1:
437 1.32 itojun pf = PF_INET;
438 1.32 itojun break;
439 1.32 itojun case 2:
440 1.32 itojun pf = PF_INET6;
441 1.32 itojun break;
442 1.32 itojun default:
443 1.32 itojun pf = -1; /*junk*/
444 1.32 itojun break;
445 1.32 itojun }
446 1.32 itojun long_passive("EPSV", pf);
447 1.32 itojun }
448 1.32 itojun
449 1.32 itojun | EPSV SP ALL CRLF
450 1.32 itojun {
451 1.32 itojun if (!logged_in) {
452 1.32 itojun syslog(LOG_NOTICE, "long passive but not logged in");
453 1.32 itojun reply(503, "Login with USER first.");
454 1.32 itojun } else {
455 1.32 itojun reply(200, "EPSV ALL command successful.");
456 1.32 itojun epsvall++;
457 1.32 itojun }
458 1.32 itojun }
459 1.32 itojun
460 1.32 itojun | EPSV CRLF
461 1.32 itojun {
462 1.32 itojun long_passive("EPSV", PF_UNSPEC);
463 1.32 itojun }
464 1.32 itojun
465 1.4 deraadt | TYPE SP type_code CRLF
466 1.4 deraadt {
467 1.1 cgd switch (cmd_type) {
468 1.1 cgd
469 1.1 cgd case TYPE_A:
470 1.1 cgd if (cmd_form == FORM_N) {
471 1.1 cgd reply(200, "Type set to A.");
472 1.1 cgd type = cmd_type;
473 1.1 cgd form = cmd_form;
474 1.1 cgd } else
475 1.1 cgd reply(504, "Form must be N.");
476 1.1 cgd break;
477 1.1 cgd
478 1.1 cgd case TYPE_E:
479 1.1 cgd reply(504, "Type E not implemented.");
480 1.1 cgd break;
481 1.1 cgd
482 1.1 cgd case TYPE_I:
483 1.1 cgd reply(200, "Type set to I.");
484 1.1 cgd type = cmd_type;
485 1.1 cgd break;
486 1.1 cgd
487 1.1 cgd case TYPE_L:
488 1.1 cgd #if NBBY == 8
489 1.1 cgd if (cmd_bytesz == 8) {
490 1.1 cgd reply(200,
491 1.1 cgd "Type set to L (byte size 8).");
492 1.1 cgd type = cmd_type;
493 1.1 cgd } else
494 1.1 cgd reply(504, "Byte size must be 8.");
495 1.1 cgd #else /* NBBY == 8 */
496 1.1 cgd UNIMPLEMENTED for NBBY != 8
497 1.1 cgd #endif /* NBBY == 8 */
498 1.1 cgd }
499 1.1 cgd }
500 1.23 lukem
501 1.4 deraadt | STRU SP struct_code CRLF
502 1.4 deraadt {
503 1.1 cgd switch ($3) {
504 1.1 cgd
505 1.1 cgd case STRU_F:
506 1.1 cgd reply(200, "STRU F ok.");
507 1.1 cgd break;
508 1.1 cgd
509 1.1 cgd default:
510 1.1 cgd reply(504, "Unimplemented STRU type.");
511 1.1 cgd }
512 1.1 cgd }
513 1.23 lukem
514 1.4 deraadt | MODE SP mode_code CRLF
515 1.4 deraadt {
516 1.1 cgd switch ($3) {
517 1.1 cgd
518 1.1 cgd case MODE_S:
519 1.1 cgd reply(200, "MODE S ok.");
520 1.1 cgd break;
521 1.1 cgd
522 1.1 cgd default:
523 1.1 cgd reply(502, "Unimplemented MODE type.");
524 1.1 cgd }
525 1.1 cgd }
526 1.23 lukem
527 1.4 deraadt | RETR check_login SP pathname CRLF
528 1.4 deraadt {
529 1.1 cgd if ($2 && $4 != NULL)
530 1.22 lukem retrieve(NULL, $4);
531 1.1 cgd if ($4 != NULL)
532 1.4 deraadt free($4);
533 1.1 cgd }
534 1.23 lukem
535 1.4 deraadt | STOR check_login SP pathname CRLF
536 1.4 deraadt {
537 1.1 cgd if ($2 && $4 != NULL)
538 1.4 deraadt store($4, "w", 0);
539 1.1 cgd if ($4 != NULL)
540 1.4 deraadt free($4);
541 1.1 cgd }
542 1.23 lukem
543 1.23 lukem | STOU check_login SP pathname CRLF
544 1.4 deraadt {
545 1.1 cgd if ($2 && $4 != NULL)
546 1.23 lukem store($4, "w", 1);
547 1.1 cgd if ($4 != NULL)
548 1.4 deraadt free($4);
549 1.1 cgd }
550 1.23 lukem
551 1.23 lukem | APPE check_login SP pathname CRLF
552 1.4 deraadt {
553 1.4 deraadt if ($2 && $4 != NULL)
554 1.23 lukem store($4, "a", 0);
555 1.1 cgd if ($4 != NULL)
556 1.4 deraadt free($4);
557 1.1 cgd }
558 1.23 lukem
559 1.23 lukem | ALLO SP NUMBER CRLF
560 1.4 deraadt {
561 1.23 lukem reply(202, "ALLO command ignored.");
562 1.1 cgd }
563 1.23 lukem
564 1.23 lukem | ALLO SP NUMBER SP R SP NUMBER CRLF
565 1.4 deraadt {
566 1.23 lukem reply(202, "ALLO command ignored.");
567 1.1 cgd }
568 1.23 lukem
569 1.4 deraadt | RNTO SP pathname CRLF
570 1.4 deraadt {
571 1.1 cgd if (fromname) {
572 1.4 deraadt renamecmd(fromname, $3);
573 1.1 cgd free(fromname);
574 1.22 lukem fromname = NULL;
575 1.1 cgd } else {
576 1.1 cgd reply(503, "Bad sequence of commands.");
577 1.1 cgd }
578 1.4 deraadt free($3);
579 1.1 cgd }
580 1.23 lukem
581 1.4 deraadt | ABOR CRLF
582 1.4 deraadt {
583 1.1 cgd reply(225, "ABOR command successful.");
584 1.1 cgd }
585 1.23 lukem
586 1.23 lukem | DELE check_modify SP pathname CRLF
587 1.4 deraadt {
588 1.23 lukem if ($2 && $4 != NULL)
589 1.23 lukem delete($4);
590 1.23 lukem if ($4 != NULL)
591 1.23 lukem free($4);
592 1.1 cgd }
593 1.23 lukem
594 1.23 lukem | RMD check_modify SP pathname CRLF
595 1.4 deraadt {
596 1.1 cgd if ($2 && $4 != NULL)
597 1.23 lukem removedir($4);
598 1.1 cgd if ($4 != NULL)
599 1.4 deraadt free($4);
600 1.1 cgd }
601 1.1 cgd
602 1.12 lukem | MKD check_modify SP pathname CRLF
603 1.4 deraadt {
604 1.1 cgd if ($2 && $4 != NULL)
605 1.4 deraadt makedir($4);
606 1.1 cgd if ($4 != NULL)
607 1.4 deraadt free($4);
608 1.1 cgd }
609 1.23 lukem
610 1.23 lukem | PWD check_login CRLF
611 1.23 lukem {
612 1.23 lukem if ($2)
613 1.23 lukem pwd();
614 1.23 lukem }
615 1.23 lukem
616 1.23 lukem | LIST check_login CRLF
617 1.23 lukem {
618 1.40 lukem char *argv[] = { INTERNAL_LS, "-lgA", NULL };
619 1.40 lukem
620 1.23 lukem if ($2)
621 1.40 lukem retrieve(argv, "");
622 1.23 lukem }
623 1.23 lukem
624 1.23 lukem | LIST check_login SP pathname CRLF
625 1.4 deraadt {
626 1.40 lukem char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
627 1.40 lukem
628 1.40 lukem if ($2 && $4 != NULL) {
629 1.40 lukem argv[2] = $4;
630 1.40 lukem retrieve(argv, $4);
631 1.40 lukem }
632 1.1 cgd if ($4 != NULL)
633 1.4 deraadt free($4);
634 1.1 cgd }
635 1.23 lukem
636 1.23 lukem | NLST check_login CRLF
637 1.4 deraadt {
638 1.1 cgd if ($2)
639 1.23 lukem send_file_list(".");
640 1.1 cgd }
641 1.23 lukem
642 1.23 lukem | NLST check_login SP STRING CRLF
643 1.4 deraadt {
644 1.23 lukem if ($2 && $4 != NULL)
645 1.23 lukem send_file_list($4);
646 1.23 lukem if ($4 != NULL)
647 1.23 lukem free($4);
648 1.1 cgd }
649 1.23 lukem
650 1.4 deraadt | SITE SP HELP CRLF
651 1.4 deraadt {
652 1.22 lukem help(sitetab, NULL);
653 1.1 cgd }
654 1.23 lukem
655 1.4 deraadt | SITE SP HELP SP STRING CRLF
656 1.4 deraadt {
657 1.4 deraadt help(sitetab, $5);
658 1.1 cgd }
659 1.23 lukem
660 1.4 deraadt | SITE SP UMASK check_login CRLF
661 1.4 deraadt {
662 1.1 cgd int oldmask;
663 1.1 cgd
664 1.1 cgd if ($4) {
665 1.1 cgd oldmask = umask(0);
666 1.1 cgd (void) umask(oldmask);
667 1.1 cgd reply(200, "Current UMASK is %03o", oldmask);
668 1.1 cgd }
669 1.1 cgd }
670 1.23 lukem
671 1.12 lukem | SITE SP UMASK check_modify SP octal_number CRLF
672 1.4 deraadt {
673 1.1 cgd int oldmask;
674 1.1 cgd
675 1.1 cgd if ($4) {
676 1.1 cgd if (($6 == -1) || ($6 > 0777)) {
677 1.1 cgd reply(501, "Bad UMASK value");
678 1.1 cgd } else {
679 1.1 cgd oldmask = umask($6);
680 1.1 cgd reply(200,
681 1.1 cgd "UMASK set to %03o (was %03o)",
682 1.1 cgd $6, oldmask);
683 1.1 cgd }
684 1.1 cgd }
685 1.1 cgd }
686 1.23 lukem
687 1.12 lukem | SITE SP CHMOD check_modify SP octal_number SP pathname CRLF
688 1.4 deraadt {
689 1.1 cgd if ($4 && ($8 != NULL)) {
690 1.1 cgd if ($6 > 0777)
691 1.1 cgd reply(501,
692 1.1 cgd "CHMOD: Mode value must be between 0 and 0777");
693 1.4 deraadt else if (chmod($8, $6) < 0)
694 1.4 deraadt perror_reply(550, $8);
695 1.1 cgd else
696 1.1 cgd reply(200, "CHMOD command successful.");
697 1.1 cgd }
698 1.1 cgd if ($8 != NULL)
699 1.4 deraadt free($8);
700 1.1 cgd }
701 1.23 lukem
702 1.4 deraadt | SITE SP IDLE CRLF
703 1.4 deraadt {
704 1.1 cgd reply(200,
705 1.1 cgd "Current IDLE time limit is %d seconds; max %d",
706 1.12 lukem curclass.timeout, curclass.maxtimeout);
707 1.1 cgd }
708 1.23 lukem
709 1.4 deraadt | SITE SP IDLE SP NUMBER CRLF
710 1.4 deraadt {
711 1.12 lukem if ($5 < 30 || $5 > curclass.maxtimeout) {
712 1.1 cgd reply(501,
713 1.12 lukem "IDLE time limit must be between 30 and %d seconds",
714 1.12 lukem curclass.maxtimeout);
715 1.1 cgd } else {
716 1.12 lukem curclass.timeout = $5;
717 1.12 lukem (void) alarm(curclass.timeout);
718 1.1 cgd reply(200,
719 1.12 lukem "IDLE time limit set to %d seconds",
720 1.12 lukem curclass.timeout);
721 1.1 cgd }
722 1.1 cgd }
723 1.23 lukem
724 1.23 lukem | SYST CRLF
725 1.23 lukem {
726 1.23 lukem reply(215, "UNIX Type: L%d %s", NBBY, version);
727 1.23 lukem }
728 1.23 lukem
729 1.23 lukem | STAT check_login SP pathname CRLF
730 1.4 deraadt {
731 1.1 cgd if ($2 && $4 != NULL)
732 1.23 lukem statfilecmd($4);
733 1.1 cgd if ($4 != NULL)
734 1.4 deraadt free($4);
735 1.1 cgd }
736 1.23 lukem
737 1.23 lukem | STAT CRLF
738 1.23 lukem {
739 1.23 lukem statcmd();
740 1.23 lukem }
741 1.23 lukem
742 1.23 lukem | HELP CRLF
743 1.23 lukem {
744 1.23 lukem help(cmdtab, NULL);
745 1.23 lukem }
746 1.23 lukem
747 1.23 lukem | HELP SP STRING CRLF
748 1.23 lukem {
749 1.23 lukem char *cp = $3;
750 1.23 lukem
751 1.23 lukem if (strncasecmp(cp, "SITE", 4) == 0) {
752 1.23 lukem cp = $3 + 4;
753 1.23 lukem if (*cp == ' ')
754 1.23 lukem cp++;
755 1.23 lukem if (*cp)
756 1.23 lukem help(sitetab, cp);
757 1.23 lukem else
758 1.23 lukem help(sitetab, NULL);
759 1.23 lukem } else
760 1.23 lukem help(cmdtab, $3);
761 1.23 lukem }
762 1.23 lukem
763 1.23 lukem | NOOP CRLF
764 1.23 lukem {
765 1.23 lukem reply(200, "NOOP command successful.");
766 1.23 lukem }
767 1.23 lukem
768 1.25 lukem /* RFC 2228 */
769 1.25 lukem | AUTH SP mechanism_name CRLF
770 1.25 lukem {
771 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
772 1.25 lukem }
773 1.25 lukem
774 1.25 lukem | ADAT SP base64data CRLF
775 1.25 lukem {
776 1.25 lukem reply(503,
777 1.25 lukem "Please set authentication state with AUTH.");
778 1.25 lukem }
779 1.25 lukem
780 1.25 lukem | PROT SP prot_code CRLF
781 1.25 lukem {
782 1.25 lukem reply(503,
783 1.25 lukem "Please set protection buffer size with PBSZ.");
784 1.25 lukem }
785 1.25 lukem
786 1.25 lukem | PBSZ SP decimal_integer CRLF
787 1.25 lukem {
788 1.25 lukem reply(503,
789 1.25 lukem "Please set authentication state with AUTH.");
790 1.25 lukem }
791 1.25 lukem
792 1.25 lukem | CCC CRLF
793 1.25 lukem {
794 1.25 lukem reply(533, "No protection enabled.");
795 1.25 lukem }
796 1.25 lukem
797 1.25 lukem | MIC SP base64data CRLF
798 1.25 lukem {
799 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
800 1.25 lukem }
801 1.25 lukem
802 1.25 lukem | CONF SP base64data CRLF
803 1.25 lukem {
804 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
805 1.25 lukem }
806 1.25 lukem
807 1.25 lukem | ENC SP base64data CRLF
808 1.25 lukem {
809 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
810 1.25 lukem }
811 1.25 lukem
812 1.23 lukem /* RFC 2389 */
813 1.23 lukem | FEAT CRLF
814 1.23 lukem {
815 1.23 lukem lreply(211, "Features supported");
816 1.27 lukem lreply(-1, " MDTM");
817 1.27 lukem lreply(-1, " REST STREAM");
818 1.27 lukem lreply(-1, " SIZE");
819 1.27 lukem reply(211, "End");
820 1.23 lukem }
821 1.23 lukem
822 1.23 lukem | OPTS SP STRING CRLF
823 1.4 deraadt {
824 1.23 lukem
825 1.23 lukem opts($3);
826 1.1 cgd }
827 1.1 cgd
828 1.23 lukem
829 1.23 lukem /* BSD extensions */
830 1.23 lukem
831 1.1 cgd /*
832 1.21 lukem * SIZE is not in RFC 959, but Postel has blessed it and
833 1.1 cgd * it will be in the updated RFC.
834 1.1 cgd *
835 1.1 cgd * Return size of file in a format suitable for
836 1.1 cgd * using with RESTART (we just count bytes).
837 1.1 cgd */
838 1.4 deraadt | SIZE check_login SP pathname CRLF
839 1.4 deraadt {
840 1.1 cgd if ($2 && $4 != NULL)
841 1.4 deraadt sizecmd($4);
842 1.1 cgd if ($4 != NULL)
843 1.4 deraadt free($4);
844 1.1 cgd }
845 1.1 cgd
846 1.1 cgd /*
847 1.21 lukem * MDTM is not in RFC 959, but Postel has blessed it and
848 1.1 cgd * it will be in the updated RFC.
849 1.1 cgd *
850 1.1 cgd * Return modification time of file as an ISO 3307
851 1.1 cgd * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
852 1.1 cgd * where xxx is the fractional second (of any precision,
853 1.1 cgd * not necessarily 3 digits)
854 1.1 cgd */
855 1.4 deraadt | MDTM check_login SP pathname CRLF
856 1.4 deraadt {
857 1.1 cgd if ($2 && $4 != NULL) {
858 1.1 cgd struct stat stbuf;
859 1.4 deraadt if (stat($4, &stbuf) < 0)
860 1.22 lukem perror_reply(550, $4);
861 1.4 deraadt else if (!S_ISREG(stbuf.st_mode)) {
862 1.4 deraadt reply(550, "%s: not a plain file.", $4);
863 1.1 cgd } else {
864 1.4 deraadt struct tm *t;
865 1.1 cgd t = gmtime(&stbuf.st_mtime);
866 1.1 cgd reply(213,
867 1.7 jtc "%04d%02d%02d%02d%02d%02d",
868 1.18 lukem TM_YEAR_BASE + t->tm_year,
869 1.7 jtc t->tm_mon+1, t->tm_mday,
870 1.1 cgd t->tm_hour, t->tm_min, t->tm_sec);
871 1.1 cgd }
872 1.1 cgd }
873 1.1 cgd if ($4 != NULL)
874 1.4 deraadt free($4);
875 1.1 cgd }
876 1.23 lukem
877 1.4 deraadt | error CRLF
878 1.4 deraadt {
879 1.1 cgd yyerrok;
880 1.1 cgd }
881 1.1 cgd ;
882 1.21 lukem
883 1.4 deraadt rcmd
884 1.23 lukem : REST SP byte_size CRLF
885 1.23 lukem {
886 1.23 lukem fromname = NULL;
887 1.23 lukem restart_point = $3; /* XXX $3 is only "int" */
888 1.30 ross reply(350, "Restarting at %qd. %s",
889 1.30 ross (qdfmt_t)restart_point,
890 1.23 lukem "Send STORE or RETRIEVE to initiate transfer.");
891 1.23 lukem }
892 1.23 lukem | RNFR check_modify SP pathname CRLF
893 1.4 deraadt {
894 1.1 cgd restart_point = (off_t) 0;
895 1.1 cgd if ($2 && $4) {
896 1.4 deraadt fromname = renamefrom($4);
897 1.22 lukem if (fromname == NULL && $4) {
898 1.4 deraadt free($4);
899 1.1 cgd }
900 1.1 cgd }
901 1.1 cgd }
902 1.1 cgd ;
903 1.4 deraadt
904 1.4 deraadt username
905 1.4 deraadt : STRING
906 1.1 cgd ;
907 1.1 cgd
908 1.4 deraadt password
909 1.4 deraadt : /* empty */
910 1.4 deraadt {
911 1.4 deraadt $$ = (char *)calloc(1, sizeof(char));
912 1.1 cgd }
913 1.23 lukem
914 1.4 deraadt | STRING
915 1.1 cgd ;
916 1.1 cgd
917 1.4 deraadt byte_size
918 1.4 deraadt : NUMBER
919 1.1 cgd ;
920 1.1 cgd
921 1.4 deraadt host_port
922 1.4 deraadt : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
923 1.1 cgd NUMBER COMMA NUMBER
924 1.4 deraadt {
925 1.4 deraadt char *a, *p;
926 1.1 cgd
927 1.32 itojun data_dest.su_len = sizeof(struct sockaddr_in);
928 1.32 itojun data_dest.su_family = AF_INET;
929 1.32 itojun p = (char *)&data_dest.su_sin.sin_port;
930 1.6 mycroft p[0] = $9; p[1] = $11;
931 1.32 itojun a = (char *)&data_dest.su_sin.sin_addr;
932 1.1 cgd a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
933 1.1 cgd }
934 1.1 cgd ;
935 1.1 cgd
936 1.34 itojun host_long_port4
937 1.34 itojun : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
938 1.34 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
939 1.34 itojun NUMBER
940 1.34 itojun {
941 1.34 itojun char *a, *p;
942 1.34 itojun
943 1.34 itojun data_dest.su_sin.sin_len =
944 1.34 itojun sizeof(struct sockaddr_in);
945 1.34 itojun data_dest.su_family = AF_INET;
946 1.34 itojun p = (char *)&data_dest.su_port;
947 1.34 itojun p[0] = $15; p[1] = $17;
948 1.34 itojun a = (char *)&data_dest.su_sin.sin_addr;
949 1.34 itojun a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
950 1.35 itojun
951 1.35 itojun /* reject invalid LPRT command */
952 1.35 itojun if ($1 != 4 || $3 != 4 || $13 != 2)
953 1.35 itojun memset(&data_dest, 0, sizeof(data_dest));
954 1.34 itojun }
955 1.34 itojun ;
956 1.34 itojun
957 1.34 itojun host_long_port6
958 1.32 itojun : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
959 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
960 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
961 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
962 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
963 1.32 itojun NUMBER
964 1.32 itojun {
965 1.32 itojun char *a, *p;
966 1.32 itojun
967 1.32 itojun data_dest.su_sin6.sin6_len =
968 1.32 itojun sizeof(struct sockaddr_in6);
969 1.32 itojun data_dest.su_family = AF_INET6;
970 1.32 itojun p = (char *)&data_dest.su_port;
971 1.32 itojun p[0] = $39; p[1] = $41;
972 1.32 itojun a = (char *)&data_dest.su_sin6.sin6_addr;
973 1.32 itojun a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
974 1.32 itojun a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
975 1.32 itojun a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
976 1.32 itojun a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
977 1.37 itojun if (his_addr.su_family == AF_INET6) {
978 1.37 itojun /* XXX more sanity checks! */
979 1.37 itojun data_dest.su_sin6.sin6_scope_id =
980 1.37 itojun his_addr.su_sin6.sin6_scope_id;
981 1.37 itojun }
982 1.35 itojun
983 1.35 itojun /* reject invalid LPRT command */
984 1.35 itojun if ($1 != 6 || $3 != 16 || $37 != 2)
985 1.35 itojun memset(&data_dest, 0, sizeof(data_dest));
986 1.32 itojun }
987 1.32 itojun ;
988 1.32 itojun
989 1.4 deraadt form_code
990 1.4 deraadt : N
991 1.4 deraadt {
992 1.4 deraadt $$ = FORM_N;
993 1.4 deraadt }
994 1.23 lukem
995 1.4 deraadt | T
996 1.4 deraadt {
997 1.4 deraadt $$ = FORM_T;
998 1.4 deraadt }
999 1.23 lukem
1000 1.4 deraadt | C
1001 1.4 deraadt {
1002 1.4 deraadt $$ = FORM_C;
1003 1.4 deraadt }
1004 1.1 cgd ;
1005 1.1 cgd
1006 1.4 deraadt type_code
1007 1.4 deraadt : A
1008 1.4 deraadt {
1009 1.4 deraadt cmd_type = TYPE_A;
1010 1.4 deraadt cmd_form = FORM_N;
1011 1.4 deraadt }
1012 1.23 lukem
1013 1.4 deraadt | A SP form_code
1014 1.4 deraadt {
1015 1.4 deraadt cmd_type = TYPE_A;
1016 1.4 deraadt cmd_form = $3;
1017 1.4 deraadt }
1018 1.23 lukem
1019 1.4 deraadt | E
1020 1.4 deraadt {
1021 1.4 deraadt cmd_type = TYPE_E;
1022 1.4 deraadt cmd_form = FORM_N;
1023 1.4 deraadt }
1024 1.23 lukem
1025 1.4 deraadt | E SP form_code
1026 1.4 deraadt {
1027 1.4 deraadt cmd_type = TYPE_E;
1028 1.4 deraadt cmd_form = $3;
1029 1.4 deraadt }
1030 1.23 lukem
1031 1.4 deraadt | I
1032 1.4 deraadt {
1033 1.4 deraadt cmd_type = TYPE_I;
1034 1.4 deraadt }
1035 1.23 lukem
1036 1.4 deraadt | L
1037 1.4 deraadt {
1038 1.4 deraadt cmd_type = TYPE_L;
1039 1.4 deraadt cmd_bytesz = NBBY;
1040 1.4 deraadt }
1041 1.23 lukem
1042 1.4 deraadt | L SP byte_size
1043 1.4 deraadt {
1044 1.4 deraadt cmd_type = TYPE_L;
1045 1.4 deraadt cmd_bytesz = $3;
1046 1.4 deraadt }
1047 1.23 lukem
1048 1.4 deraadt /* this is for a bug in the BBN ftp */
1049 1.4 deraadt | L byte_size
1050 1.4 deraadt {
1051 1.4 deraadt cmd_type = TYPE_L;
1052 1.4 deraadt cmd_bytesz = $2;
1053 1.4 deraadt }
1054 1.1 cgd ;
1055 1.1 cgd
1056 1.4 deraadt struct_code
1057 1.4 deraadt : F
1058 1.4 deraadt {
1059 1.4 deraadt $$ = STRU_F;
1060 1.4 deraadt }
1061 1.23 lukem
1062 1.4 deraadt | R
1063 1.4 deraadt {
1064 1.4 deraadt $$ = STRU_R;
1065 1.4 deraadt }
1066 1.23 lukem
1067 1.4 deraadt | P
1068 1.4 deraadt {
1069 1.4 deraadt $$ = STRU_P;
1070 1.4 deraadt }
1071 1.1 cgd ;
1072 1.1 cgd
1073 1.4 deraadt mode_code
1074 1.4 deraadt : S
1075 1.4 deraadt {
1076 1.4 deraadt $$ = MODE_S;
1077 1.4 deraadt }
1078 1.23 lukem
1079 1.4 deraadt | B
1080 1.4 deraadt {
1081 1.4 deraadt $$ = MODE_B;
1082 1.4 deraadt }
1083 1.23 lukem
1084 1.4 deraadt | C
1085 1.4 deraadt {
1086 1.4 deraadt $$ = MODE_C;
1087 1.4 deraadt }
1088 1.1 cgd ;
1089 1.1 cgd
1090 1.4 deraadt pathname
1091 1.4 deraadt : pathstring
1092 1.4 deraadt {
1093 1.4 deraadt /*
1094 1.4 deraadt * Problem: this production is used for all pathname
1095 1.4 deraadt * processing, but only gives a 550 error reply.
1096 1.9 lukem * This is a valid reply in some cases but not in
1097 1.9 lukem * others.
1098 1.4 deraadt */
1099 1.4 deraadt if (logged_in && $1 && *$1 == '~') {
1100 1.4 deraadt glob_t gl;
1101 1.4 deraadt int flags =
1102 1.19 kleink GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
1103 1.4 deraadt
1104 1.9 lukem if ($1[1] == '\0')
1105 1.23 lukem $$ = xstrdup(pw->pw_dir);
1106 1.9 lukem else {
1107 1.9 lukem memset(&gl, 0, sizeof(gl));
1108 1.9 lukem if (glob($1, flags, NULL, &gl) ||
1109 1.9 lukem gl.gl_pathc == 0) {
1110 1.9 lukem reply(550, "not found");
1111 1.9 lukem $$ = NULL;
1112 1.9 lukem } else
1113 1.23 lukem $$ = xstrdup(gl.gl_pathv[0]);
1114 1.9 lukem globfree(&gl);
1115 1.4 deraadt }
1116 1.4 deraadt free($1);
1117 1.4 deraadt } else
1118 1.4 deraadt $$ = $1;
1119 1.4 deraadt }
1120 1.1 cgd ;
1121 1.1 cgd
1122 1.4 deraadt pathstring
1123 1.4 deraadt : STRING
1124 1.1 cgd ;
1125 1.1 cgd
1126 1.4 deraadt octal_number
1127 1.4 deraadt : NUMBER
1128 1.4 deraadt {
1129 1.4 deraadt int ret, dec, multby, digit;
1130 1.1 cgd
1131 1.4 deraadt /*
1132 1.4 deraadt * Convert a number that was read as decimal number
1133 1.4 deraadt * to what it would be if it had been read as octal.
1134 1.4 deraadt */
1135 1.4 deraadt dec = $1;
1136 1.4 deraadt multby = 1;
1137 1.4 deraadt ret = 0;
1138 1.4 deraadt while (dec) {
1139 1.4 deraadt digit = dec%10;
1140 1.4 deraadt if (digit > 7) {
1141 1.4 deraadt ret = -1;
1142 1.4 deraadt break;
1143 1.4 deraadt }
1144 1.4 deraadt ret += digit * multby;
1145 1.4 deraadt multby *= 8;
1146 1.4 deraadt dec /= 10;
1147 1.1 cgd }
1148 1.4 deraadt $$ = ret;
1149 1.1 cgd }
1150 1.1 cgd ;
1151 1.1 cgd
1152 1.25 lukem mechanism_name
1153 1.25 lukem : STRING
1154 1.25 lukem ;
1155 1.25 lukem
1156 1.25 lukem base64data
1157 1.25 lukem : STRING
1158 1.25 lukem ;
1159 1.25 lukem
1160 1.25 lukem prot_code
1161 1.25 lukem : STRING
1162 1.25 lukem ;
1163 1.25 lukem
1164 1.25 lukem decimal_integer
1165 1.25 lukem : NUMBER
1166 1.25 lukem ;
1167 1.25 lukem
1168 1.4 deraadt check_login
1169 1.4 deraadt : /* empty */
1170 1.4 deraadt {
1171 1.4 deraadt if (logged_in)
1172 1.4 deraadt $$ = 1;
1173 1.4 deraadt else {
1174 1.4 deraadt reply(530, "Please login with USER and PASS.");
1175 1.4 deraadt $$ = 0;
1176 1.22 lukem hasyyerrored = 1;
1177 1.4 deraadt }
1178 1.1 cgd }
1179 1.1 cgd ;
1180 1.21 lukem
1181 1.12 lukem check_modify
1182 1.8 cjs : /* empty */
1183 1.8 cjs {
1184 1.22 lukem if (logged_in) {
1185 1.22 lukem if (curclass.modify)
1186 1.12 lukem $$ = 1;
1187 1.22 lukem else {
1188 1.8 cjs reply(502,
1189 1.12 lukem "No permission to use this command.");
1190 1.8 cjs $$ = 0;
1191 1.22 lukem hasyyerrored = 1;
1192 1.14 hannken }
1193 1.8 cjs } else {
1194 1.8 cjs reply(530, "Please login with USER and PASS.");
1195 1.8 cjs $$ = 0;
1196 1.22 lukem hasyyerrored = 1;
1197 1.8 cjs }
1198 1.8 cjs }
1199 1.1 cgd
1200 1.1 cgd %%
1201 1.1 cgd
1202 1.1 cgd #define CMD 0 /* beginning of command */
1203 1.1 cgd #define ARGS 1 /* expect miscellaneous arguments */
1204 1.1 cgd #define STR1 2 /* expect SP followed by STRING */
1205 1.1 cgd #define STR2 3 /* expect STRING */
1206 1.1 cgd #define OSTR 4 /* optional SP then STRING */
1207 1.1 cgd #define ZSTR1 5 /* SP then optional STRING */
1208 1.1 cgd #define ZSTR2 6 /* optional STRING after SP */
1209 1.1 cgd #define SITECMD 7 /* SITE command */
1210 1.1 cgd #define NSTR 8 /* Number followed by a string */
1211 1.21 lukem #define NOARGS 9 /* No arguments allowed */
1212 1.1 cgd
1213 1.1 cgd struct tab {
1214 1.1 cgd char *name;
1215 1.23 lukem short token;
1216 1.23 lukem short state;
1217 1.23 lukem short implemented; /* 1 if command is implemented */
1218 1.23 lukem short hasopts; /* 1 if command takes options */
1219 1.1 cgd char *help;
1220 1.23 lukem char *options;
1221 1.1 cgd };
1222 1.1 cgd
1223 1.21 lukem struct tab cmdtab[] = {
1224 1.21 lukem /* From RFC 959, in order defined (5.3.1) */
1225 1.23 lukem { "USER", USER, STR1, 1, 0, "<sp> username" },
1226 1.23 lukem { "PASS", PASS, ZSTR1, 1, 0, "<sp> password" },
1227 1.23 lukem { "ACCT", ACCT, STR1, 0, 0, "(specify account)" },
1228 1.23 lukem { "CWD", CWD, OSTR, 1, 0, "[ <sp> directory-name ]" },
1229 1.23 lukem { "CDUP", CDUP, NOARGS, 1, 0, "(change to parent directory)" },
1230 1.23 lukem { "SMNT", SMNT, ARGS, 0, 0, "(structure mount)" },
1231 1.38 simonb { "QUIT", QUIT, NOARGS, 1, 0, "(terminate service)" },
1232 1.23 lukem { "REIN", REIN, NOARGS, 0, 0, "(reinitialize server state)" },
1233 1.23 lukem { "PORT", PORT, ARGS, 1, 0, "<sp> b0, b1, b2, b3, b4" },
1234 1.32 itojun { "LPRT", LPRT, ARGS, 1, 0, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1235 1.32 itojun { "EPRT", EPRT, STR1, 1, 0, "<sp> |af|addr|port|" },
1236 1.23 lukem { "PASV", PASV, NOARGS, 1, 0, "(set server in passive mode)" },
1237 1.32 itojun { "LPSV", LPSV, ARGS, 1, 0, "(set server in passive mode)" },
1238 1.32 itojun { "EPSV", EPSV, ARGS, 1, 0, "[<sp> af|ALL]" },
1239 1.23 lukem { "TYPE", TYPE, ARGS, 1, 0, "<sp> [ A | E | I | L ]" },
1240 1.23 lukem { "STRU", STRU, ARGS, 1, 0, "(specify file structure)" },
1241 1.23 lukem { "MODE", MODE, ARGS, 1, 0, "(specify transfer mode)" },
1242 1.23 lukem { "RETR", RETR, STR1, 1, 0, "<sp> file-name" },
1243 1.23 lukem { "STOR", STOR, STR1, 1, 0, "<sp> file-name" },
1244 1.23 lukem { "STOU", STOU, STR1, 1, 0, "<sp> file-name" },
1245 1.23 lukem { "APPE", APPE, STR1, 1, 0, "<sp> file-name" },
1246 1.23 lukem { "ALLO", ALLO, ARGS, 1, 0, "allocate storage (vacuously)" },
1247 1.23 lukem { "REST", REST, ARGS, 1, 0, "<sp> offset (restart command)" },
1248 1.23 lukem { "RNFR", RNFR, STR1, 1, 0, "<sp> file-name" },
1249 1.23 lukem { "RNTO", RNTO, STR1, 1, 0, "<sp> file-name" },
1250 1.23 lukem { "ABOR", ABOR, NOARGS, 1, 0, "(abort operation)" },
1251 1.23 lukem { "DELE", DELE, STR1, 1, 0, "<sp> file-name" },
1252 1.23 lukem { "RMD", RMD, STR1, 1, 0, "<sp> path-name" },
1253 1.23 lukem { "MKD", MKD, STR1, 1, 0, "<sp> path-name" },
1254 1.23 lukem { "PWD", PWD, NOARGS, 1, 0, "(return current directory)" },
1255 1.23 lukem { "LIST", LIST, OSTR, 1, 0, "[ <sp> path-name ]" },
1256 1.23 lukem { "NLST", NLST, OSTR, 1, 0, "[ <sp> path-name ]" },
1257 1.23 lukem { "SITE", SITE, SITECMD, 1, 0, "site-cmd [ <sp> arguments ]" },
1258 1.23 lukem { "SYST", SYST, NOARGS, 1, 0, "(get type of operating system)" },
1259 1.23 lukem { "STAT", STAT, OSTR, 1, 0, "[ <sp> path-name ]" },
1260 1.23 lukem { "HELP", HELP, OSTR, 1, 0, "[ <sp> <string> ]" },
1261 1.23 lukem { "NOOP", NOOP, NOARGS, 1, 1, "" },
1262 1.23 lukem
1263 1.25 lukem /* From RFC 2228, in order defined */
1264 1.25 lukem { "AUTH", AUTH, STR1, 1, 0, "<sp> mechanism-name" },
1265 1.25 lukem { "ADAT", ADAT, STR1, 1, 0, "<sp> base-64-data" },
1266 1.25 lukem { "PROT", PROT, STR1, 1, 0, "<sp> prot-code" },
1267 1.25 lukem { "PBSZ", PBSZ, ARGS, 1, 0, "<sp> decimal-integer" },
1268 1.25 lukem { "CCC", CCC, NOARGS, 1, 0, "(Disable data protection)" },
1269 1.25 lukem { "MIC", MIC, STR1, 1, 0, "<sp> base64data" },
1270 1.25 lukem { "CONF", CONF, STR1, 1, 0, "<sp> base64data" },
1271 1.25 lukem { "ENC", ENC, STR1, 1, 0, "<sp> base64data" },
1272 1.25 lukem
1273 1.25 lukem /* From RFC 2389, in order defined */
1274 1.23 lukem { "FEAT", FEAT, NOARGS, 1, 0, "(display extended features)" },
1275 1.23 lukem { "OPTS", OPTS, STR1, 1, 0, "<sp> command [ <sp> options ]" },
1276 1.23 lukem
1277 1.23 lukem /* Non standardized extensions */
1278 1.23 lukem { "SIZE", SIZE, OSTR, 1, 0, "<sp> path-name" },
1279 1.23 lukem { "MDTM", MDTM, OSTR, 1, 0, "<sp> path-name" },
1280 1.21 lukem
1281 1.21 lukem /* obsolete commands */
1282 1.23 lukem { "MAIL", MAIL, OSTR, 0, 0, "(mail to user)" },
1283 1.23 lukem { "MLFL", MLFL, OSTR, 0, 0, "(mail file)" },
1284 1.23 lukem { "MRCP", MRCP, STR1, 0, 0, "(mail recipient)" },
1285 1.23 lukem { "MRSQ", MRSQ, OSTR, 0, 0, "(mail recipient scheme question)" },
1286 1.23 lukem { "MSAM", MSAM, OSTR, 0, 0, "(mail send to terminal and mailbox)" },
1287 1.23 lukem { "MSND", MSND, OSTR, 0, 0, "(mail send to terminal)" },
1288 1.23 lukem { "MSOM", MSOM, OSTR, 0, 0, "(mail send to terminal or mailbox)" },
1289 1.23 lukem { "XCUP", CDUP, NOARGS, 1, 0, "(change to parent directory)" },
1290 1.38 simonb { "XCWD", CWD, OSTR, 1, 0, "[ <sp> directory-name ]" },
1291 1.23 lukem { "XMKD", MKD, STR1, 1, 0, "<sp> path-name" },
1292 1.23 lukem { "XPWD", PWD, NOARGS, 1, 0, "(return current directory)" },
1293 1.23 lukem { "XRMD", RMD, STR1, 1, 0, "<sp> path-name" },
1294 1.21 lukem
1295 1.23 lukem { NULL, 0, 0, 0, 0, 0 }
1296 1.1 cgd };
1297 1.1 cgd
1298 1.1 cgd struct tab sitetab[] = {
1299 1.23 lukem { "UMASK", UMASK, ARGS, 1, 0, "[ <sp> umask ]" },
1300 1.23 lukem { "IDLE", IDLE, ARGS, 1, 0, "[ <sp> maximum-idle-time ]" },
1301 1.23 lukem { "CHMOD", CHMOD, NSTR, 1, 0, "<sp> mode <sp> file-name" },
1302 1.23 lukem { "HELP", HELP, OSTR, 1, 0, "[ <sp> <string> ]" },
1303 1.23 lukem { NULL, 0, 0, 0, 0, 0 }
1304 1.1 cgd };
1305 1.1 cgd
1306 1.23 lukem static void help __P((struct tab *, char *));
1307 1.23 lukem static struct tab *lookup __P((struct tab *, const char *));
1308 1.23 lukem static void opts __P((const char *));
1309 1.23 lukem static void sizecmd __P((char *));
1310 1.23 lukem static void toolong __P((int));
1311 1.23 lukem static int yylex __P((void));
1312 1.4 deraadt
1313 1.32 itojun extern int epsvall;
1314 1.32 itojun
1315 1.4 deraadt static struct tab *
1316 1.1 cgd lookup(p, cmd)
1317 1.4 deraadt struct tab *p;
1318 1.23 lukem const char *cmd;
1319 1.1 cgd {
1320 1.1 cgd
1321 1.1 cgd for (; p->name != NULL; p++)
1322 1.23 lukem if (strcasecmp(cmd, p->name) == 0)
1323 1.1 cgd return (p);
1324 1.1 cgd return (0);
1325 1.1 cgd }
1326 1.1 cgd
1327 1.1 cgd #include <arpa/telnet.h>
1328 1.1 cgd
1329 1.1 cgd /*
1330 1.1 cgd * getline - a hacked up version of fgets to ignore TELNET escape codes.
1331 1.1 cgd */
1332 1.1 cgd char *
1333 1.1 cgd getline(s, n, iop)
1334 1.1 cgd char *s;
1335 1.4 deraadt int n;
1336 1.4 deraadt FILE *iop;
1337 1.1 cgd {
1338 1.27 lukem off_t b;
1339 1.4 deraadt int c;
1340 1.21 lukem char *cs;
1341 1.1 cgd
1342 1.1 cgd cs = s;
1343 1.1 cgd /* tmpline may contain saved command from urgent mode interruption */
1344 1.1 cgd for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1345 1.1 cgd *cs++ = tmpline[c];
1346 1.1 cgd if (tmpline[c] == '\n') {
1347 1.1 cgd *cs++ = '\0';
1348 1.1 cgd if (debug)
1349 1.1 cgd syslog(LOG_DEBUG, "command: %s", s);
1350 1.1 cgd tmpline[0] = '\0';
1351 1.1 cgd return(s);
1352 1.1 cgd }
1353 1.1 cgd if (c == 0)
1354 1.1 cgd tmpline[0] = '\0';
1355 1.1 cgd }
1356 1.1 cgd while ((c = getc(iop)) != EOF) {
1357 1.27 lukem total_bytes++;
1358 1.27 lukem total_bytes_in++;
1359 1.1 cgd c &= 0377;
1360 1.1 cgd if (c == IAC) {
1361 1.1 cgd if ((c = getc(iop)) != EOF) {
1362 1.27 lukem total_bytes++;
1363 1.27 lukem total_bytes_in++;
1364 1.1 cgd c &= 0377;
1365 1.1 cgd switch (c) {
1366 1.1 cgd case WILL:
1367 1.1 cgd case WONT:
1368 1.1 cgd c = getc(iop);
1369 1.27 lukem total_bytes++;
1370 1.27 lukem total_bytes_in++;
1371 1.27 lukem b = printf("%c%c%c", IAC, DONT, 0377&c);
1372 1.27 lukem total_bytes += b;
1373 1.27 lukem total_bytes_out += b;
1374 1.1 cgd (void) fflush(stdout);
1375 1.1 cgd continue;
1376 1.1 cgd case DO:
1377 1.1 cgd case DONT:
1378 1.1 cgd c = getc(iop);
1379 1.27 lukem total_bytes++;
1380 1.27 lukem total_bytes_in++;
1381 1.27 lukem b = printf("%c%c%c", IAC, WONT, 0377&c);
1382 1.27 lukem total_bytes += b;
1383 1.27 lukem total_bytes_out += b;
1384 1.1 cgd (void) fflush(stdout);
1385 1.1 cgd continue;
1386 1.1 cgd case IAC:
1387 1.1 cgd break;
1388 1.1 cgd default:
1389 1.1 cgd continue; /* ignore command */
1390 1.1 cgd }
1391 1.1 cgd }
1392 1.1 cgd }
1393 1.1 cgd *cs++ = c;
1394 1.1 cgd if (--n <= 0 || c == '\n')
1395 1.1 cgd break;
1396 1.1 cgd }
1397 1.1 cgd if (c == EOF && cs == s)
1398 1.1 cgd return (NULL);
1399 1.1 cgd *cs++ = '\0';
1400 1.4 deraadt if (debug) {
1401 1.4 deraadt if (!guest && strncasecmp("pass ", s, 5) == 0) {
1402 1.4 deraadt /* Don't syslog passwords */
1403 1.4 deraadt syslog(LOG_DEBUG, "command: %.5s ???", s);
1404 1.4 deraadt } else {
1405 1.21 lukem char *cp;
1406 1.21 lukem int len;
1407 1.4 deraadt
1408 1.4 deraadt /* Don't syslog trailing CR-LF */
1409 1.4 deraadt len = strlen(s);
1410 1.4 deraadt cp = s + len - 1;
1411 1.4 deraadt while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1412 1.4 deraadt --cp;
1413 1.4 deraadt --len;
1414 1.4 deraadt }
1415 1.4 deraadt syslog(LOG_DEBUG, "command: %.*s", len, s);
1416 1.4 deraadt }
1417 1.4 deraadt }
1418 1.1 cgd return (s);
1419 1.1 cgd }
1420 1.1 cgd
1421 1.1 cgd static void
1422 1.4 deraadt toolong(signo)
1423 1.4 deraadt int signo;
1424 1.1 cgd {
1425 1.1 cgd
1426 1.1 cgd reply(421,
1427 1.12 lukem "Timeout (%d seconds): closing control connection.",
1428 1.12 lukem curclass.timeout);
1429 1.4 deraadt if (logging)
1430 1.4 deraadt syslog(LOG_INFO, "User %s timed out after %d seconds",
1431 1.12 lukem (pw ? pw -> pw_name : "unknown"), curclass.timeout);
1432 1.1 cgd dologout(1);
1433 1.1 cgd }
1434 1.1 cgd
1435 1.4 deraadt static int
1436 1.1 cgd yylex()
1437 1.1 cgd {
1438 1.1 cgd static int cpos, state;
1439 1.4 deraadt char *cp, *cp2;
1440 1.4 deraadt struct tab *p;
1441 1.22 lukem int n;
1442 1.4 deraadt char c;
1443 1.1 cgd
1444 1.23 lukem switch (state) {
1445 1.1 cgd
1446 1.23 lukem case CMD:
1447 1.23 lukem hasyyerrored = 0;
1448 1.23 lukem (void) signal(SIGALRM, toolong);
1449 1.23 lukem (void) alarm(curclass.timeout);
1450 1.23 lukem if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1451 1.23 lukem reply(221, "You could at least say goodbye.");
1452 1.23 lukem dologout(0);
1453 1.23 lukem }
1454 1.23 lukem (void) alarm(0);
1455 1.3 cgd #ifdef HASSETPROCTITLE
1456 1.23 lukem if (strncasecmp(cbuf, "PASS", 4) != 0)
1457 1.23 lukem setproctitle("%s: %s", proctitle, cbuf);
1458 1.3 cgd #endif /* HASSETPROCTITLE */
1459 1.23 lukem if ((cp = strchr(cbuf, '\r'))) {
1460 1.23 lukem *cp++ = '\n';
1461 1.23 lukem *cp = '\0';
1462 1.23 lukem }
1463 1.23 lukem if ((cp = strpbrk(cbuf, " \n")))
1464 1.23 lukem cpos = cp - cbuf;
1465 1.23 lukem if (cpos == 0)
1466 1.23 lukem cpos = 4;
1467 1.23 lukem c = cbuf[cpos];
1468 1.23 lukem cbuf[cpos] = '\0';
1469 1.23 lukem p = lookup(cmdtab, cbuf);
1470 1.23 lukem cbuf[cpos] = c;
1471 1.23 lukem if (p != NULL) {
1472 1.23 lukem if (p->implemented == 0) {
1473 1.23 lukem reply(502, "%s command not implemented.",
1474 1.23 lukem p->name);
1475 1.23 lukem hasyyerrored = 1;
1476 1.23 lukem break;
1477 1.23 lukem }
1478 1.23 lukem state = p->state;
1479 1.23 lukem yylval.s = p->name;
1480 1.23 lukem return (p->token);
1481 1.23 lukem }
1482 1.23 lukem break;
1483 1.23 lukem
1484 1.23 lukem case SITECMD:
1485 1.23 lukem if (cbuf[cpos] == ' ') {
1486 1.23 lukem cpos++;
1487 1.23 lukem return (SP);
1488 1.23 lukem }
1489 1.23 lukem cp = &cbuf[cpos];
1490 1.23 lukem if ((cp2 = strpbrk(cp, " \n")))
1491 1.23 lukem cpos = cp2 - cbuf;
1492 1.23 lukem c = cbuf[cpos];
1493 1.23 lukem cbuf[cpos] = '\0';
1494 1.23 lukem p = lookup(sitetab, cp);
1495 1.23 lukem cbuf[cpos] = c;
1496 1.23 lukem if (p != NULL) {
1497 1.23 lukem if (p->implemented == 0) {
1498 1.23 lukem reply(502, "SITE %s command not implemented.",
1499 1.23 lukem p->name);
1500 1.23 lukem hasyyerrored = 1;
1501 1.23 lukem break;
1502 1.23 lukem }
1503 1.23 lukem state = p->state;
1504 1.23 lukem yylval.s = p->name;
1505 1.23 lukem return (p->token);
1506 1.23 lukem }
1507 1.23 lukem break;
1508 1.23 lukem
1509 1.23 lukem case OSTR:
1510 1.23 lukem if (cbuf[cpos] == '\n') {
1511 1.23 lukem state = CMD;
1512 1.23 lukem return (CRLF);
1513 1.23 lukem }
1514 1.23 lukem /* FALLTHROUGH */
1515 1.23 lukem
1516 1.23 lukem case STR1:
1517 1.23 lukem case ZSTR1:
1518 1.23 lukem dostr1:
1519 1.23 lukem if (cbuf[cpos] == ' ') {
1520 1.23 lukem cpos++;
1521 1.39 tron state = state == OSTR ? STR2 : state+1;
1522 1.23 lukem return (SP);
1523 1.23 lukem }
1524 1.23 lukem break;
1525 1.23 lukem
1526 1.23 lukem case ZSTR2:
1527 1.23 lukem if (cbuf[cpos] == '\n') {
1528 1.23 lukem state = CMD;
1529 1.23 lukem return (CRLF);
1530 1.23 lukem }
1531 1.23 lukem /* FALLTHROUGH */
1532 1.23 lukem
1533 1.23 lukem case STR2:
1534 1.23 lukem cp = &cbuf[cpos];
1535 1.23 lukem n = strlen(cp);
1536 1.23 lukem cpos += n - 1;
1537 1.23 lukem /*
1538 1.23 lukem * Make sure the string is nonempty and \n terminated.
1539 1.23 lukem */
1540 1.23 lukem if (n > 1 && cbuf[cpos] == '\n') {
1541 1.23 lukem cbuf[cpos] = '\0';
1542 1.23 lukem yylval.s = xstrdup(cp);
1543 1.23 lukem cbuf[cpos] = '\n';
1544 1.23 lukem state = ARGS;
1545 1.23 lukem return (STRING);
1546 1.23 lukem }
1547 1.23 lukem break;
1548 1.23 lukem
1549 1.23 lukem case NSTR:
1550 1.23 lukem if (cbuf[cpos] == ' ') {
1551 1.23 lukem cpos++;
1552 1.23 lukem return (SP);
1553 1.23 lukem }
1554 1.23 lukem if (isdigit(cbuf[cpos])) {
1555 1.23 lukem cp = &cbuf[cpos];
1556 1.23 lukem while (isdigit(cbuf[++cpos]))
1557 1.23 lukem ;
1558 1.1 cgd c = cbuf[cpos];
1559 1.1 cgd cbuf[cpos] = '\0';
1560 1.23 lukem yylval.i = atoi(cp);
1561 1.1 cgd cbuf[cpos] = c;
1562 1.23 lukem state = STR1;
1563 1.23 lukem return (NUMBER);
1564 1.23 lukem }
1565 1.23 lukem state = STR1;
1566 1.23 lukem goto dostr1;
1567 1.1 cgd
1568 1.23 lukem case ARGS:
1569 1.23 lukem if (isdigit(cbuf[cpos])) {
1570 1.1 cgd cp = &cbuf[cpos];
1571 1.23 lukem while (isdigit(cbuf[++cpos]))
1572 1.23 lukem ;
1573 1.1 cgd c = cbuf[cpos];
1574 1.1 cgd cbuf[cpos] = '\0';
1575 1.23 lukem yylval.i = atoi(cp);
1576 1.1 cgd cbuf[cpos] = c;
1577 1.23 lukem return (NUMBER);
1578 1.32 itojun }
1579 1.32 itojun if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1580 1.32 itojun && !isalnum(cbuf[cpos + 3])) {
1581 1.32 itojun yylval.s = xstrdup("ALL");
1582 1.32 itojun cpos += 3;
1583 1.32 itojun return ALL;
1584 1.23 lukem }
1585 1.23 lukem switch (cbuf[cpos++]) {
1586 1.23 lukem
1587 1.23 lukem case '\n':
1588 1.1 cgd state = CMD;
1589 1.23 lukem return (CRLF);
1590 1.23 lukem
1591 1.23 lukem case ' ':
1592 1.23 lukem return (SP);
1593 1.23 lukem
1594 1.23 lukem case ',':
1595 1.23 lukem return (COMMA);
1596 1.23 lukem
1597 1.23 lukem case 'A':
1598 1.23 lukem case 'a':
1599 1.23 lukem return (A);
1600 1.23 lukem
1601 1.23 lukem case 'B':
1602 1.23 lukem case 'b':
1603 1.23 lukem return (B);
1604 1.23 lukem
1605 1.23 lukem case 'C':
1606 1.23 lukem case 'c':
1607 1.23 lukem return (C);
1608 1.23 lukem
1609 1.23 lukem case 'E':
1610 1.23 lukem case 'e':
1611 1.23 lukem return (E);
1612 1.23 lukem
1613 1.23 lukem case 'F':
1614 1.23 lukem case 'f':
1615 1.23 lukem return (F);
1616 1.23 lukem
1617 1.23 lukem case 'I':
1618 1.23 lukem case 'i':
1619 1.23 lukem return (I);
1620 1.1 cgd
1621 1.23 lukem case 'L':
1622 1.23 lukem case 'l':
1623 1.23 lukem return (L);
1624 1.1 cgd
1625 1.23 lukem case 'N':
1626 1.23 lukem case 'n':
1627 1.23 lukem return (N);
1628 1.1 cgd
1629 1.23 lukem case 'P':
1630 1.23 lukem case 'p':
1631 1.23 lukem return (P);
1632 1.1 cgd
1633 1.23 lukem case 'R':
1634 1.23 lukem case 'r':
1635 1.23 lukem return (R);
1636 1.1 cgd
1637 1.23 lukem case 'S':
1638 1.23 lukem case 's':
1639 1.23 lukem return (S);
1640 1.1 cgd
1641 1.23 lukem case 'T':
1642 1.23 lukem case 't':
1643 1.23 lukem return (T);
1644 1.1 cgd
1645 1.23 lukem }
1646 1.23 lukem break;
1647 1.21 lukem
1648 1.23 lukem case NOARGS:
1649 1.23 lukem if (cbuf[cpos] == '\n') {
1650 1.23 lukem state = CMD;
1651 1.23 lukem return (CRLF);
1652 1.1 cgd }
1653 1.23 lukem c = cbuf[cpos];
1654 1.23 lukem cbuf[cpos] = '\0';
1655 1.23 lukem reply(501, "'%s' command does not take any arguments.", cbuf);
1656 1.23 lukem hasyyerrored = 1;
1657 1.23 lukem cbuf[cpos] = c;
1658 1.23 lukem break;
1659 1.23 lukem
1660 1.23 lukem default:
1661 1.23 lukem fatal("Unknown state in scanner.");
1662 1.1 cgd }
1663 1.23 lukem yyerror(NULL);
1664 1.23 lukem state = CMD;
1665 1.23 lukem longjmp(errcatch, 0);
1666 1.23 lukem /* NOTREACHED */
1667 1.1 cgd }
1668 1.1 cgd
1669 1.22 lukem /* ARGSUSED */
1670 1.22 lukem void
1671 1.22 lukem yyerror(s)
1672 1.22 lukem char *s;
1673 1.22 lukem {
1674 1.22 lukem char *cp;
1675 1.22 lukem
1676 1.22 lukem if (hasyyerrored)
1677 1.22 lukem return;
1678 1.22 lukem if ((cp = strchr(cbuf,'\n')) != NULL)
1679 1.22 lukem *cp = '\0';
1680 1.22 lukem reply(500, "'%s': command not understood.", cbuf);
1681 1.22 lukem hasyyerrored = 1;
1682 1.22 lukem }
1683 1.22 lukem
1684 1.4 deraadt static void
1685 1.1 cgd help(ctab, s)
1686 1.1 cgd struct tab *ctab;
1687 1.1 cgd char *s;
1688 1.1 cgd {
1689 1.4 deraadt struct tab *c;
1690 1.4 deraadt int width, NCMDS;
1691 1.27 lukem off_t b;
1692 1.1 cgd char *type;
1693 1.1 cgd
1694 1.1 cgd if (ctab == sitetab)
1695 1.1 cgd type = "SITE ";
1696 1.1 cgd else
1697 1.1 cgd type = "";
1698 1.1 cgd width = 0, NCMDS = 0;
1699 1.1 cgd for (c = ctab; c->name != NULL; c++) {
1700 1.1 cgd int len = strlen(c->name);
1701 1.1 cgd
1702 1.1 cgd if (len > width)
1703 1.1 cgd width = len;
1704 1.1 cgd NCMDS++;
1705 1.1 cgd }
1706 1.1 cgd width = (width + 8) &~ 7;
1707 1.1 cgd if (s == 0) {
1708 1.4 deraadt int i, j, w;
1709 1.1 cgd int columns, lines;
1710 1.1 cgd
1711 1.28 lukem lreply(214, "");
1712 1.28 lukem lreply(0, "The following %scommands are recognized.", type);
1713 1.27 lukem lreply(0, "(`-' = not implemented, `+' = supports options)");
1714 1.1 cgd columns = 76 / width;
1715 1.1 cgd if (columns == 0)
1716 1.1 cgd columns = 1;
1717 1.1 cgd lines = (NCMDS + columns - 1) / columns;
1718 1.1 cgd for (i = 0; i < lines; i++) {
1719 1.28 lukem b = printf(" ");
1720 1.27 lukem total_bytes += b;
1721 1.27 lukem total_bytes_out += b;
1722 1.1 cgd for (j = 0; j < columns; j++) {
1723 1.1 cgd c = ctab + j * lines + i;
1724 1.27 lukem b = printf("%s", c->name);
1725 1.27 lukem total_bytes += b;
1726 1.27 lukem total_bytes_out += b;
1727 1.23 lukem w = strlen(c->name);
1728 1.23 lukem if (! c->implemented) {
1729 1.24 lukem putchar('-');
1730 1.28 lukem total_bytes++;
1731 1.28 lukem total_bytes_out++;
1732 1.23 lukem w++;
1733 1.23 lukem }
1734 1.23 lukem if (c->hasopts) {
1735 1.23 lukem putchar('+');
1736 1.28 lukem total_bytes++;
1737 1.28 lukem total_bytes_out++;
1738 1.23 lukem w++;
1739 1.23 lukem }
1740 1.1 cgd if (c + lines >= &ctab[NCMDS])
1741 1.1 cgd break;
1742 1.1 cgd while (w < width) {
1743 1.1 cgd putchar(' ');
1744 1.28 lukem total_bytes++;
1745 1.28 lukem total_bytes_out++;
1746 1.1 cgd w++;
1747 1.1 cgd }
1748 1.1 cgd }
1749 1.27 lukem b = printf("\r\n");
1750 1.27 lukem total_bytes += b;
1751 1.27 lukem total_bytes_out += b;
1752 1.1 cgd }
1753 1.1 cgd (void) fflush(stdout);
1754 1.1 cgd reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1755 1.1 cgd return;
1756 1.1 cgd }
1757 1.1 cgd c = lookup(ctab, s);
1758 1.1 cgd if (c == (struct tab *)0) {
1759 1.1 cgd reply(502, "Unknown command %s.", s);
1760 1.1 cgd return;
1761 1.1 cgd }
1762 1.1 cgd if (c->implemented)
1763 1.1 cgd reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1764 1.1 cgd else
1765 1.23 lukem reply(214, "%s%-*s\t%s; not implemented.", type, width,
1766 1.1 cgd c->name, c->help);
1767 1.1 cgd }
1768 1.1 cgd
1769 1.4 deraadt static void
1770 1.1 cgd sizecmd(filename)
1771 1.4 deraadt char *filename;
1772 1.1 cgd {
1773 1.1 cgd switch (type) {
1774 1.1 cgd case TYPE_L:
1775 1.1 cgd case TYPE_I: {
1776 1.1 cgd struct stat stbuf;
1777 1.4 deraadt if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1778 1.1 cgd reply(550, "%s: not a plain file.", filename);
1779 1.1 cgd else
1780 1.30 ross reply(213, "%qu", (qufmt_t)stbuf.st_size);
1781 1.4 deraadt break; }
1782 1.1 cgd case TYPE_A: {
1783 1.1 cgd FILE *fin;
1784 1.4 deraadt int c;
1785 1.4 deraadt off_t count;
1786 1.1 cgd struct stat stbuf;
1787 1.1 cgd fin = fopen(filename, "r");
1788 1.1 cgd if (fin == NULL) {
1789 1.1 cgd perror_reply(550, filename);
1790 1.1 cgd return;
1791 1.1 cgd }
1792 1.4 deraadt if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1793 1.1 cgd reply(550, "%s: not a plain file.", filename);
1794 1.1 cgd (void) fclose(fin);
1795 1.1 cgd return;
1796 1.1 cgd }
1797 1.1 cgd
1798 1.1 cgd count = 0;
1799 1.1 cgd while((c=getc(fin)) != EOF) {
1800 1.1 cgd if (c == '\n') /* will get expanded to \r\n */
1801 1.1 cgd count++;
1802 1.1 cgd count++;
1803 1.1 cgd }
1804 1.1 cgd (void) fclose(fin);
1805 1.1 cgd
1806 1.30 ross reply(213, "%qd", (qdfmt_t)count);
1807 1.4 deraadt break; }
1808 1.1 cgd default:
1809 1.1 cgd reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1810 1.1 cgd }
1811 1.1 cgd }
1812 1.22 lukem
1813 1.23 lukem static void
1814 1.23 lukem opts(command)
1815 1.23 lukem const char *command;
1816 1.23 lukem {
1817 1.23 lukem struct tab *c;
1818 1.23 lukem char *ep;
1819 1.23 lukem
1820 1.23 lukem if ((ep = strchr(command, ' ')) != NULL)
1821 1.23 lukem *ep++ = '\0';
1822 1.23 lukem c = lookup(cmdtab, command);
1823 1.23 lukem if (c == NULL) {
1824 1.23 lukem reply(502, "Unknown command %s.", command);
1825 1.23 lukem return;
1826 1.23 lukem }
1827 1.23 lukem if (c->implemented == 0) {
1828 1.23 lukem reply(502, "%s command not implemented.", c->name);
1829 1.23 lukem return;
1830 1.23 lukem }
1831 1.23 lukem if (c->hasopts == 0) {
1832 1.23 lukem reply(501, "%s command does not support persistent options.",
1833 1.23 lukem c->name);
1834 1.23 lukem return;
1835 1.23 lukem }
1836 1.23 lukem
1837 1.23 lukem if (ep != NULL && *ep != '\0') {
1838 1.23 lukem if (c->options != NULL)
1839 1.23 lukem free(c->options);
1840 1.23 lukem c->options = xstrdup(ep);
1841 1.23 lukem }
1842 1.23 lukem if (c->options != NULL)
1843 1.23 lukem reply(200, "Options for %s are '%s'.", c->name, c->options);
1844 1.23 lukem else
1845 1.23 lukem reply(200, "No options defined for %s.", c->name);
1846 1.23 lukem }
1847