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