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