1*73fe6daaSflorian /* $OpenBSD: ftpcmd.y,v 1.75 2024/04/28 16:42:53 florian Exp $ */
269a3ff95Sderaadt /* $NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp $ */
3df930be7Sderaadt
4df930be7Sderaadt /*
5df930be7Sderaadt * Copyright (c) 1985, 1988, 1993, 1994
6df930be7Sderaadt * The Regents of the University of California. All rights reserved.
7df930be7Sderaadt *
8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt * modification, are permitted provided that the following conditions
10df930be7Sderaadt * are met:
11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt * documentation and/or other materials provided with the distribution.
16e33d3bd3Smillert * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt * may be used to endorse or promote products derived from this software
18df930be7Sderaadt * without specific prior written permission.
19df930be7Sderaadt *
20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt * SUCH DAMAGE.
31df930be7Sderaadt *
32df930be7Sderaadt * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
33df930be7Sderaadt */
34df930be7Sderaadt
35df930be7Sderaadt /*
36df930be7Sderaadt * Grammar for FTP commands.
37df930be7Sderaadt * See RFC 959.
38df930be7Sderaadt */
39df930be7Sderaadt
40df930be7Sderaadt %{
41df930be7Sderaadt
42b9fc9a72Sderaadt #include <sys/types.h>
43df930be7Sderaadt #include <sys/socket.h>
44df930be7Sderaadt #include <sys/stat.h>
45df930be7Sderaadt
46df930be7Sderaadt #include <netinet/in.h>
47df930be7Sderaadt #include <arpa/ftp.h>
48df930be7Sderaadt
49df930be7Sderaadt #include <ctype.h>
50df930be7Sderaadt #include <errno.h>
51df930be7Sderaadt #include <glob.h>
52df930be7Sderaadt #include <pwd.h>
53df930be7Sderaadt #include <signal.h>
54df930be7Sderaadt #include <stdio.h>
55df930be7Sderaadt #include <stdlib.h>
56df930be7Sderaadt #include <string.h>
57df930be7Sderaadt #include <syslog.h>
58df930be7Sderaadt #include <time.h>
59df930be7Sderaadt #include <unistd.h>
60b1750805Sitojun #include <netdb.h>
6109a2692aSmillert #include <limits.h>
62df930be7Sderaadt
63b96c0bc5Shenning #include "monitor.h"
6492f68775Sragge #include "extern.h"
65df930be7Sderaadt
66b1750805Sitojun extern union sockunion data_dest;
67df930be7Sderaadt extern int logged_in;
68df930be7Sderaadt extern struct passwd *pw;
69df930be7Sderaadt extern int guest;
70df930be7Sderaadt extern int logging;
71df930be7Sderaadt extern int type;
72df930be7Sderaadt extern int form;
73df930be7Sderaadt extern int debug;
74df930be7Sderaadt extern int timeout;
75df930be7Sderaadt extern int maxtimeout;
76df930be7Sderaadt extern int pdata;
77df930be7Sderaadt extern char hostname[], remotehost[];
78df930be7Sderaadt extern char proctitle[];
79df930be7Sderaadt extern int usedefault;
80df930be7Sderaadt extern int transflag;
81df930be7Sderaadt extern char tmpline[];
82fc12f3acSderaadt extern int portcheck;
83b1750805Sitojun extern union sockunion his_addr;
84b3f5c309Sderaadt extern int umaskchange;
85df930be7Sderaadt
86df930be7Sderaadt off_t restart_point;
87df930be7Sderaadt
88df930be7Sderaadt static int cmd_type;
89df930be7Sderaadt static int cmd_form;
90df930be7Sderaadt static int cmd_bytesz;
912378d5feSmillert static int state;
92b96c0bc5Shenning static int quit;
93df930be7Sderaadt char cbuf[512];
94df930be7Sderaadt char *fromname;
95df930be7Sderaadt
96df930be7Sderaadt %}
97df930be7Sderaadt
98df930be7Sderaadt %union {
99df930be7Sderaadt int i;
10009a2692aSmillert off_t o;
101df930be7Sderaadt char *s;
102df930be7Sderaadt }
103df930be7Sderaadt
104df930be7Sderaadt %token
105df930be7Sderaadt A B C E F I
106df930be7Sderaadt L N P R S T
107df930be7Sderaadt
108b77009c8Smpech SP CRLF COMMA ALL
109df930be7Sderaadt
110df930be7Sderaadt USER PASS ACCT REIN QUIT PORT
111df930be7Sderaadt PASV TYPE STRU MODE RETR STOR
112df930be7Sderaadt APPE MLFL MAIL MSND MSOM MSAM
113df930be7Sderaadt MRSQ MRCP ALLO REST RNFR RNTO
114df930be7Sderaadt ABOR DELE CWD LIST NLST SITE
115df930be7Sderaadt STAT HELP NOOP MKD RMD PWD
116df930be7Sderaadt CDUP STOU SMNT SYST SIZE MDTM
117df930be7Sderaadt
118b1750805Sitojun LPRT LPSV EPRT EPSV
119b1750805Sitojun
120df930be7Sderaadt UMASK IDLE CHMOD
121df930be7Sderaadt
122df930be7Sderaadt LEXERR
123df930be7Sderaadt
124df930be7Sderaadt %token <s> STRING
125df930be7Sderaadt %token <i> NUMBER
12609a2692aSmillert %token <o> BIGNUM
127df930be7Sderaadt
12841fb778fSitojun %type <i> check_login check_login_epsvall octal_number byte_size
129df930be7Sderaadt %type <i> struct_code mode_code type_code form_code
130b1750805Sitojun %type <i> host_port host_long_port4 host_long_port6
13109a2692aSmillert %type <o> file_size
13209a2692aSmillert %type <s> pathstring pathname password username
133df930be7Sderaadt
134df930be7Sderaadt %start cmd_list
135df930be7Sderaadt
136df930be7Sderaadt %%
137df930be7Sderaadt
138df930be7Sderaadt cmd_list
139df930be7Sderaadt : /* empty */
140df930be7Sderaadt | cmd_list cmd
141df930be7Sderaadt {
142f59925a5Smillert if (fromname) {
143f59925a5Smillert free(fromname);
144f59925a5Smillert fromname = NULL;
145f59925a5Smillert }
146a20a5177Sguenther restart_point = 0;
147df930be7Sderaadt }
148df930be7Sderaadt | cmd_list rcmd
149df930be7Sderaadt ;
150df930be7Sderaadt
151df930be7Sderaadt cmd
152df930be7Sderaadt : USER SP username CRLF
153df930be7Sderaadt {
154b96c0bc5Shenning monitor_user($3);
155df930be7Sderaadt free($3);
156df930be7Sderaadt }
157df930be7Sderaadt | PASS SP password CRLF
158df930be7Sderaadt {
159b96c0bc5Shenning quit = monitor_pass($3);
16072547754Sderaadt explicit_bzero($3, strlen($3));
161df930be7Sderaadt free($3);
162b96c0bc5Shenning
163b96c0bc5Shenning /* Terminate unprivileged pre-auth slave */
164b96c0bc5Shenning if (quit)
1653e113d76Smoritz _exit(0);
166df930be7Sderaadt }
16741fb778fSitojun | PORT check_login_epsvall SP host_port CRLF
168df930be7Sderaadt {
16941fb778fSitojun if ($2) {
17041fb778fSitojun if ($4) {
171e7d279e1Sderaadt usedefault = 1;
172e7d279e1Sderaadt reply(500,
173e7d279e1Sderaadt "Illegal PORT rejected (range errors).");
174e7d279e1Sderaadt } else if (portcheck &&
175b1750805Sitojun ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) {
176e7d279e1Sderaadt usedefault = 1;
177e7d279e1Sderaadt reply(500,
178e7d279e1Sderaadt "Illegal PORT rejected (reserved port).");
179e7d279e1Sderaadt } else if (portcheck &&
180b1750805Sitojun memcmp(&data_dest.su_sin.sin_addr,
181b1750805Sitojun &his_addr.su_sin.sin_addr,
182b1750805Sitojun sizeof data_dest.su_sin.sin_addr)) {
183fc12f3acSderaadt usedefault = 1;
184e7d279e1Sderaadt reply(500,
185e7d279e1Sderaadt "Illegal PORT rejected (address wrong).");
186fc12f3acSderaadt } else {
187df930be7Sderaadt usedefault = 0;
188df930be7Sderaadt if (pdata >= 0) {
189df930be7Sderaadt (void) close(pdata);
190df930be7Sderaadt pdata = -1;
191df930be7Sderaadt }
192df930be7Sderaadt reply(200, "PORT command successful.");
193df930be7Sderaadt }
19424713697Sbitblt }
195fc12f3acSderaadt }
19641fb778fSitojun | LPRT check_login_epsvall SP host_long_port4 CRLF
197b1750805Sitojun {
19841fb778fSitojun if ($2) {
199b1750805Sitojun /* reject invalid host_long_port4 */
20041fb778fSitojun if ($4) {
201e14c8eb3Sitojun reply(500,
202e14c8eb3Sitojun "Illegal LPRT command rejected");
203b1750805Sitojun usedefault = 1;
204b1750805Sitojun } else {
205b1750805Sitojun usedefault = 0;
206b1750805Sitojun if (pdata >= 0) {
207b1750805Sitojun (void) close(pdata);
208b1750805Sitojun pdata = -1;
209b1750805Sitojun }
210b1750805Sitojun reply(200, "LPRT command successful.");
211b1750805Sitojun }
212b1750805Sitojun }
213e14c8eb3Sitojun }
214b1750805Sitojun
21541fb778fSitojun | LPRT check_login_epsvall SP host_long_port6 CRLF
216b1750805Sitojun {
21741fb778fSitojun if ($2) {
218b1750805Sitojun /* reject invalid host_long_port6 */
21941fb778fSitojun if ($4) {
220e14c8eb3Sitojun reply(500,
221e14c8eb3Sitojun "Illegal LPRT command rejected");
222b1750805Sitojun usedefault = 1;
223b1750805Sitojun } else {
224b1750805Sitojun usedefault = 0;
225b1750805Sitojun if (pdata >= 0) {
226b1750805Sitojun (void) close(pdata);
227b1750805Sitojun pdata = -1;
228b1750805Sitojun }
229b1750805Sitojun reply(200, "LPRT command successful.");
230b1750805Sitojun }
231b1750805Sitojun }
232e14c8eb3Sitojun }
233b1750805Sitojun
23441fb778fSitojun | EPRT check_login_epsvall SP STRING CRLF
235b1750805Sitojun {
236d94808dfSitojun if ($2)
237d94808dfSitojun extended_port($4);
23839ff527aSmillert free($4);
239b1750805Sitojun }
240b1750805Sitojun
24141fb778fSitojun | PASV check_login_epsvall CRLF
242df930be7Sderaadt {
24341fb778fSitojun if ($2)
244df930be7Sderaadt passive();
245df930be7Sderaadt }
24641fb778fSitojun | LPSV check_login_epsvall CRLF
247b1750805Sitojun {
24841fb778fSitojun if ($2)
249b1750805Sitojun long_passive("LPSV", PF_UNSPEC);
250b1750805Sitojun }
251e14c8eb3Sitojun | EPSV check_login SP NUMBER CRLF
252b1750805Sitojun {
253d94808dfSitojun if ($2)
254d94808dfSitojun long_passive("EPSV", epsvproto2af($4));
255e14c8eb3Sitojun }
256e14c8eb3Sitojun | EPSV check_login SP ALL CRLF
257b1750805Sitojun {
258e14c8eb3Sitojun if ($2) {
259b1750805Sitojun reply(200, "EPSV ALL command successful.");
260b1750805Sitojun epsvall++;
261b1750805Sitojun }
262b1750805Sitojun }
263e14c8eb3Sitojun | EPSV check_login CRLF
264b1750805Sitojun {
265e14c8eb3Sitojun if ($2)
266b1750805Sitojun long_passive("EPSV", PF_UNSPEC);
267b1750805Sitojun }
26824713697Sbitblt | TYPE check_login SP type_code CRLF
269df930be7Sderaadt {
27024713697Sbitblt if ($2) {
271df930be7Sderaadt switch (cmd_type) {
272df930be7Sderaadt
273df930be7Sderaadt case TYPE_A:
274df930be7Sderaadt if (cmd_form == FORM_N) {
275df930be7Sderaadt reply(200, "Type set to A.");
276df930be7Sderaadt type = cmd_type;
277df930be7Sderaadt form = cmd_form;
278df930be7Sderaadt } else
279df930be7Sderaadt reply(504, "Form must be N.");
280df930be7Sderaadt break;
281df930be7Sderaadt
282df930be7Sderaadt case TYPE_E:
283df930be7Sderaadt reply(504, "Type E not implemented.");
284df930be7Sderaadt break;
285df930be7Sderaadt
286df930be7Sderaadt case TYPE_I:
287df930be7Sderaadt reply(200, "Type set to I.");
288df930be7Sderaadt type = cmd_type;
289df930be7Sderaadt break;
290df930be7Sderaadt
291df930be7Sderaadt case TYPE_L:
292df930be7Sderaadt if (cmd_bytesz == 8) {
293df930be7Sderaadt reply(200,
294df930be7Sderaadt "Type set to L (byte size 8).");
295df930be7Sderaadt type = cmd_type;
296df930be7Sderaadt } else
297df930be7Sderaadt reply(504, "Byte size must be 8.");
29824713697Sbitblt
299df930be7Sderaadt }
300df930be7Sderaadt }
30124713697Sbitblt }
30224713697Sbitblt | STRU check_login SP struct_code CRLF
303df930be7Sderaadt {
30424713697Sbitblt if ($2) {
30524713697Sbitblt switch ($4) {
306df930be7Sderaadt
307df930be7Sderaadt case STRU_F:
308df930be7Sderaadt reply(200, "STRU F ok.");
309df930be7Sderaadt break;
310df930be7Sderaadt
311df930be7Sderaadt default:
312df930be7Sderaadt reply(504, "Unimplemented STRU type.");
313df930be7Sderaadt }
314df930be7Sderaadt }
31524713697Sbitblt }
31624713697Sbitblt | MODE check_login SP mode_code CRLF
317df930be7Sderaadt {
31824713697Sbitblt if ($2) {
31924713697Sbitblt switch ($4) {
320df930be7Sderaadt
321df930be7Sderaadt case MODE_S:
322df930be7Sderaadt reply(200, "MODE S ok.");
323df930be7Sderaadt break;
324df930be7Sderaadt
325df930be7Sderaadt default:
326df930be7Sderaadt reply(502, "Unimplemented MODE type.");
327df930be7Sderaadt }
328df930be7Sderaadt }
32924713697Sbitblt }
33024713697Sbitblt | ALLO check_login SP NUMBER CRLF
331df930be7Sderaadt {
33224713697Sbitblt if ($2) {
333df930be7Sderaadt reply(202, "ALLO command ignored.");
334df930be7Sderaadt }
33524713697Sbitblt }
33624713697Sbitblt | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
337df930be7Sderaadt {
33824713697Sbitblt if ($2) {
339df930be7Sderaadt reply(202, "ALLO command ignored.");
340df930be7Sderaadt }
34124713697Sbitblt }
342df930be7Sderaadt | RETR check_login SP pathname CRLF
343df930be7Sderaadt {
344df930be7Sderaadt if ($2 && $4 != NULL)
3457a7d71fbStedu retrieve(RET_FILE, $4);
346df930be7Sderaadt if ($4 != NULL)
347df930be7Sderaadt free($4);
348df930be7Sderaadt }
349df930be7Sderaadt | STOR check_login SP pathname CRLF
350df930be7Sderaadt {
351df930be7Sderaadt if ($2 && $4 != NULL)
352df930be7Sderaadt store($4, "w", 0);
353df930be7Sderaadt if ($4 != NULL)
354df930be7Sderaadt free($4);
355df930be7Sderaadt }
356df930be7Sderaadt | APPE check_login SP pathname CRLF
357df930be7Sderaadt {
358df930be7Sderaadt if ($2 && $4 != NULL)
359df930be7Sderaadt store($4, "a", 0);
360df930be7Sderaadt if ($4 != NULL)
361df930be7Sderaadt free($4);
362df930be7Sderaadt }
363df930be7Sderaadt | NLST check_login CRLF
364df930be7Sderaadt {
365df930be7Sderaadt if ($2)
366df930be7Sderaadt send_file_list(".");
367df930be7Sderaadt }
368df930be7Sderaadt | NLST check_login SP STRING CRLF
369df930be7Sderaadt {
370df930be7Sderaadt if ($2 && $4 != NULL)
371df930be7Sderaadt send_file_list($4);
372df930be7Sderaadt free($4);
373df930be7Sderaadt }
374df930be7Sderaadt | LIST check_login CRLF
375df930be7Sderaadt {
376df930be7Sderaadt if ($2)
377aed6457bSmillert retrieve(RET_LIST, ".");
378df930be7Sderaadt }
379df930be7Sderaadt | LIST check_login SP pathname CRLF
380df930be7Sderaadt {
381df930be7Sderaadt if ($2 && $4 != NULL)
3827a7d71fbStedu retrieve(RET_LIST, $4);
383df930be7Sderaadt if ($4 != NULL)
384df930be7Sderaadt free($4);
385df930be7Sderaadt }
386df930be7Sderaadt | STAT check_login SP pathname CRLF
387df930be7Sderaadt {
388df930be7Sderaadt if ($2 && $4 != NULL)
389df930be7Sderaadt statfilecmd($4);
390df930be7Sderaadt if ($4 != NULL)
391df930be7Sderaadt free($4);
392df930be7Sderaadt }
39324713697Sbitblt | STAT check_login CRLF
394df930be7Sderaadt {
39524713697Sbitblt if ($2)
396df930be7Sderaadt statcmd();
397df930be7Sderaadt }
398df930be7Sderaadt | DELE check_login SP pathname CRLF
399df930be7Sderaadt {
400df930be7Sderaadt if ($2 && $4 != NULL)
401df930be7Sderaadt delete($4);
402df930be7Sderaadt if ($4 != NULL)
403df930be7Sderaadt free($4);
404df930be7Sderaadt }
40524713697Sbitblt | RNTO check_login SP pathname CRLF
406df930be7Sderaadt {
407b6679d89Smpech if ($2 && $4 != NULL) {
408df930be7Sderaadt if (fromname) {
40924713697Sbitblt renamecmd(fromname, $4);
410df930be7Sderaadt free(fromname);
411a14f8237Sericj fromname = NULL;
412df930be7Sderaadt } else {
41324713697Sbitblt reply(503,
41424713697Sbitblt "Bad sequence of commands.");
415df930be7Sderaadt }
416df930be7Sderaadt }
417bf165b6aSmpech if ($4 != NULL)
41824713697Sbitblt free($4);
41924713697Sbitblt }
42024713697Sbitblt | ABOR check_login CRLF
421df930be7Sderaadt {
42224713697Sbitblt if ($2)
423df930be7Sderaadt reply(225, "ABOR command successful.");
424df930be7Sderaadt }
425df930be7Sderaadt | CWD check_login CRLF
426df930be7Sderaadt {
427df930be7Sderaadt if ($2)
428df930be7Sderaadt cwd(pw->pw_dir);
429df930be7Sderaadt }
430df930be7Sderaadt | CWD check_login SP pathname CRLF
431df930be7Sderaadt {
432df930be7Sderaadt if ($2 && $4 != NULL)
433df930be7Sderaadt cwd($4);
434df930be7Sderaadt if ($4 != NULL)
435df930be7Sderaadt free($4);
436df930be7Sderaadt }
437df930be7Sderaadt | HELP CRLF
438df930be7Sderaadt {
439a14f8237Sericj help(cmdtab, NULL);
440df930be7Sderaadt }
441df930be7Sderaadt | HELP SP STRING CRLF
442df930be7Sderaadt {
443df930be7Sderaadt char *cp = $3;
444df930be7Sderaadt
445df930be7Sderaadt if (strncasecmp(cp, "SITE", 4) == 0) {
446df930be7Sderaadt cp = $3 + 4;
447df930be7Sderaadt if (*cp == ' ')
448df930be7Sderaadt cp++;
449df930be7Sderaadt if (*cp)
450df930be7Sderaadt help(sitetab, cp);
451df930be7Sderaadt else
452a14f8237Sericj help(sitetab, NULL);
453df930be7Sderaadt } else
454df930be7Sderaadt help(cmdtab, $3);
45590d60234Sbitblt free ($3);
456df930be7Sderaadt }
457df930be7Sderaadt | NOOP CRLF
458df930be7Sderaadt {
459df930be7Sderaadt reply(200, "NOOP command successful.");
460df930be7Sderaadt }
461df930be7Sderaadt | MKD check_login SP pathname CRLF
462df930be7Sderaadt {
463df930be7Sderaadt if ($2 && $4 != NULL)
464df930be7Sderaadt makedir($4);
465df930be7Sderaadt if ($4 != NULL)
466df930be7Sderaadt free($4);
467df930be7Sderaadt }
468df930be7Sderaadt | RMD check_login SP pathname CRLF
469df930be7Sderaadt {
470df930be7Sderaadt if ($2 && $4 != NULL)
471df930be7Sderaadt removedir($4);
472df930be7Sderaadt if ($4 != NULL)
473df930be7Sderaadt free($4);
474df930be7Sderaadt }
475df930be7Sderaadt | PWD check_login CRLF
476df930be7Sderaadt {
477df930be7Sderaadt if ($2)
478df930be7Sderaadt pwd();
479df930be7Sderaadt }
480df930be7Sderaadt | CDUP check_login CRLF
481df930be7Sderaadt {
482df930be7Sderaadt if ($2)
483df930be7Sderaadt cwd("..");
484df930be7Sderaadt }
485df930be7Sderaadt | SITE SP HELP CRLF
486df930be7Sderaadt {
487a14f8237Sericj help(sitetab, NULL);
488df930be7Sderaadt }
489df930be7Sderaadt | SITE SP HELP SP STRING CRLF
490df930be7Sderaadt {
491df930be7Sderaadt help(sitetab, $5);
49290d60234Sbitblt free ($5);
493df930be7Sderaadt }
494df930be7Sderaadt | SITE SP UMASK check_login CRLF
495df930be7Sderaadt {
49695cc8c4aSderaadt mode_t oldmask;
497df930be7Sderaadt
498df930be7Sderaadt if ($4) {
499df930be7Sderaadt oldmask = umask(0);
500df930be7Sderaadt (void) umask(oldmask);
501df930be7Sderaadt reply(200, "Current UMASK is %03o", oldmask);
502df930be7Sderaadt }
503df930be7Sderaadt }
504df930be7Sderaadt | SITE SP UMASK check_login SP octal_number CRLF
505df930be7Sderaadt {
50695cc8c4aSderaadt mode_t oldmask;
507df930be7Sderaadt
508df930be7Sderaadt if ($4) {
509df930be7Sderaadt if (($6 == -1) || ($6 > 0777)) {
510df930be7Sderaadt reply(501, "Bad UMASK value");
511b3f5c309Sderaadt } else if (!umaskchange) {
512b3f5c309Sderaadt reply(550,
513b3f5c309Sderaadt "No permission to change umask.");
514df930be7Sderaadt } else {
515df930be7Sderaadt oldmask = umask($6);
516df930be7Sderaadt reply(200,
517df930be7Sderaadt "UMASK set to %03o (was %03o)",
518df930be7Sderaadt $6, oldmask);
519df930be7Sderaadt }
520df930be7Sderaadt }
521df930be7Sderaadt }
522df930be7Sderaadt | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
523df930be7Sderaadt {
524df930be7Sderaadt if ($4 && ($8 != NULL)) {
525e2b3faedSmpech if (($6 == -1) || ($6 > 0777))
526df930be7Sderaadt reply(501,
527b3f5c309Sderaadt "CHMOD: Mode value must be between "
528b3f5c309Sderaadt "0 and 0777");
529b3f5c309Sderaadt else if (!umaskchange)
530b3f5c309Sderaadt reply(550,
531b3f5c309Sderaadt "No permission to change mode of %s.",
532b3f5c309Sderaadt $8);
533df69c215Sderaadt else if (chmod($8, $6) == -1)
534df930be7Sderaadt perror_reply(550, $8);
535df930be7Sderaadt else
536b3f5c309Sderaadt reply(200,
537b3f5c309Sderaadt "CHMOD command successful.");
538df930be7Sderaadt }
539df930be7Sderaadt if ($8 != NULL)
540df930be7Sderaadt free($8);
541df930be7Sderaadt }
54224713697Sbitblt | SITE SP check_login IDLE CRLF
543df930be7Sderaadt {
54424713697Sbitblt if ($3)
545df930be7Sderaadt reply(200,
5465c7d4e7cSderaadt "Current IDLE time limit is %d "
5475c7d4e7cSderaadt "seconds; max %d",
548df930be7Sderaadt timeout, maxtimeout);
549df930be7Sderaadt }
55040916ffbSbitblt | SITE SP check_login IDLE SP NUMBER CRLF
551df930be7Sderaadt {
55240916ffbSbitblt if ($3) {
55324713697Sbitblt if ($6 < 30 || $6 > maxtimeout) {
554df930be7Sderaadt reply(501,
555b3f5c309Sderaadt "Maximum IDLE time must be between "
556b3f5c309Sderaadt "30 and %d seconds",
557df930be7Sderaadt maxtimeout);
558df930be7Sderaadt } else {
55924713697Sbitblt timeout = $6;
560df930be7Sderaadt (void) alarm((unsigned) timeout);
561df930be7Sderaadt reply(200,
562df930be7Sderaadt "Maximum IDLE time set to %d seconds",
563df930be7Sderaadt timeout);
564df930be7Sderaadt }
565df930be7Sderaadt }
56624713697Sbitblt }
567df930be7Sderaadt | STOU check_login SP pathname CRLF
568df930be7Sderaadt {
569df930be7Sderaadt if ($2 && $4 != NULL)
570df930be7Sderaadt store($4, "w", 1);
571df930be7Sderaadt if ($4 != NULL)
572df930be7Sderaadt free($4);
573df930be7Sderaadt }
57424713697Sbitblt | SYST check_login CRLF
575df930be7Sderaadt {
57624713697Sbitblt if ($2)
57708cb63c2Stedu reply(215, "UNIX Type: L8");
578df930be7Sderaadt }
579df930be7Sderaadt
580df930be7Sderaadt /*
581df930be7Sderaadt * SIZE is not in RFC959, but Postel has blessed it and
582df930be7Sderaadt * it will be in the updated RFC.
583df930be7Sderaadt *
584df930be7Sderaadt * Return size of file in a format suitable for
585df930be7Sderaadt * using with RESTART (we just count bytes).
586df930be7Sderaadt */
587df930be7Sderaadt | SIZE check_login SP pathname CRLF
588df930be7Sderaadt {
589df930be7Sderaadt if ($2 && $4 != NULL)
590df930be7Sderaadt sizecmd($4);
591df930be7Sderaadt if ($4 != NULL)
592df930be7Sderaadt free($4);
593df930be7Sderaadt }
594df930be7Sderaadt
595df930be7Sderaadt /*
596df930be7Sderaadt * MDTM is not in RFC959, but Postel has blessed it and
597df930be7Sderaadt * it will be in the updated RFC.
598df930be7Sderaadt *
599df930be7Sderaadt * Return modification time of file as an ISO 3307
600df930be7Sderaadt * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
601df930be7Sderaadt * where xxx is the fractional second (of any precision,
602df930be7Sderaadt * not necessarily 3 digits)
603df930be7Sderaadt */
604df930be7Sderaadt | MDTM check_login SP pathname CRLF
605df930be7Sderaadt {
606df930be7Sderaadt if ($2 && $4 != NULL) {
607df930be7Sderaadt struct stat stbuf;
608df69c215Sderaadt if (stat($4, &stbuf) == -1)
609df930be7Sderaadt reply(550, "%s: %s",
610df930be7Sderaadt $4, strerror(errno));
611df930be7Sderaadt else if (!S_ISREG(stbuf.st_mode)) {
612df930be7Sderaadt reply(550, "%s: not a plain file.", $4);
613df930be7Sderaadt } else {
614df930be7Sderaadt struct tm *t;
615df930be7Sderaadt t = gmtime(&stbuf.st_mtime);
616*73fe6daaSflorian if (t == NULL) {
617*73fe6daaSflorian /* invalid time, use epoch */
618*73fe6daaSflorian stbuf.st_mtime = 0;
619*73fe6daaSflorian t = gmtime(&stbuf.st_mtime);
620*73fe6daaSflorian }
621df930be7Sderaadt reply(213,
62269a3ff95Sderaadt "%04d%02d%02d%02d%02d%02d",
62378badebcSmillert 1900 + t->tm_year,
62469a3ff95Sderaadt t->tm_mon+1, t->tm_mday,
625df930be7Sderaadt t->tm_hour, t->tm_min, t->tm_sec);
626df930be7Sderaadt }
627df930be7Sderaadt }
628df930be7Sderaadt if ($4 != NULL)
629df930be7Sderaadt free($4);
630df930be7Sderaadt }
631df930be7Sderaadt | QUIT CRLF
632df930be7Sderaadt {
633df930be7Sderaadt reply(221, "Goodbye.");
634df930be7Sderaadt dologout(0);
635df930be7Sderaadt }
6362378d5feSmillert | error
637df930be7Sderaadt {
6382378d5feSmillert yyclearin; /* discard lookahead data */
6392378d5feSmillert yyerrok; /* clear error condition */
6402378d5feSmillert state = 0; /* reset lexer state */
641df930be7Sderaadt }
642df930be7Sderaadt ;
643df930be7Sderaadt rcmd
644df930be7Sderaadt : RNFR check_login SP pathname CRLF
645df930be7Sderaadt {
646a20a5177Sguenther restart_point = 0;
647df930be7Sderaadt if ($2 && $4) {
648f59925a5Smillert if (fromname)
649f59925a5Smillert free(fromname);
650df930be7Sderaadt fromname = renamefrom($4);
651f59925a5Smillert if (fromname == NULL)
652df930be7Sderaadt free($4);
653f59925a5Smillert } else if ($4) {
65424713697Sbitblt free ($4);
655df930be7Sderaadt }
656df930be7Sderaadt }
65724713697Sbitblt
65809a2692aSmillert | REST check_login SP file_size CRLF
659df930be7Sderaadt {
66024713697Sbitblt if ($2) {
661f59925a5Smillert if (fromname) {
662f59925a5Smillert free(fromname);
663a14f8237Sericj fromname = NULL;
664f59925a5Smillert }
66509a2692aSmillert restart_point = $4;
66609a2692aSmillert reply(350, "Restarting at %lld. %s",
66709a2692aSmillert (long long)restart_point,
668df930be7Sderaadt "Send STORE or RETRIEVE to initiate transfer.");
669df930be7Sderaadt }
67024713697Sbitblt }
671df930be7Sderaadt ;
672df930be7Sderaadt
673df930be7Sderaadt username
674df930be7Sderaadt : STRING
675df930be7Sderaadt ;
676df930be7Sderaadt
677df930be7Sderaadt password
678df930be7Sderaadt : /* empty */
679df930be7Sderaadt {
680dc88c29eSderaadt $$ = calloc(1, sizeof(char));
681df930be7Sderaadt }
682df930be7Sderaadt | STRING
683df930be7Sderaadt ;
684df930be7Sderaadt
685df930be7Sderaadt byte_size
686df930be7Sderaadt : NUMBER
687df930be7Sderaadt ;
688df930be7Sderaadt
68909a2692aSmillert file_size
69009a2692aSmillert : NUMBER
69109a2692aSmillert {
69209a2692aSmillert $$ = $1;
69309a2692aSmillert }
69409a2692aSmillert | BIGNUM
69509a2692aSmillert {
69609a2692aSmillert $$ = $1;
69709a2692aSmillert }
69809a2692aSmillert ;
69909a2692aSmillert
700df930be7Sderaadt host_port
701df930be7Sderaadt : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
702df930be7Sderaadt NUMBER COMMA NUMBER
703df930be7Sderaadt {
704df930be7Sderaadt char *a, *p;
705df930be7Sderaadt
706501751e5Sderaadt if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 ||
707501751e5Sderaadt $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
708501751e5Sderaadt $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) {
709501751e5Sderaadt $$ = 1;
710501751e5Sderaadt } else {
711b1750805Sitojun data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
712b1750805Sitojun data_dest.su_sin.sin_family = AF_INET;
713b1750805Sitojun p = (char *)&data_dest.su_sin.sin_port;
714df930be7Sderaadt p[0] = $9; p[1] = $11;
715b1750805Sitojun a = (char *)&data_dest.su_sin.sin_addr;
716df930be7Sderaadt a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
717501751e5Sderaadt $$ = 0;
718501751e5Sderaadt }
719df930be7Sderaadt }
720df930be7Sderaadt ;
721df930be7Sderaadt
722b1750805Sitojun host_long_port4
723b1750805Sitojun : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
724b1750805Sitojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
725b1750805Sitojun NUMBER
726b1750805Sitojun {
727b1750805Sitojun char *a, *p;
728b1750805Sitojun
729b1750805Sitojun /* reject invalid LPRT command */
73095cc8c4aSderaadt if ($1 != 4 || $3 != 4 ||
73195cc8c4aSderaadt $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
73295cc8c4aSderaadt $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 ||
73395cc8c4aSderaadt $13 != 2 ||
73495cc8c4aSderaadt $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) {
735b1750805Sitojun $$ = 1;
736b1750805Sitojun } else {
737b1750805Sitojun data_dest.su_sin.sin_len =
738b1750805Sitojun sizeof(struct sockaddr_in);
739b1750805Sitojun data_dest.su_family = AF_INET;
740b1750805Sitojun p = (char *)&data_dest.su_port;
741b1750805Sitojun p[0] = $15; p[1] = $17;
742b1750805Sitojun a = (char *)&data_dest.su_sin.sin_addr;
743b1750805Sitojun a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
744b1750805Sitojun $$ = 0;
745b1750805Sitojun }
746b1750805Sitojun }
747b1750805Sitojun ;
748b1750805Sitojun
749b1750805Sitojun host_long_port6
750b1750805Sitojun : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
751b1750805Sitojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
752b1750805Sitojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
753b1750805Sitojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
754b1750805Sitojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
755b1750805Sitojun NUMBER
756b1750805Sitojun {
757b1750805Sitojun char *a, *p;
758b1750805Sitojun
759b1750805Sitojun /* reject invalid LPRT command */
76095cc8c4aSderaadt if ($1 != 6 || $3 != 16 ||
76195cc8c4aSderaadt $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
76295cc8c4aSderaadt $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 ||
76395cc8c4aSderaadt $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 ||
76495cc8c4aSderaadt $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 ||
76595cc8c4aSderaadt $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 ||
76695cc8c4aSderaadt $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 ||
76795cc8c4aSderaadt $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 ||
76895cc8c4aSderaadt $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 ||
76995cc8c4aSderaadt $37 != 2 ||
77095cc8c4aSderaadt $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) {
771b1750805Sitojun $$ = 1;
772b1750805Sitojun } else {
773b1750805Sitojun data_dest.su_sin6.sin6_len =
774b1750805Sitojun sizeof(struct sockaddr_in6);
775b1750805Sitojun data_dest.su_family = AF_INET6;
776b1750805Sitojun p = (char *)&data_dest.su_port;
777b1750805Sitojun p[0] = $39; p[1] = $41;
778b1750805Sitojun a = (char *)&data_dest.su_sin6.sin6_addr;
779b1750805Sitojun a[0] = $5; a[1] = $7;
780b1750805Sitojun a[2] = $9; a[3] = $11;
781b1750805Sitojun a[4] = $13; a[5] = $15;
782b1750805Sitojun a[6] = $17; a[7] = $19;
783b1750805Sitojun a[8] = $21; a[9] = $23;
784b1750805Sitojun a[10] = $25; a[11] = $27;
785b1750805Sitojun a[12] = $29; a[13] = $31;
786b1750805Sitojun a[14] = $33; a[15] = $35;
787b1750805Sitojun if (his_addr.su_family == AF_INET6) {
788b1750805Sitojun /* XXX more sanity checks! */
789b1750805Sitojun data_dest.su_sin6.sin6_scope_id =
790b1750805Sitojun his_addr.su_sin6.sin6_scope_id;
791b1750805Sitojun }
792b1750805Sitojun
793b1750805Sitojun $$ = 0;
794b1750805Sitojun }
795b1750805Sitojun }
796b1750805Sitojun ;
797b1750805Sitojun
798df930be7Sderaadt form_code
799df930be7Sderaadt : N
800df930be7Sderaadt {
801df930be7Sderaadt $$ = FORM_N;
802df930be7Sderaadt }
803df930be7Sderaadt | T
804df930be7Sderaadt {
805df930be7Sderaadt $$ = FORM_T;
806df930be7Sderaadt }
807df930be7Sderaadt | C
808df930be7Sderaadt {
809df930be7Sderaadt $$ = FORM_C;
810df930be7Sderaadt }
811df930be7Sderaadt ;
812df930be7Sderaadt
813df930be7Sderaadt type_code
814df930be7Sderaadt : A
815df930be7Sderaadt {
816df930be7Sderaadt cmd_type = TYPE_A;
817df930be7Sderaadt cmd_form = FORM_N;
818df930be7Sderaadt }
819df930be7Sderaadt | A SP form_code
820df930be7Sderaadt {
821df930be7Sderaadt cmd_type = TYPE_A;
822df930be7Sderaadt cmd_form = $3;
823df930be7Sderaadt }
824df930be7Sderaadt | E
825df930be7Sderaadt {
826df930be7Sderaadt cmd_type = TYPE_E;
827df930be7Sderaadt cmd_form = FORM_N;
828df930be7Sderaadt }
829df930be7Sderaadt | E SP form_code
830df930be7Sderaadt {
831df930be7Sderaadt cmd_type = TYPE_E;
832df930be7Sderaadt cmd_form = $3;
833df930be7Sderaadt }
834df930be7Sderaadt | I
835df930be7Sderaadt {
836df930be7Sderaadt cmd_type = TYPE_I;
837df930be7Sderaadt }
838df930be7Sderaadt | L
839df930be7Sderaadt {
840df930be7Sderaadt cmd_type = TYPE_L;
84108cb63c2Stedu cmd_bytesz = 8;
842df930be7Sderaadt }
843df930be7Sderaadt | L SP byte_size
844df930be7Sderaadt {
845df930be7Sderaadt cmd_type = TYPE_L;
846df930be7Sderaadt cmd_bytesz = $3;
847df930be7Sderaadt }
848df930be7Sderaadt /* this is for a bug in the BBN ftp */
849df930be7Sderaadt | L byte_size
850df930be7Sderaadt {
851df930be7Sderaadt cmd_type = TYPE_L;
852df930be7Sderaadt cmd_bytesz = $2;
853df930be7Sderaadt }
854df930be7Sderaadt ;
855df930be7Sderaadt
856df930be7Sderaadt struct_code
857df930be7Sderaadt : F
858df930be7Sderaadt {
859df930be7Sderaadt $$ = STRU_F;
860df930be7Sderaadt }
861df930be7Sderaadt | R
862df930be7Sderaadt {
863df930be7Sderaadt $$ = STRU_R;
864df930be7Sderaadt }
865df930be7Sderaadt | P
866df930be7Sderaadt {
867df930be7Sderaadt $$ = STRU_P;
868df930be7Sderaadt }
869df930be7Sderaadt ;
870df930be7Sderaadt
871df930be7Sderaadt mode_code
872df930be7Sderaadt : S
873df930be7Sderaadt {
874df930be7Sderaadt $$ = MODE_S;
875df930be7Sderaadt }
876df930be7Sderaadt | B
877df930be7Sderaadt {
878df930be7Sderaadt $$ = MODE_B;
879df930be7Sderaadt }
880df930be7Sderaadt | C
881df930be7Sderaadt {
882df930be7Sderaadt $$ = MODE_C;
883df930be7Sderaadt }
884df930be7Sderaadt ;
885df930be7Sderaadt
886df930be7Sderaadt pathname
887df930be7Sderaadt : pathstring
888df930be7Sderaadt {
889df930be7Sderaadt /*
890df930be7Sderaadt * Problem: this production is used for all pathname
891df930be7Sderaadt * processing, but only gives a 550 error reply.
892df930be7Sderaadt * This is a valid reply in some cases but not in others.
893df930be7Sderaadt */
89435ac9933Sdownsj if (logged_in && $1 && strchr($1, '~') != NULL) {
895df930be7Sderaadt glob_t gl;
896df930be7Sderaadt int flags =
897df930be7Sderaadt GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
89835ac9933Sdownsj char *pptr = $1;
89935ac9933Sdownsj
90035ac9933Sdownsj /*
90135ac9933Sdownsj * glob() will only find a leading ~, but
90235ac9933Sdownsj * Netscape kindly puts a slash in front of
90335ac9933Sdownsj * it for publish URLs. There needs to be
90435ac9933Sdownsj * a flag for glob() that expands tildes
90535ac9933Sdownsj * anywhere in the string.
90635ac9933Sdownsj */
90735ac9933Sdownsj if ((pptr[0] == '/') && (pptr[1] == '~'))
90835ac9933Sdownsj pptr++;
909df930be7Sderaadt
910df930be7Sderaadt memset(&gl, 0, sizeof(gl));
91135ac9933Sdownsj if (glob(pptr, flags, NULL, &gl) ||
912df930be7Sderaadt gl.gl_pathc == 0) {
913df930be7Sderaadt reply(550, "not found");
914df930be7Sderaadt $$ = NULL;
915df930be7Sderaadt } else {
916df930be7Sderaadt $$ = strdup(gl.gl_pathv[0]);
917df930be7Sderaadt }
918df930be7Sderaadt globfree(&gl);
919df930be7Sderaadt free($1);
920df930be7Sderaadt } else
921df930be7Sderaadt $$ = $1;
922df930be7Sderaadt }
923df930be7Sderaadt ;
924df930be7Sderaadt
925df930be7Sderaadt pathstring
926df930be7Sderaadt : STRING
927df930be7Sderaadt ;
928df930be7Sderaadt
929df930be7Sderaadt octal_number
930df930be7Sderaadt : NUMBER
931df930be7Sderaadt {
932df930be7Sderaadt int ret, dec, multby, digit;
933df930be7Sderaadt
934df930be7Sderaadt /*
935df930be7Sderaadt * Convert a number that was read as decimal number
936df930be7Sderaadt * to what it would be if it had been read as octal.
937df930be7Sderaadt */
938df930be7Sderaadt dec = $1;
939df930be7Sderaadt multby = 1;
940df930be7Sderaadt ret = 0;
941df930be7Sderaadt while (dec) {
942df930be7Sderaadt digit = dec%10;
943df930be7Sderaadt if (digit > 7) {
944df930be7Sderaadt ret = -1;
945df930be7Sderaadt break;
946df930be7Sderaadt }
947df930be7Sderaadt ret += digit * multby;
948df930be7Sderaadt multby *= 8;
949df930be7Sderaadt dec /= 10;
950df930be7Sderaadt }
951df930be7Sderaadt $$ = ret;
952df930be7Sderaadt }
953df930be7Sderaadt ;
954df930be7Sderaadt
955df930be7Sderaadt
956df930be7Sderaadt check_login
957df930be7Sderaadt : /* empty */
958df930be7Sderaadt {
959df930be7Sderaadt if (logged_in)
960df930be7Sderaadt $$ = 1;
961df930be7Sderaadt else {
962df930be7Sderaadt reply(530, "Please login with USER and PASS.");
963df930be7Sderaadt $$ = 0;
964ad3b829aSmikeb state = 0;
965ad3b829aSmikeb YYABORT;
966df930be7Sderaadt }
967df930be7Sderaadt }
968df930be7Sderaadt ;
969df930be7Sderaadt
97041fb778fSitojun check_login_epsvall
971e14c8eb3Sitojun : /* empty */
972e14c8eb3Sitojun {
97341fb778fSitojun if (!logged_in) {
97441fb778fSitojun reply(530, "Please login with USER and PASS.");
97541fb778fSitojun $$ = 0;
976ad3b829aSmikeb state = 0;
977ad3b829aSmikeb YYABORT;
97841fb778fSitojun } else if (epsvall) {
979e14c8eb3Sitojun reply(501, "the command is disallowed "
980e14c8eb3Sitojun "after EPSV ALL");
981e14c8eb3Sitojun usedefault = 1;
982e14c8eb3Sitojun $$ = 0;
983e14c8eb3Sitojun } else
984e14c8eb3Sitojun $$ = 1;
985e14c8eb3Sitojun }
986e14c8eb3Sitojun ;
987e14c8eb3Sitojun
988df930be7Sderaadt %%
989df930be7Sderaadt
990df930be7Sderaadt #define CMD 0 /* beginning of command */
991df930be7Sderaadt #define ARGS 1 /* expect miscellaneous arguments */
992df930be7Sderaadt #define STR1 2 /* expect SP followed by STRING */
993df930be7Sderaadt #define STR2 3 /* expect STRING */
994df930be7Sderaadt #define OSTR 4 /* optional SP then STRING */
995df930be7Sderaadt #define ZSTR1 5 /* SP then optional STRING */
996df930be7Sderaadt #define ZSTR2 6 /* optional STRING after SP */
997df930be7Sderaadt #define SITECMD 7 /* SITE command */
998df930be7Sderaadt #define NSTR 8 /* Number followed by a string */
999df930be7Sderaadt
1000df930be7Sderaadt struct tab {
1001df930be7Sderaadt char *name;
1002df930be7Sderaadt short token;
1003df930be7Sderaadt short state;
1004df930be7Sderaadt short implemented; /* 1 if command is implemented */
1005df930be7Sderaadt char *help;
1006df930be7Sderaadt };
1007df930be7Sderaadt
1008df930be7Sderaadt struct tab cmdtab[] = { /* In order defined in RFC 765 */
1009df930be7Sderaadt { "USER", USER, STR1, 1, "<sp> username" },
1010df930be7Sderaadt { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1011df930be7Sderaadt { "ACCT", ACCT, STR1, 0, "(specify account)" },
1012df930be7Sderaadt { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1013df930be7Sderaadt { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1014df930be7Sderaadt { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1015df930be7Sderaadt { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1016b1750805Sitojun { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1017b1750805Sitojun { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1018df930be7Sderaadt { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1019b1750805Sitojun { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1020b1750805Sitojun { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1021df930be7Sderaadt { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1022df930be7Sderaadt { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1023df930be7Sderaadt { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1024df930be7Sderaadt { "RETR", RETR, STR1, 1, "<sp> file-name" },
1025df930be7Sderaadt { "STOR", STOR, STR1, 1, "<sp> file-name" },
1026df930be7Sderaadt { "APPE", APPE, STR1, 1, "<sp> file-name" },
1027df930be7Sderaadt { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1028df930be7Sderaadt { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1029df930be7Sderaadt { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1030df930be7Sderaadt { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1031df930be7Sderaadt { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1032df930be7Sderaadt { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1033df930be7Sderaadt { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1034df930be7Sderaadt { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1035df930be7Sderaadt { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1036df930be7Sderaadt { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1037df930be7Sderaadt { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1038df930be7Sderaadt { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1039df930be7Sderaadt { "DELE", DELE, STR1, 1, "<sp> file-name" },
1040df930be7Sderaadt { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1041df930be7Sderaadt { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1042df930be7Sderaadt { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1043df930be7Sderaadt { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1044df930be7Sderaadt { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1045df930be7Sderaadt { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1046df930be7Sderaadt { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1047df930be7Sderaadt { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1048df930be7Sderaadt { "NOOP", NOOP, ARGS, 1, "" },
1049df930be7Sderaadt { "MKD", MKD, STR1, 1, "<sp> path-name" },
1050df930be7Sderaadt { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1051df930be7Sderaadt { "RMD", RMD, STR1, 1, "<sp> path-name" },
1052df930be7Sderaadt { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1053df930be7Sderaadt { "PWD", PWD, ARGS, 1, "(return current directory)" },
1054df930be7Sderaadt { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1055df930be7Sderaadt { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1056df930be7Sderaadt { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1057df930be7Sderaadt { "STOU", STOU, STR1, 1, "<sp> file-name" },
1058df930be7Sderaadt { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1059df930be7Sderaadt { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1060df930be7Sderaadt { NULL, 0, 0, 0, 0 }
1061df930be7Sderaadt };
1062df930be7Sderaadt
1063df930be7Sderaadt struct tab sitetab[] = {
1064df930be7Sderaadt { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1065df930be7Sderaadt { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1066df930be7Sderaadt { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1067df930be7Sderaadt { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1068df930be7Sderaadt { NULL, 0, 0, 0, 0 }
1069df930be7Sderaadt };
1070df930be7Sderaadt
1071c72b5b24Smillert static void help(struct tab *, char *);
1072df930be7Sderaadt static struct tab *
10730c50dd5dSjan lookup(struct tab *, const char *);
10740c50dd5dSjan static void sizecmd(const char *);
1075c72b5b24Smillert static int yylex(void);
1076df930be7Sderaadt
1077b1750805Sitojun extern int epsvall;
1078b1750805Sitojun
1079df930be7Sderaadt static struct tab *
lookup(struct tab * p,const char * cmd)108018a2fcc3Sjan lookup(struct tab *p, const char *cmd)
1081df930be7Sderaadt {
1082df930be7Sderaadt
1083df930be7Sderaadt for (; p->name != NULL; p++)
1084df930be7Sderaadt if (strcmp(cmd, p->name) == 0)
1085df930be7Sderaadt return (p);
1086cbe1e71dSmpech return (NULL);
1087df930be7Sderaadt }
1088df930be7Sderaadt
1089df930be7Sderaadt #include <arpa/telnet.h>
1090df930be7Sderaadt
1091df930be7Sderaadt /*
1092f9bbbf45Sfgsch * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1093df930be7Sderaadt */
10940391227fSmoritz int
get_line(char * s,int n)109518a2fcc3Sjan get_line(char *s, int n)
1096df930be7Sderaadt {
1097df930be7Sderaadt int c;
109850d2b651Smpech char *cs;
1099df930be7Sderaadt
1100df930be7Sderaadt cs = s;
1101df930be7Sderaadt /* tmpline may contain saved command from urgent mode interruption */
1102df930be7Sderaadt for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1103df930be7Sderaadt *cs++ = tmpline[c];
1104df930be7Sderaadt if (tmpline[c] == '\n') {
1105df930be7Sderaadt *cs++ = '\0';
1106df930be7Sderaadt if (debug)
1107df930be7Sderaadt syslog(LOG_DEBUG, "command: %s", s);
1108df930be7Sderaadt tmpline[0] = '\0';
11090391227fSmoritz return(0);
1110df930be7Sderaadt }
1111df930be7Sderaadt if (c == 0)
1112df930be7Sderaadt tmpline[0] = '\0';
1113df930be7Sderaadt }
1114330fc432Sjan while ((c = getc(stdin)) != EOF) {
1115df930be7Sderaadt c &= 0377;
1116df930be7Sderaadt if (c == IAC) {
1117330fc432Sjan if ((c = getc(stdin)) != EOF) {
1118df930be7Sderaadt c &= 0377;
1119df930be7Sderaadt switch (c) {
1120df930be7Sderaadt case WILL:
1121df930be7Sderaadt case WONT:
1122330fc432Sjan c = getc(stdin);
1123df930be7Sderaadt printf("%c%c%c", IAC, DONT, 0377&c);
1124df930be7Sderaadt (void) fflush(stdout);
1125df930be7Sderaadt continue;
1126df930be7Sderaadt case DO:
1127df930be7Sderaadt case DONT:
1128330fc432Sjan c = getc(stdin);
1129df930be7Sderaadt printf("%c%c%c", IAC, WONT, 0377&c);
1130df930be7Sderaadt (void) fflush(stdout);
1131df930be7Sderaadt continue;
1132df930be7Sderaadt case IAC:
1133df930be7Sderaadt break;
1134df930be7Sderaadt default:
1135df930be7Sderaadt continue; /* ignore command */
1136df930be7Sderaadt }
1137df930be7Sderaadt }
1138df930be7Sderaadt }
1139df930be7Sderaadt *cs++ = c;
11400391227fSmoritz if (--n <= 0) {
11410391227fSmoritz /*
11420391227fSmoritz * If command doesn't fit into buffer, discard the
11430391227fSmoritz * rest of the command and indicate truncation.
11440391227fSmoritz * This prevents the command to be split up into
11450391227fSmoritz * multiple commands.
11460391227fSmoritz */
1147330fc432Sjan while (c != '\n' && (c = getc(stdin)) != EOF)
11480391227fSmoritz ;
11490391227fSmoritz return (-2);
11500391227fSmoritz }
11510391227fSmoritz if (c == '\n')
1152df930be7Sderaadt break;
1153df930be7Sderaadt }
1154df930be7Sderaadt if (c == EOF && cs == s)
11550391227fSmoritz return (-1);
1156df930be7Sderaadt *cs++ = '\0';
1157df930be7Sderaadt if (debug) {
1158df930be7Sderaadt if (!guest && strncasecmp("pass ", s, 5) == 0) {
1159df930be7Sderaadt /* Don't syslog passwords */
1160df930be7Sderaadt syslog(LOG_DEBUG, "command: %.5s ???", s);
1161df930be7Sderaadt } else {
116250d2b651Smpech char *cp;
116350d2b651Smpech int len;
1164df930be7Sderaadt
1165df930be7Sderaadt /* Don't syslog trailing CR-LF */
1166df930be7Sderaadt len = strlen(s);
1167df930be7Sderaadt cp = s + len - 1;
1168df930be7Sderaadt while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1169df930be7Sderaadt --cp;
1170df930be7Sderaadt --len;
1171df930be7Sderaadt }
1172df930be7Sderaadt syslog(LOG_DEBUG, "command: %.*s", len, s);
1173df930be7Sderaadt }
1174df930be7Sderaadt }
11750391227fSmoritz return (0);
1176df930be7Sderaadt }
1177df930be7Sderaadt
1178c5d07883Sbitblt void
toolong(int signo)117918a2fcc3Sjan toolong(int signo)
1180df930be7Sderaadt {
1181abe43e41Sderaadt struct syslog_data sdata = SYSLOG_DATA_INIT;
1182df930be7Sderaadt
11838b6f1defSderaadt reply_r(421,
1184df930be7Sderaadt "Timeout (%d seconds): closing control connection.", timeout);
1185df930be7Sderaadt if (logging)
1186abe43e41Sderaadt syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds",
1187df930be7Sderaadt (pw ? pw -> pw_name : "unknown"), timeout);
1188df930be7Sderaadt dologout(1);
1189df930be7Sderaadt }
1190df930be7Sderaadt
1191df930be7Sderaadt static int
yylex(void)119218a2fcc3Sjan yylex(void)
1193df930be7Sderaadt {
11942378d5feSmillert static int cpos;
1195df930be7Sderaadt char *cp, *cp2;
1196df930be7Sderaadt struct tab *p;
1197df930be7Sderaadt int n;
1198df930be7Sderaadt char c;
1199df930be7Sderaadt
1200df930be7Sderaadt for (;;) {
1201df930be7Sderaadt switch (state) {
1202df930be7Sderaadt
1203df930be7Sderaadt case CMD:
1204df930be7Sderaadt (void) alarm((unsigned) timeout);
1205330fc432Sjan n = get_line(cbuf, sizeof(cbuf)-1);
12060391227fSmoritz if (n == -1) {
1207df930be7Sderaadt reply(221, "You could at least say goodbye.");
1208df930be7Sderaadt dologout(0);
12090391227fSmoritz } else if (n == -2) {
1210e9093204Smoritz reply(500, "Command too long.");
1211e9093204Smoritz alarm(0);
1212e9093204Smoritz continue;
1213df930be7Sderaadt }
1214df930be7Sderaadt (void) alarm(0);
1215df930be7Sderaadt if ((cp = strchr(cbuf, '\r'))) {
1216df930be7Sderaadt *cp++ = '\n';
1217df930be7Sderaadt *cp = '\0';
1218df930be7Sderaadt }
1219a14f8237Sericj if (strncasecmp(cbuf, "PASS", 4) != 0) {
1220d36a909dSderaadt if ((cp = strpbrk(cbuf, "\n"))) {
1221d36a909dSderaadt c = *cp;
1222d36a909dSderaadt *cp = '\0';
1223d36a909dSderaadt setproctitle("%s: %s", proctitle, cbuf);
1224d36a909dSderaadt *cp = c;
1225d36a909dSderaadt }
12261f83c55eSderaadt }
1227df930be7Sderaadt if ((cp = strpbrk(cbuf, " \n")))
1228df930be7Sderaadt cpos = cp - cbuf;
1229df930be7Sderaadt if (cpos == 0)
1230df930be7Sderaadt cpos = 4;
1231df930be7Sderaadt c = cbuf[cpos];
1232df930be7Sderaadt cbuf[cpos] = '\0';
1233df930be7Sderaadt upper(cbuf);
1234df930be7Sderaadt p = lookup(cmdtab, cbuf);
1235df930be7Sderaadt cbuf[cpos] = c;
1236cbe1e71dSmpech if (p != NULL) {
1237df930be7Sderaadt if (p->implemented == 0) {
1238df930be7Sderaadt nack(p->name);
12392378d5feSmillert return (LEXERR);
1240df930be7Sderaadt }
1241df930be7Sderaadt state = p->state;
1242df930be7Sderaadt yylval.s = p->name;
1243df930be7Sderaadt return (p->token);
1244df930be7Sderaadt }
1245df930be7Sderaadt break;
1246df930be7Sderaadt
1247df930be7Sderaadt case SITECMD:
1248df930be7Sderaadt if (cbuf[cpos] == ' ') {
1249df930be7Sderaadt cpos++;
1250df930be7Sderaadt return (SP);
1251df930be7Sderaadt }
1252df930be7Sderaadt cp = &cbuf[cpos];
1253df930be7Sderaadt if ((cp2 = strpbrk(cp, " \n")))
1254df930be7Sderaadt cpos = cp2 - cbuf;
1255df930be7Sderaadt c = cbuf[cpos];
1256df930be7Sderaadt cbuf[cpos] = '\0';
1257df930be7Sderaadt upper(cp);
1258df930be7Sderaadt p = lookup(sitetab, cp);
1259df930be7Sderaadt cbuf[cpos] = c;
1260cbe1e71dSmpech if (p != NULL) {
1261df930be7Sderaadt if (p->implemented == 0) {
1262df930be7Sderaadt state = CMD;
1263df930be7Sderaadt nack(p->name);
12642378d5feSmillert return (LEXERR);
1265df930be7Sderaadt }
1266df930be7Sderaadt state = p->state;
1267df930be7Sderaadt yylval.s = p->name;
1268df930be7Sderaadt return (p->token);
1269df930be7Sderaadt }
1270df930be7Sderaadt state = CMD;
1271df930be7Sderaadt break;
1272df930be7Sderaadt
1273df930be7Sderaadt case OSTR:
1274df930be7Sderaadt if (cbuf[cpos] == '\n') {
1275df930be7Sderaadt state = CMD;
1276df930be7Sderaadt return (CRLF);
1277df930be7Sderaadt }
1278df930be7Sderaadt /* FALLTHROUGH */
1279df930be7Sderaadt
1280df930be7Sderaadt case STR1:
1281df930be7Sderaadt case ZSTR1:
1282df930be7Sderaadt dostr1:
1283df930be7Sderaadt if (cbuf[cpos] == ' ') {
1284df930be7Sderaadt cpos++;
1285d9661006Sderaadt state = state == OSTR ? STR2 : state+1;
1286df930be7Sderaadt return (SP);
1287df930be7Sderaadt }
1288df930be7Sderaadt break;
1289df930be7Sderaadt
1290df930be7Sderaadt case ZSTR2:
1291df930be7Sderaadt if (cbuf[cpos] == '\n') {
1292df930be7Sderaadt state = CMD;
1293df930be7Sderaadt return (CRLF);
1294df930be7Sderaadt }
1295df930be7Sderaadt /* FALLTHROUGH */
1296df930be7Sderaadt
1297df930be7Sderaadt case STR2:
1298df930be7Sderaadt cp = &cbuf[cpos];
1299df930be7Sderaadt n = strlen(cp);
1300df930be7Sderaadt cpos += n - 1;
1301df930be7Sderaadt /*
1302df930be7Sderaadt * Make sure the string is nonempty and \n terminated.
1303df930be7Sderaadt */
1304df930be7Sderaadt if (n > 1 && cbuf[cpos] == '\n') {
1305df930be7Sderaadt cbuf[cpos] = '\0';
13060cae0519Sdownsj yylval.s = strdup(cp);
13070cae0519Sdownsj if (yylval.s == NULL)
13080cae0519Sdownsj fatal("Ran out of memory.");
1309df930be7Sderaadt cbuf[cpos] = '\n';
1310df930be7Sderaadt state = ARGS;
1311df930be7Sderaadt return (STRING);
1312df930be7Sderaadt }
1313df930be7Sderaadt break;
1314df930be7Sderaadt
1315df930be7Sderaadt case NSTR:
1316df930be7Sderaadt if (cbuf[cpos] == ' ') {
1317df930be7Sderaadt cpos++;
1318df930be7Sderaadt return (SP);
1319df930be7Sderaadt }
13204207a9b6Sderaadt if (isdigit((unsigned char)cbuf[cpos])) {
1321df930be7Sderaadt cp = &cbuf[cpos];
13224207a9b6Sderaadt while (isdigit((unsigned char)cbuf[++cpos]))
1323df930be7Sderaadt ;
1324df930be7Sderaadt c = cbuf[cpos];
1325df930be7Sderaadt cbuf[cpos] = '\0';
1326df930be7Sderaadt yylval.i = atoi(cp);
1327df930be7Sderaadt cbuf[cpos] = c;
1328df930be7Sderaadt state = STR1;
1329df930be7Sderaadt return (NUMBER);
1330df930be7Sderaadt }
1331df930be7Sderaadt state = STR1;
1332df930be7Sderaadt goto dostr1;
1333df930be7Sderaadt
1334df930be7Sderaadt case ARGS:
13354207a9b6Sderaadt if (isdigit((unsigned char)cbuf[cpos])) {
133609a2692aSmillert long long llval;
133709a2692aSmillert
1338df930be7Sderaadt cp = &cbuf[cpos];
133909a2692aSmillert errno = 0;
134009a2692aSmillert llval = strtoll(cp, &cp2, 10);
134109a2692aSmillert if (llval < 0 ||
134209a2692aSmillert (errno == ERANGE && llval == LLONG_MAX))
134309a2692aSmillert break;
134409a2692aSmillert
134509a2692aSmillert cpos = (int)(cp2 - cbuf);
134609a2692aSmillert if (llval > INT_MAX) {
134709a2692aSmillert yylval.o = llval;
134809a2692aSmillert return (BIGNUM);
134909a2692aSmillert } else {
135009a2692aSmillert yylval.i = (int)llval;
1351df930be7Sderaadt return (NUMBER);
1352df930be7Sderaadt }
135309a2692aSmillert }
135495cc8c4aSderaadt if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 &&
13554207a9b6Sderaadt !isalnum((unsigned char)cbuf[cpos + 3])) {
1356b1750805Sitojun cpos += 3;
1357b1750805Sitojun return ALL;
1358b1750805Sitojun }
1359df930be7Sderaadt switch (cbuf[cpos++]) {
1360df930be7Sderaadt
1361df930be7Sderaadt case '\n':
1362df930be7Sderaadt state = CMD;
1363df930be7Sderaadt return (CRLF);
1364df930be7Sderaadt
1365df930be7Sderaadt case ' ':
1366df930be7Sderaadt return (SP);
1367df930be7Sderaadt
1368df930be7Sderaadt case ',':
1369df930be7Sderaadt return (COMMA);
1370df930be7Sderaadt
1371df930be7Sderaadt case 'A':
1372df930be7Sderaadt case 'a':
1373df930be7Sderaadt return (A);
1374df930be7Sderaadt
1375df930be7Sderaadt case 'B':
1376df930be7Sderaadt case 'b':
1377df930be7Sderaadt return (B);
1378df930be7Sderaadt
1379df930be7Sderaadt case 'C':
1380df930be7Sderaadt case 'c':
1381df930be7Sderaadt return (C);
1382df930be7Sderaadt
1383df930be7Sderaadt case 'E':
1384df930be7Sderaadt case 'e':
1385df930be7Sderaadt return (E);
1386df930be7Sderaadt
1387df930be7Sderaadt case 'F':
1388df930be7Sderaadt case 'f':
1389df930be7Sderaadt return (F);
1390df930be7Sderaadt
1391df930be7Sderaadt case 'I':
1392df930be7Sderaadt case 'i':
1393df930be7Sderaadt return (I);
1394df930be7Sderaadt
1395df930be7Sderaadt case 'L':
1396df930be7Sderaadt case 'l':
1397df930be7Sderaadt return (L);
1398df930be7Sderaadt
1399df930be7Sderaadt case 'N':
1400df930be7Sderaadt case 'n':
1401df930be7Sderaadt return (N);
1402df930be7Sderaadt
1403df930be7Sderaadt case 'P':
1404df930be7Sderaadt case 'p':
1405df930be7Sderaadt return (P);
1406df930be7Sderaadt
1407df930be7Sderaadt case 'R':
1408df930be7Sderaadt case 'r':
1409df930be7Sderaadt return (R);
1410df930be7Sderaadt
1411df930be7Sderaadt case 'S':
1412df930be7Sderaadt case 's':
1413df930be7Sderaadt return (S);
1414df930be7Sderaadt
1415df930be7Sderaadt case 'T':
1416df930be7Sderaadt case 't':
1417df930be7Sderaadt return (T);
1418df930be7Sderaadt
1419df930be7Sderaadt }
1420df930be7Sderaadt break;
1421df930be7Sderaadt
1422df930be7Sderaadt default:
1423df930be7Sderaadt fatal("Unknown state in scanner.");
1424df930be7Sderaadt }
1425df930be7Sderaadt state = CMD;
14262378d5feSmillert return (LEXERR);
1427df930be7Sderaadt }
1428df930be7Sderaadt }
1429df930be7Sderaadt
1430df930be7Sderaadt void
upper(char * s)143118a2fcc3Sjan upper(char *s)
1432df930be7Sderaadt {
143327b9e962Smpech char *p;
143427b9e962Smpech
1435c7141df2Sjan for (p = s; *p; p++)
14364207a9b6Sderaadt *p = (char)toupper((unsigned char)*p);
1437df930be7Sderaadt }
1438df930be7Sderaadt
1439df930be7Sderaadt static void
help(struct tab * ctab,char * s)144018a2fcc3Sjan help(struct tab *ctab, char *s)
1441df930be7Sderaadt {
1442df930be7Sderaadt struct tab *c;
1443df930be7Sderaadt int width, NCMDS;
1444df930be7Sderaadt char *type;
1445df930be7Sderaadt
1446df930be7Sderaadt if (ctab == sitetab)
1447df930be7Sderaadt type = "SITE ";
1448df930be7Sderaadt else
1449df930be7Sderaadt type = "";
1450df930be7Sderaadt width = 0, NCMDS = 0;
1451df930be7Sderaadt for (c = ctab; c->name != NULL; c++) {
1452df930be7Sderaadt int len = strlen(c->name);
1453df930be7Sderaadt
1454df930be7Sderaadt if (len > width)
1455df930be7Sderaadt width = len;
1456df930be7Sderaadt NCMDS++;
1457df930be7Sderaadt }
1458df930be7Sderaadt width = (width + 8) &~ 7;
14596ba2067dSmpech if (s == NULL) {
1460df930be7Sderaadt int i, j, w;
1461df930be7Sderaadt int columns, lines;
1462df930be7Sderaadt
1463df930be7Sderaadt lreply(214, "The following %scommands are recognized %s.",
1464df930be7Sderaadt type, "(* =>'s unimplemented)");
1465df930be7Sderaadt columns = 76 / width;
1466df930be7Sderaadt if (columns == 0)
1467df930be7Sderaadt columns = 1;
1468df930be7Sderaadt lines = (NCMDS + columns - 1) / columns;
1469df930be7Sderaadt for (i = 0; i < lines; i++) {
1470df930be7Sderaadt printf(" ");
1471df930be7Sderaadt for (j = 0; j < columns; j++) {
1472df930be7Sderaadt c = ctab + j * lines + i;
1473df930be7Sderaadt printf("%s%c", c->name,
1474df930be7Sderaadt c->implemented ? ' ' : '*');
1475df930be7Sderaadt if (c + lines >= &ctab[NCMDS])
1476df930be7Sderaadt break;
1477df930be7Sderaadt w = strlen(c->name) + 1;
1478df930be7Sderaadt while (w < width) {
1479df930be7Sderaadt putchar(' ');
1480df930be7Sderaadt w++;
1481df930be7Sderaadt }
1482df930be7Sderaadt }
1483df930be7Sderaadt printf("\r\n");
1484df930be7Sderaadt }
1485df930be7Sderaadt (void) fflush(stdout);
1486df930be7Sderaadt reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1487df930be7Sderaadt return;
1488df930be7Sderaadt }
1489df930be7Sderaadt upper(s);
1490df930be7Sderaadt c = lookup(ctab, s);
1491cbe1e71dSmpech if (c == NULL) {
1492df930be7Sderaadt reply(502, "Unknown command %s.", s);
1493df930be7Sderaadt return;
1494df930be7Sderaadt }
1495df930be7Sderaadt if (c->implemented)
1496df930be7Sderaadt reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1497df930be7Sderaadt else
1498df930be7Sderaadt reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1499df930be7Sderaadt c->name, c->help);
1500df930be7Sderaadt }
1501df930be7Sderaadt
1502df930be7Sderaadt static void
sizecmd(const char * filename)150318a2fcc3Sjan sizecmd(const char *filename)
1504df930be7Sderaadt {
1505df930be7Sderaadt switch (type) {
1506df930be7Sderaadt case TYPE_L:
1507df930be7Sderaadt case TYPE_I: {
1508df930be7Sderaadt struct stat stbuf;
1509df69c215Sderaadt if (stat(filename, &stbuf) == -1 || !S_ISREG(stbuf.st_mode))
1510df930be7Sderaadt reply(550, "%s: not a plain file.", filename);
1511df930be7Sderaadt else
151209a2692aSmillert reply(213, "%lld", (long long)stbuf.st_size);
1513df930be7Sderaadt break; }
1514df930be7Sderaadt case TYPE_A: {
1515df930be7Sderaadt FILE *fin;
1516df930be7Sderaadt int c;
1517df930be7Sderaadt off_t count;
1518df930be7Sderaadt struct stat stbuf;
1519df930be7Sderaadt fin = fopen(filename, "r");
1520df930be7Sderaadt if (fin == NULL) {
1521df930be7Sderaadt perror_reply(550, filename);
1522df930be7Sderaadt return;
1523df930be7Sderaadt }
1524df69c215Sderaadt if (fstat(fileno(fin), &stbuf) == -1 || !S_ISREG(stbuf.st_mode)) {
1525df930be7Sderaadt reply(550, "%s: not a plain file.", filename);
1526df930be7Sderaadt (void) fclose(fin);
1527df930be7Sderaadt return;
1528df930be7Sderaadt }
152913b440dcSitojun if (stbuf.st_size > 10240) {
153013b440dcSitojun reply(550, "%s: file too large for SIZE.", filename);
153113b440dcSitojun (void) fclose(fin);
153213b440dcSitojun return;
153313b440dcSitojun }
1534df930be7Sderaadt
1535df930be7Sderaadt count = 0;
1536df930be7Sderaadt while((c = getc(fin)) != EOF) {
1537df930be7Sderaadt if (c == '\n') /* will get expanded to \r\n */
1538df930be7Sderaadt count++;
1539df930be7Sderaadt count++;
1540df930be7Sderaadt }
1541df930be7Sderaadt (void) fclose(fin);
1542df930be7Sderaadt
154309a2692aSmillert reply(213, "%lld", (long long)count);
1544df930be7Sderaadt break; }
1545df930be7Sderaadt default:
1546df930be7Sderaadt reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1547df930be7Sderaadt }
1548df930be7Sderaadt }
1549