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