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