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