xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 36620)
122499Sdist /*
236304Skarels  * Copyright (c) 1985, 1988 Regents of the University of California.
333738Sbostic  * All rights reserved.
433738Sbostic  *
533738Sbostic  * Redistribution and use in source and binary forms are permitted
634769Sbostic  * provided that the above copyright notice and this paragraph are
734769Sbostic  * duplicated in all such forms and that any documentation,
834769Sbostic  * advertising materials, and other materials related to such
934769Sbostic  * distribution and use acknowledge that the software was developed
1034769Sbostic  * by the University of California, Berkeley.  The name of the
1134769Sbostic  * University may not be used to endorse or promote products derived
1234769Sbostic  * from this software without specific prior written permission.
1334769Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434769Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534769Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1622499Sdist  */
1722499Sdist 
1810275Ssam #ifndef lint
1922499Sdist char copyright[] =
2036304Skarels "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\
2122499Sdist  All rights reserved.\n";
2233738Sbostic #endif /* not lint */
2310275Ssam 
2422499Sdist #ifndef lint
25*36620Srick static char sccsid[] = "@(#)ftpd.c	5.26	(Berkeley) 01/25/89";
2633738Sbostic #endif /* not lint */
2722499Sdist 
2810275Ssam /*
2910275Ssam  * FTP server.
3010275Ssam  */
3110303Ssam #include <sys/param.h>
3210275Ssam #include <sys/stat.h>
3310275Ssam #include <sys/ioctl.h>
3410275Ssam #include <sys/socket.h>
3513247Ssam #include <sys/file.h>
3613595Ssam #include <sys/wait.h>
37*36620Srick #include <sys/dir.h>
3810275Ssam 
3910275Ssam #include <netinet/in.h>
4010275Ssam 
4113034Ssam #include <arpa/ftp.h>
4213211Sroot #include <arpa/inet.h>
4326044Sminshall #include <arpa/telnet.h>
4413034Ssam 
4510275Ssam #include <stdio.h>
4610275Ssam #include <signal.h>
4710275Ssam #include <pwd.h>
4810275Ssam #include <setjmp.h>
4910275Ssam #include <netdb.h>
5010423Ssam #include <errno.h>
5126044Sminshall #include <strings.h>
5226493Sminshall #include <syslog.h>
5336435Sbostic #include <varargs.h>
5410275Ssam 
5510695Ssam /*
5610695Ssam  * File containing login names
5710695Ssam  * NOT to be used on this machine.
5810695Ssam  * Commonly used to disallow uucp.
5910695Ssam  */
6010695Ssam #define	FTPUSERS	"/etc/ftpusers"
6110695Ssam 
6210275Ssam extern	int errno;
6310275Ssam extern	char *sys_errlist[];
6436304Skarels extern	int sys_nerr;
6510275Ssam extern	char *crypt();
6610275Ssam extern	char version[];
6710275Ssam extern	char *home;		/* pointer to home directory for glob */
6836276Sbostic extern	FILE *ftpd_popen(), *fopen(), *freopen();
6936304Skarels extern	int  ftpd_pclose(), fclose();
7026044Sminshall extern	char *getline();
7126044Sminshall extern	char cbuf[];
7236551Sbostic extern	off_t restart_point;
7310275Ssam 
7410275Ssam struct	sockaddr_in ctrl_addr;
7510275Ssam struct	sockaddr_in data_source;
7610275Ssam struct	sockaddr_in data_dest;
7710275Ssam struct	sockaddr_in his_addr;
7810275Ssam 
7910275Ssam int	data;
8026044Sminshall jmp_buf	errcatch, urgcatch;
8110275Ssam int	logged_in;
8210275Ssam struct	passwd *pw;
8310275Ssam int	debug;
8426493Sminshall int	timeout = 900;    /* timeout after 15 minutes of inactivity */
8511757Ssam int	logging;
8610275Ssam int	guest;
8710275Ssam int	type;
8810275Ssam int	form;
8910275Ssam int	stru;			/* avoid C keyword */
9010275Ssam int	mode;
9110321Ssam int	usedefault = 1;		/* for data transfers */
9236304Skarels int	pdata = -1;		/* for passive mode */
9326044Sminshall int	transflag;
9426044Sminshall char	tmpline[7];
9536276Sbostic char	hostname[MAXHOSTNAMELEN];
9636276Sbostic char	remotehost[MAXHOSTNAMELEN];
9710275Ssam 
9811653Ssam /*
9911653Ssam  * Timeout intervals for retrying connections
10011653Ssam  * to hosts that don't accept PORT cmds.  This
10111653Ssam  * is a kludge, but given the problems with TCP...
10211653Ssam  */
10311653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
10411653Ssam #define	SWAITINT	5	/* interval between retries */
10511653Ssam 
10611653Ssam int	swaitmax = SWAITMAX;
10711653Ssam int	swaitint = SWAITINT;
10811653Ssam 
10910275Ssam int	lostconn();
11026044Sminshall int	myoob();
11110275Ssam FILE	*getdatasock(), *dataconn();
11210275Ssam 
113*36620Srick #ifdef SETPROCTITLE
114*36620Srick char	**Argv = NULL;		/* pointer to argument vector */
115*36620Srick char	*LastArgv = NULL;	/* end of argv */
116*36620Srick #endif /* SETPROCTITLE */
117*36620Srick 
118*36620Srick main(argc, argv, envp)
11910275Ssam 	int argc;
12010275Ssam 	char *argv[];
121*36620Srick 	char **envp;
12210275Ssam {
12327750Sminshall 	int addrlen, on = 1;
12410275Ssam 	char *cp;
12510275Ssam 
12616339Skarels 	addrlen = sizeof (his_addr);
12736304Skarels 	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
12826493Sminshall 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
12910275Ssam 		exit(1);
13010275Ssam 	}
13116339Skarels 	addrlen = sizeof (ctrl_addr);
13236304Skarels 	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
13326493Sminshall 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
13416339Skarels 		exit(1);
13516339Skarels 	}
13616339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
13710275Ssam 	debug = 0;
13826493Sminshall 	openlog("ftpd", LOG_PID, LOG_DAEMON);
139*36620Srick #ifdef SETPROCTITLE
140*36620Srick 	/*
141*36620Srick 	 *  Save start and extent of argv for setproctitle.
142*36620Srick 	 */
143*36620Srick 	Argv = argv;
144*36620Srick 	while (*envp)
145*36620Srick 		envp++;
146*36620Srick 	LastArgv = envp[-1] + strlen(envp[-1]);
147*36620Srick #endif /* SETPROCTITLE */
148*36620Srick 
14910275Ssam 	argc--, argv++;
15010275Ssam 	while (argc > 0 && *argv[0] == '-') {
15110275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
15210275Ssam 
15311653Ssam 		case 'v':
15411653Ssam 			debug = 1;
15511653Ssam 			break;
15611653Ssam 
15710275Ssam 		case 'd':
15810275Ssam 			debug = 1;
15910275Ssam 			break;
16010275Ssam 
16111757Ssam 		case 'l':
16211757Ssam 			logging = 1;
16311757Ssam 			break;
16411757Ssam 
16511653Ssam 		case 't':
16611653Ssam 			timeout = atoi(++cp);
16711653Ssam 			goto nextopt;
16811653Ssam 
16910275Ssam 		default:
17016339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
17116339Skarels 			     *cp);
17210275Ssam 			break;
17310275Ssam 		}
17411653Ssam nextopt:
17510275Ssam 		argc--, argv++;
17610275Ssam 	}
17730944Scsvsj 	(void) freopen("/dev/null", "w", stderr);
17826493Sminshall 	(void) signal(SIGPIPE, lostconn);
17926493Sminshall 	(void) signal(SIGCHLD, SIG_IGN);
18035691Sbostic 	if ((int)signal(SIGURG, myoob) < 0)
18126493Sminshall 		syslog(LOG_ERR, "signal: %m");
18235691Sbostic 
18327750Sminshall 	/* handle urgent data inline */
18436276Sbostic 	/* Sequent defines this, but it doesn't work */
18527750Sminshall #ifdef SO_OOBINLINE
18636276Sbostic 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
18727750Sminshall 		syslog(LOG_ERR, "setsockopt: %m");
18836276Sbostic #endif
18936304Skarels 	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
19036304Skarels 		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
19116760Slepreau 	dolog(&his_addr);
19216339Skarels 	/* do telnet option negotiation here */
19316339Skarels 	/*
19416339Skarels 	 * Set up default state
19516339Skarels 	 */
19616339Skarels 	data = -1;
19716339Skarels 	type = TYPE_A;
19816339Skarels 	form = FORM_N;
19916339Skarels 	stru = STRU_F;
20016339Skarels 	mode = MODE_S;
20126044Sminshall 	tmpline[0] = '\0';
20226493Sminshall 	(void) gethostname(hostname, sizeof (hostname));
20336276Sbostic 	reply(220, "%s FTP server (%s) ready.", hostname, version);
20436304Skarels 	(void) setjmp(errcatch);
20536304Skarels 	for (;;)
20626493Sminshall 		(void) yyparse();
207*36620Srick 	/* NOTREACHED */
20810275Ssam }
20910419Ssam 
21010275Ssam lostconn()
21110275Ssam {
21210275Ssam 
21314089Ssam 	if (debug)
21426493Sminshall 		syslog(LOG_DEBUG, "lost connection");
21514089Ssam 	dologout(-1);
21610275Ssam }
21710275Ssam 
21835672Sbostic static char ttyline[20];
21935672Sbostic 
22036185Sbostic /*
22136185Sbostic  * Helper function for sgetpwnam().
22236185Sbostic  */
22336185Sbostic char *
22436185Sbostic sgetsave(s)
22536185Sbostic 	char *s;
22636185Sbostic {
22736185Sbostic 	char *malloc();
22836185Sbostic 	char *new = malloc((unsigned) strlen(s) + 1);
22936185Sbostic 
23036185Sbostic 	if (new == NULL) {
231*36620Srick 		perror_reply(421, "Local resource failure: malloc");
23236185Sbostic 		dologout(1);
233*36620Srick 		/* NOTREACHED */
23436185Sbostic 	}
23536185Sbostic 	(void) strcpy(new, s);
23636185Sbostic 	return (new);
23736185Sbostic }
23836185Sbostic 
23936185Sbostic /*
24036185Sbostic  * Save the result of a getpwnam.  Used for USER command, since
24136185Sbostic  * the data returned must not be clobbered by any other command
24236185Sbostic  * (e.g., globbing).
24336185Sbostic  */
24436185Sbostic struct passwd *
24536185Sbostic sgetpwnam(name)
24636185Sbostic 	char *name;
24736185Sbostic {
24836185Sbostic 	static struct passwd save;
24936185Sbostic 	register struct passwd *p;
25036185Sbostic 	char *sgetsave();
25136185Sbostic 
25236185Sbostic 	if ((p = getpwnam(name)) == NULL)
25336185Sbostic 		return (p);
25436185Sbostic 	if (save.pw_name) {
25536185Sbostic 		free(save.pw_name);
25636185Sbostic 		free(save.pw_passwd);
25736185Sbostic 		free(save.pw_comment);
25836185Sbostic 		free(save.pw_gecos);
25936185Sbostic 		free(save.pw_dir);
26036185Sbostic 		free(save.pw_shell);
26136185Sbostic 	}
26236185Sbostic 	save = *p;
26336185Sbostic 	save.pw_name = sgetsave(p->pw_name);
26436185Sbostic 	save.pw_passwd = sgetsave(p->pw_passwd);
26536185Sbostic 	save.pw_comment = sgetsave(p->pw_comment);
26636185Sbostic 	save.pw_gecos = sgetsave(p->pw_gecos);
26736185Sbostic 	save.pw_dir = sgetsave(p->pw_dir);
26836185Sbostic 	save.pw_shell = sgetsave(p->pw_shell);
26936185Sbostic 	return (&save);
27036185Sbostic }
27136185Sbostic 
27236304Skarels int login_attempts;		/* number of failed login attempts */
27336304Skarels int askpasswd;			/* had user command, ask for passwd */
27436304Skarels 
27536304Skarels /*
27636304Skarels  * USER command.
27736304Skarels  * Sets global passwd pointer pw if named account exists
27836304Skarels  * and is acceptable; sets askpasswd if a PASS command is
27936304Skarels  * expected. If logged in previously, need to reset state.
28036304Skarels  * If name is "ftp" or "anonymous" and ftp account exists,
28136304Skarels  * set guest and pw, then just return.
28236304Skarels  * If account doesn't exist, ask for passwd anyway.
28336304Skarels  * Otherwise, check user requesting login privileges.
28436304Skarels  * Disallow anyone who does not have a standard
28536304Skarels  * shell returned by getusershell() (/etc/shells).
28636304Skarels  * Disallow anyone mentioned in the file FTPUSERS
28736304Skarels  * to allow people such as root and uucp to be avoided.
28836304Skarels  */
28936304Skarels user(name)
29036304Skarels 	char *name;
29136304Skarels {
29236304Skarels 	register char *cp;
29336304Skarels 	FILE *fd;
29436304Skarels 	char *shell;
29536551Sbostic 	char line[BUFSIZ], *getusershell();
29636304Skarels 
29736304Skarels 	if (logged_in) {
29836304Skarels 		if (guest) {
29936304Skarels 			reply(530, "Can't change user from guest login.");
30036304Skarels 			return;
30136304Skarels 		}
30236304Skarels 		end_login();
30336304Skarels 	}
30436304Skarels 
30536304Skarels 	guest = 0;
30636304Skarels 	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
30736304Skarels 		if ((pw = sgetpwnam("ftp")) != NULL) {
30836304Skarels 			guest = 1;
30936304Skarels 			askpasswd = 1;
31036304Skarels 			reply(331, "Guest login ok, send ident as password.");
31136551Sbostic 		}
31236551Sbostic 		else
31336304Skarels 			reply(530, "User %s unknown.", name);
31436304Skarels 		return;
31536304Skarels 	}
31636304Skarels 	if (pw = sgetpwnam(name)) {
31736304Skarels 		if ((shell = pw->pw_shell) == NULL || *shell == 0)
31836304Skarels 			shell = "/bin/sh";
31936304Skarels 		while ((cp = getusershell()) != NULL)
32036304Skarels 			if (strcmp(cp, shell) == 0)
32136304Skarels 				break;
32236304Skarels 		endusershell();
32336304Skarels 		if (cp == NULL) {
32436304Skarels 			reply(530, "User %s access denied.", name);
32536550Sbostic 			syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s",
32636550Sbostic 			    remotehost, name);
32736304Skarels 			pw = (struct passwd *) NULL;
32836304Skarels 			return;
32936304Skarels 		}
33036304Skarels 		if ((fd = fopen(FTPUSERS, "r")) != NULL) {
33136304Skarels 		    while (fgets(line, sizeof (line), fd) != NULL) {
33236304Skarels 			if ((cp = index(line, '\n')) != NULL)
33336304Skarels 				*cp = '\0';
33436304Skarels 			if (strcmp(line, name) == 0) {
33536304Skarels 				reply(530, "User %s access denied.", name);
33636550Sbostic 				syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s",
33736550Sbostic 				    remotehost, name);
33836304Skarels 				pw = (struct passwd *) NULL;
33936304Skarels 				return;
34036304Skarels 			}
34136304Skarels 		    }
34236304Skarels 		}
34336304Skarels 		(void) fclose(fd);
34436304Skarels 	}
34536304Skarels 	reply(331, "Password required for %s.", name);
34636304Skarels 	askpasswd = 1;
34736304Skarels 	/*
34836304Skarels 	 * Delay before reading passwd after first failed
34936304Skarels 	 * attempt to slow down passwd-guessing programs.
35036304Skarels 	 */
35136304Skarels 	if (login_attempts)
35236304Skarels 		sleep((unsigned) login_attempts);
35336304Skarels }
35436304Skarels 
35536304Skarels /*
35636304Skarels  * Terminate login as previous user, if any, resetting state;
35736304Skarels  * used when USER command is given or login fails.
35836304Skarels  */
35936304Skarels end_login()
36036304Skarels {
36136304Skarels 
36236304Skarels 	(void) seteuid((uid_t)0);
36336304Skarels 	if (logged_in)
36436304Skarels 		logwtmp(ttyline, "", "");
36536304Skarels 	pw = NULL;
36636304Skarels 	logged_in = 0;
36736304Skarels 	guest = 0;
36836304Skarels }
36936304Skarels 
37010275Ssam pass(passwd)
37110275Ssam 	char *passwd;
37210275Ssam {
37336304Skarels 	char *xpasswd, *salt;
37410275Ssam 
37536304Skarels 	if (logged_in || askpasswd == 0) {
37610275Ssam 		reply(503, "Login with USER first.");
37710275Ssam 		return;
37810275Ssam 	}
37936304Skarels 	askpasswd = 0;
38010275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
38136304Skarels 		if (pw == NULL)
38236304Skarels 			salt = "xx";
38336304Skarels 		else
38436304Skarels 			salt = pw->pw_passwd;
38536304Skarels 		xpasswd = crypt(passwd, salt);
38616760Slepreau 		/* The strcmp does not catch null passwords! */
38736304Skarels 		if (pw == NULL || *pw->pw_passwd == '\0' ||
38836304Skarels 		    strcmp(xpasswd, pw->pw_passwd)) {
38910275Ssam 			reply(530, "Login incorrect.");
39010275Ssam 			pw = NULL;
39136304Skarels 			if (login_attempts++ >= 5) {
39236304Skarels 				syslog(LOG_ERR,
39336304Skarels 				    "repeated login failures from %s",
39436304Skarels 				    remotehost);
39536304Skarels 				exit(0);
39636304Skarels 			}
39710275Ssam 			return;
39810275Ssam 		}
39910275Ssam 	}
40036304Skarels 	login_attempts = 0;		/* this time successful */
40136304Skarels 	(void) setegid((gid_t)pw->pw_gid);
40236304Skarels 	(void) initgroups(pw->pw_name, pw->pw_gid);
40316033Sralph 
40436192Sbostic 	/* open wtmp before chroot */
40536192Sbostic 	(void)sprintf(ttyline, "ftp%d", getpid());
40636192Sbostic 	logwtmp(ttyline, pw->pw_name, remotehost);
40736192Sbostic 	logged_in = 1;
40836192Sbostic 
40936446Sbostic 	if (guest) {
410*36620Srick 		/*
411*36620Srick 		 * We MUST do a chdir() after the chroot. Otherwise "."
412*36620Srick 		 * will be accessible outside the root!
413*36620Srick 		 */
414*36620Srick 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
41536446Sbostic 			reply(550, "Can't set guest privileges.");
41636446Sbostic 			goto bad;
41736446Sbostic 		}
41836304Skarels 	}
419*36620Srick 	else if (chdir(pw->pw_dir) < 0) {
420*36620Srick 		if (chdir("/") < 0) {
42136446Sbostic 			reply(530, "User %s: can't change directory to %s.",
42236446Sbostic 			    pw->pw_name, pw->pw_dir);
42336446Sbostic 			goto bad;
42436446Sbostic 		}
42536446Sbostic 		else
42636446Sbostic 			lreply(230, "No directory! Logging in with home=/");
427*36620Srick 	}
42836304Skarels 	if (seteuid((uid_t)pw->pw_uid) < 0) {
42936304Skarels 		reply(550, "Can't set uid.");
43036304Skarels 		goto bad;
43136304Skarels 	}
43236550Sbostic 	if (guest) {
43336192Sbostic 		reply(230, "Guest login ok, access restrictions apply.");
43436550Sbostic 		syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
43536550Sbostic 		    remotehost, passwd);
43636551Sbostic 	}
43736551Sbostic 	else {
43810275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
43936550Sbostic 		syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
44036550Sbostic 		    remotehost, pw->pw_name);
44136550Sbostic 	}
44210303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
44310303Ssam 	return;
44410303Ssam bad:
44536304Skarels 	/* Forget all about it... */
44636304Skarels 	end_login();
44710275Ssam }
44810275Ssam 
44910275Ssam retrieve(cmd, name)
45010275Ssam 	char *cmd, *name;
45110275Ssam {
45210275Ssam 	FILE *fin, *dout;
45310275Ssam 	struct stat st;
454*36620Srick 	int (*closefunc)();
45510275Ssam 
45636557Sbostic 	if (cmd == 0) {
45736446Sbostic 		fin = fopen(name, "r"), closefunc = fclose;
45836557Sbostic 		st.st_size = 0;
45936557Sbostic 	} else {
46010275Ssam 		char line[BUFSIZ];
46110275Ssam 
46226493Sminshall 		(void) sprintf(line, cmd, name), name = line;
46336304Skarels 		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
46436557Sbostic 		st.st_size = -1;
46510275Ssam 	}
46610275Ssam 	if (fin == NULL) {
46713152Ssam 		if (errno != 0)
46836304Skarels 			perror_reply(550, name);
46910275Ssam 		return;
47010275Ssam 	}
47110275Ssam 	if (cmd == 0 &&
47210275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
47310275Ssam 		reply(550, "%s: not a plain file.", name);
47410275Ssam 		goto done;
47510275Ssam 	}
476*36620Srick 	if (restart_point) {
47736551Sbostic 		if (type == TYPE_A) {
478*36620Srick 			if (fseek(fin, restart_point, L_SET) < 0) {
47936551Sbostic 				perror_reply(550, name);
480*36620Srick 				goto done;
481*36620Srick 			}
48236551Sbostic 		}
483*36620Srick 		else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
48436551Sbostic 			perror_reply(550, name);
485*36620Srick 			goto done;
486*36620Srick 		}
487*36620Srick 	}
48810275Ssam 	dout = dataconn(name, st.st_size, "w");
48910275Ssam 	if (dout == NULL)
49010275Ssam 		goto done;
491*36620Srick 	send_data(fin, dout, st.st_blksize);
49226493Sminshall 	(void) fclose(dout);
49326044Sminshall 	data = -1;
49426044Sminshall 	pdata = -1;
49510275Ssam done:
49610275Ssam 	(*closefunc)(fin);
49710275Ssam }
49810275Ssam 
49936304Skarels store(name, mode, unique)
50010275Ssam 	char *name, *mode;
50136304Skarels 	int unique;
50210275Ssam {
50310275Ssam 	FILE *fout, *din;
50436446Sbostic 	struct stat st;
505*36620Srick 	int (*closefunc)();
50636304Skarels 	char *gunique();
50710275Ssam 
50836446Sbostic 	if (unique && stat(name, &st) == 0 &&
50936446Sbostic 	    (name = gunique(name)) == NULL)
51036446Sbostic 		return;
51110303Ssam 
512*36620Srick 	if (restart_point)
513*36620Srick 		mode = "r+w";
514*36620Srick 	fout = fopen(name, mode);
515*36620Srick 	closefunc = fclose;
51610275Ssam 	if (fout == NULL) {
51736304Skarels 		perror_reply(553, name);
51810275Ssam 		return;
51910275Ssam 	}
520*36620Srick 	if (restart_point) {
52136551Sbostic 		if (type == TYPE_A) {
522*36620Srick 			if (fseek(fout, restart_point, L_SET) < 0) {
52336551Sbostic 				perror_reply(550, name);
524*36620Srick 				goto done;
525*36620Srick 			}
52636551Sbostic 		}
527*36620Srick 		else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
52836551Sbostic 			perror_reply(550, name);
529*36620Srick 			goto done;
530*36620Srick 		}
531*36620Srick 	}
53236304Skarels 	din = dataconn(name, (off_t)-1, "r");
53310275Ssam 	if (din == NULL)
53410275Ssam 		goto done;
535*36620Srick 	if (receive_data(din, fout) == 0) {
536*36620Srick 		if (unique)
53736304Skarels 			reply(226, "Transfer complete (unique file name:%s).",
538*36620Srick 				name);
53936304Skarels 		else
54036304Skarels 			reply(226, "Transfer complete.");
54126044Sminshall 	}
54226493Sminshall 	(void) fclose(din);
54326044Sminshall 	data = -1;
54426044Sminshall 	pdata = -1;
54510275Ssam done:
54610275Ssam 	(*closefunc)(fout);
54710275Ssam }
54810275Ssam 
54910275Ssam FILE *
55010275Ssam getdatasock(mode)
55110275Ssam 	char *mode;
55210275Ssam {
55317157Ssam 	int s, on = 1;
55410275Ssam 
55510275Ssam 	if (data >= 0)
55610275Ssam 		return (fdopen(data, mode));
55713247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
55810602Ssam 	if (s < 0)
55910275Ssam 		return (NULL);
56036304Skarels 	(void) seteuid((uid_t)0);
56126493Sminshall 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
56210602Ssam 		goto bad;
56313152Ssam 	/* anchor socket to avoid multi-homing problems */
56413152Ssam 	data_source.sin_family = AF_INET;
56513152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
56636304Skarels 	if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0)
56710602Ssam 		goto bad;
56836304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
56910275Ssam 	return (fdopen(s, mode));
57010602Ssam bad:
57136304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
57226493Sminshall 	(void) close(s);
57310602Ssam 	return (NULL);
57410275Ssam }
57510275Ssam 
57610275Ssam FILE *
57710275Ssam dataconn(name, size, mode)
57810275Ssam 	char *name;
57911653Ssam 	off_t size;
58010275Ssam 	char *mode;
58110275Ssam {
58210275Ssam 	char sizebuf[32];
58310275Ssam 	FILE *file;
58411653Ssam 	int retry = 0;
58510275Ssam 
58636304Skarels 	if (size != (off_t) -1)
58726493Sminshall 		(void) sprintf (sizebuf, " (%ld bytes)", size);
58810275Ssam 	else
58910275Ssam 		(void) strcpy(sizebuf, "");
59036304Skarels 	if (pdata >= 0) {
59126044Sminshall 		struct sockaddr_in from;
59226044Sminshall 		int s, fromlen = sizeof(from);
59326044Sminshall 
59436304Skarels 		s = accept(pdata, (struct sockaddr *)&from, &fromlen);
59526044Sminshall 		if (s < 0) {
59626044Sminshall 			reply(425, "Can't open data connection.");
59726044Sminshall 			(void) close(pdata);
59826044Sminshall 			pdata = -1;
59926044Sminshall 			return(NULL);
60026044Sminshall 		}
60126044Sminshall 		(void) close(pdata);
60226044Sminshall 		pdata = s;
60336235Skarels 		reply(150, "Opening %s mode data connection for %s%s.",
60436235Skarels 		     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
60526044Sminshall 		return(fdopen(pdata, mode));
60626044Sminshall 	}
60710275Ssam 	if (data >= 0) {
60810275Ssam 		reply(125, "Using existing data connection for %s%s.",
60910275Ssam 		    name, sizebuf);
61010321Ssam 		usedefault = 1;
61110275Ssam 		return (fdopen(data, mode));
61210275Ssam 	}
61310566Ssam 	if (usedefault)
61410422Ssam 		data_dest = his_addr;
61510422Ssam 	usedefault = 1;
61610275Ssam 	file = getdatasock(mode);
61710275Ssam 	if (file == NULL) {
61810275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
61913247Ssam 		    inet_ntoa(data_source.sin_addr),
62010275Ssam 		    ntohs(data_source.sin_port),
62136304Skarels 		    errno < sys_nerr ? sys_errlist[errno] : "unknown error");
62210275Ssam 		return (NULL);
62310275Ssam 	}
62410275Ssam 	data = fileno(file);
62536304Skarels 	while (connect(data, (struct sockaddr *)&data_dest,
62636304Skarels 	    sizeof (data_dest)) < 0) {
62711653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
62826493Sminshall 			sleep((unsigned) swaitint);
62911653Ssam 			retry += swaitint;
63011653Ssam 			continue;
63111653Ssam 		}
63236304Skarels 		perror_reply(425, "Can't build data connection");
63310275Ssam 		(void) fclose(file);
63410275Ssam 		data = -1;
63510275Ssam 		return (NULL);
63610275Ssam 	}
63736235Skarels 	reply(150, "Opening %s mode data connection for %s%s.",
63836235Skarels 	     type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
63910275Ssam 	return (file);
64010275Ssam }
64110275Ssam 
64210275Ssam /*
64310275Ssam  * Tranfer the contents of "instr" to
64410275Ssam  * "outstr" peer using the appropriate
64510275Ssam  * encapulation of the date subject
64610275Ssam  * to Mode, Structure, and Type.
64710275Ssam  *
64810275Ssam  * NB: Form isn't handled.
64910275Ssam  */
65036446Sbostic send_data(instr, outstr, blksize)
65110275Ssam 	FILE *instr, *outstr;
65236446Sbostic 	off_t blksize;
65310275Ssam {
65436446Sbostic 	register int c, cnt;
65536446Sbostic 	register char *buf;
65636446Sbostic 	int netfd, filefd;
65710275Ssam 
65826044Sminshall 	transflag++;
65926044Sminshall 	if (setjmp(urgcatch)) {
66026044Sminshall 		transflag = 0;
661*36620Srick 		return;
66226044Sminshall 	}
66310275Ssam 	switch (type) {
66410275Ssam 
66510275Ssam 	case TYPE_A:
66610275Ssam 		while ((c = getc(instr)) != EOF) {
66711220Ssam 			if (c == '\n') {
668*36620Srick 				if (ferror (outstr))
669*36620Srick 					goto data_err;
67027750Sminshall 				(void) putc('\r', outstr);
67111220Ssam 			}
67227750Sminshall 			(void) putc(c, outstr);
67310275Ssam 		}
67426044Sminshall 		transflag = 0;
675*36620Srick 		fflush(outstr);
676*36620Srick 		if (ferror (instr) || ferror (outstr))
677*36620Srick 			goto data_err;
678*36620Srick 		reply(226, "Transfer complete.");
679*36620Srick 		return;
68036446Sbostic 
68110275Ssam 	case TYPE_I:
68210275Ssam 	case TYPE_L:
68336446Sbostic 		if ((buf = malloc((u_int)blksize)) == NULL) {
68436446Sbostic 			transflag = 0;
685*36620Srick 			perror_reply(421, "Local resource failure: malloc");
686*36620Srick 			dologout(1);
687*36620Srick 			/* NOTREACHED */
68836446Sbostic 		}
68910275Ssam 		netfd = fileno(outstr);
69010275Ssam 		filefd = fileno(instr);
69136446Sbostic 		while ((cnt = read(filefd, buf, sizeof(buf))) > 0 &&
692*36620Srick 		    write(netfd, buf, cnt) == cnt)
693*36620Srick 			/* LOOP */;
69426044Sminshall 		transflag = 0;
69536446Sbostic 		(void)free(buf);
696*36620Srick 		if (cnt != 0)
697*36620Srick 			goto data_err;
698*36620Srick 		reply(226, "Transfer complete.");
699*36620Srick 		return;
700*36620Srick 	default:
701*36620Srick 		transflag = 0;
702*36620Srick 		reply(550, "Unimplemented TYPE %d in send_data", type);
703*36620Srick 		return;
70410275Ssam 	}
705*36620Srick 
706*36620Srick data_err:
70726044Sminshall 	transflag = 0;
708*36620Srick 	perror_reply(421, "Data connection");
709*36620Srick 	dologout(1);
710*36620Srick 	/* NOTREACHED */
71110275Ssam }
71210275Ssam 
71310275Ssam /*
71410275Ssam  * Transfer data from peer to
71510275Ssam  * "outstr" using the appropriate
71610275Ssam  * encapulation of the data subject
71710275Ssam  * to Mode, Structure, and Type.
71810275Ssam  *
71910275Ssam  * N.B.: Form isn't handled.
72010275Ssam  */
72110275Ssam receive_data(instr, outstr)
72210275Ssam 	FILE *instr, *outstr;
72310275Ssam {
72410275Ssam 	register int c;
72511220Ssam 	int cnt;
72610275Ssam 	char buf[BUFSIZ];
72710275Ssam 
72810275Ssam 
72926044Sminshall 	transflag++;
73026044Sminshall 	if (setjmp(urgcatch)) {
73126044Sminshall 		transflag = 0;
73226044Sminshall 		return(-1);
73326044Sminshall 	}
73410275Ssam 	switch (type) {
73510275Ssam 
73610275Ssam 	case TYPE_I:
73710275Ssam 	case TYPE_L:
73826044Sminshall 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
739*36620Srick 			if (write(fileno(outstr), buf, cnt) != cnt)
740*36620Srick 				goto data_err;
74126044Sminshall 		}
742*36620Srick 		if (cnt < 0)
743*36620Srick 			goto data_err;
74426044Sminshall 		transflag = 0;
745*36620Srick 		return 0;
74610275Ssam 
74710275Ssam 	case TYPE_E:
74827106Smckusick 		reply(553, "TYPE E not implemented.");
74926044Sminshall 		transflag = 0;
75027106Smckusick 		return (-1);
75110275Ssam 
75210275Ssam 	case TYPE_A:
75310275Ssam 		while ((c = getc(instr)) != EOF) {
75427750Sminshall 			while (c == '\r') {
755*36620Srick 				if (ferror (outstr))
756*36620Srick 					goto data_err;
75711220Ssam 				if ((c = getc(instr)) != '\n')
75827750Sminshall 					(void) putc ('\r', outstr);
75910275Ssam 			}
76027750Sminshall 			(void) putc (c, outstr);
76110275Ssam 		}
762*36620Srick 		fflush(outstr);
763*36620Srick 		if (ferror (instr) || ferror (outstr))
764*36620Srick 			goto data_err;
76526044Sminshall 		transflag = 0;
76610275Ssam 		return (0);
767*36620Srick 	default:
768*36620Srick 		reply(550, "Unimplemented TYPE %d in receive_data", type);
769*36620Srick 		transflag = 0;
770*36620Srick 		return 1;
77110275Ssam 	}
772*36620Srick 
773*36620Srick data_err:
77426044Sminshall 	transflag = 0;
775*36620Srick 	perror_reply(421, "Data Connection");
776*36620Srick 	dologout(1);
777*36620Srick 	/* NOTREACHED */
77810275Ssam }
77910275Ssam 
78010275Ssam fatal(s)
78110275Ssam 	char *s;
78210275Ssam {
78310275Ssam 	reply(451, "Error in server: %s\n", s);
78410275Ssam 	reply(221, "Closing connection due to server error.");
78513247Ssam 	dologout(0);
786*36620Srick 	/* NOTREACHED */
78710275Ssam }
78810275Ssam 
78936446Sbostic /* VARARGS2 */
79036446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5)
79110275Ssam 	int n;
79236446Sbostic 	char *fmt;
79310275Ssam {
79410275Ssam 	printf("%d ", n);
79536446Sbostic 	printf(fmt, p0, p1, p2, p3, p4, p5);
79610275Ssam 	printf("\r\n");
79736435Sbostic 	(void)fflush(stdout);
79810275Ssam 	if (debug) {
79926493Sminshall 		syslog(LOG_DEBUG, "<--- %d ", n);
80036446Sbostic 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
80110275Ssam }
802*36620Srick }
80310275Ssam 
80436446Sbostic /* VARARGS2 */
80536446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5)
80610275Ssam 	int n;
80736446Sbostic 	char *fmt;
80810275Ssam {
80936446Sbostic 	printf("%d- ", n);
81036446Sbostic 	printf(fmt, p0, p1, p2, p3, p4, p5);
81136446Sbostic 	printf("\r\n");
81236435Sbostic 	(void)fflush(stdout);
81336446Sbostic 	if (debug) {
81436446Sbostic 		syslog(LOG_DEBUG, "<--- %d- ", n);
81536446Sbostic 		syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
81636446Sbostic 	}
81710275Ssam }
81810275Ssam 
81910275Ssam ack(s)
82010275Ssam 	char *s;
82110275Ssam {
82227106Smckusick 	reply(250, "%s command successful.", s);
82310275Ssam }
82410275Ssam 
82510275Ssam nack(s)
82610275Ssam 	char *s;
82710275Ssam {
82810275Ssam 	reply(502, "%s command not implemented.", s);
82910275Ssam }
83010275Ssam 
83136304Skarels /* ARGSUSED */
83226493Sminshall yyerror(s)
83326493Sminshall 	char *s;
83410275Ssam {
83526044Sminshall 	char *cp;
83626044Sminshall 
83736551Sbostic 	if (cp = index(cbuf,'\n'))
83836551Sbostic 		*cp = '\0';
83926044Sminshall 	reply(500, "'%s': command not understood.",cbuf);
84010275Ssam }
84110275Ssam 
84210275Ssam delete(name)
84310275Ssam 	char *name;
84410275Ssam {
84510275Ssam 	struct stat st;
84610275Ssam 
84710275Ssam 	if (stat(name, &st) < 0) {
84836304Skarels 		perror_reply(550, name);
84910275Ssam 		return;
85010275Ssam 	}
85110275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
85210275Ssam 		if (rmdir(name) < 0) {
85336304Skarels 			perror_reply(550, name);
85410275Ssam 			return;
85510275Ssam 		}
85610275Ssam 		goto done;
85710275Ssam 	}
85810275Ssam 	if (unlink(name) < 0) {
85936304Skarels 		perror_reply(550, name);
86010275Ssam 		return;
86110275Ssam 	}
86210275Ssam done:
86310275Ssam 	ack("DELE");
86410275Ssam }
86510275Ssam 
86610275Ssam cwd(path)
86710275Ssam 	char *path;
86810275Ssam {
869*36620Srick 	if (chdir(path) < 0)
87036304Skarels 		perror_reply(550, path);
871*36620Srick 	else
872*36620Srick 		ack("CWD");
87310275Ssam }
87410275Ssam 
87510303Ssam makedir(name)
87610275Ssam 	char *name;
87710275Ssam {
87836276Sbostic 	if (mkdir(name, 0777) < 0)
87936304Skarels 		perror_reply(550, name);
88036276Sbostic 	else
88136276Sbostic 		reply(257, "MKD command successful.");
88210275Ssam }
88310275Ssam 
88410303Ssam removedir(name)
88510275Ssam 	char *name;
88610275Ssam {
887*36620Srick 	if (rmdir(name) < 0)
88836304Skarels 		perror_reply(550, name);
889*36620Srick 	else
890*36620Srick 		ack("RMD");
89110275Ssam }
89210275Ssam 
89310303Ssam pwd()
89410275Ssam {
89510303Ssam 	char path[MAXPATHLEN + 1];
89636304Skarels 	extern char *getwd();
89710275Ssam 
898*36620Srick 	if (getwd(path) == (char *)NULL)
89927106Smckusick 		reply(550, "%s.", path);
900*36620Srick 	else
901*36620Srick 		reply(257, "\"%s\" is current directory.", path);
90210275Ssam }
90310275Ssam 
90410275Ssam char *
90510275Ssam renamefrom(name)
90610275Ssam 	char *name;
90710275Ssam {
90810275Ssam 	struct stat st;
90910275Ssam 
91010275Ssam 	if (stat(name, &st) < 0) {
91136304Skarels 		perror_reply(550, name);
91210275Ssam 		return ((char *)0);
91310275Ssam 	}
91410303Ssam 	reply(350, "File exists, ready for destination name");
91510275Ssam 	return (name);
91610275Ssam }
91710275Ssam 
91810275Ssam renamecmd(from, to)
91910275Ssam 	char *from, *to;
92010275Ssam {
921*36620Srick 	if (rename(from, to) < 0)
92236304Skarels 		perror_reply(550, "rename");
923*36620Srick 	else
924*36620Srick 		ack("RNTO");
92510275Ssam }
92610275Ssam 
92710275Ssam dolog(sin)
92810275Ssam 	struct sockaddr_in *sin;
92910275Ssam {
93036304Skarels 	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
93110275Ssam 		sizeof (struct in_addr), AF_INET);
93236304Skarels 	time_t t, time();
93326493Sminshall 	extern char *ctime();
93410275Ssam 
93536304Skarels 	if (hp)
93626493Sminshall 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
93736304Skarels 	else
93826493Sminshall 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
93913247Ssam 		    sizeof (remotehost));
94013247Ssam 	if (!logging)
94113247Ssam 		return;
94226493Sminshall 	t = time((time_t *) 0);
94336304Skarels 	syslog(LOG_INFO, "connection from %s at %s",
94436304Skarels 	    remotehost, ctime(&t));
945*36620Srick #ifdef SETPROCTITLE
946*36620Srick 	setproctitle("%s: connected", remotehost);
947*36620Srick #endif /* SETPROCTITLE */
94810275Ssam }
94910695Ssam 
95010695Ssam /*
95113247Ssam  * Record logout in wtmp file
95213247Ssam  * and exit with supplied status.
95313247Ssam  */
95413247Ssam dologout(status)
95513247Ssam 	int status;
95613247Ssam {
95717580Ssam 	if (logged_in) {
95836304Skarels 		(void) seteuid((uid_t)0);
95935672Sbostic 		logwtmp(ttyline, "", "");
96013247Ssam 	}
96114436Ssam 	/* beware of flushing buffers after a SIGPIPE */
96214436Ssam 	_exit(status);
96313247Ssam }
96413247Ssam 
96526044Sminshall myoob()
96626044Sminshall {
96727750Sminshall 	char *cp;
96826044Sminshall 
96927750Sminshall 	/* only process if transfer occurring */
97036304Skarels 	if (!transflag)
97126044Sminshall 		return;
97227750Sminshall 	cp = tmpline;
97327750Sminshall 	if (getline(cp, 7, stdin) == NULL) {
97436304Skarels 		reply(221, "You could at least say goodbye.");
97527750Sminshall 		dologout(0);
97626044Sminshall 	}
97726044Sminshall 	upper(cp);
97826227Ssam 	if (strcmp(cp, "ABOR\r\n"))
97926044Sminshall 		return;
98026044Sminshall 	tmpline[0] = '\0';
98126044Sminshall 	reply(426,"Transfer aborted. Data connection closed.");
98226044Sminshall 	reply(226,"Abort successful");
98326044Sminshall 	longjmp(urgcatch, 1);
98426044Sminshall }
98526044Sminshall 
98627106Smckusick /*
987*36620Srick  * Note: a response of 425 is not mentioned as a possible response to
988*36620Srick  * 	the PASV command in RFC959. However, it has been blessed as
989*36620Srick  * 	a legitimate response by Jon Postel in a telephone conversation
990*36620Srick  *	with Rick Adams on 25 Jan 89.
99127106Smckusick  */
99226044Sminshall passive()
99326044Sminshall {
99426044Sminshall 	int len;
99526044Sminshall 	struct sockaddr_in tmp;
99626044Sminshall 	register char *p, *a;
99726044Sminshall 
99826044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
99926044Sminshall 	if (pdata < 0) {
1000*36620Srick 		perror_reply(425, "Can't open passive connection");
100126044Sminshall 		return;
100226044Sminshall 	}
100326044Sminshall 	tmp = ctrl_addr;
100426044Sminshall 	tmp.sin_port = 0;
100536304Skarels 	(void) seteuid((uid_t)0);
100626493Sminshall 	if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
100736304Skarels 		(void) seteuid((uid_t)pw->pw_uid);
1008*36620Srick 		goto pasv_error;
100926044Sminshall 	}
101036304Skarels 	(void) seteuid((uid_t)pw->pw_uid);
101126044Sminshall 	len = sizeof(tmp);
1012*36620Srick 	if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0)
1013*36620Srick 		goto pasv_error;
1014*36620Srick 	if (listen(pdata, 1) < 0)
1015*36620Srick 		goto pasv_error;
101626044Sminshall 	a = (char *) &tmp.sin_addr;
101726044Sminshall 	p = (char *) &tmp.sin_port;
101826044Sminshall 
101926044Sminshall #define UC(b) (((int) b) & 0xff)
102026044Sminshall 
102126044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
102226044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1023*36620Srick 	return;
1024*36620Srick 
1025*36620Srick pasv_error:
1026*36620Srick 	(void) close(pdata);
1027*36620Srick 	pdata = -1;
1028*36620Srick 	perror_reply(425, "Can't open passive connection");
1029*36620Srick 	return;
103026044Sminshall }
103126044Sminshall 
103236304Skarels /*
103336304Skarels  * Generate unique name for file with basename "local".
103436304Skarels  * The file named "local" is already known to exist.
103536304Skarels  * Generates failure reply on error.
103636304Skarels  */
103726044Sminshall char *
103826044Sminshall gunique(local)
103926044Sminshall 	char *local;
104026044Sminshall {
104126044Sminshall 	static char new[MAXPATHLEN];
104236304Skarels 	struct stat st;
104326044Sminshall 	char *cp = rindex(local, '/');
1044*36620Srick 	int count=0;
104526044Sminshall 
104636304Skarels 	if (cp)
104726044Sminshall 		*cp = '\0';
1048*36620Srick 	if (stat(cp ? local : ".", &st) < 0) {
104936304Skarels 		perror_reply(553, local);
105026044Sminshall 		return((char *) 0);
105126044Sminshall 	}
1052*36620Srick 	if (cp)
1053*36620Srick 		*cp = '/';
105426044Sminshall 	(void) strcpy(new, local);
105526044Sminshall 	cp = new + strlen(new);
105626044Sminshall 	*cp++ = '.';
105736304Skarels 	for (count = 1; count < 100; count++) {
105836304Skarels 		(void) sprintf(cp, "%d", count);
105936304Skarels 		if (stat(new, &st) < 0)
106036304Skarels 			return(new);
106126044Sminshall 	}
106236304Skarels 	reply(452, "Unique file name cannot be created.");
106336304Skarels 	return((char *) 0);
106426044Sminshall }
106536304Skarels 
106636304Skarels /*
106736304Skarels  * Format and send reply containing system error number.
106836304Skarels  */
106936304Skarels perror_reply(code, string)
107036304Skarels 	int code;
107136304Skarels 	char *string;
107236304Skarels {
107336304Skarels 	if (errno < sys_nerr)
107436304Skarels 		reply(code, "%s: %s.", string, sys_errlist[errno]);
107536304Skarels 	else
107636304Skarels 		reply(code, "%s: unknown error %d.", string, errno);
107736304Skarels }
1078*36620Srick 
1079*36620Srick static char *onefile[] = {
1080*36620Srick 	"",
1081*36620Srick 	0
1082*36620Srick };
1083*36620Srick 
1084*36620Srick send_file_list(whichfiles)
1085*36620Srick 	char *whichfiles;
1086*36620Srick {
1087*36620Srick 	struct stat st;
1088*36620Srick 	DIR *dirp = NULL;
1089*36620Srick 	struct direct *dir;
1090*36620Srick 	FILE *dout = NULL;
1091*36620Srick 	register char **dirlist, *dirname;
1092*36620Srick 	char *strpbrk();
1093*36620Srick 
1094*36620Srick 	if (strpbrk(whichfiles, "~{[*?") != NULL) {
1095*36620Srick 		extern char **glob(), *globerr;
1096*36620Srick 		globerr = NULL;
1097*36620Srick 		dirlist = glob(whichfiles);
1098*36620Srick 		if (globerr != NULL) {
1099*36620Srick 			reply(550, globerr);
1100*36620Srick 			return;
1101*36620Srick 		} else if (dirlist == NULL) {
1102*36620Srick 			errno = ENOENT;
1103*36620Srick 			perror_reply(550, whichfiles);
1104*36620Srick 			return;
1105*36620Srick 		}
1106*36620Srick 	} else {
1107*36620Srick 		onefile[0] = whichfiles;
1108*36620Srick 		dirlist = onefile;
1109*36620Srick 	}
1110*36620Srick 
1111*36620Srick 	while (dirname = *dirlist++) {
1112*36620Srick 		if (stat(dirname, &st) < 0) {
1113*36620Srick 			perror_reply(550, whichfiles);
1114*36620Srick 			if (dout != NULL) {
1115*36620Srick 				(void) fclose(dout);
1116*36620Srick 				data = -1;
1117*36620Srick 				pdata = -1;
1118*36620Srick 			}
1119*36620Srick 			return;
1120*36620Srick 		}
1121*36620Srick 
1122*36620Srick 		if ((st.st_mode&S_IFMT) == S_IFREG) {
1123*36620Srick 			if (dout == NULL) {
1124*36620Srick 				dout = dataconn(whichfiles, (off_t)-1, "w");
1125*36620Srick 				if (dout == NULL)
1126*36620Srick 					return;
1127*36620Srick 			}
1128*36620Srick 			fprintf(dout, "%s\n", dirname);
1129*36620Srick 			continue;
1130*36620Srick 		} else if ((st.st_mode&S_IFMT) != S_IFDIR)
1131*36620Srick 			continue;
1132*36620Srick 
1133*36620Srick 		if ((dirp = opendir(dirname)) == NULL)
1134*36620Srick 			continue;
1135*36620Srick 
1136*36620Srick 		while ((dir = readdir(dirp)) != NULL) {
1137*36620Srick 			char nbuf[BUFSIZ];
1138*36620Srick 
1139*36620Srick 			if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1140*36620Srick 				continue;
1141*36620Srick 			if (dir->d_name[0] == '.' && dir->d_name[1] == '.'
1142*36620Srick 			    && dir->d_namlen == 2)
1143*36620Srick 				continue;
1144*36620Srick 
1145*36620Srick 			sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1146*36620Srick 
1147*36620Srick 			/*
1148*36620Srick 			 * we have to do a stat to insure it's
1149*36620Srick 			 * not a directory
1150*36620Srick 			 */
1151*36620Srick 			if (stat(nbuf, &st) == 0 &&
1152*36620Srick 			    (st.st_mode&S_IFMT) == S_IFREG) {
1153*36620Srick 				if (dout == NULL) {
1154*36620Srick 					dout = dataconn(whichfiles, (off_t)-1,
1155*36620Srick 						"w");
1156*36620Srick 					if (dout == NULL)
1157*36620Srick 						return;
1158*36620Srick 				}
1159*36620Srick 				if (nbuf[0] == '.' && nbuf[1] == '/')
1160*36620Srick 					fprintf(dout, "%s\n", &nbuf[2]);
1161*36620Srick 				else
1162*36620Srick 					fprintf(dout, "%s\n", nbuf);
1163*36620Srick 			}
1164*36620Srick 		}
1165*36620Srick 		(void) closedir(dirp);
1166*36620Srick 	}
1167*36620Srick 
1168*36620Srick 	if (dout != NULL && ferror(dout) != 0)
1169*36620Srick 		perror_reply(550, whichfiles);
1170*36620Srick 	else
1171*36620Srick 		reply(226, "Transfer complete.");
1172*36620Srick 
1173*36620Srick 	if (dout != NULL)
1174*36620Srick 		(void) fclose(dout);
1175*36620Srick 	data = -1;
1176*36620Srick 	pdata = -1;
1177*36620Srick }
1178*36620Srick 
1179*36620Srick #ifdef SETPROCTITLE
1180*36620Srick /*
1181*36620Srick  * clobber argv so ps will show what we're doing.
1182*36620Srick  * (stolen from sendmail)
1183*36620Srick  * warning, since this is usually started from inetd.conf, it
1184*36620Srick  * often doesn't have much of an environment or arglist to overwrite.
1185*36620Srick  */
1186*36620Srick 
1187*36620Srick /*VARARGS1*/
1188*36620Srick setproctitle(fmt, a, b, c)
1189*36620Srick char *fmt;
1190*36620Srick {
1191*36620Srick 	register char *p, *bp, ch;
1192*36620Srick 	register int i;
1193*36620Srick 	char buf[BUFSIZ];
1194*36620Srick 
1195*36620Srick 	(void) sprintf(buf, fmt, a, b, c);
1196*36620Srick 
1197*36620Srick 	/* make ps print our process name */
1198*36620Srick 	p = Argv[0];
1199*36620Srick 	*p++ = '-';
1200*36620Srick 
1201*36620Srick 	i = strlen(buf);
1202*36620Srick 	if (i > LastArgv - p - 2) {
1203*36620Srick 		i = LastArgv - p - 2;
1204*36620Srick 		buf[i] = '\0';
1205*36620Srick 	}
1206*36620Srick 	bp = buf;
1207*36620Srick 	while (ch = *bp++)
1208*36620Srick 		if (ch != '\n' && ch != '\r')
1209*36620Srick 			*p++ = ch;
1210*36620Srick 	while (p < LastArgv)
1211*36620Srick 		*p++ = ' ';
1212*36620Srick }
1213*36620Srick #endif /* SETPROCTITLE */
1214